WIP: osmo-trx-ipc

Change-Id: Ice63d3499026293ade8aad675ff7a883bcdd5756
This commit is contained in:
Pau Espin Pedrol
2020-03-24 17:19:27 +01:00
committed by Oliver Smith
parent fdefbfac39
commit cf66e6a45e
9 changed files with 1831 additions and 0 deletions

View File

@@ -104,3 +104,10 @@ osmo_trx_lms_LDADD = \
$(LMS_LIBS)
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif
bin_PROGRAMS += osmo-trx-ipc
osmo_trx_ipc_SOURCES = osmo-trx.cpp
osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD)
oosmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)

View File

@@ -2,6 +2,10 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = common
#if DEVICE_IPC
SUBDIRS += ipc
#endif
if DEVICE_USRP1
SUBDIRS += usrp1
endif

View File

@@ -0,0 +1,741 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <map>
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "Utils.h"
#include "IPCDevice.h"
extern "C" {
#include "osmo_signal.h"
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define SAMPLE_BUF_SZ (1 << 20)
using namespace std;
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), started(false)
{
LOGC(DDEV, INFO) << "creating IPC device...";
//m_IPC_stream_rx.resize(chans);
//m_IPC_stream_tx.resize(chans);
rx_gains.resize(chans);
tx_gains.resize(chans);
rx_buffers.resize(chans);
/* Set up per-channel Rx timestamp based Ring buffers */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
}
IPCDevice::~IPCDevice()
{
//unsigned int i;
LOGC(DDEV, INFO) << "Closing IPC device";
/* disable all channels */
for (size_t i = 0; i < rx_buffers.size(); i++)
delete rx_buffers[i];
}
int IPCDevice::ipc_shm_connect(const char *shm_name)
{
int fd;
size_t shm_len;
int rc;
LOGP(DDEV, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
if ((fd = shm_open(shm_name, O_CREAT|O_RDWR, S_IRUSR|S_IWUSR)) < 0) {
LOGP(DDEV, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_shm_open;
}
// Get size of the allocated memory
struct stat shm_stat;
if (fstat(fd, &shm_stat) < 0) {
LOGP(DDEV, LOGL_ERROR, "fstat %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
shm_len = shm_stat.st_size;
LOGP(DDEV, LOGL_NOTICE, "mmaping shared memory fd %d (size=%zu)\n", fd,shm_len);
if ((shm = mmap(NULL, shm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
LOGP(DDEV, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DDEV, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
LOGP(DDEV, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *) shm, 80));
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
close(fd);
return 0;
err_mmap:
shm_unlink(shm_name);
close(fd);
err_shm_open:
return rc;
}
static int ipc_sock_send(struct ipc_sock_state* state, struct msgb *msg);
struct msgb *ipc_msgb_alloc(uint8_t msg_type)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000);
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->msg_type = msg_type;
return msg;
}
int ipc_tx_greeting_req(struct ipc_sock_state* state, uint8_t req_version)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
LOGC(DDEV, NOTICE) << "Tx Greeting Req (" << IPC_IF_MSG_GREETING_REQ << ")\n";
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_REQ);
if (!msg) {
LOGC(DDEV, INFO) << "ipc_msgb_alloc() returns NULL!";
return -ENOMEM;
}
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->u.greeting_req.req_version = req_version;
return ipc_sock_send(state, msg);
}
int ipc_tx_info_req(struct ipc_sock_state* state)
{
struct msgb *msg;
//struct ipc_sk_if *ipc_prim;
LOGC(DDEV, NOTICE) << "Tx INFO Req\n";
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_REQ);
if (!msg)
return -ENOMEM;
//ipc_prim = (struct ipc_sk_if *) msg->data;
return ipc_sock_send(state, msg);
}
int ipc_tx_open_req(struct ipc_sock_state* state, uint32_t num_chans)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
struct ipc_sk_if_open_req_chan *chan_info;
LOGC(DDEV, NOTICE) << "Tx Open Req\n";
msg = ipc_msgb_alloc(IPC_IF_MSG_OPEN_REQ);
if (!msg) {
return -ENOMEM;
}
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->u.open_req.num_chans = num_chans;
chan_info = ipc_prim->u.open_req.chan_info;
OSMO_STRLCPY_ARRAY(chan_info->rx_path, "RxAntenna1");
OSMO_STRLCPY_ARRAY(chan_info->tx_path, "TxAntenna2");
return ipc_sock_send(state, msg);
}
static void ipc_sock_timeout(void *_priv)
{
LOGC(DDEV, INFO) << "UNIX SOCKET TIMEOUT!";
exit(1);
}
int IPCDevice::ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf)
{
if (greeting_cnf->req_version == IPC_SOCK_API_VERSION) {
LOGC(DDEV, NOTICE) << "Rx Greeting CNF: correct sock API version" << greeting_cnf->req_version;
tmp_state = IPC_IF_MSG_GREETING_CNF;
} else {
LOGC(DDEV, ERROR) << "Wrong IPC SOCK API VERSION RECEIVED!" << greeting_cnf->req_version;
exit(1);
}
return 0;
}
int IPCDevice::ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf)
{
/* Here:
* verify info_cnf->max_num_chans >= requested chans
* verify supports setting reflock as asked by user looking in info_cnf->feature_mask
* cache locally min/max tx/rxGain values from info_cnf
* do whatever validations or print info_cnf->dev_desc
* cache rx/tx paths per channel, and make sure it matches the one the user wants to set
*/
unsigned int i;
LOGC(DDEV, NOTICE) << "Rx Info CNF:"
<< " max_num_chans=" << info_cnf->max_num_chans
<< " feature_mask=" << info_cnf->feature_mask
<< " min_rx_gain=" << info_cnf->min_rx_gain
<< " max_rx_gain=" << info_cnf->max_rx_gain;
for (i = 0; i < info_cnf->max_num_chans; i++) {
int j = 0;
while (strcmp(info_cnf->chan_info[i].rx_path[j],"")!=0) {
LOGC(DDEV, NOTICE) << "chan " << i << ": RxPath[" << j <<"]: " << info_cnf->chan_info[i].rx_path[j];
j++;
}
j = 0;
while (strcmp(info_cnf->chan_info[i].tx_path[j],"")!=0) {
LOGC(DDEV, NOTICE) << "chan " << i << ": TxPath[" << j <<"]: " << info_cnf->chan_info[i].tx_path[j];
j++;
}
}
tmp_state = IPC_IF_MSG_INFO_CNF;
return 0;
}
int IPCDevice::ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf)
{
unsigned int i;
LOGC(DDEV, NOTICE) << "Rx Open CNF:"
<< " return_code=" << (unsigned int)open_cnf->return_code
<< " shm_name=" << open_cnf->shm_name;
for (i = 0; i < chans; i++) {
LOGC(DDEV, NOTICE) << "chan " << i << ": sk_path=" << open_cnf->chan_info[i].chan_ipc_sk_path;
}
OSMO_STRLCPY_ARRAY(shm_name, open_cnf->shm_name);
if (ipc_shm_connect(shm_name) < 0)
return -1;
shm_dec = ipc_shm_decode_region(NULL, (ipc_shm_raw_region*)shm);
LOGC(DDEV, NOTICE) << "shm: num_chans=" << shm_dec->num_chans;
LOGC(DDEV, NOTICE) << "shm: chan0/dl: num_buffers=" << shm_dec->channels[0]->dl_stream->num_buffers;
LOGC(DDEV, NOTICE) << "shm: chan0/dl: buffer_size=" << shm_dec->channels[0]->dl_stream->buffer_size;
tmp_state = IPC_IF_MSG_OPEN_CNF;
return 0;
}
int IPCDevice::ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim)
{
int rc = 0;
switch (msg_type) {
case IPC_IF_MSG_GREETING_CNF:
rc = ipc_rx_greeting_cnf(&ipc_prim->u.greeting_cnf);
break;
case IPC_IF_MSG_INFO_CNF:
rc = ipc_rx_info_cnf(&ipc_prim->u.info_cnf);
break;
case IPC_IF_MSG_OPEN_CNF:
rc = ipc_rx_open_cnf(&ipc_prim->u.open_cnf);
break;
default:
LOGP(DDEV, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return rc;
}
static int ipc_sock_send(struct ipc_sock_state* state, struct msgb *msg)
{
struct osmo_fd *conn_bfd;
//struct ipc_sk_if *ipc_prim = (struct ipc_sk_if *) msg->data;
if (!state) {
LOGP(DMAIN, LOGL_INFO, "PCU socket not created, "
"dropping message\n");
msgb_free(msg);
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DMAIN, LOGL_NOTICE, "PCU socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
void IPCDevice::ipc_sock_close()
{
struct osmo_fd *bfd = &sk_state.conn_bfd;
LOGP(DDEV, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&sk_state.upqueue)) {
struct msgb *msg = msgb_dequeue(&sk_state.upqueue);
msgb_free(msg);
}
}
int IPCDevice::ipc_sock_read(struct osmo_fd *bfd)
{
struct ipc_sk_if *ipc_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_sock_rx");
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
msgb_free(msg);
return 0;
}
goto close;
}
if ((size_t)rc < sizeof(*ipc_prim)) {
LOGP(DDEV, LOGL_ERROR, "Received %d bytes on Unix Socket, but primitive size "
"is %zu, discarding\n", rc, sizeof(*ipc_prim));
msgb_free(msg);
return 0;
}
rc = ipc_rx(ipc_prim->msg_type, ipc_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
ipc_sock_close();
return -1;
}
int IPCDevice::ipc_sock_write(struct osmo_fd *bfd)
{
int rc;
while (!llist_empty(&sk_state.upqueue)) {
struct msgb *msg, *msg2;
struct ipc_sk_if *ipc_prim;
/* peek at the beginning of the queue */
msg = llist_entry(sk_state.upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DDEV, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", ipc_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&sk_state.upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
ipc_sock_close();
return -1;
}
static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
IPCDevice *device = (IPCDevice *)bfd->data;
int rc = 0;
if (flags & BSC_FD_READ)
rc = device->ipc_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = device->ipc_sock_write(bfd);
return rc;
}
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
{
//float_type sr_host, sr_rf;
//unsigned int i, n;
//int rc, dev_id;
int rc;
LOGC(DDEV, INFO) << "Opening IPC device..";
memset(&sk_state, 0x00, sizeof(sk_state));
INIT_LLIST_HEAD(&sk_state.upqueue);
rc = osmo_sock_unix_init_ofd(&sk_state.conn_bfd, SOCK_SEQPACKET, 0,
IPC_SOCK_PATH, OSMO_SOCK_F_CONNECT);
if (rc < 0) {
LOGC(DDEV, ERROR) << "Failed to connect to the BTS (" << IPC_SOCK_PATH << "). " <<
"Retrying...\n";
osmo_timer_setup(&sk_state.timer, ipc_sock_timeout, NULL);
osmo_timer_schedule(&sk_state.timer, 5, 0);
return -1;
}
sk_state.conn_bfd.cb = ipc_sock_cb;
sk_state.conn_bfd.data = this;
ipc_tx_greeting_req(&sk_state, IPC_SOCK_API_VERSION);
/* Wait until confirmation is recieved */
while(tmp_state != IPC_IF_MSG_GREETING_CNF)
osmo_select_main(0);
ipc_tx_info_req(&sk_state);
/* Wait until confirmation is recieved */
while(tmp_state != IPC_IF_MSG_INFO_CNF)
osmo_select_main(0);
ipc_tx_open_req(&sk_state, chans);
/* Wait until confirmation is recieved */
while(tmp_state != IPC_IF_MSG_OPEN_CNF)
osmo_select_main(0);
LOGC(DDEV, NOTICE) << "Device driver opened successfuly!";
/* configure antennas */
if (!set_antennas()) {
LOGC(DDEV, FATAL) << "IPC antenna setting failed";
goto out_close;
}
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
out_close:
LOGC(DDEV, FATAL) << "Error in IPC open, closing";
return -1;
}
bool IPCDevice::start()
{
LOGC(DDEV, INFO) << "starting IPC...";
unsigned int i;
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return false;
}
/* configure the channels/streams */
for (i=0; i<chans; i++) {
/* Set gains for calibration/filter setup */
/* TX gain to maximum */
setTxGain(maxTxGain(), i);
/* RX gain to midpoint */
setRxGain((minRxGain() + maxRxGain()) / 2, i);
/* set up Rx and Tx filters */
if (!do_filters(i))
return false;
/* Perform Rx and Tx calibration */
if (!do_calib(i))
return false;
/* configure Streams */
}
/* now start the streams in a second loop, as we can no longer call
* IPC_SetupStream() after IPC_StartStream() of the first stream */
//for (i = 0; i < chans; i++) {
//}
flush_recv(10);
started = true;
return true;
}
bool IPCDevice::stop()
{
//unsigned int i;
if (!started)
return true;
/*
for (i=0; i<chans; i++) {
IPC_StopStream(&m_IPC_stream_tx[i]);
IPC_StopStream(&m_IPC_stream_rx[i]);
}
for (i=0; i<chans; i++) {
IPC_DestroyStream(m_IPC_dev, &m_IPC_stream_tx[i]);
IPC_DestroyStream(m_IPC_dev, &m_IPC_stream_rx[i]);
}*/
started = false;
return true;
}
/* do rx/tx calibration - depends on gain, freq and bw */
bool IPCDevice::do_calib(size_t chan)
{
LOGCHAN(chan, DDEV, INFO) << "Calibrating";
return true;
}
/* do rx/tx filter config - depends on bw only? */
bool IPCDevice::do_filters(size_t chan)
{
LOGCHAN(chan, DDEV, INFO) << "Setting filters";
return true;
}
double IPCDevice::maxTxGain()
{
//return dev_param_map.at(m_dev_type).max_tx_gain;
return 70.0;
}
double IPCDevice::minTxGain()
{
return 0.0;
}
double IPCDevice::maxRxGain()
{
return 73.0;
}
double IPCDevice::minRxGain()
{
return 0.0;
}
double IPCDevice::setTxGain(double dB, size_t chan)
{
if (dB > maxTxGain())
dB = maxTxGain();
if (dB < minTxGain())
dB = minTxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX gain to " << dB << " dB";
//if (IPC_SetGaindB(m_IPC_dev, IPC_CH_TX, chan, dB) < 0)
// LOGCHAN(chan, DDEV, ERR) << "Error setting TX gain to " << dB << " dB";
//else
tx_gains[chan] = dB;
return tx_gains[chan];
}
double IPCDevice::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
//if (IPC_SetGaindB(m_IPC_dev, IPC_CH_RX, chan, dB) < 0)
// LOGCHAN(chan, DDEV, ERR) << "Error setting RX gain to " << dB << " dB";
//else
rx_gains[chan] = dB;
return rx_gains[chan];
}
void IPCDevice::log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os)
{
int num_names = 0;
int i;
for (i = 0; i < num_names; i++) {
if (i)
os << ", ";
os << "''";
}
}
bool IPCDevice::flush_recv(size_t num_pkts)
{/*
#define CHUNK 625
int len = CHUNK * tx_sps;
short *buffer = (short*) alloca(sizeof(short) * len * 2);
int rc;
IPC_stream_meta_t rx_metadata = {};
rx_metadata.flushPartialPacket = false;
rx_metadata.waitForTimestamp = false;
ts_initial = 0;
while (!ts_initial || (num_pkts-- > 0)) {
rc = IPC_RecvStream(&m_IPC_stream_rx[0], &buffer[0], len, &rx_metadata, 100);
LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp;
if (rc != len) {
LOGC(DDEV, ERROR) << "Flush: Device receive timed out";
return false;
}
ts_initial = rx_metadata.timestamp + len;
}
*/
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
return true;
}
bool IPCDevice::setRxAntenna(const std::string & ant, size_t chan)
{
return true;
}
std::string IPCDevice::getRxAntenna(size_t chan)
{
return "";
}
bool IPCDevice::setTxAntenna(const std::string & ant, size_t chan)
{
return true;
}
std::string IPCDevice::getTxAntenna(size_t chan)
{
return "";
}
bool IPCDevice::requiresRadioAlign()
{
return false;
}
GSM::Time IPCDevice::minLatency() {
/* UNUSED */
return GSM::Time(0,0);
}
// NOTE: Assumes sequential reads
int IPCDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun,
TIMESTAMP timestamp, bool * underrun)
{
return len;
}
int IPCDevice::writeSamples(std::vector < short *>&bufs, int len,
bool * underrun, unsigned long long timestamp)
{
return len;
}
bool IPCDevice::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool IPCDevice::setTxFreq(double wFreq, size_t chan)
{
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
return true;
}
bool IPCDevice::setRxFreq(double wFreq, size_t chan)
{
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector < std::string > &tx_paths,
const std::vector < std::string > &rx_paths)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -0,0 +1,218 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#ifndef _IPC_DEVICE_H_
#define _IPC_DEVICE_H_
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
extern "C" {
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "shm.h"
}
#include "radioDevice.h"
#include "smpl_buf.h"
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
#include <lime/LimeSuite.h>
struct ipc_sock_state {
struct osmo_fd conn_bfd; /* fd for connection to the BTS */
struct osmo_timer_list timer; /* socket connect retry timer */
struct llist_head upqueue; /* queue for sending messages */
};
/** A class to handle a LimeSuite supported device */
class IPCDevice:public RadioDevice {
private:
struct ipc_sock_state sk_state;
uint8_t tmp_state;
char shm_name[SHM_NAME_MAX];
int ipc_shm_connect(const char *shm_name);
void *shm;
struct ipc_shm_region *shm_dec;
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate; ///< the actual USRP sampling rate
bool started; ///< flag indicates LMS has started
bool skipRx; ///< set if LMS is transmit-only.
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
bool do_calib(size_t chan);
bool do_filters(size_t chan);
void log_ant_list(bool dir_tx, size_t chan, std::ostringstream& os);
int get_ant_idx(const std::string & name, bool dir_tx, size_t chan);
bool flush_recv(size_t num_pkts);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
public:
void ipc_sock_close();
int ipc_sock_read(struct osmo_fd *bfd);
int ipc_sock_write(struct osmo_fd *bfd);
int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim);
int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf);
int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf);
int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf);
/** Object constructor */
IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~IPCDevice();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** Start the LMS */
bool start();
/** Stop the LMS */
bool stop();
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@return The number of samples actually read
*/
int readSamples(std::vector < short *>&buf, int len, bool * overrun,
TIMESTAMP timestamp = 0xffffffff, bool * underrun =
NULL);
/**
Write samples to the LMS.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@return The number of samples actually written
*/
int writeSamples(std::vector < short *>&bufs, int len, bool * underrun,
TIMESTAMP timestamp = 0xffffffff);
/** Update the alignment between the read and write timestamps */
bool updateAlignment(TIMESTAMP timestamp);
/** Set the transmitter frequency */
bool setTxFreq(double wFreq, size_t chan = 0);
/** Set the receiver frequency */
bool setRxFreq(double wFreq, size_t chan = 0);
/** Returns the starting write Timestamp*/
TIMESTAMP initialWriteTimestamp(void) {
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP initialReadTimestamp(void) {
return ts_initial;
}
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue() {
#define LIMESDR_TX_AMPL 0.3
return(double) SHRT_MAX * LIMESDR_TX_AMPL;
}
/** returns the full-scale receive amplitude **/
double fullScaleOutputValue() {
return (double) SHRT_MAX;
}
/** sets the receive chan gain, returns the gain setting **/
double setRxGain(double dB, size_t chan = 0);
/** get the current receive gain */
double getRxGain(size_t chan = 0) {
return rx_gains[chan];
}
/** return maximum Rx Gain **/
double maxRxGain(void);
/** return minimum Rx Gain **/
double minRxGain(void);
/** sets the transmit chan gain, returns the gain setting **/
double setTxGain(double dB, size_t chan = 0);
/** get transmit gain */
double getTxGain(size_t chan = 0) {
return tx_gains[chan];
}
/** return maximum Tx Gain **/
double maxTxGain(void);
/** return minimum Rx Gain **/
double minTxGain(void);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setRxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getRxAntenna(size_t chan = 0);
/** sets the RX path to use, returns true if successful and false otherwise */
bool setTxAntenna(const std::string & ant, size_t chan = 0);
/* return the used RX path */
std::string getTxAntenna(size_t chan = 0);
/** return whether user drives synchronization of Tx/Rx of USRP */
bool requiresRadioAlign();
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency();
/** Return internal status values */
inline double getTxFreq(size_t chan = 0) {
return 0;
}
inline double getRxFreq(size_t chan = 0) {
return 0;
}
inline double getSampleRate() {
return actualSampleRate;
}
};
#endif // _IPC_DEVICE_H_

View File

@@ -0,0 +1,25 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
noinst_HEADERS = IPCDevice.h shm.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp shm.c
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
bin_PROGRAMS = ipc-driver-test
ipc_driver_test_SOURCES = ipc-driver-test.c
ipc_driver_test_LDADD = \
shm.lo \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS)
ipc_driver_test_CFLAGS = $(AM_CFLAGS)
ipc_driver_test_LDFLAGS = $(AM_LDFLAGS)

View File

@@ -0,0 +1,507 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "shm.h"
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2"
struct ipc_sock_state {
struct osmo_fd listen_bfd; /* fd for listen socket */
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct llist_head upqueue; /* queue for sending messages */
};
static void *tall_ctx;
static struct ipc_sock_state *global_ipc_sock_state;
void *shm;
/* Debug Areas of the code */
enum {
DMAIN,
};
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.description = "Main generic category",
.color = NULL,
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
static int ipc_shm_setup(const char *shm_name, size_t shm_len) {
int fd;
int rc;
LOGP(DMAIN, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
if ((fd = shm_open(shm_name, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR)) < 0) {
LOGP(DMAIN, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_shm_open;
}
LOGP(DMAIN, LOGL_NOTICE, "Truncating %d to size %zu\n", fd, shm_len);
if (ftruncate(fd, shm_len) < 0) {
LOGP(DMAIN, LOGL_ERROR, "ftruncate %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmaping shared memory fd %d\n", fd);
if ((shm = mmap(NULL, shm_len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
LOGP(DMAIN, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
close(fd);
return 0;
err_mmap:
shm_unlink(shm_name);
close(fd);
err_shm_open:
return rc;
}
static int ipc_sock_send(struct msgb *msg);
struct msgb *ipc_msgb_alloc(uint8_t msg_type)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000);
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->msg_type = msg_type;
return msg;
}
static int ipc_tx_greeting_cnf(uint8_t req_version)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->u.greeting_cnf.req_version = req_version;
return ipc_sock_send(msg);
}
static int ipc_tx_info_cnf()
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
struct ipc_sk_if_info_chan *chan_info;
int i;
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->u.info_cnf.feature_mask = FEATURE_MASK_CLOCKREF_INTERNAL |
FEATURE_MASK_CLOCKREF_EXTERNAL;
ipc_prim->u.info_cnf.min_rx_gain = 0.0;
ipc_prim->u.info_cnf.max_rx_gain = 70.0;
ipc_prim->u.info_cnf.min_tx_gain = 0.0;
ipc_prim->u.info_cnf.max_tx_gain = 63.0;
ipc_prim->u.info_cnf.max_num_chans = 2;
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, "Hello To my Virtual device!");
chan_info = ipc_prim->u.info_cnf.chan_info;
for (i = 0; i < ipc_prim->u.info_cnf.max_num_chans; i++) {
OSMO_STRLCPY_ARRAY(chan_info->tx_path[0], "TxAntenna1");
OSMO_STRLCPY_ARRAY(chan_info->tx_path[1], "TxAntenna2");
OSMO_STRLCPY_ARRAY(chan_info->tx_path[2], "TxAntenna3");
OSMO_STRLCPY_ARRAY(chan_info->rx_path[0], "RxAntenna1");
OSMO_STRLCPY_ARRAY(chan_info->rx_path[1], "RxAntenna2");
chan_info++;
}
return ipc_sock_send(msg);
}
static int ipc_tx_open_cnf(int rc, uint32_t num_chans)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
struct ipc_sk_if_open_cnf_chan *chan_info;
int i;
msg = ipc_msgb_alloc(IPC_IF_MSG_OPEN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *) msg->data;
ipc_prim->u.open_cnf.return_code = rc;
OSMO_STRLCPY_ARRAY(ipc_prim->u.open_cnf.shm_name, DEFAULT_SHM_NAME);
chan_info = ipc_prim->u.open_cnf.chan_info;
for (i = 0; i < num_chans; i++) {
snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path), "%s_%d", IPC_SOCK_PATH, i+1);
chan_info++;
}
return ipc_sock_send(msg);
}
static int ipc_rx_greeting_req(const struct ipc_sk_if_greeting *greeting_req)
{
if (greeting_req->req_version == IPC_SOCK_API_VERSION)
ipc_tx_greeting_cnf(IPC_SOCK_API_VERSION);
else
ipc_tx_greeting_cnf(0);
return 0;
}
static int ipc_rx_info_req(const struct ipc_sk_if_info_req *info_req)
{
ipc_tx_info_cnf();
return 0;
}
static int ipc_rx_open_req(const struct ipc_sk_if_open_req *open_req)
{
/* calculate size needed */
unsigned int len;
len = ipc_shm_encode_region(NULL, open_req->num_chans, 4, 4096);
/* Here we verify num_chans, rx_path, tx_path, clockref, etc. */
int rc = ipc_shm_setup(DEFAULT_SHM_NAME, len);
len = ipc_shm_encode_region((struct ipc_shm_raw_region *)shm, open_req->num_chans, 4, 4096);
LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *) shm, 80));
ipc_tx_open_cnf(-rc, open_req->num_chans);
return 0;
}
static int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim)
{
int rc = 0;
switch (msg_type) {
case IPC_IF_MSG_GREETING_REQ:
rc = ipc_rx_greeting_req(&ipc_prim->u.greeting_req);
break;
case IPC_IF_MSG_INFO_REQ:
rc = ipc_rx_info_req(&ipc_prim->u.info_req);
break;
case IPC_IF_MSG_OPEN_REQ:
rc = ipc_rx_open_req(&ipc_prim->u.open_req);
break;
default:
LOGP(DMAIN, LOGL_ERROR, "Received unknown PCU msg type %d\n",
msg_type);
rc = -EINVAL;
}
return rc;
}
static int ipc_sock_send(struct msgb *msg)
{
struct ipc_sock_state *state = global_ipc_sock_state;
struct osmo_fd *conn_bfd;
//struct ipc_sk_if *ipc_prim = (struct ipc_sk_if *) msg->data;
if (!state) {
LOGP(DMAIN, LOGL_INFO, "PCU socket not created, "
"dropping message\n");
msgb_free(msg);
return -EINVAL;
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DMAIN, LOGL_NOTICE, "PCU socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
static void ipc_sock_close(struct ipc_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
LOGP(DMAIN, LOGL_NOTICE, "PCU socket has LOST connection\n");
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
state->listen_bfd.when |= BSC_FD_READ;
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
struct msgb *msg = msgb_dequeue(&state->upqueue);
msgb_free(msg);
}
}
static int ipc_sock_read(struct osmo_fd *bfd)
{
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
struct ipc_sk_if *ipc_prim;
struct msgb *msg;
int rc;
msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_sock_rx");
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *) msg->tail;
rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
msgb_free(msg);
return 0;
}
goto close;
}
if (rc < sizeof(*ipc_prim)) {
LOGP(DMAIN, LOGL_ERROR, "Received %d bytes on Unix Socket, but primitive size "
"is %zu, discarding\n", rc, sizeof(*ipc_prim));
msgb_free(msg);
return 0;
}
rc = ipc_rx(ipc_prim->msg_type, ipc_prim);
/* as we always synchronously process the message in pcu_rx() and
* its callbacks, we can free the message here. */
msgb_free(msg);
return rc;
close:
msgb_free(msg);
ipc_sock_close(state);
return -1;
}
static int ipc_sock_write(struct osmo_fd *bfd)
{
struct ipc_sock_state *state = bfd->data;
int rc;
while (!llist_empty(&state->upqueue)) {
struct msgb *msg, *msg2;
struct ipc_sk_if *ipc_prim;
/* peek at the beginning of the queue */
msg = llist_entry(state->upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_if *)msg->data;
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DMAIN, LOGL_ERROR, "message type (%d) with ZERO "
"bytes!\n", ipc_prim->msg_type);
goto dontsend;
}
/* try to send it over the socket */
rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
if (rc == 0)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
}
return 0;
close:
ipc_sock_close(state);
return -1;
}
static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & BSC_FD_READ)
rc = ipc_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & BSC_FD_WRITE)
rc = ipc_sock_write(bfd);
return rc;
}
/* accept connection coming from PCU */
static int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
{
struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data;
struct osmo_fd *conn_bfd = &state->conn_bfd;
struct sockaddr_un un_addr;
socklen_t len;
int rc;
len = sizeof(un_addr);
rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len);
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DMAIN, LOGL_NOTICE, "osmo-trx connects but we already have "
"another active connection ?!?\n");
/* We already have one PCU connected, this is all we support */
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = ipc_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DMAIN, LOGL_ERROR, "Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
conn_bfd->fd = -1;
return -1;
}
LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
/* send current info */
//pcu_tx_info_ind();
return 0;
}
int ipc_sock_init(const char *path)
{
struct ipc_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct ipc_sock_state);
if (!state)
return -ENOMEM;
global_ipc_sock_state = state;
INIT_LLIST_HEAD(&state->upqueue);
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path,
OSMO_SOCK_F_BIND);
if (bfd->fd < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not create %s unix socket: %s\n",
path, strerror(errno));
talloc_free(state);
return -1;
}
bfd->when = BSC_FD_READ;
bfd->cb = ipc_sock_accept;
bfd->data = state;
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not register listen fd: %d\n",
rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
//osmo_signal_register_handler(SS_GLOBAL, pcu_if_signal_cb, NULL);
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path);
return 0;
}
int main(int argc, char** argv) {
tall_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_info);
log_enable_multithread();
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]);
ipc_sock_init(IPC_SOCK_PATH);
while (true)
osmo_select_main(0);
//ipc_sock_close()
return 0;
}

View File

@@ -0,0 +1,121 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <osmocom/core/talloc.h>
#include "shm.h"
/* Convert offsets to pointers */
struct ipc_shm_stream *ipc_shm_decode_stream(void *tall_ctx, struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_stream *stream_raw)
{
int i;
struct ipc_shm_stream *stream;
stream = talloc_zero(tall_ctx, struct ipc_shm_stream);
stream = talloc_zero_size(tall_ctx, sizeof(struct ipc_shm_stream) +
sizeof(struct ipc_shm_raw_smpl_buf *) * stream_raw->num_buffers);
if (!stream)
return NULL;
stream->num_buffers = stream_raw->num_buffers;
stream->buffer_size = stream_raw->buffer_size;
stream->raw = stream_raw;
for (i = 0; i < stream->num_buffers; i++) {
fprintf(stderr, "decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]);
stream->buffers[i] = (struct ipc_shm_raw_smpl_buf *)(((uint8_t*)root_raw) + stream_raw->buffer_offset[i]);
}
return stream;
}
struct ipc_shm_channel *ipc_shm_decode_channel(void *tall_ctx, struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_channel *chan_raw)
{
struct ipc_shm_channel *chan;
chan = talloc_zero(tall_ctx, struct ipc_shm_channel);
if (!chan)
return NULL;
fprintf(stderr, "decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset);
chan->dl_stream = ipc_shm_decode_stream(chan, root_raw, (struct ipc_shm_raw_stream *)(((uint8_t*)root_raw) + chan_raw->dl_buf_offset));
chan->ul_stream = ipc_shm_decode_stream(chan, root_raw, (struct ipc_shm_raw_stream *)(((uint8_t*)root_raw) + chan_raw->ul_buf_offset));
return chan;
}
struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_region *root_raw)
{
int i;
struct ipc_shm_region *root;
root = talloc_zero_size(tall_ctx, sizeof(struct ipc_shm_region) +
sizeof(struct ipc_shm_channel*) * root_raw->num_chans);
if (!root)
return NULL;
root->num_chans = root_raw->num_chans;
for (i = 0; i < root->num_chans; i++) {
fprintf(stderr, "decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]);
root->channels[i] = ipc_shm_decode_channel(root, root_raw, (struct ipc_shm_raw_channel *)(((uint8_t*)root_raw) + root_raw->chan_offset[i]));
}
return root;
}
unsigned int ipc_shm_encode_smpl_buf(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_smpl_buf *smpl_buf_raw, uint32_t buffer_size)
{
uint8_t* start = (uint8_t*)smpl_buf_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_smpl_buf);
fprintf(stderr, "encode: smpl_buf at offset %lu\n", (start - (uint8_t*)root_raw));
offset += buffer_size * sizeof(uint16_t); /* samples */
return offset;
}
unsigned int ipc_shm_encode_stream(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_stream *stream_raw, uint32_t num_buffers, uint32_t buffer_size)
{
int i;
uint8_t* start = (uint8_t*)stream_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_stream) + sizeof(uint32_t)*num_buffers;
fprintf(stderr, "encode: stream at offset %lu\n", (start - (uint8_t*)root_raw));
if (root_raw) {
stream_raw->num_buffers = num_buffers;
stream_raw->buffer_size = buffer_size;
stream_raw->read_next = 0;
stream_raw->write_next = 0;
}
for (i = 0; i < num_buffers; i++) {
if (root_raw)
stream_raw->buffer_offset[i] = (start + offset - (uint8_t*)root_raw);
offset += ipc_shm_encode_smpl_buf(root_raw, (struct ipc_shm_raw_smpl_buf *)(start + offset), buffer_size);
}
return offset;
}
unsigned int ipc_shm_encode_channel(struct ipc_shm_raw_region *root_raw, struct ipc_shm_raw_channel *chan_raw, uint32_t num_buffers, uint32_t buffer_size)
{
uint8_t* start = (uint8_t*)chan_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_channel);
fprintf(stderr, "encode: channel at offset %lu\n", (start - (uint8_t*)root_raw));
if (root_raw)
chan_raw->dl_buf_offset = (start + offset - (uint8_t*)root_raw);
offset += ipc_shm_encode_stream(root_raw, (struct ipc_shm_raw_stream *)(start + offset), num_buffers, buffer_size);
if (root_raw)
chan_raw->ul_buf_offset = (start + offset - (uint8_t*)root_raw);
offset += ipc_shm_encode_stream(root_raw, (struct ipc_shm_raw_stream *)(start + offset), num_buffers, buffer_size);
return offset;
}
/* if root_raw is NULL, then do a dry run, aka only calculate final offset */
unsigned int ipc_shm_encode_region(struct ipc_shm_raw_region *root_raw, uint32_t num_chans, uint32_t num_buffers, uint32_t buffer_size)
{
int i;
uint8_t* start = (uint8_t*)root_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_region) + sizeof(uint32_t)*num_chans;
if (root_raw)
root_raw->num_chans = num_chans;
for (i = 0; i < num_chans; i++) {
if (root_raw) {
root_raw->chan_offset[i] = (start + offset - (uint8_t*)root_raw);
fprintf(stderr, "encode: channel %d chan_offset[i]=%u\n", i, root_raw->chan_offset[i]);
offset += ipc_shm_encode_channel(root_raw, (struct ipc_shm_raw_channel *)(start + offset), num_buffers, buffer_size);
}
}
//TODO: pass maximum size and verify we didn't go through
return offset;
}

View File

@@ -0,0 +1,207 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* This software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
/* RAW structures */
struct ipc_shm_raw_smpl_buf {
uint32_t timestamp;
uint32_t data_len; /* In samples */
uint16_t samples[0];
};
struct ipc_shm_raw_stream {
uint32_t num_buffers;
uint32_t buffer_size; /* In samples */
uint32_t read_next;
uint32_t write_next;
uint32_t buffer_offset[0];
//struct ipc_shm_smpl_buf buffers[0];
};
struct ipc_shm_raw_channel {
uint32_t dl_buf_offset;
uint32_t ul_buf_offset;
};
struct ipc_shm_raw_region {
uint32_t num_chans;
uint32_t chan_offset[0];
};
/* non-raw, Pointer converted structures */
struct ipc_shm_stream {
uint32_t num_buffers;
uint32_t buffer_size;
struct ipc_shm_raw_stream *raw;
struct ipc_shm_raw_smpl_buf *buffers[0];
};
struct ipc_shm_channel {
struct ipc_shm_stream *dl_stream;
struct ipc_shm_stream *ul_stream;
};
/* Pointer converted structures */
struct ipc_shm_region {
uint32_t num_chans;
struct ipc_shm_channel *channels[0];
};
unsigned int ipc_shm_encode_region(struct ipc_shm_raw_region *root_raw, uint32_t num_chans, uint32_t num_buffers, uint32_t buffer_size);
struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_region *root_raw);
/****************************************/
/* UNIX SOCKET API */
/****************************************/
//////////////////
// Master socket
//////////////////
#define IPC_SOCK_PATH "/tmp/ipc_sock"
#define IPC_SOCK_API_VERSION 1
/* msg_type */
#define IPC_IF_MSG_GREETING_REQ 0x00
#define IPC_IF_MSG_GREETING_CNF 0x01
#define IPC_IF_MSG_INFO_REQ 0x02
#define IPC_IF_MSG_INFO_CNF 0x03
#define IPC_IF_MSG_OPEN_REQ 0x04
#define IPC_IF_MSG_OPEN_CNF 0x05
#define MAX_NUM_CHANS 30
#define RF_PATH_NAME_SIZE 25
#define MAX_NUM_RF_PATHS 10
#define SHM_NAME_MAX NAME_MAX /* 255 */
#define FEATURE_MASK_CLOCKREF_INTERNAL (0x1 << 0)
#define FEATURE_MASK_CLOCKREF_EXTERNAL (0x1 << 1)
struct ipc_sk_if_info_chan {
char tx_path[MAX_NUM_RF_PATHS][RF_PATH_NAME_SIZE];
char rx_path[MAX_NUM_RF_PATHS][RF_PATH_NAME_SIZE];
} __attribute__ ((packed));
struct ipc_sk_if_open_req_chan {
char tx_path[RF_PATH_NAME_SIZE];
char rx_path[RF_PATH_NAME_SIZE];
} __attribute__ ((packed));
struct ipc_sk_if_open_cnf_chan {
char chan_ipc_sk_path[108];
} __attribute__ ((packed));
struct ipc_sk_if_greeting {
uint8_t req_version;
} __attribute__ ((packed));
struct ipc_sk_if_info_req {
uint8_t spare;
} __attribute__ ((packed));
struct ipc_sk_if_info_cnf {
uint32_t feature_mask;
double min_rx_gain;
double max_rx_gain;
double min_tx_gain;
double max_tx_gain;
uint32_t max_num_chans;
char dev_desc[200];
struct ipc_sk_if_info_chan chan_info[0];
} __attribute__ ((packed));
struct ipc_sk_if_open_req {
uint32_t num_chans;
uint32_t clockref; /* One of FEATUER_MASK_CLOCKREF_* */
uint32_t rx_sps;
uint32_t tx_sps;
uint32_t bandwidth;
struct ipc_sk_if_open_req_chan chan_info[0];
} __attribute__ ((packed));
struct ipc_sk_if_open_cnf {
uint8_t return_code;
char shm_name[SHM_NAME_MAX];
struct ipc_sk_if_open_cnf_chan chan_info[0];
} __attribute__ ((packed));
struct ipc_sk_if {
uint8_t msg_type; /* message type */
uint8_t spare[2];
union {
struct ipc_sk_if_greeting greeting_req;
struct ipc_sk_if_greeting greeting_cnf;
struct ipc_sk_if_info_req info_req;
struct ipc_sk_if_info_cnf info_cnf;
struct ipc_sk_if_open_req open_req;
struct ipc_sk_if_open_cnf open_cnf;
} u;
} __attribute__ ((packed));
//////////////////
// Channel socket
//////////////////
#define IPC_IF_MSG_START_REQ 0x00
#define IPC_IF_MSG_START_CNF 0x01
#define IPC_IF_MSG_STOP_REQ 0x02
#define IPC_IF_MSG_STOP_CNF 0x03
#define IPC_IF_MSG_SETGAIN_REQ 0x04
#define IPC_IF_MSG_SETGAIN_CNF 0x05
#define IPC_IF_MSG_SETFREQ_REQ 0x04
#define IPC_IF_MSG_SETFREQ_CNF 0x05
struct ipc_sk_chan_if_op_void {
} __attribute__ ((packed));
struct ipc_sk_chan_if_op_rc {
uint8_t return_code;
} __attribute__ ((packed));
struct ipc_sk_chan_if_gain {
double gain;
uint8_t is_tx;
} __attribute__ ((packed));
struct ipc_sk_chan_if_freq_req {
double freq;
uint8_t is_tx;
} __attribute__ ((packed));
struct ipc_sk_chan_if_freq_cnf {
uint8_t return_code;
} __attribute__ ((packed));
struct ipc_sk_chan_if {
uint8_t msg_type; /* message type */
uint8_t spare[2];
union {
struct ipc_sk_chan_if_op_void start_req;
struct ipc_sk_chan_if_op_rc start_cnf;
struct ipc_sk_chan_if_op_void stop_req;
struct ipc_sk_chan_if_op_rc stop_cnf;
struct ipc_sk_chan_if_gain set_gain_req;
struct ipc_sk_chan_if_gain set_gain_cnf;
struct ipc_sk_chan_if_freq_req set_freq_req;
struct ipc_sk_chan_if_freq_cnf set_freq_cnf;
} u;
} __attribute__ ((packed));

View File

@@ -325,6 +325,7 @@ AC_CONFIG_FILES([\
Transceiver52M/device/uhd/Makefile \
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \