diff options
author | Eric <ewild@sysmocom.de> | 2020-07-16 18:09:39 +0200 |
---|---|---|
committer | Eric <ewild@sysmocom.de> | 2020-08-26 17:35:18 +0200 |
commit | 39ae5730cda3493bd51c1ee1a75a13d138fc1e4f (patch) | |
tree | 27abd55ed0ecdd9782540e0a7228999ee9d5d7b1 | |
parent | 4e131b2dd4c3db5849030b3c1fec1e97ab2066fb (diff) |
v1
Change-Id: I057e272623b41421f442206f550bcc8c60f4a747
22 files changed, 2305 insertions, 897 deletions
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 4e01e9c..ec65bfd 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -109,5 +109,6 @@ 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) + $(COMMON_LDADD) \ + $(UHD_LIBS) +osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) diff --git a/Transceiver52M/device/ipc/IPCDevice.cpp b/Transceiver52M/device/ipc/IPCDevice.cpp index 1c04558..a96f796 100644 --- a/Transceiver52M/device/ipc/IPCDevice.cpp +++ b/Transceiver52M/device/ipc/IPCDevice.cpp @@ -22,8 +22,8 @@ #include <string.h> #include <stdlib.h> #include <sys/mman.h> -#include <sys/stat.h> /* For mode constants */ -#include <fcntl.h> /* For O_* constants */ +#include <sys/stat.h> /* For mode constants */ +#include <fcntl.h> /* For O_* constants */ #include <map> @@ -50,15 +50,16 @@ extern "C" { #include "config.h" #endif -#define SAMPLE_BUF_SZ (1 << 20) +#define SAMPLE_BUF_SZ (1 << 20) using namespace std; +static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags); + 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) + 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..."; @@ -73,6 +74,7 @@ IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t c for (size_t i = 0; i < rx_buffers.size(); i++) rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t)); + memset(&sk_chan_state, 0, sizeof(sk_chan_state)); } IPCDevice::~IPCDevice() @@ -83,6 +85,14 @@ IPCDevice::~IPCDevice() for (size_t i = 0; i < rx_buffers.size(); i++) delete rx_buffers[i]; + + for (unsigned int i = 0; i < ARRAY_SIZE(sk_chan_state); i++) + ipc_sock_close(&sk_chan_state[i]); + + for (auto i : shm_io_rx_streams) + ipc_shm_close(i); + for (auto i : shm_io_tx_streams) + ipc_shm_close(i); } int IPCDevice::ipc_shm_connect(const char *shm_name) @@ -92,43 +102,43 @@ int IPCDevice::ipc_shm_connect(const char *shm_name) 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) { + 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; - } + 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; + 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; + 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); + shm_unlink(shm_name); + close(fd); err_shm_open: - return rc; + return rc; } -static int ipc_sock_send(struct ipc_sock_state* state, struct msgb *msg); +static int ipc_sock_send(struct ipc_sock_state *state, struct msgb *msg); -struct msgb *ipc_msgb_alloc(uint8_t msg_type) +static struct msgb *ipc_msgb_alloc(uint8_t msg_type) { struct msgb *msg; struct ipc_sk_if *ipc_prim; @@ -137,13 +147,13 @@ struct msgb *ipc_msgb_alloc(uint8_t msg_type) if (!msg) return NULL; msgb_put(msg, sizeof(struct ipc_sk_if) + 1000); - ipc_prim = (struct ipc_sk_if *) msg->data; + 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) +static int ipc_tx_greeting_req(struct ipc_sock_state *state, uint8_t req_version) { struct msgb *msg; struct ipc_sk_if *ipc_prim; @@ -155,13 +165,13 @@ int ipc_tx_greeting_req(struct ipc_sock_state* state, uint8_t req_version) LOGC(DDEV, INFO) << "ipc_msgb_alloc() returns NULL!"; return -ENOMEM; } - ipc_prim = (struct ipc_sk_if *) msg->data; + 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) +static int ipc_tx_info_req(struct ipc_sock_state *state) { struct msgb *msg; //struct ipc_sk_if *ipc_prim; @@ -177,7 +187,7 @@ int ipc_tx_info_req(struct ipc_sock_state* state) return ipc_sock_send(state, msg); } -int ipc_tx_open_req(struct ipc_sock_state* state, uint32_t num_chans) +int IPCDevice::ipc_tx_open_req(struct ipc_sock_state *state, uint32_t num_chans, uint32_t ref) { struct msgb *msg; struct ipc_sk_if *ipc_prim; @@ -189,11 +199,33 @@ int ipc_tx_open_req(struct ipc_sock_state* state, uint32_t num_chans) if (!msg) { return -ENOMEM; } - ipc_prim = (struct ipc_sk_if *) msg->data; + 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"); + + /* FIXME: pass fractional freq */ + ipc_prim->u.open_req.rx_sample_freq_num = rx_sps; + ipc_prim->u.open_req.rx_sample_freq_den = 1; + ipc_prim->u.open_req.tx_sample_freq_num = tx_sps; + ipc_prim->u.open_req.tx_sample_freq_den = 1; + + switch (ref) { + case ReferenceType::REF_EXTERNAL: + ipc_prim->u.open_req.clockref = FEATURE_MASK_CLOCKREF_EXTERNAL; + break; + case ReferenceType::REF_INTERNAL: + case ReferenceType::REF_GPS: + ipc_prim->u.open_req.clockref = FEATURE_MASK_CLOCKREF_INTERNAL; + break; + } + + /* FIXME: clock ref part of config, not open */ + ipc_prim->u.open_req.clockref = FEATURE_MASK_CLOCKREF_EXTERNAL; + + for (unsigned int i = 0; i < num_chans; i++) { + chan_info = &ipc_prim->u.open_req.chan_info[i]; + OSMO_STRLCPY_ARRAY(chan_info->rx_path, rx_paths[i].c_str()); + OSMO_STRLCPY_ARRAY(chan_info->tx_path, tx_paths[i].c_str()); + } return ipc_sock_send(state, msg); } @@ -206,18 +238,24 @@ static void ipc_sock_timeout(void *_priv) 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; + 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; + } else { + LOGC(DDEV, ERROR) << "Wrong IPC SOCK API VERSION RECEIVED!" << greeting_cnf->req_version; exit(1); } - return 0; + return 0; } int IPCDevice::ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf) { + current_info_cnf = *info_cnf; + unsigned int i; + + if (info_cnf->max_num_chans < chans) + return -1; + /* Here: * verify info_cnf->max_num_chans >= requested chans * verify supports setting reflock as asked by user looking in info_cnf->feature_mask @@ -225,48 +263,102 @@ int IPCDevice::ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *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; + << " name=" << info_cnf->dev_desc << std::endl + << " 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 + << " min_tx_gain=" << info_cnf->min_tx_gain << " max_tx_gain=" << info_cnf->max_tx_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]; + bool rx_found = false, tx_found = false; + 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]; + + if (rx_paths.size() < (i + 1) || + strcmp(rx_paths[i].c_str(), info_cnf->chan_info[i].rx_path[j]) == 0) { + rx_found = true; + break; + } 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]; + 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]; + if (tx_paths.size() < (i + 1) || + strcmp(tx_paths[i].c_str(), info_cnf->chan_info[i].tx_path[j]) == 0) { + tx_found = true; + break; + } j++; } + + if (!rx_found) { + LOGC(DDEV, ERROR) << "rx antenna not found: " << rx_paths[i]; + exit(0); + } + if (!tx_found) { + LOGC(DDEV, ERROR) << "tx antenna not found: " << rx_paths[i]; + exit(0); + } } - tmp_state = IPC_IF_MSG_INFO_CNF; - return 0; + 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; + current_open_cnf = *open_cnf; + + 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++) { + int rc; LOGC(DDEV, NOTICE) << "chan " << i << ": sk_path=" << open_cnf->chan_info[i].chan_ipc_sk_path; + + /* FIXME: current limit 8 chans, make dynamic */ + if (i < 8) { + struct ipc_sock_state *state = &sk_chan_state[i]; + memset(state, 0x00, sizeof(*state)); + + INIT_LLIST_HEAD(&state->upqueue); + rc = osmo_sock_unix_init_ofd(&state->conn_bfd, SOCK_SEQPACKET, 0, + open_cnf->chan_info[i].chan_ipc_sk_path, OSMO_SOCK_F_CONNECT); + if (rc < 0) { + LOGC(DDEV, ERROR) << "Failed to connect to the BTS (" + << open_cnf->chan_info[i].chan_ipc_sk_path << "). " + << "Retrying...\n"; + osmo_timer_setup(&state->timer, ipc_sock_timeout, NULL); + osmo_timer_schedule(&state->timer, 5, 0); + return -1; + } + state->conn_bfd.cb = ipc_chan_sock_cb; + state->conn_bfd.data = this; + state->conn_bfd.priv_nr = i; + } } 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); + 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; + /* server inits both producers */ + for (unsigned int i = 0; i < shm_dec->num_chans; i++) { + shm_io_rx_streams.push_back(ipc_shm_init_consumer(shm_dec->channels[i]->ul_stream)); + shm_io_tx_streams.push_back(ipc_shm_init_consumer(shm_dec->channels[i]->dl_stream)); + // shm_io_tx_streams.push_back(ipc_shm_init_producer(shm_dec->channels[i]->dl_stream)); + } + + tmp_state = IPC_IF_MSG_OPEN_CNF; + return 0; } int IPCDevice::ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim) @@ -275,8 +367,8 @@ int IPCDevice::ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim) switch (msg_type) { case IPC_IF_MSG_GREETING_CNF: - rc = ipc_rx_greeting_cnf(&ipc_prim->u.greeting_cnf); - break; + 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; @@ -284,29 +376,87 @@ int IPCDevice::ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim) 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); + LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type %d\n", msg_type); rc = -EINVAL; } return rc; } -static int ipc_sock_send(struct ipc_sock_state* state, struct msgb *msg) +int IPCDevice::ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr) +{ + tmp_state = IPC_IF_MSG_START_CNF; + return 0; +} +int IPCDevice::ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr) +{ + return 0; +} +int IPCDevice::ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr) +{ + return 0; +} +int IPCDevice::ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr) +{ + return 0; +} +int IPCDevice::ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr) +{ + return 0; +} +int IPCDevice::ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr) +{ + return 0; +} + +int IPCDevice::ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, uint8_t chan_nr) +{ + int rc = 0; + + switch (msg_type) { + case IPC_IF_MSG_START_CNF: + rc = ipc_rx_chan_start_cnf(&ipc_prim->u.start_cnf, chan_nr); + break; + case IPC_IF_MSG_STOP_CNF: + rc = ipc_rx_chan_stop_cnf(&ipc_prim->u.stop_cnf, chan_nr); + break; + case IPC_IF_MSG_SETGAIN_CNF: + rc = ipc_rx_chan_setgain_cnf(&ipc_prim->u.set_gain_cnf, chan_nr); + break; + case IPC_IF_MSG_SETFREQ_CNF: + rc = ipc_rx_chan_setfreq_cnf(&ipc_prim->u.set_freq_cnf, chan_nr); + break; + case IPC_IF_NOTIFY_UNDERFLOW: + rc = ipc_rx_chan_notify_underflow(&ipc_prim->u.notify, chan_nr); + break; + case IPC_IF_NOTIFY_OVERFLOW: + rc = ipc_rx_chan_notify_overflow(&ipc_prim->u.notify, chan_nr); + break; + default: + LOGP(DMAIN, LOGL_ERROR, "Received unknown IPC 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"); + LOGP(DMAIN, LOGL_INFO, + "IPC 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"); + LOGP(DMAIN, LOGL_NOTICE, + "IPC socket not connected, " + "dropping message\n"); msgb_free(msg); return -EIO; } @@ -316,19 +466,25 @@ static int ipc_sock_send(struct ipc_sock_state* state, struct msgb *msg) return 0; } -void IPCDevice::ipc_sock_close() +void IPCDevice::ipc_sock_close(struct ipc_sock_state *state) { - struct osmo_fd *bfd = &sk_state.conn_bfd; + if (state == 0) + return; - LOGP(DDEV, LOGL_NOTICE, "PCU socket has LOST connection\n"); + struct osmo_fd *bfd = &state->conn_bfd; + + if (bfd->fd <= 0) + return; + + LOGP(DDEV, LOGL_NOTICE, "IPC 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); + while (!llist_empty(&state->upqueue)) { + struct msgb *msg = msgb_dequeue(&state->upqueue); msgb_free(msg); } } @@ -343,7 +499,7 @@ int IPCDevice::ipc_sock_read(struct osmo_fd *bfd) if (!msg) return -ENOMEM; - ipc_prim = (struct ipc_sk_if *) msg->tail; + ipc_prim = (struct ipc_sk_if *)msg->tail; rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); if (rc == 0) @@ -358,15 +514,17 @@ int IPCDevice::ipc_sock_read(struct osmo_fd *bfd) } 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)); + 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 + /* as we always synchronously process the message in IPC_rx() and * its callbacks, we can free the message here. */ msgb_free(msg); @@ -374,7 +532,54 @@ int IPCDevice::ipc_sock_read(struct osmo_fd *bfd) close: msgb_free(msg); - ipc_sock_close(); + ipc_sock_close(&sk_state); + return -1; +} + +int IPCDevice::ipc_chan_sock_read(struct osmo_fd *bfd) +{ + struct ipc_sk_chan_if *ipc_prim; + struct msgb *msg; + int rc; + + msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_chan_sock_rx"); + if (!msg) + return -ENOMEM; + + ipc_prim = (struct ipc_sk_chan_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_chan_rx(ipc_prim->msg_type, ipc_prim, bfd->priv_nr); + + /* as we always synchronously process the message in IPC_rx() and + * its callbacks, we can free the message here. */ + msgb_free(msg); + + return rc; + +close: + msgb_free(msg); + ipc_sock_close(&sk_chan_state[bfd->priv_nr]); return -1; } @@ -394,8 +599,10 @@ int IPCDevice::ipc_sock_write(struct osmo_fd *bfd) /* 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); + LOGP(DDEV, LOGL_ERROR, + "message type (%d) with ZERO " + "bytes!\n", + ipc_prim->msg_type); goto dontsend; } @@ -411,7 +618,7 @@ int IPCDevice::ipc_sock_write(struct osmo_fd *bfd) goto close; } -dontsend: + dontsend: /* _after_ we send it, we can deueue */ msg2 = msgb_dequeue(&sk_state.upqueue); assert(msg == msg2); @@ -420,13 +627,59 @@ dontsend: return 0; close: - ipc_sock_close(); + ipc_sock_close(&sk_state); + return -1; +} + +int IPCDevice::ipc_chan_sock_write(struct osmo_fd *bfd) +{ + int rc; + + while (!llist_empty(&sk_chan_state[bfd->priv_nr].upqueue)) { + struct msgb *msg, *msg2; + struct ipc_sk_chan_if *ipc_prim; + + /* peek at the beginning of the queue */ + msg = llist_entry(sk_chan_state[bfd->priv_nr].upqueue.next, struct msgb, list); + ipc_prim = (struct ipc_sk_chan_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_chan_state[bfd->priv_nr].upqueue); + assert(msg == msg2); + msgb_free(msg); + } + return 0; + +close: + ipc_sock_close(&sk_chan_state[bfd->priv_nr]); return -1; } static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags) { - IPCDevice *device = (IPCDevice *)bfd->data; + IPCDevice *device = static_cast<IPCDevice *>(bfd->data); int rc = 0; if (flags & BSC_FD_READ) @@ -440,6 +693,22 @@ static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags) return rc; } +static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags) +{ + IPCDevice *device = static_cast<IPCDevice *>(bfd->data); + int rc = 0; + + if (flags & BSC_FD_READ) + rc = device->ipc_chan_sock_read(bfd); + if (rc < 0) + return rc; + + if (flags & BSC_FD_WRITE) + rc = device->ipc_chan_sock_write(bfd); + + return rc; +} + int IPCDevice::open(const std::string &args, int ref, bool swap_channels) { //float_type sr_host, sr_rf; @@ -451,11 +720,10 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels) 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); + 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"; + 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; @@ -465,17 +733,17 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels) ipc_tx_greeting_req(&sk_state, IPC_SOCK_API_VERSION); /* Wait until confirmation is recieved */ - while(tmp_state != IPC_IF_MSG_GREETING_CNF) + 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) + while (tmp_state != IPC_IF_MSG_INFO_CNF) osmo_select_main(0); - ipc_tx_open_req(&sk_state, chans); + ipc_tx_open_req(&sk_state, chans, ref); /* Wait until confirmation is recieved */ - while(tmp_state != IPC_IF_MSG_OPEN_CNF) + while (tmp_state != IPC_IF_MSG_OPEN_CNF) osmo_select_main(0); LOGC(DDEV, NOTICE) << "Device driver opened successfuly!"; @@ -496,35 +764,23 @@ bool IPCDevice::start() { LOGC(DDEV, INFO) << "starting IPC..."; - unsigned int i; - if (started) { LOGC(DDEV, ERR) << "Device already started"; - return false; + return true; } - /* 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; + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; - /* configure Streams */ - } + msg = ipc_msgb_alloc(IPC_IF_MSG_START_REQ); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.start_req.dummy = 0; - /* 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++) { - //} + ipc_sock_send(&sk_chan_state[0], msg); + while (tmp_state != IPC_IF_MSG_START_CNF) + osmo_select_main(0); flush_recv(10); @@ -539,16 +795,16 @@ bool IPCDevice::stop() if (!started) return true; - /* - for (i=0; i<chans; i++) { - IPC_StopStream(&m_IPC_stream_tx[i]); - IPC_StopStream(&m_IPC_stream_rx[i]); - } + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; - 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]); - }*/ + msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_REQ); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.start_req.dummy = 0; + + ipc_sock_send(&sk_chan_state[0], msg); started = false; return true; @@ -568,26 +824,25 @@ bool IPCDevice::do_filters(size_t chan) return true; } - double IPCDevice::maxTxGain() { //return dev_param_map.at(m_dev_type).max_tx_gain; - return 70.0; + return current_info_cnf.max_tx_gain; } double IPCDevice::minTxGain() { - return 0.0; + return current_info_cnf.min_tx_gain; } double IPCDevice::maxRxGain() { - return 73.0; + return current_info_cnf.max_rx_gain; } double IPCDevice::minRxGain() { - return 0.0; + return current_info_cnf.min_rx_gain; } double IPCDevice::setTxGain(double dB, size_t chan) @@ -602,7 +857,7 @@ double IPCDevice::setTxGain(double dB, size_t chan) //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; + tx_gains[chan] = dB; return tx_gains[chan]; } @@ -618,50 +873,27 @@ double IPCDevice::setRxGain(double dB, size_t chan) //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; + 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; - } +{ + std::vector<uint16_t> tmp(4096); + uint64_t tmps; + uint32_t read; - ts_initial = rx_metadata.timestamp + len; + for (uint32_t j = 0; j < num_pkts; j++) { + for (unsigned int i = 0; i < chans; i++) + read = ipc_shm_read(shm_io_rx_streams[i], (uint16_t *)&tmp.front(), 4096 / 2, &tmps, 1); } -*/ + ts_initial = tmps + read; + return ts_initial; LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl; return true; } -bool IPCDevice::setRxAntenna(const std::string & ant, size_t chan) +bool IPCDevice::setRxAntenna(const std::string &ant, size_t chan) { return true; } @@ -669,10 +901,9 @@ bool IPCDevice::setRxAntenna(const std::string & ant, size_t chan) std::string IPCDevice::getRxAntenna(size_t chan) { return ""; - } -bool IPCDevice::setTxAntenna(const std::string & ant, size_t chan) +bool IPCDevice::setTxAntenna(const std::string &ant, size_t chan) { return true; } @@ -687,22 +918,127 @@ bool IPCDevice::requiresRadioAlign() return false; } -GSM::Time IPCDevice::minLatency() { +GSM::Time IPCDevice::minLatency() +{ /* UNUSED */ - return GSM::Time(0,0); + return GSM::Time(0, 0); +} + +/** Returns the starting write Timestamp*/ +TIMESTAMP IPCDevice::initialWriteTimestamp(void) +{ + return ts_initial; +} + +/** Returns the starting read Timestamp*/ +TIMESTAMP IPCDevice::initialReadTimestamp(void) +{ + return ts_initial; } // NOTE: Assumes sequential reads -int IPCDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun, - TIMESTAMP timestamp, bool * underrun) +int IPCDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun) { + int rc, num_smpls, expect_smpls; + ssize_t avail_smpls; + TIMESTAMP expect_timestamp; + unsigned int i; + + if (bufs.size() != chans) { + LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size(); + return -1; + } + + *overrun = false; + *underrun = false; + + timestamp += current_open_cnf.path_delay; + + /* Check that timestamp is valid */ + rc = rx_buffers[0]->avail_smpls(timestamp); + if (rc < 0) { + LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc); + LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp); + return 0; + } + + for (i = 0; i < chans; i++) { + /* Receive samples from HW until we have enough */ + while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) { + uint64_t recv_timestamp = 0; + + thread_enable_cancel(false); + + num_smpls = ipc_shm_read(shm_io_rx_streams[i], (uint16_t *)bufs[i], len - avail_smpls, + &recv_timestamp, 1); + expect_timestamp = timestamp + avail_smpls; + thread_enable_cancel(true); + + LOGCHAN(i, DDEV, DEBUG) + "Received timestamp = " << (TIMESTAMP)recv_timestamp << " (" << num_smpls << ")"; + + expect_smpls = len - avail_smpls; + // if (expect_smpls != num_smpls) + // LOGCHAN(i, DDEV, NOTICE) + // << "Unexpected recv buffer len: expect " << expect_smpls << " got " << num_smpls + // << ", diff=" << expect_smpls - num_smpls; + + //expect_timestamp = timestamp + avail_smpls; + if (expect_timestamp != (TIMESTAMP)recv_timestamp) + LOGCHAN(i, DDEV, ERROR) + << "Unexpected recv buffer timestamp: expect " << expect_timestamp << " got " + << recv_timestamp << ", diff=" << recv_timestamp - expect_timestamp; + + rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)recv_timestamp); + if (rc < 0) { + LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc); + LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp); + if (rc != smpl_buf::ERROR_OVERFLOW) + return 0; + } + } + } + + /* We have enough samples */ + for (size_t i = 0; i < rx_buffers.size(); i++) { + rc = rx_buffers[i]->read(bufs[i], len, timestamp); + if ((rc < 0) || (rc != len)) { + LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". " + << rx_buffers[i]->str_status(timestamp) << ", (len=" << len << ")"; + return 0; + } + } + return len; } -int IPCDevice::writeSamples(std::vector < short *>&bufs, int len, - bool * underrun, unsigned long long timestamp) +int IPCDevice::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp) { - return len; + int rc = 0; + unsigned int i; + + if (bufs.size() != chans) { + LOGC(DDEV, ERROR) << "Invalid channel combination " << bufs.size(); + return -1; + } + + *underrun = false; + + for (i = 0; i < chans; i++) { + LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp; + // thread_enable_cancel(false); + rc = ipc_shm_enqueue(shm_io_tx_streams[i], timestamp, len, (uint16_t *)bufs[i]); + + // rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100); + // update_stream_stats_tx(i, underrun); + // thread_enable_cancel(true); + if (rc != len) { + LOGCHAN(i, DDEV, ERROR) << "LMS: Device Tx timed out (" << rc << " vs exp " << len << ")."; + return -1; + } + } + + return rc; } bool IPCDevice::updateAlignment(TIMESTAMP timestamp) @@ -712,16 +1048,46 @@ bool IPCDevice::updateAlignment(TIMESTAMP timestamp) bool IPCDevice::setTxFreq(double wFreq, size_t chan) { + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz"; - return true; + msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_REQ); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.set_freq_req.is_tx = 1; + ipc_prim->u.set_freq_req.freq = wFreq; + + return ipc_sock_send(&sk_chan_state[chan], msg) < 0 ? false : true; } bool IPCDevice::setRxFreq(double wFreq, size_t chan) { + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz"; - return true; -} + msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_REQ); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.set_freq_req.is_tx = 0; + ipc_prim->u.set_freq_req.freq = wFreq; + return ipc_sock_send(&sk_chan_state[chan], msg) < 0 ? false : 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); +} diff --git a/Transceiver52M/device/ipc/IPCDevice.h b/Transceiver52M/device/ipc/IPCDevice.h index c7a2a1b..335ed10 100644 --- a/Transceiver52M/device/ipc/IPCDevice.h +++ b/Transceiver52M/device/ipc/IPCDevice.h @@ -27,6 +27,7 @@ extern "C" { #include <osmocom/core/select.h> #include <osmocom/core/timer.h> #include "shm.h" +#include "ipc_shm.h" } #include "radioDevice.h" @@ -40,17 +41,17 @@ extern "C" { #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 */ + 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: +class IPCDevice : public RadioDevice { + protected: struct ipc_sock_state sk_state; + /* FIXME: current limit 8 chans, make dynamic */ + struct ipc_sock_state sk_chan_state[8]; uint8_t tmp_state; char shm_name[SHM_NAME_MAX]; int ipc_shm_connect(const char *shm_name); @@ -58,49 +59,56 @@ private: struct ipc_shm_region *shm_dec; std::vector<smpl_buf *> rx_buffers; - double actualSampleRate; ///< the actual USRP sampling rate + double actualSampleRate; ///< the actual USRP sampling rate - bool started; ///< flag indicates LMS has started - bool skipRx; ///< set if LMS is transmit-only. + 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; + struct ipc_sk_if_info_req current_info_req; + struct ipc_sk_if_info_cnf current_info_cnf; + struct ipc_sk_if_open_cnf current_open_cnf; + + std::vector<struct ipc_shm_io *> shm_io_rx_streams; + std::vector<struct ipc_shm_io *> shm_io_tx_streams; + 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); + int get_ant_idx(const std::string &name, bool dir_tx, size_t chan); + virtual 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: - virtual void ipc_sock_close(); + public: + virtual void ipc_sock_close(ipc_sock_state *state); virtual int ipc_sock_read(struct osmo_fd *bfd); virtual int ipc_sock_write(struct osmo_fd *bfd); virtual int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim); virtual int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf); virtual int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf); virtual int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf); + virtual int ipc_tx_open_req(struct ipc_sock_state *state, uint32_t num_chans, uint32_t ref); /** 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); - virtual ~IPCDevice(); + const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths); + virtual ~IPCDevice() override; /** Instantiate the LMS */ - virtual int open(const std::string &args, int ref, bool swap_channels); + virtual int open(const std::string &args, int ref, bool swap_channels) override; /** Start the LMS */ - virtual bool start(); + virtual bool start() override; /** Stop the LMS */ - virtual bool stop(); + virtual bool stop() override; - enum TxWindowType getWindowType() { + enum TxWindowType getWindowType() override + { return TX_WINDOW_LMS1; } @@ -113,9 +121,8 @@ public: @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 */ - virtual int readSamples(std::vector < short *>&buf, int len, bool * overrun, - TIMESTAMP timestamp = 0xffffffff, bool * underrun = - NULL); + virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff, + bool *underrun = NULL) override; /** Write samples to the LMS. @param buf Contains the data to be written. @@ -124,95 +131,106 @@ public: @param timestamp The timestamp of the first sample of the data buffer. @return The number of samples actually written */ - virtual int writeSamples(std::vector < short *>&bufs, int len, bool * underrun, - TIMESTAMP timestamp = 0xffffffff); + virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun, + TIMESTAMP timestamp = 0xffffffff) override; /** Update the alignment between the read and write timestamps */ - virtual bool updateAlignment(TIMESTAMP timestamp); + virtual bool updateAlignment(TIMESTAMP timestamp) override; /** Set the transmitter frequency */ - virtual bool setTxFreq(double wFreq, size_t chan = 0); + virtual bool setTxFreq(double wFreq, size_t chan = 0) override; /** Set the receiver frequency */ - virtual bool setRxFreq(double wFreq, size_t chan = 0); + virtual bool setRxFreq(double wFreq, size_t chan = 0) override; /** Returns the starting write Timestamp*/ - virtual TIMESTAMP initialWriteTimestamp(void) { - return ts_initial; - } + virtual TIMESTAMP initialWriteTimestamp(void) override; /** Returns the starting read Timestamp*/ - virtual TIMESTAMP initialReadTimestamp(void) { - return ts_initial; - } + virtual TIMESTAMP initialReadTimestamp(void) override; /** returns the full-scale transmit amplitude **/ - virtual double fullScaleInputValue() { - #define LIMESDR_TX_AMPL 0.3 - return(double) SHRT_MAX * LIMESDR_TX_AMPL; + virtual double fullScaleInputValue() override + { + return (double)SHRT_MAX * current_info_cnf.iq_scaling_val_rx; } /** returns the full-scale receive amplitude **/ - virtual double fullScaleOutputValue() { - return (double) SHRT_MAX; + virtual double fullScaleOutputValue() override + { + return (double)SHRT_MAX * current_info_cnf.iq_scaling_val_tx; } /** sets the receive chan gain, returns the gain setting **/ - virtual double setRxGain(double dB, size_t chan = 0); + virtual double setRxGain(double dB, size_t chan = 0) override; /** get the current receive gain */ - virtual double getRxGain(size_t chan = 0) { + virtual double getRxGain(size_t chan = 0) override + { return rx_gains[chan]; } /** return maximum Rx Gain **/ - virtual double maxRxGain(void); + virtual double maxRxGain(void) override; /** return minimum Rx Gain **/ - virtual double minRxGain(void); + virtual double minRxGain(void) override; /** sets the transmit chan gain, returns the gain setting **/ - virtual double setTxGain(double dB, size_t chan = 0); + virtual double setTxGain(double dB, size_t chan = 0) override; /** get transmit gain */ - virtual double getTxGain(size_t chan = 0) { + virtual double getTxGain(size_t chan = 0) override + { return tx_gains[chan]; } /** return maximum Tx Gain **/ - virtual double maxTxGain(void); + virtual double maxTxGain(void) override; /** return minimum Rx Gain **/ - virtual double minTxGain(void); + virtual double minTxGain(void) override; /** sets the RX path to use, returns true if successful and false otherwise */ - virtual bool setRxAntenna(const std::string & ant, size_t chan = 0); + virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) override; /* return the used RX path */ - virtual std::string getRxAntenna(size_t chan = 0); + virtual std::string getRxAntenna(size_t chan = 0) override; /** sets the RX path to use, returns true if successful and false otherwise */ - virtual bool setTxAntenna(const std::string & ant, size_t chan = 0); + virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) override; /* return the used RX path */ - virtual std::string getTxAntenna(size_t chan = 0); + virtual std::string getTxAntenna(size_t chan = 0) override; /** return whether user drives synchronization of Tx/Rx of USRP */ - virtual bool requiresRadioAlign(); + virtual bool requiresRadioAlign() override; - /** return whether user drives synchronization of Tx/Rx of USRP */ - virtual GSM::Time minLatency(); + /** return whether user drives synchronization of Tx/Rx of USRP */ + virtual GSM::Time minLatency() override; /** Return internal status values */ - virtual inline double getTxFreq(size_t chan = 0) { + virtual inline double getTxFreq(size_t chan = 0) override + { return 0; } - virtual inline double getRxFreq(size_t chan = 0) { + virtual inline double getRxFreq(size_t chan = 0) override + { return 0; } - virtual inline double getSampleRate() { + virtual inline double getSampleRate() override + { return actualSampleRate; } + int ipc_chan_sock_read(osmo_fd *bfd); + int ipc_chan_sock_write(osmo_fd *bfd); + int ipc_chan_rx(uint8_t msg_type, ipc_sk_chan_if *ipc_prim, uint8_t chan_nr); + int ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr); + int ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr); + int ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr); + int ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr); + int ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr); + int ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr); }; #endif // _IPC_DEVICE_H_ diff --git a/Transceiver52M/device/ipc/Makefile.am b/Transceiver52M/device/ipc/Makefile.am index 40146f7..82be5a9 100644 --- a/Transceiver52M/device/ipc/Makefile.am +++ b/Transceiver52M/device/ipc/Makefile.am @@ -5,21 +5,25 @@ AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_C AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) AM_LDFLAGS = -lpthread -lrt -noinst_HEADERS = IPCDevice.h shm.h ipcb210.h magicwrap.h +noinst_HEADERS = IPCDevice.h shm.h ../uhd/UHDDevice.h uhdwrap.h noinst_LTLIBRARIES = libdevice.la -libdevice_la_SOURCES = ipcb210.cpp IPCDevice.cpp shm.c magicwrap.cpp +libdevice_la_SOURCES = ipc-driver-test.c IPCDevice.cpp shm.c ../uhd/UHDDevice.cpp uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la - +libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS) -DIPCMAGIC bin_PROGRAMS = ipc-driver-test -ipc_driver_test_SOURCES = ipc-driver-test.c +#ipc_driver_test_SHORTNAME = drvt +ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c ../uhd/UHDDevice.cpp 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) +ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS) +ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) +ipc_driver_test_CFLAGS = $(AM_CFLAGS) $(UHD_CFLAGS) +ipc_driver_test_LDFLAGS = $(AM_LDFLAGS) $(UHD_LIBS) +ipc_driver_test_LDADD += $(top_builddir)/Transceiver52M/device/common/libdevice_common.la $(top_builddir)/CommonLibs/libcommon.la + diff --git a/Transceiver52M/device/ipc/ipc-driver-test.c b/Transceiver52M/device/ipc/ipc-driver-test.c index 55a8a09..e431620 100644 --- a/Transceiver52M/device/ipc/ipc-driver-test.c +++ b/Transceiver52M/device/ipc/ipc-driver-test.c @@ -2,21 +2,24 @@ * Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * Author: Pau Espin Pedrol <pespin@sysmocom.de> * -* SPDX-License-Identifier: AGPL-3.0+ +* SPDX-License-Identifier: 0BSD * - 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/>. + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. */ +#ifdef __cplusplus +extern "C" { +#endif + +#define _GNU_SOURCE +#include <pthread.h> + + +#include <debug.h> #include <stdio.h> #include <stdlib.h> @@ -28,8 +31,9 @@ #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 <sys/stat.h> /* For mode constants */ +#include <fcntl.h> /* For O_* constants */ + #include <osmocom/core/application.h> #include <osmocom/core/talloc.h> @@ -42,25 +46,28 @@ #include <osmocom/core/timer.h> #include "shm.h" +#include "ipc_shm.h" +#include "ipc_chan.h" +#include "ipc_sock.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; +struct ipc_sock_state *global_ipc_sock_state; + +/* 8 channels are plenty */ +struct ipc_sock_state *global_ctrl_socks[8]; +struct ipc_shm_io *ios_tx_to_device[8]; +struct ipc_shm_io *ios_rx_from_device[8]; void *shm; +void *global_dev; +static struct ipc_shm_region *decoded_region; /* Debug Areas of the code */ -enum { - DMAIN, -}; +//enum { DMAIN, +//}; static const struct log_info_cat default_categories[] = { [DMAIN] = { .name = "DMAIN", @@ -70,49 +77,55 @@ static const struct log_info_cat default_categories[] = { }, }; -const struct log_info log_info = { +const struct log_info log_infox = { .cat = default_categories, .num_cat = ARRAY_SIZE(default_categories), }; +#ifdef __cplusplus +} +#endif + +#include "uhdwrap.h" -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; +volatile int ipc_exit_requested = 0; + +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); + shm_unlink(shm_name); + close(fd); err_shm_open: - return rc; + return rc; } -static int ipc_sock_send(struct msgb *msg); - struct msgb *ipc_msgb_alloc(uint8_t msg_type) { struct msgb *msg; @@ -122,7 +135,7 @@ struct msgb *ipc_msgb_alloc(uint8_t msg_type) if (!msg) return NULL; msgb_put(msg, sizeof(struct ipc_sk_if) + 1000); - ipc_prim = (struct ipc_sk_if *) msg->data; + ipc_prim = (struct ipc_sk_if *)msg->data; ipc_prim->msg_type = msg_type; return msg; @@ -136,7 +149,7 @@ static int ipc_tx_greeting_cnf(uint8_t req_version) msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF); if (!msg) return -ENOMEM; - ipc_prim = (struct ipc_sk_if *) msg->data; + ipc_prim = (struct ipc_sk_if *)msg->data; ipc_prim->u.greeting_cnf.req_version = req_version; return ipc_sock_send(msg); @@ -146,308 +159,191 @@ static int ipc_tx_info_cnf() { struct msgb *msg; struct ipc_sk_if *ipc_prim; - struct ipc_sk_if_info_chan *chan_info; - unsigned 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++; - } + ipc_prim = (struct ipc_sk_if *)msg->data; + + uhdwrap_fill_info_cnf(ipc_prim); return ipc_sock_send(msg); } -static int ipc_tx_open_cnf(int rc, uint32_t num_chans) +static int ipc_tx_open_cnf(int rc, uint32_t num_chans, int32_t timingoffset) { struct msgb *msg; struct ipc_sk_if *ipc_prim; - struct ipc_sk_if_open_cnf_chan *chan_info; - unsigned int i; + struct ipc_sk_if_open_cnf_chan *chan_info; + unsigned 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++; - } + ipc_prim = (struct ipc_sk_if *)msg->data; + ipc_prim->u.open_cnf.return_code = rc; + ipc_prim->u.open_cnf.path_delay = timingoffset; // 6.18462e-5 * 1625e3 / 6; + 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); + /* FIXME: dynamc chan limit, currently 8 */ + if (i < 8) + ipc_sock_init(chan_info->chan_ipc_sk_path, &global_ctrl_socks[i], ipc_chan_sock_accept, i); + 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) +int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req) { - ipc_tx_info_cnf(); - return 0; + 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_open_req(const struct ipc_sk_if_open_req *open_req) +int ipc_rx_info_req(struct ipc_sk_if_info_req *info_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; + ipc_tx_info_cnf(); + return 0; } -static int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim) +int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req) { - 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; + /* calculate size needed */ + unsigned int len; + + global_dev = uhdwrap_open(open_req); + + /* b210 packet size is 2040, but our tx size is 2500, so just do *2 */ + int shmbuflen = uhdwrap_get_bufsizerx(global_dev) * 2; + + len = ipc_shm_encode_region(NULL, open_req->num_chans, 4, shmbuflen); + /* 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, shmbuflen); + LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80)); + + /* set up our own copy of the decoded area, we have to do it here, + * since the uhd wrapper does not allow starting single channels + * additionally go for the producer init for both, so only we are responsible for the init, instead + * of splitting it with the client and causing potential races if one side uses it too early */ + decoded_region = ipc_shm_decode_region(0, (struct ipc_shm_raw_region *)shm); + for (unsigned int i = 0; i < open_req->num_chans; i++) { + // ios_tx_to_device[i] = ipc_shm_init_consumer(decoded_region->channels[i]->dl_stream); + ios_tx_to_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->dl_stream); + ios_rx_from_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->ul_stream); } - return rc; + ipc_tx_open_cnf(-rc, open_req->num_chans, uhdwrap_get_timingoffset(global_dev)); + return 0; } -static int ipc_sock_send(struct msgb *msg) +void *uplink_thread(void *x_void_ptr) { - 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; + uint32_t chann = decoded_region->num_chans; + + pthread_setname_np(pthread_self(), "uplink rx"); + while (!ipc_exit_requested) { + int32_t read = uhdwrap_read(global_dev, chann); + if (read < 0) + return 0; + } return 0; } -static void ipc_sock_close(struct ipc_sock_state *state) +void *downlink_thread(void *x_void_ptr) { - struct osmo_fd *bfd = &state->conn_bfd; - - LOGP(DMAIN, LOGL_NOTICE, "PCU socket has LOST connection\n"); + int chann = decoded_region->num_chans; + pthread_setname_np(pthread_self(), "downlink tx"); - 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); + while (!ipc_exit_requested) { + bool underrun; + uhdwrap_write(global_dev, chann, &underrun); } + return 0; } -static int ipc_sock_read(struct osmo_fd *bfd) +int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr) { - struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data; - struct ipc_sk_if *ipc_prim; struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; int rc; - msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_sock_rx"); - if (!msg) - return -ENOMEM; + /* no per-chan start/stop */ + rc = uhdwrap_start(global_dev, chan_nr); - ipc_prim = (struct ipc_sk_if *) msg->tail; + pthread_t rx, tx; + pthread_create(&rx, NULL, uplink_thread, 0); + pthread_create(&tx, NULL, downlink_thread, 0); - 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 < (int)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; + msg = ipc_msgb_alloc(IPC_IF_MSG_START_CNF); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.start_cnf.return_code = rc ? 0 : -1; -close: - msgb_free(msg); - ipc_sock_close(state); - return -1; + return ipc_chan_sock_send(msg, chan_nr); } - -static int ipc_sock_write(struct osmo_fd *bfd) +int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr) { - struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data; + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; 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; + /* no per-chan start/stop */ + rc = uhdwrap_stop(global_dev, chan_nr); -close: - ipc_sock_close(state); - return -1; -} + msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_CNF); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.stop_cnf.return_code = rc ? 0 : -1; -static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags) + return ipc_chan_sock_send(msg, chan_nr); +} +int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr) { - int rc = 0; + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; + double rv; - if (flags & BSC_FD_READ) - rc = ipc_sock_read(bfd); - if (rc < 0) - return rc; + rv = uhdwrap_set_gain(global_dev, req->gain, chan_nr, req->is_tx); - if (flags & BSC_FD_WRITE) - rc = ipc_sock_write(bfd); + msg = ipc_msgb_alloc(IPC_IF_MSG_SETGAIN_CNF); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.set_gain_cnf.is_tx = req->is_tx; + ipc_prim->u.set_gain_cnf.gain = rv; - return rc; + return ipc_chan_sock_send(msg, chan_nr); } -/* accept connection coming from PCU */ -static int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags) +int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr) { - 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; - } + struct msgb *msg; + struct ipc_sk_chan_if *ipc_prim; + bool rv; - LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n"); + rv = uhdwrap_set_freq(global_dev, req->freq, chan_nr, req->is_tx); - /* send current info */ - //pcu_tx_info_ind(); + msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_CNF); + if (!msg) + return -ENOMEM; + ipc_prim = (struct ipc_sk_chan_if *)msg->data; + ipc_prim->u.set_freq_cnf.return_code = rv ? 0 : 1; - return 0; + return ipc_chan_sock_send(msg, chan_nr); } -int ipc_sock_init(const char *path) +int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var, + int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n) { struct ipc_sock_state *state; struct osmo_fd *bfd; @@ -456,58 +352,61 @@ int ipc_sock_init(const char *path) state = talloc_zero(NULL, struct ipc_sock_state); if (!state) return -ENOMEM; - global_ipc_sock_state = state; + *global_state_var = state; INIT_LLIST_HEAD(&state->upqueue); - state->conn_bfd.fd = -1; + state->conn_bfd.fd = -1; bfd = &state->listen_bfd; - bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, - OSMO_SOCK_F_BIND); + 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)); + 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->cb = sock_callback_fn; bfd->data = state; + bfd->priv_nr = n; rc = osmo_fd_register(bfd); if (rc < 0) { - LOGP(DMAIN, LOGL_ERROR, "Could not register listen fd: %d\n", - rc); + 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); + //osmo_signal_register_handler(SS_GLOBAL, IPC_if_signal_cb, NULL); LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path); return 0; } -#ifdef IPCMAGIC -extern "C" int osmo_ctx_init ( const char * id ); -int magicmain(int argc, char** argv) { - osmo_ctx_init("main"); - osmo_select_init(); +#if defined(IPCMAGIC) && defined(__cplusplus) +extern "C" int osmo_ctx_init(const char *id); + +extern "C" int magicmain(int argc, char **argv) +{ + osmo_ctx_init("main"); + osmo_select_init(); + #else -int main(int argc, char** argv) { +int main(int argc, char **argv) +{ #endif - 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; + tall_ctx = talloc_named_const(NULL, 0, "OsmoTRX"); + msgb_talloc_ctx_init(tall_ctx, 0); + osmo_init_logging2(tall_ctx, &log_infox); + log_enable_multithread(); + + LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]); + ipc_sock_init(IPC_SOCK_PATH, &global_ipc_sock_state, ipc_sock_accept, 0); + while (!ipc_exit_requested) + osmo_select_main(0); + //ipc_sock_close() + return 0; } diff --git a/Transceiver52M/device/ipc/ipc-driver-test.h b/Transceiver52M/device/ipc/ipc-driver-test.h new file mode 100644 index 0000000..705f70b --- /dev/null +++ b/Transceiver52M/device/ipc/ipc-driver-test.h @@ -0,0 +1,42 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#pragma once + +#include <osmocom/core/select.h> +#include "shm.h" + +extern struct ipc_sock_state *global_ipc_sock_state; + +/* 8 channels are plenty */ +extern struct ipc_sock_state *global_ctrl_socks[8]; +extern struct ipc_shm_io *ios_tx_to_device[8]; +extern struct ipc_shm_io *ios_rx_from_device[8]; + +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 */ +}; + +int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var, + int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n); + +int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req); +int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req); +int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req); + +int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr); +int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr); +int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr); +int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr); diff --git a/Transceiver52M/device/ipc/ipc_chan.c b/Transceiver52M/device/ipc/ipc_chan.c new file mode 100644 index 0000000..91aa52d --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_chan.c @@ -0,0 +1,254 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#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 <debug.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> + +#include "shm.h" +#include "ipc-driver-test.h" +#include "ipc_chan.h" +#include "ipc_sock.h" + +static int ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, uint8_t chan_nr) +{ + int rc = 0; + + switch (msg_type) { + case IPC_IF_MSG_START_REQ: + rc = ipc_rx_chan_start_req(&ipc_prim->u.start_req, chan_nr); + break; + case IPC_IF_MSG_STOP_REQ: + rc = ipc_rx_chan_stop_req(&ipc_prim->u.stop_req, chan_nr); + break; + case IPC_IF_MSG_SETGAIN_REQ: + rc = ipc_rx_chan_setgain_req(&ipc_prim->u.set_gain_req, chan_nr); + break; + case IPC_IF_MSG_SETFREQ_REQ: + rc = ipc_rx_chan_setfreq_req(&ipc_prim->u.set_freq_req, chan_nr); + break; + default: + LOGP(DMAIN, LOGL_ERROR, "Received unknown IPC msg type %d\n", msg_type); + rc = -EINVAL; + } + + return rc; +} + +static int ipc_chan_sock_read(struct osmo_fd *bfd) +{ + struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data; + struct ipc_sk_chan_if *ipc_prim; + struct msgb *msg; + int rc; + + msg = msgb_alloc(sizeof(*ipc_prim) + 1000, "ipc_chan_sock_rx"); + if (!msg) + return -ENOMEM; + + ipc_prim = (struct ipc_sk_chan_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 < (int)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_chan_rx(ipc_prim->msg_type, ipc_prim, bfd->priv_nr); + + /* as we always synchronously process the message in IPC_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; +} + +int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr) +{ + struct ipc_sock_state *state = global_ctrl_socks[chan_nr]; + struct osmo_fd *conn_bfd; + + if (!state) + return -EINVAL; + + if (!state) { + LOGP(DMAIN, LOGL_INFO, + "IPC 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, + "IPC 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 int ipc_chan_sock_write(struct osmo_fd *bfd) +{ + struct ipc_sock_state *state = (struct ipc_sock_state *)bfd->data; + int rc; + + while (!llist_empty(&state->upqueue)) { + struct msgb *msg, *msg2; + struct ipc_sk_chan_if *ipc_prim; + + /* peek at the beginning of the queue */ + msg = llist_entry(state->upqueue.next, struct msgb, list); + ipc_prim = (struct ipc_sk_chan_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_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags) +{ + int rc = 0; + + if (flags & BSC_FD_READ) + rc = ipc_chan_sock_read(bfd); + if (rc < 0) + return rc; + + if (flags & BSC_FD_WRITE) + rc = ipc_chan_sock_write(bfd); + + return rc; +} + +int ipc_chan_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 IPC 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_chan_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 */ + //IPC_tx_info_ind(); + + return 0; +} diff --git a/Transceiver52M/device/ipc/ipc_chan.h b/Transceiver52M/device/ipc/ipc_chan.h new file mode 100644 index 0000000..ff1d5f4 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_chan.h @@ -0,0 +1,23 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#ifndef IPC_CHAN_H +#define IPC_CHAN_H + +#include "shm.h" +#include "ipc-driver-test.h" + +int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr); +int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags); + +#endif // IPC_CHAN_H diff --git a/Transceiver52M/device/ipc/ipc_shm.c b/Transceiver52M/device/ipc/ipc_shm.c new file mode 100644 index 0000000..d860ce7 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_shm.c @@ -0,0 +1,204 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Eric Wild <ewild@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#ifdef __cplusplus +extern "C" { +#endif + +#include <shm.h> +#include "ipc_shm.h" +#include <pthread.h> +#include <semaphore.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> + +#ifdef __cplusplus +} +#endif + +#define SAMPLE_SIZE_BYTE sizeof(uint16_t) * 2 + +struct ipc_shm_io *ipc_shm_init_consumer(struct ipc_shm_stream *s) +{ + unsigned int i; + + struct ipc_shm_io *r = (struct ipc_shm_io *)malloc(sizeof(struct ipc_shm_io)); + r->this_stream = s->raw; + r->buf_ptrs = + (volatile struct ipc_shm_raw_smpl_buf **)malloc(sizeof(struct ipc_shm_raw_smpl_buf *) * s->num_buffers); + + /* save actual ptrs */ + for (i = 0; i < s->num_buffers; i++) + r->buf_ptrs[i] = s->buffers[i]; + + r->partial_read_begin_ptr = 0; + return r; +} + +struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s) +{ + int rv; + pthread_mutexattr_t att; + pthread_condattr_t t1, t2; + struct ipc_shm_io *r = ipc_shm_init_consumer(s); + rv = pthread_mutexattr_init(&att); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_mutex_init((pthread_mutex_t *)&r->this_stream->lock, &att); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + pthread_mutexattr_destroy(&att); + + rv = pthread_condattr_setpshared(&t1, PTHREAD_PROCESS_SHARED); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->cf, &t1); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->ce, &t2); + if (rv != 0) { + fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv); + exit(EXIT_FAILURE); + } + + pthread_condattr_destroy(&t1); + pthread_condattr_destroy(&t2); + + r->this_stream->read_next = 0; + r->this_stream->write_next = 0; + return r; +} + +void ipc_shm_close(struct ipc_shm_io *r) +{ + if (r) { + if (r->buf_ptrs) + free(r->buf_ptrs); + free(r); + } +} + +int32_t ipc_shm_enqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data) +{ + volatile struct ipc_shm_raw_smpl_buf *buf; + int32_t rv; + struct timespec tv; + clock_gettime(CLOCK_REALTIME, &tv); + tv.tv_sec += 1; + + rv = pthread_mutex_timedlock((pthread_mutex_t *)&r->this_stream->lock, &tv); + if (rv != 0) + return -rv; + + while (((r->this_stream->write_next + 1) & (r->this_stream->num_buffers - 1)) == r->this_stream->read_next && + rv == 0) + rv = pthread_cond_timedwait((pthread_cond_t *)&r->this_stream->ce, + (pthread_mutex_t *)&r->this_stream->lock, &tv); + if (rv != 0) + return -rv; + + buf = r->buf_ptrs[r->this_stream->write_next]; + buf->timestamp = timestamp; + + rv = len_in_sps <= r->this_stream->buffer_size ? len_in_sps : r->this_stream->buffer_size; + + memcpy((void *)buf->samples, data, SAMPLE_SIZE_BYTE * rv); + buf->data_len = rv; + + r->this_stream->write_next = (r->this_stream->write_next + 1) & (r->this_stream->num_buffers - 1); + + pthread_cond_signal((pthread_cond_t *)&r->this_stream->cf); + pthread_mutex_unlock((pthread_mutex_t *)&r->this_stream->lock); + + return rv; +} + +int32_t ipc_shm_read(struct ipc_shm_io *r, uint16_t *out_buf, uint32_t num_samples, uint64_t *timestamp, + uint32_t timeout_seconds) +{ + volatile struct ipc_shm_raw_smpl_buf *buf; + int32_t rv; + uint8_t freeflag = 0; + struct timespec tv; + clock_gettime(CLOCK_REALTIME, &tv); + tv.tv_sec += timeout_seconds; + + rv = pthread_mutex_timedlock((pthread_mutex_t *)&r->this_stream->lock, &tv); + if (rv != 0) + return -rv; + + while (r->this_stream->write_next == r->this_stream->read_next && rv == 0) + rv = pthread_cond_timedwait((pthread_cond_t *)&r->this_stream->cf, + (pthread_mutex_t *)&r->this_stream->lock, &tv); + if (rv != 0) + return -rv; + + buf = r->buf_ptrs[r->this_stream->read_next]; + if (buf->data_len <= num_samples) { + memcpy(out_buf, (void *)&buf->samples[r->partial_read_begin_ptr * 2], SAMPLE_SIZE_BYTE * buf->data_len); + r->partial_read_begin_ptr = 0; + rv = buf->data_len; + buf->data_len = 0; + r->this_stream->read_next = (r->this_stream->read_next + 1) & (r->this_stream->num_buffers - 1); + freeflag = 1; + + } else /*if (buf->data_len > num_samples)*/ { + memcpy(out_buf, (void *)&buf->samples[r->partial_read_begin_ptr * 2], SAMPLE_SIZE_BYTE * num_samples); + r->partial_read_begin_ptr += num_samples; + buf->data_len -= num_samples; + rv = num_samples; + } + + *timestamp = buf->timestamp; + buf->timestamp += rv; + + if (freeflag) + pthread_cond_signal((pthread_cond_t *)&r->this_stream->ce); + + pthread_mutex_unlock((pthread_mutex_t *)&r->this_stream->lock); + + return rv; +} diff --git a/Transceiver52M/device/ipc/ipc_shm.h b/Transceiver52M/device/ipc/ipc_shm.h new file mode 100644 index 0000000..66f59c9 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_shm.h @@ -0,0 +1,43 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Eric Wild <ewild@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* +Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. +*/ +#ifndef IPC_SHM_H +#define IPC_SHM_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <shm.h> +#include <stdint.h> + +#ifdef __cplusplus +} +#endif + +struct ipc_shm_io { + volatile struct ipc_shm_raw_stream *this_stream; // plus num_buffers at end + volatile struct ipc_shm_raw_smpl_buf **volatile buf_ptrs; + uint32_t partial_read_begin_ptr; +}; + +struct ipc_shm_io *ipc_shm_init_consumer(struct ipc_shm_stream *s); +struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s); +void ipc_shm_close(struct ipc_shm_io *r); +int32_t ipc_shm_enqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data); +int32_t ipc_shm_tryenqueue(struct ipc_shm_io *r, uint64_t timestamp, uint32_t len_in_sps, uint16_t *data); +volatile struct ipc_shm_raw_smpl_buf *ipc_shm_dequeue(struct ipc_shm_io *r); +int32_t ipc_shm_read(struct ipc_shm_io *r, uint16_t *out_buf, uint32_t num_samples, uint64_t *timestamp, + uint32_t timeout_seconds); + +#endif // IPC_SHM_H diff --git a/Transceiver52M/device/ipc/ipc_sock.c b/Transceiver52M/device/ipc/ipc_sock.c new file mode 100644 index 0000000..1936674 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_sock.c @@ -0,0 +1,271 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#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 <debug.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> + +#include "shm.h" +#include "ipc-driver-test.h" + +extern volatile int ipc_exit_requested; +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 IPC msg type %d\n", msg_type); + rc = -EINVAL; + } + + return rc; +} + +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, + "IPC 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, + "IPC 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 ipc_sock_close(struct ipc_sock_state *state) +{ + struct osmo_fd *bfd = &state->conn_bfd; + + LOGP(DMAIN, LOGL_NOTICE, "IPC socket has LOST connection\n"); + + ipc_exit_requested = 1; + + 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); + } +} + +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 < (int)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 IPC_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 = (struct ipc_sock_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 IPC */ +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, + "ip clent connects but we already have " + "another active connection ?!?\n"); + /* We already have one IPC 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 */ + //IPC_tx_info_ind(); + + return 0; +} diff --git a/Transceiver52M/device/ipc/ipc_sock.h b/Transceiver52M/device/ipc/ipc_sock.h new file mode 100644 index 0000000..24c6756 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_sock.h @@ -0,0 +1,24 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#ifndef IPC_SOCK_H +#define IPC_SOCK_H + +#include "shm.h" +#include "ipc-driver-test.h" + +int ipc_sock_send(struct msgb *msg); +int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags); +void ipc_sock_close(struct ipc_sock_state *state); + +#endif // IPC_SOCK_H diff --git a/Transceiver52M/device/ipc/ipcb210.cpp b/Transceiver52M/device/ipc/ipcb210.cpp deleted file mode 100644 index 7d030bb..0000000 --- a/Transceiver52M/device/ipc/ipcb210.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "ipcb210.h" - -#include "trx_vty.h" -#include "Logger.h" -#include "Threads.h" -#include "Utils.h" -#include "IPCDevice.h" - - - - -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 IPC_b210(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); -} diff --git a/Transceiver52M/device/ipc/ipcb210.h b/Transceiver52M/device/ipc/ipcb210.h deleted file mode 100644 index fbcf1da..0000000 --- a/Transceiver52M/device/ipc/ipcb210.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef IPC_B210_H -#define IPC_B210_H - -#include "magicwrap.h" -#include "IPCDevice.h" -#include "../uhd/UHDDevice.h" - - - - -class IPC_b210 : public IPCDevice { - magicwrap m; - public: - template<typename... Args> - IPC_b210(Args... args): IPCDevice(args...){ - //drvtest::main(0,0); - } - virtual ~IPC_b210() {}; - - int foo(){return 32;} -// void ipc_sock_close() override {}; -}; - -#endif // IPC_B210_H diff --git a/Transceiver52M/device/ipc/magicwrap.cpp b/Transceiver52M/device/ipc/magicwrap.cpp deleted file mode 100644 index bc4eb26..0000000 --- a/Transceiver52M/device/ipc/magicwrap.cpp +++ /dev/null @@ -1,20 +0,0 @@ -//has to come first outside of NS, or wrapping of c headers fails due to wrong order -#include <cstdlib> -#include <chrono> -#include "magicwrap.h" - -namespace drvtest -{ -extern "C" { -#define IPCMAGIC -#include "ipc-driver-test.c" -} -} // namespace drvtest - -char *argv[] = { { "magic" } }; -magicwrap::magicwrap() -{ - t = new std::thread([] { drvtest::magicmain(1, argv); }); - // give the thread some time to start and set up - std::this_thread::sleep_for(std::chrono::seconds(1)); -} diff --git a/Transceiver52M/device/ipc/magicwrap.h b/Transceiver52M/device/ipc/magicwrap.h deleted file mode 100644 index 2aa6d23..0000000 --- a/Transceiver52M/device/ipc/magicwrap.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef MAGICWRAP_H -#define MAGICWRAP_H - -#include <thread> - - -class magicwrap -{ - std::thread* t; -public: - magicwrap(); -}; - -#endif // MAGICWRAP_H diff --git a/Transceiver52M/device/ipc/shm.c b/Transceiver52M/device/ipc/shm.c index 6b16f8d..33f2a4a 100644 --- a/Transceiver52M/device/ipc/shm.c +++ b/Transceiver52M/device/ipc/shm.c @@ -1,3 +1,17 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Pau Espin Pedrol <pespin@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ + #include <stdint.h> #include <string.h> #include <stdlib.h> @@ -6,117 +20,146 @@ #include "shm.h" +//#define ENCDECDEBUG + /* 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) +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) { - unsigned 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; + unsigned 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++) { +#ifdef ENCDECDEBUG + fprintf(stderr, "decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]); +#endif + 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 *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_channel *chan; + chan = talloc_zero(tall_ctx, struct ipc_shm_channel); + if (!chan) + return NULL; +#ifdef ENCDECDEBUG + fprintf(stderr, "decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset); +#endif + 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) { - unsigned 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); + unsigned 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; + root->num_chans = root_raw->num_chans; + for (i = 0; i < root->num_chans; i++) { +#ifdef ENCDECDEBUG + fprintf(stderr, "decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]); +#endif + 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) +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; + uint8_t *start = (uint8_t *)smpl_buf_raw; + unsigned int offset = sizeof(struct ipc_shm_raw_smpl_buf); + offset += (((uintptr_t)offset + 7) & ~0x07ULL); +#ifdef ENCDECDEBUG + fprintf(stderr, "encode: smpl_buf at offset %lu\n", (start - (uint8_t *)root_raw)); +#endif + offset += buffer_size * sizeof(uint16_t) * 2; /* 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) +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) { - unsigned int i; - ptrdiff_t start = (ptrdiff_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 - (ptrdiff_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 - (ptrdiff_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 i; + ptrdiff_t start = (ptrdiff_t)stream_raw; + unsigned int offset = sizeof(struct ipc_shm_raw_stream) + sizeof(uint32_t) * num_buffers; + offset += (((uintptr_t)offset + 7) & ~0x07ULL); +#ifdef ENCDECDEBUG + fprintf(stderr, "encode: stream at offset %lu\n", (start - (ptrdiff_t)root_raw)); +#endif + 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 - (ptrdiff_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) +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; + uint8_t *start = (uint8_t *)chan_raw; + unsigned int offset = sizeof(struct ipc_shm_raw_channel); + offset += (((uintptr_t)offset + 7) & ~0x07ULL); +#ifdef ENCDECDEBUG + fprintf(stderr, "encode: channel at offset %lu\n", (start - (uint8_t *)root_raw)); +#endif + 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) +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) { - unsigned i; - ptrdiff_t start = (ptrdiff_t)root_raw; - unsigned int offset = sizeof(struct ipc_shm_raw_region) + sizeof(uint32_t)*num_chans; + unsigned i; + uintptr_t start = (uintptr_t)root_raw; + unsigned int offset = sizeof(struct ipc_shm_raw_region) + sizeof(uint32_t) * num_chans; + offset += (((uintptr_t)offset + 7) & ~0x07ULL); - if (root_raw) - root_raw->num_chans = num_chans; - for (i = 0; i < num_chans; i++) { - uint32_t ofs = (start + offset - (ptrdiff_t)root_raw); - if (root_raw) - root_raw->chan_offset[i] = (start + offset - (ptrdiff_t)root_raw); - fprintf(stderr, "encode: channel %d chan_offset[i]=%u\n", i, ofs); - 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; + if (root_raw) + root_raw->num_chans = num_chans; + for (i = 0; i < num_chans; i++) { + uint32_t ofs = (start + offset - (uintptr_t)root_raw); + if (root_raw) + root_raw->chan_offset[i] = (start + offset - (uintptr_t)root_raw); +#ifdef ENCDECDEBUG + fprintf(stderr, "encode: channel %d chan_offset[i]=%u\n", i, ofs); +#endif + 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; } diff --git a/Transceiver52M/device/ipc/shm.h b/Transceiver52M/device/ipc/shm.h index 0ad0fd9..5ff8f2d 100644 --- a/Transceiver52M/device/ipc/shm.h +++ b/Transceiver52M/device/ipc/shm.h @@ -2,112 +2,73 @@ * Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * Author: Pau Espin Pedrol <pespin@sysmocom.de> * -* SPDX-License-Identifier: AGPL-3.0+ +* SPDX-License-Identifier: 0BSD * -* 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. - -*/ - -/* -https://www.softprayog.in/programming/interprocess-communication-using-posix-shared-memory-in-linux - -man shm_open: link with -lrt -Link with -pthread. - -#include <sys/mman.h> -#include <sys/stat.h> // For mode constants -#include <fcntl.h> // For O_* constants -#include <semaphore.h> - -On start: -int fd = shm_open(const char *name, int oflag, mode_t mode); -* name must start with "/" and not contain more slashes. -* name must be a null-terminted string of up to NAME_MAX (255) -* oflag: O_CREAT|O_RDWR|O_EXCL -* mode: check man open - -ftruncate(fd, len = sizeof(struct myshamemorystruct) to expand the memory region - -shm = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); - -int sem_init(sem_t *&(shm->sem), 1, unsigned int value) == 0; - -close(fd); // After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. - - -On exit: -int shm_unlink(const char *name); - - -sem_t *sem_open(const char *name, int oflag, - mode_t mode, unsigned int value); -* by a name of the form /somename; that is, a null-terminated string of up to NAME_MAX-4 (i.e., 251) characters - consisting of an initial slash, followed by one or more characters, none of which are slashes. - - -* unamed semaphore: sem_init + sem_destroy -* Programs using the POSIX semaphores API must be compiled with cc -pthread to link against the real-time library, librt + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. */ +#pragma once #include <stdint.h> #include <unistd.h> #include <limits.h> +//#include <pthread.h> +#include <semaphore.h> /* RAW structures */ struct ipc_shm_raw_smpl_buf { - uint32_t timestamp; - uint32_t data_len; /* In samples */ - uint16_t samples[0]; + uint64_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]; + pthread_mutex_t lock; + pthread_cond_t cf; + pthread_cond_t ce; + 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; + uint32_t dl_buf_offset; + uint32_t ul_buf_offset; }; struct ipc_shm_raw_region { - uint32_t num_chans; - uint32_t chan_offset[0]; + 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]; + uint32_t num_buffers; + uint32_t buffer_size; + volatile struct ipc_shm_raw_stream *raw; + volatile struct ipc_shm_raw_smpl_buf *buffers[0]; }; struct ipc_shm_channel { - struct ipc_shm_stream *dl_stream; - struct ipc_shm_stream *ul_stream; + 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]; + 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); +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 */ @@ -121,12 +82,12 @@ struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_ #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 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 @@ -136,113 +97,128 @@ struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_ #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)); + 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)); + 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)); + char chan_ipc_sk_path[108]; +} __attribute__((packed)); struct ipc_sk_if_greeting { - uint8_t req_version; -} __attribute__ ((packed)); + uint8_t req_version; +} __attribute__((packed)); struct ipc_sk_if_info_req { - uint8_t spare; -} __attribute__ ((packed)); + 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)); + uint32_t feature_mask; + double min_rx_gain; + double max_rx_gain; + double min_tx_gain; + double max_tx_gain; + double iq_scaling_val_rx; + double iq_scaling_val_tx; + 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)); + uint32_t num_chans; + uint32_t clockref; /* One of FEATUER_MASK_CLOCKREF_* */ + uint32_t rx_sample_freq_num; + uint32_t rx_sample_freq_den; + uint32_t tx_sample_freq_num; + uint32_t tx_sample_freq_den; + 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)); + uint8_t return_code; + uint32_t path_delay; + 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]; + 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; + 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)); - +} __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 +#define IPC_IF_CHAN_MSG_OFFSET 50 +#define IPC_IF_MSG_START_REQ IPC_IF_CHAN_MSG_OFFSET + 0x00 +#define IPC_IF_MSG_START_CNF IPC_IF_CHAN_MSG_OFFSET + 0x01 +#define IPC_IF_MSG_STOP_REQ IPC_IF_CHAN_MSG_OFFSET + 0x02 +#define IPC_IF_MSG_STOP_CNF IPC_IF_CHAN_MSG_OFFSET + 0x03 +#define IPC_IF_MSG_SETGAIN_REQ IPC_IF_CHAN_MSG_OFFSET + 0x04 +#define IPC_IF_MSG_SETGAIN_CNF IPC_IF_CHAN_MSG_OFFSET + 0x05 +#define IPC_IF_MSG_SETFREQ_REQ IPC_IF_CHAN_MSG_OFFSET + 0x06 +#define IPC_IF_MSG_SETFREQ_CNF IPC_IF_CHAN_MSG_OFFSET + 0x07 + +#define IPC_IF_NOTIFY_UNDERFLOW IPC_IF_CHAN_MSG_OFFSET + 0x08 +#define IPC_IF_NOTIFY_OVERFLOW IPC_IF_CHAN_MSG_OFFSET + 0x09 struct ipc_sk_chan_if_op_void { -} __attribute__ ((packed)); + // at least one dummy byte, to allow c/c++ compatibility + uint8_t dummy; +} __attribute__((packed)); struct ipc_sk_chan_if_op_rc { - uint8_t return_code; -} __attribute__ ((packed)); + uint8_t return_code; +} __attribute__((packed)); struct ipc_sk_chan_if_gain { - double gain; - uint8_t is_tx; -} __attribute__ ((packed)); + double gain; + uint8_t is_tx; +} __attribute__((packed)); struct ipc_sk_chan_if_freq_req { - double freq; - uint8_t is_tx; -} __attribute__ ((packed)); + double freq; + uint8_t is_tx; +} __attribute__((packed)); struct ipc_sk_chan_if_freq_cnf { - uint8_t return_code; -} __attribute__ ((packed)); + uint8_t return_code; +} __attribute__((packed)); + +struct ipc_sk_chan_if_notfiy { + uint8_t dummy; +} __attribute__((packed)); struct ipc_sk_chan_if { - uint8_t msg_type; /* message type */ - uint8_t spare[2]; + 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; + 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; + struct ipc_sk_chan_if_notfiy notify; } u; -} __attribute__ ((packed)); +} __attribute__((packed)); diff --git a/Transceiver52M/device/ipc/uhdwrap.cpp b/Transceiver52M/device/ipc/uhdwrap.cpp new file mode 100644 index 0000000..1175bd1 --- /dev/null +++ b/Transceiver52M/device/ipc/uhdwrap.cpp @@ -0,0 +1,243 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Eric Wild <ewild@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +extern "C" { +#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" +#include "ipc_shm.h" +#include "ipc-driver-test.h" +} +#include "../uhd/UHDDevice.h" +#include "uhdwrap.h" + +#include "trx_vty.h" +#include "Logger.h" +#include "Threads.h" +#include "Utils.h" + +int uhd_wrap::open(const std::string &args, int ref, bool swap_channels) +{ + int rv = uhd_device::open(args, ref, swap_channels); + samps_per_buff_rx = rx_stream->get_max_num_samps(); + samps_per_buff_tx = tx_stream->get_max_num_samps(); + channel_count = usrp_dev->get_rx_num_channels(); + + wrap_rx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * samps_per_buff_rx)); + for (size_t i = 0; i < wrap_rx_buffs.size(); i++) + wrap_rx_buf_ptrs.push_back(&wrap_rx_buffs[i].front()); + + wrap_tx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * 5000)); + for (size_t i = 0; i < wrap_tx_buffs.size(); i++) + wrap_tx_buf_ptrs.push_back(&wrap_tx_buffs[i].front()); + + return rv; +} + +uhd_wrap::~uhd_wrap() +{ + // drvtest::gshutdown = 1; + //t->join(); +} + +size_t uhd_wrap::bufsizerx() +{ + return samps_per_buff_rx; +} + +size_t uhd_wrap::bufsizetx() +{ + return samps_per_buff_tx; +} + +int uhd_wrap::chancount() +{ + return channel_count; +} + +int uhd_wrap::wrap_read(TIMESTAMP *timestamp) +{ + uhd::rx_metadata_t md; + size_t num_rx_samps = rx_stream->recv(wrap_rx_buf_ptrs, samps_per_buff_rx, md, 0.1, true); + *timestamp = md.time_spec.to_ticks(rx_rate); + return num_rx_samps; //uhd_device::readSamples(bufs, len, overrun, timestamp, underrun); +} + +extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req) +{ + unsigned int rx_sps, tx_sps; + + /* FIXME: dev arg string* */ + /* FIXME: rx frontend bw? */ + /* FIXME: tx frontend bw? */ + ReferenceType cref; + switch (open_req->clockref) { + case FEATURE_MASK_CLOCKREF_EXTERNAL: + cref = ReferenceType::REF_EXTERNAL; + break; + case FEATURE_MASK_CLOCKREF_INTERNAL: + default: + cref = ReferenceType::REF_INTERNAL; + break; + } + + std::vector<std::string> tx_paths; + std::vector<std::string> rx_paths; + for (unsigned int i = 0; i < open_req->num_chans; i++) { + tx_paths.push_back(open_req->chan_info[i].tx_path); + rx_paths.push_back(open_req->chan_info[i].rx_path); + } + + rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den; + tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den; + uhd_wrap *uhd_wrap_dev = + new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths); + uhd_wrap_dev->open("", cref, false); + + return uhd_wrap_dev; +} +extern "C" int32_t uhdwrap_get_bufsizerx(void *dev) +{ + uhd_wrap *d = (uhd_wrap *)dev; + return d->bufsizerx(); +} + +extern "C" int32_t uhdwrap_get_timingoffset(void *dev) +{ + uhd_wrap *d = (uhd_wrap *)dev; + return d->getTimingOffset(); +} + +extern "C" int32_t uhdwrap_read(void *dev, uint32_t num_chans) +{ + TIMESTAMP t; + uhd_wrap *d = (uhd_wrap *)dev; + + if (num_chans != d->wrap_rx_buf_ptrs.size()) { + perror("omg chans?!"); + } + + int32_t read = d->wrap_read(&t); + if (read < 0) + return read; + + for (uint32_t i = 0; i < num_chans; i++) { + ipc_shm_enqueue(ios_rx_from_device[i], t, read, (uint16_t *)&d->wrap_rx_buffs[i].front()); + } + return read; +} + +extern "C" int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun) +{ + uhd_wrap *d = (uhd_wrap *)dev; + + uint64_t timestamp; + int32_t len; + for (uint32_t i = 0; i < num_chans; i++) { + len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, ×tamp, 1); + if (len < 0) + return 0; + } + + return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp); +} + +extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx) +{ + uhd_wrap *d = (uhd_wrap *)dev; + if (for_tx) + return d->setTxFreq(f, chan); + else + return d->setRxFreq(f, chan); +} + +extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx) +{ + uhd_wrap *d = (uhd_wrap *)dev; + if (for_tx) + return d->setTxGain(f, chan); + else + return d->setRxGain(f, chan); +} + +extern "C" int32_t uhdwrap_start(void *dev, int chan) +{ + uhd_wrap *d = (uhd_wrap *)dev; + return d->start(); +} + +extern "C" int32_t uhdwrap_stop(void *dev, int chan) +{ + uhd_wrap *d = (uhd_wrap *)dev; + return d->stop(); +} + +extern "C" void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim) +{ + struct ipc_sk_if_info_chan *chan_info; + + uhd::device_addr_t args(""); + uhd::device_addrs_t devs_found = uhd::device::find(args); + if (devs_found.size() < 1) { + std::cout << "\n No device found!"; + exit(0); + } + + uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(devs_found[0]); + auto rxchans = usrp->get_rx_num_channels(); + auto txchans = usrp->get_tx_num_channels(); + auto rx_range = usrp->get_rx_gain_range(); + auto tx_range = usrp->get_tx_gain_range(); + + //auto nboards = usrp->get_num_mboards(); + auto refs = usrp->get_clock_sources(0); + auto devname = usrp->get_mboard_name(0); + + ipc_prim->u.info_cnf.feature_mask = 0; + if (std::find(refs.begin(), refs.end(), "internal") != refs.end()) + ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_INTERNAL; + if (std::find(refs.begin(), refs.end(), "external") != refs.end()) + ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_EXTERNAL; + + // at least one duplex channel + auto num_chans = rxchans == txchans ? txchans : 1; + + ipc_prim->u.info_cnf.min_rx_gain = rx_range.start(); + ipc_prim->u.info_cnf.max_rx_gain = rx_range.stop(); + ipc_prim->u.info_cnf.min_tx_gain = tx_range.start(); + ipc_prim->u.info_cnf.max_tx_gain = tx_range.stop(); + ipc_prim->u.info_cnf.iq_scaling_val_rx = 0.3; + ipc_prim->u.info_cnf.iq_scaling_val_tx = 1; + ipc_prim->u.info_cnf.max_num_chans = num_chans; + OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, devname.c_str()); + chan_info = ipc_prim->u.info_cnf.chan_info; + for (unsigned int i = 0; i < ipc_prim->u.info_cnf.max_num_chans; i++) { + auto rxant = usrp->get_rx_antennas(i); + auto txant = usrp->get_tx_antennas(i); + for (unsigned int j = 0; j < txant.size(); j++) { + OSMO_STRLCPY_ARRAY(chan_info->tx_path[j], txant[j].c_str()); + } + for (unsigned int j = 0; j < rxant.size(); j++) { + OSMO_STRLCPY_ARRAY(chan_info->rx_path[j], rxant[j].c_str()); + } + chan_info++; + } +} diff --git a/Transceiver52M/device/ipc/uhdwrap.h b/Transceiver52M/device/ipc/uhdwrap.h new file mode 100644 index 0000000..f1152c8 --- /dev/null +++ b/Transceiver52M/device/ipc/uhdwrap.h @@ -0,0 +1,79 @@ +/* +* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> +* Author: Eric Wild <ewild@sysmocom.de> +* +* SPDX-License-Identifier: 0BSD +* + Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. +*/ +#ifndef IPC_UHDWRAP_H +#define IPC_UHDWRAP_H + +#ifdef __cplusplus +#include "../uhd/UHDDevice.h" + +class uhd_wrap : public uhd_device { + public: + // std::thread *t; + size_t samps_per_buff_rx; + size_t samps_per_buff_tx; + int channel_count; + + std::vector<std::vector<short> > wrap_rx_buffs; + std::vector<std::vector<short> > wrap_tx_buffs; + std::vector<short *> wrap_rx_buf_ptrs; + std::vector<short *> wrap_tx_buf_ptrs; + + template <typename... Args> uhd_wrap(Args... args) : uhd_device(args...) + { + // t = new std::thread(magicthread); + // give the thread some time to start and set up + // std::this_thread::sleep_for(std::chrono::seconds(1)); + } + virtual ~uhd_wrap(); + + // void ipc_sock_close() override {}; + int wrap_read(TIMESTAMP *timestamp); + virtual int open(const std::string &args, int ref, bool swap_channels) override; + + // bool start() override; + // bool stop() override; + // virtual TIMESTAMP initialWriteTimestamp() override; + // virtual TIMESTAMP initialReadTimestamp() override; + + int getTimingOffset() + { + return ts_offset; + } + size_t bufsizerx(); + size_t bufsizetx(); + int chancount(); +}; +#else +void *uhdwrap_open(struct ipc_sk_if_open_req *open_req); + +int32_t uhdwrap_get_bufsizerx(void *dev); + +int32_t uhdwrap_get_timingoffset(void *dev); + +int32_t uhdwrap_read(void *dev, uint32_t num_chans); + +int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun); + +double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx); + +double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx); + +int32_t uhdwrap_start(void *dev, int chan); + +int32_t uhdwrap_stop(void *dev, int chan); + +void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim); +#endif + +#endif // IPC_B210_H diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp index b5dda28..037f3d9 100644 --- a/Transceiver52M/device/uhd/UHDDevice.cpp +++ b/Transceiver52M/device/uhd/UHDDevice.cpp @@ -1316,6 +1316,7 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata) return ost.str(); } +#ifndef IPCMAGIC 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, @@ -1323,3 +1324,4 @@ RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, { return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); } +#endif diff --git a/Transceiver52M/device/uhd/UHDDevice.h b/Transceiver52M/device/uhd/UHDDevice.h index 22a0948..c159e63 100644 --- a/Transceiver52M/device/uhd/UHDDevice.h +++ b/Transceiver52M/device/uhd/UHDDevice.h @@ -133,7 +133,7 @@ public: ERROR_UNHANDLED = -4, }; -private: +protected: uhd::usrp::multi_usrp::sptr usrp_dev; uhd::tx_streamer::sptr tx_stream; uhd::rx_streamer::sptr rx_stream; |