Browse Source
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: Ice63d3499026293ade8aad675ff7a883bcdd5756changes/41/19641/11
30 changed files with 3765 additions and 10 deletions
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de> |
||||
* Author: Pau Espin Pedrol <pespin@sysmocom.de> |
||||
* |
||||
* SPDX-License-Identifier: AGPL-3.0+ |
||||
* |
||||
* This program is free software: you can redistribute it and/or modify |
||||
* it under the terms of the GNU Affero General Public License as published by |
||||
* the Free Software Foundation, either version 3 of the License, or |
||||
* (at your option) any later version. |
||||
* |
||||
* This program is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||
* GNU Affero General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Affero General Public License |
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
* See the COPYING file in the main directory for details. |
||||
*/ |
||||
|
||||
#ifndef _IPC_DEVICE_H_ |
||||
#define _IPC_DEVICE_H_ |
||||
|
||||
#include <cstdint> |
||||
#include <cstddef> |
||||
#include <climits> |
||||
#include <string> |
||||
|
||||
#ifdef HAVE_CONFIG_H |
||||
#include "config.h" |
||||
#endif |
||||
|
||||
extern "C" { |
||||
#include <osmocom/core/select.h> |
||||
#include <osmocom/core/timer.h> |
||||
#include "shm.h" |
||||
} |
||||
|
||||
#include "radioDevice.h" |
||||
|
||||
class smpl_buf; |
||||
|
||||
#define IPC_MAX_NUM_TRX 8 |
||||
|
||||
struct ipc_per_trx_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 */ |
||||
uint32_t messages_processed_mask; // (=| IPC_IF_MSG_xxx-IPC_IF_CHAN_MSG_OFFSET) bitmask
|
||||
ipc_per_trx_sock_state() : conn_bfd(), timer(), upqueue(), messages_processed_mask() |
||||
{ |
||||
conn_bfd.fd = -1; |
||||
} |
||||
}; |
||||
|
||||
class IPCDevice : public RadioDevice { |
||||
protected: |
||||
struct ipc_per_trx_sock_state master_sk_state; |
||||
|
||||
std::vector<struct ipc_per_trx_sock_state> sk_chan_state; |
||||
|
||||
uint32_t tx_attenuation[IPC_MAX_NUM_TRX]; |
||||
uint8_t tmp_state; |
||||
char shm_name[SHM_NAME_MAX]; |
||||
int ipc_shm_connect(const char *shm_name); |
||||
void *shm; |
||||
struct ipc_shm_region *shm_dec; |
||||
|
||||
std::vector<smpl_buf *> rx_buffers; |
||||
double actualSampleRate; |
||||
|
||||
bool started; |
||||
|
||||
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 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); |
||||
void manually_poll_sock_fds(); |
||||
|
||||
void ipc_sock_close(ipc_per_trx_sock_state *state); |
||||
|
||||
int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim); |
||||
int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf); |
||||
int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf); |
||||
int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf); |
||||
int ipc_tx_open_req(struct ipc_per_trx_sock_state *state, uint32_t num_chans, uint32_t ref); |
||||
|
||||
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); |
||||
int ipc_rx_chan_settxattn_cnf(ipc_sk_chan_if_tx_attenuation *ret, uint8_t chan_nr); |
||||
|
||||
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id); |
||||
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect); |
||||
|
||||
public: |
||||
int ipc_sock_read(struct osmo_fd *bfd); |
||||
int ipc_sock_write(struct osmo_fd *bfd); |
||||
int ipc_chan_sock_read(osmo_fd *bfd); |
||||
int ipc_chan_sock_write(osmo_fd *bfd); |
||||
|
||||
/** 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() override; |
||||
|
||||
/** Instantiate the IPC */ |
||||
virtual int open(const std::string &args, int ref, bool swap_channels) override; |
||||
|
||||
/** Start the IPC */ |
||||
virtual bool start() override; |
||||
|
||||
/** Stop the IPC */ |
||||
virtual bool stop() override; |
||||
|
||||
/* FIXME: any != USRP1 will do for now... */ |
||||
enum TxWindowType getWindowType() override |
||||
{ |
||||
return TX_WINDOW_LMS1; |
||||
} |
||||
|
||||
/**
|
||||
Read samples from the IPC. |
||||
@param buf preallocated buf to contain read result |
||||
@param len number of samples desired |
||||
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough |
||||
@param timestamp The timestamp of the first samples to be read |
||||
@param underrun Set if IPC 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) override; |
||||
/**
|
||||
Write samples to the IPC. |
||||
@param buf Contains the data to be written. |
||||
@param len number of samples to write. |
||||
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough |
||||
@param timestamp The timestamp of the first sample of the data buffer. |
||||
@return The number of samples actually written |
||||
*/ |
||||
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) override; |
||||
|
||||
/** Set the transmitter frequency */ |
||||
virtual bool setTxFreq(double wFreq, size_t chan = 0) override; |
||||
|
||||
/** Set the receiver frequency */ |
||||
virtual bool setRxFreq(double wFreq, size_t chan = 0) override; |
||||
|
||||
/** Returns the starting write Timestamp*/ |
||||
virtual TIMESTAMP initialWriteTimestamp(void) override; |
||||
|
||||
/** Returns the starting read Timestamp*/ |
||||
virtual TIMESTAMP initialReadTimestamp(void) override; |
||||
|
||||
/** returns the full-scale transmit amplitude **/ |
||||
virtual double fullScaleInputValue() override |
||||
{ |
||||
return (double)SHRT_MAX * current_info_cnf.iq_scaling_val_rx; |
||||
} |
||||
|
||||
/** returns the full-scale receive amplitude **/ |
||||
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) override; |
||||
|
||||
/** get the current receive gain */ |
||||
virtual double getRxGain(size_t chan = 0) override |
||||
{ |
||||
return rx_gains[chan]; |
||||
} |
||||
|
||||
/** return maximum Rx Gain **/ |
||||
virtual double maxRxGain(void) override; |
||||
|
||||
/** return minimum Rx Gain **/ |
||||
virtual double minRxGain(void) override; |
||||
|
||||
double setPowerAttenuation(int atten, size_t chan) override; |
||||
double getPowerAttenuation(size_t chan = 0) override; |
||||
|
||||
virtual int getNominalTxPower(size_t chan = 0) 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) override; |
||||
|
||||
/* return the used RX path */ |
||||
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) override; |
||||
|
||||
/* return the used RX path */ |
||||
virtual std::string getTxAntenna(size_t chan = 0) override; |
||||
|
||||
/** return whether user drives synchronization of Tx/Rx of USRP */ |
||||
virtual bool requiresRadioAlign() override; |
||||
|
||||
/** 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) override |
||||
{ |
||||
return 0; |
||||
} |
||||
virtual inline double getRxFreq(size_t chan = 0) override |
||||
{ |
||||
return 0; |
||||
} |
||||
virtual inline double getSampleRate() override |
||||
{ |
||||
return actualSampleRate; |
||||
} |
||||
}; |
||||
|
||||
#endif // _IPC_DEVICE_H_
|
@ -0,0 +1,42 @@
|
||||
include $(top_srcdir)/Makefile.common |
||||
|
||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||
AM_LDFLAGS = -lpthread -lrt
|
||||
|
||||
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
|
||||
|
||||
if DEVICE_UHD |
||||
noinst_HEADERS += ../uhd/UHDDevice.h uhdwrap.h ipc-driver-test.h
|
||||
endif |
||||
|
||||
noinst_LTLIBRARIES = libdevice.la
|
||||
|
||||
libdevice_la_SOURCES = IPCDevice.cpp shm.c 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) -DIPCMAGIC
|
||||
|
||||
if DEVICE_UHD |
||||
|
||||
#work around distclean issue on older autotools vers:
|
||||
#a direct build of ../uhd/UHDDevice.cpp tries to clean
|
||||
#../uhd/.dep/UHDDevice.Plo twice and fails
|
||||
uhddev_ipc.cpp: |
||||
echo "#include \"../uhd/UHDDevice.cpp\"" >$@
|
||||
CLEANFILES= uhddev_ipc.cpp
|
||||
|
||||
bin_PROGRAMS = ipc-driver-test
|
||||
#ipc_driver_test_SHORTNAME = drvt
|
||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c uhddev_ipc.cpp
|
||||
ipc_driver_test_LDADD = \
|
||||
shm.lo \
|
||||
$(LIBOSMOCORE_LIBS) \
|
||||
$(LIBOSMOCTRL_LIBS) \
|
||||
$(LIBOSMOVTY_LIBS)
|
||||
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
|
||||
endif |
@ -0,0 +1,477 @@
|
||||
/*
|
||||
* 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. |
||||
*/ |
||||
|
||||
#define _GNU_SOURCE |
||||
#include <pthread.h> |
||||
|
||||
#include <debug.h> |
||||
|
||||
#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 <getopt.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_shm.h" |
||||
#include "ipc_chan.h" |
||||
#include "ipc_sock.h" |
||||
|
||||
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2" |
||||
|
||||
static void *tall_ctx; |
||||
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; |
||||
|
||||
static const struct log_info_cat default_categories[] = { |
||||
[DMAIN] = { |
||||
.name = "DMAIN", |
||||
.color = NULL, |
||||
.description = "Main generic category", |
||||
.loglevel = LOGL_DEBUG,.enabled = 1, |
||||
}, |
||||
[DDEV] = { |
||||
.name = "DDEV", |
||||
.description = "Device/Driver specific code", |
||||
.color = NULL, |
||||
.enabled = 1, .loglevel = LOGL_DEBUG, |
||||
}, |
||||
}; |
||||
|
||||
const struct log_info log_infox = { |
||||
.cat = default_categories, |
||||
.num_cat = ARRAY_SIZE(default_categories), |
||||
}; |
||||
|
||||
#include "uhdwrap.h" |
||||
|
||||
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); |
||||
err_shm_open: |
||||
return rc; |
||||
} |
||||
|
||||
struct msgb *ipc_msgb_alloc(uint8_t msg_type) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_if *ipc_prim; |
||||
|
||||
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx"); |
||||
if (!msg) |
||||
return NULL; |
||||
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000); |
||||
ipc_prim = (struct ipc_sk_if *)msg->data; |
||||
ipc_prim->msg_type = msg_type; |
||||
|
||||
return msg; |
||||
} |
||||
|
||||
static int ipc_tx_greeting_cnf(uint8_t req_version) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_if *ipc_prim; |
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF); |
||||
if (!msg) |
||||
return -ENOMEM; |
||||
ipc_prim = (struct ipc_sk_if *)msg->data; |
||||
ipc_prim->u.greeting_cnf.req_version = req_version; |
||||
|
||||
return ipc_sock_send(msg); |
||||
} |
||||
|
||||
static int ipc_tx_info_cnf() |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_if *ipc_prim; |
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_CNF); |
||||
if (!msg) |
||||
return -ENOMEM; |
||||
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, int32_t timingoffset) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_if *ipc_prim; |
||||
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; |
||||
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_PREFIX, 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); |
||||
} |
||||
|
||||
int ipc_rx_greeting_req(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; |
||||
} |
||||
|
||||
int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req) |
||||
{ |
||||
ipc_tx_info_cnf(); |
||||
return 0; |
||||
} |
||||
|
||||
int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req) |
||||
{ |
||||
/* 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); |
||||
} |
||||
|
||||
ipc_tx_open_cnf(-rc, open_req->num_chans, uhdwrap_get_timingoffset(global_dev)); |
||||
return 0; |
||||
} |
||||
|
||||
volatile bool ul_running = false; |
||||
volatile bool dl_running = false; |
||||
|
||||
void *uplink_thread(void *x_void_ptr) |
||||
{ |
||||
uint32_t chann = decoded_region->num_chans; |
||||
ul_running = true; |
||||
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; |
||||
} |
||||
|
||||
void *downlink_thread(void *x_void_ptr) |
||||
{ |
||||
int chann = decoded_region->num_chans; |
||||
dl_running = true; |
||||
pthread_setname_np(pthread_self(), "downlink_tx"); |
||||
|
||||
while (!ipc_exit_requested) { |
||||
bool underrun; |
||||
uhdwrap_write(global_dev, chann, &underrun); |
||||
} |
||||
return 0; |
||||
} |
||||
|
||||
int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_chan_if *ipc_prim; |
||||
int rc = 0; |
||||
|
||||
rc = uhdwrap_start(global_dev, chan_nr); |
||||
|
||||
/* no per-chan start/stop */ |
||||
if (!dl_running || !ul_running) { |
||||
/* chan != first chan start will "fail", which is fine, usrp can't start/stop chans independently */ |
||||
if (rc) { |
||||
LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads.. req for chan:%d\n", chan_nr); |
||||
pthread_t rx, tx; |
||||
pthread_create(&rx, NULL, uplink_thread, 0); |
||||
pthread_create(&tx, NULL, downlink_thread, 0); |
||||
} |
||||
} else |
||||
LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads request ignored.. req for chan:%d\n", chan_nr); |
||||
|
||||
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; |
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr); |
||||
} |
||||
int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_chan_if *ipc_prim; |
||||
int rc; |
||||
|
||||
/* no per-chan start/stop */ |
||||
rc = uhdwrap_stop(global_dev, chan_nr); |
||||
|
||||
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; |
||||
|
||||
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) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_chan_if *ipc_prim; |
||||
double rv; |
||||
|
||||
rv = uhdwrap_set_gain(global_dev, req->gain, chan_nr, req->is_tx); |
||||
|
||||
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 ipc_chan_sock_send(msg, chan_nr); |
||||
} |
||||
|
||||
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_chan_if *ipc_prim; |
||||
bool rv; |
||||
|
||||
rv = uhdwrap_set_freq(global_dev, req->freq, chan_nr, req->is_tx); |
||||
|
||||
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 ipc_chan_sock_send(msg, chan_nr); |
||||
} |
||||
|
||||
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr) |
||||
{ |
||||
struct msgb *msg; |
||||
struct ipc_sk_chan_if *ipc_prim; |
||||
double rv; |
||||
|
||||
rv = uhdwrap_set_txatt(global_dev, req->attenuation, chan_nr); |
||||
|
||||
msg = ipc_msgb_alloc(IPC_IF_MSG_SETTXATTN_CNF); |
||||
if (!msg) |
||||
return -ENOMEM; |
||||
ipc_prim = (struct ipc_sk_chan_if *)msg->data; |
||||
ipc_prim->u.txatten_cnf.attenuation = rv; |
||||
|
||||
return ipc_chan_sock_send(msg, chan_nr); |
||||
} |
||||
|
||||
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; |
||||
int rc; |
||||
|
||||
state = talloc_zero(NULL, struct ipc_sock_state); |
||||
if (!state) |
||||
return -ENOMEM; |
||||
*global_state_var = state; |
||||
|
||||
INIT_LLIST_HEAD(&state->upqueue); |
||||
state->conn_bfd.fd = -1; |
||||
|
||||
bfd = &state->listen_bfd; |
||||
|
||||
bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND); |
||||
if (bfd->fd < 0) { |
||||
LOGP(DMAIN, LOGL_ERROR, "Could not create %s unix socket: %s\n", path, strerror(errno)); |
||||
talloc_free(state); |
||||
return -1; |
||||
} |
||||
|
||||
bfd->when = BSC_FD_READ; |
||||
bfd->cb = 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); |
||||
close(bfd->fd); |
||||
talloc_free(state); |
||||
return rc; |
||||
} |
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static void print_help(void) |
||||
{ |
||||
printf("ipc-driver-test Usage:\n" |
||||
" -h --help This message\n" |
||||
" -n --sock-num NR Master socket suffix number NR\n"); |
||||
} |
||||
|
||||
static int msocknum = 0; |
||||
|
||||
static void handle_options(int argc, char **argv) |
||||
{ |
||||
while (1) { |
||||
int option_index = 0, c; |
||||
const struct option long_options[] = { { "help", 0, 0, 'h' }, |
||||
{ "sock-num", 1, 0, 'n' }, |
||||
{ 0, 0, 0, 0 } }; |
||||
|
||||
c = getopt_long(argc, argv, "hn:", long_options, &option_index); |
||||
if (c == -1) |
||||
break; |
||||
|
||||
switch (c) { |
||||
case 'h': |
||||
print_help(); |
||||
exit(0); |
||||
break; |
||||
case 'n': |
||||
msocknum = atoi(optarg); |
||||
break; |
||||
default: |
||||
exit(2); |
||||
break; |
||||
} |
||||
} |
||||
|
||||
if (argc > optind) { |
||||
fprintf(stderr, "Unsupported positional arguments on command line\n"); |
||||
exit(2); |
||||
} |
||||
} |
||||
|
||||
int main(int argc, char **argv) |
||||
{ |
||||
char ipc_msock_path[sizeof(IPC_SOCK_PATH_PREFIX) + 3]; |
||||
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(); |
||||
|
||||
handle_options(argc, argv); |
||||
snprintf(ipc_msock_path, sizeof(ipc_msock_path), "%s%d", IPC_SOCK_PATH_PREFIX, msocknum); |
||||
|
||||
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]); |
||||
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0); |
||||
while (!ipc_exit_requested) |
||||
osmo_select_main(0); |
||||
|
||||
if (global_dev) |
||||
for (unsigned int i = 0; i < decoded_region->num_chans; i++) |
||||
uhdwrap_stop(global_dev, i); |
||||
|
||||
ipc_sock_close(global_ipc_sock_state); |
||||
return 0; |
||||
} |
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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 */ |
||||
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); |
||||
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr); |
@ -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; |
||||
} |
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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
|
@ -0,0 +1,200 @@
|
||||
/*
|
||||
* 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> |
||||
#include <osmocom/core/panic.h> |
||||
|
||||
#include <debug.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) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_mutex_init((pthread_mutex_t *)&r->this_stream->lock, &att); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
pthread_mutexattr_destroy(&att); |
||||
|
||||
rv = pthread_condattr_setpshared(&t1, PTHREAD_PROCESS_SHARED); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->cf, &t1); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->ce, &t2); |
||||
if (rv != 0) { |
||||
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv); |
||||
} |
||||
|
||||
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) { |
||||
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; |
||||
} |
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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> |
||||