mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-02 21:23:16 +00:00
WIP: osmo-trx-ipc
Change-Id: Ice63d3499026293ade8aad675ff7a883bcdd5756
This commit is contained in:
committed by
Oliver Smith
parent
fdefbfac39
commit
cf66e6a45e
@@ -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)
|
||||
|
||||
@@ -2,6 +2,10 @@ include $(top_srcdir)/Makefile.common
|
||||
|
||||
SUBDIRS = common
|
||||
|
||||
#if DEVICE_IPC
|
||||
SUBDIRS += ipc
|
||||
#endif
|
||||
|
||||
if DEVICE_USRP1
|
||||
SUBDIRS += usrp1
|
||||
endif
|
||||
|
||||
741
Transceiver52M/device/ipc/IPCDevice.cpp
Normal file
741
Transceiver52M/device/ipc/IPCDevice.cpp
Normal 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);
|
||||
}
|
||||
218
Transceiver52M/device/ipc/IPCDevice.h
Normal file
218
Transceiver52M/device/ipc/IPCDevice.h
Normal 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_
|
||||
25
Transceiver52M/device/ipc/Makefile.am
Normal file
25
Transceiver52M/device/ipc/Makefile.am
Normal 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)
|
||||
507
Transceiver52M/device/ipc/ipc-driver-test.c
Normal file
507
Transceiver52M/device/ipc/ipc-driver-test.c
Normal 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;
|
||||
}
|
||||
121
Transceiver52M/device/ipc/shm.c
Normal file
121
Transceiver52M/device/ipc/shm.c
Normal 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;
|
||||
}
|
||||
207
Transceiver52M/device/ipc/shm.h
Normal file
207
Transceiver52M/device/ipc/shm.h
Normal 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));
|
||||
@@ -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 \
|
||||
|
||||
Reference in New Issue
Block a user