diff options
author | Eric Wild <ewild@sysmocom.de> | 2020-03-24 17:19:27 +0100 |
---|---|---|
committer | Eric <ewild@sysmocom.de> | 2020-08-25 01:00:03 +0200 |
commit | 1e17c4fb0a77dcee49a7cd566dca7e8f48f6d8e7 (patch) | |
tree | 9e680f91408ff7741fe62d7689ad420c1f48648a /Transceiver52M/device/ipc/ipc_chan.c | |
parent | f9a2f4427277f36b9459cd349512f6ca3230fe9c (diff) |
osmo-trx-ipc
This adds a IPC backend that uses shared memory interface
to communicate with (proprietary) devices.
Requires config file option
dev-args ipc_msock=/path/to/socket
to specify the master socket the ipc backend should connect to.
If UHD is avaialble the ipc-driver-test tool can be used to test the
backend with a uhd device, this was so far only tested with a b2xx.
Change-Id: Ice63d3499026293ade8aad675ff7a883bcdd5756
Diffstat (limited to 'Transceiver52M/device/ipc/ipc_chan.c')
-rw-r--r-- | Transceiver52M/device/ipc/ipc_chan.c | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/Transceiver52M/device/ipc/ipc_chan.c b/Transceiver52M/device/ipc/ipc_chan.c new file mode 100644 index 0000000..9e35fb2 --- /dev/null +++ b/Transceiver52M/device/ipc/ipc_chan.c @@ -0,0 +1,259 @@ +/* +* 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; + case IPC_IF_MSG_SETTXATTN_REQ: + rc = ipc_rx_chan_settxatten_req(&ipc_prim->u.txatten_req, chan_nr); + break; + default: + LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type 0x%02x on chan %d\n", msg_type, chan_nr); + 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(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(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(DDEV, 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(DDEV, 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(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 dequeue */ + 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(DDEV, LOGL_ERROR, "Failed to accept a new connection\n"); + return -1; + } + + if (conn_bfd->fd >= 0) { + LOGP(DDEV, 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; + + /* copy chan nr, required for proper bfd<->chan # mapping */ + conn_bfd->priv_nr = bfd->priv_nr; + + if (osmo_fd_register(conn_bfd) != 0) { + LOGP(DDEV, LOGL_ERROR, + "Failed to register new connection " + "fd\n"); + close(conn_bfd->fd); + conn_bfd->fd = -1; + return -1; + } + + LOGP(DDEV, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n"); + + return 0; +} |