Timur Davydov has uploaded this change for review.
abis: add optional WebSDR E1 input driver
Add a new optional E1 input backend for WebSDR, enabled via
--with-websdr.
Introduce input/websdr.c and public callback API to replace
socket-based IPA transport with a callback-driven interface
for OML/RSL and OSMUX.
Intended for WebAssembly builds where Osmocom components run
in a browser and communicate with SDR via WebUSB.
Change-Id: Ib804dc8eb67a91678603f54ea0beccd5c61e4a80
---
M configure.ac
M include/Makefile.am
A include/osmocom/abis/websdr.h
M src/Makefile.am
M src/e1_input.c
M src/e1_input_vty.c
M src/input/ipaccess.c
A src/input/websdr.c
8 files changed, 775 insertions(+), 2 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-abis refs/changes/52/42652/1
diff --git a/configure.ac b/configure.ac
index e38201f..bc700d5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -194,6 +194,16 @@
fi # ENABLE_ORTP
+# Enable WebSDR Support
+AC_ARG_WITH(websdr, [
+ AS_HELP_STRING([--with-websdr],
+ [enable WebUSB based transceiver])
+])
+AM_CONDITIONAL(ENABLE_WEBSDR, [test "x$with_websdr" = "xyes"])
+AS_IF([test "x$with_websdr" = "xyes"], [
+ AC_DEFINE(ENABLE_WEBSDR, 1, Define to 1 for enable WebUSB based transceiver)
+])
+
AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"])
diff --git a/include/Makefile.am b/include/Makefile.am
index 36d3259..cec2432 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -40,3 +40,7 @@
if ENABLE_ORTP
nobase_include_HEADERS += osmocom/trau/osmo_ortp.h
endif
+
+if ENABLE_WEBSDR
+nobase_include_HEADERS += osmocom/abis/websdr.h
+endif
diff --git a/include/osmocom/abis/websdr.h b/include/osmocom/abis/websdr.h
new file mode 100644
index 0000000..2c43e6e
--- /dev/null
+++ b/include/osmocom/abis/websdr.h
@@ -0,0 +1,60 @@
+/* OpenBSC Abis input driver API for WebSDR */
+
+/* (C) 2026 by Timur Davydov <dtv.comp@gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * 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/>.
+ *
+ */
+
+#ifndef OSMO_ABIS_WEBSDR_H
+#define OSMO_ABIS_WEBSDR_H
+
+#include <unistd.h>
+
+/**
+ * @brief Callback function for sending media data via WebSDR.
+ * Call for sending media data to WebSDR client.
+ *
+ * @param msg Pointer to the message buffer.
+ * @param msglen Size of the message buffer.
+ * @return int Status code. 0 on success, negative on error.
+ */
+int ws_osmux_deliver_cb(const void *msg, unsigned msglen);
+
+/**
+ * @brief Callback function for sending ABIS messages via WebSDR.
+ * Call for sending ABIS messages to WebSDR client.
+ *
+ * @param ts_nr Transport stream number. 1 - ABIS OML messages, 2 - ABIS RSL messages.
+ * @param msg Pointer to the message buffer.
+ * @param msglen Size of the message buffer.
+ * @return int Status code. Send length on success, negative on error.
+ */
+int ws_ipa_send(unsigned ts_nr, const void *msg, unsigned msglen);
+
+/**
+ * @brief Callback function for asking RSL data via WebSDR.
+ * Call for asking RSL data from WebSDR client.
+ * The ws_ipa_push_raw_data will be called by the WebSDR client.
+ *
+ * @param ts_nr Transport stream number.
+ * @return int Status code. 0 on success, negative on error.
+ */
+int ws_rsl_connect_cb(unsigned ts_nr);
+
+#endif /* OSMO_ABIS_WEBSDR_H */
\ No newline at end of file
diff --git a/src/Makefile.am b/src/Makefile.am
index 4dc7cfe..4b77a52 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,6 +46,10 @@
libosmoabis_la_SOURCES += input/dahdi.c
endif
+if ENABLE_WEBSDR
+libosmoabis_la_SOURCES += input/websdr.c
+endif
+
libosmotrau_la_CFLAGS = $(AM_CFLAGS) $(ORTP_CFLAGS)
libosmotrau_la_LDFLAGS = $(AM_LDFLAGS) \
-version-info $(TRAU_LIBVERSION) \
diff --git a/src/e1_input.c b/src/e1_input.c
index c5ee38f..c80989a 100644
--- a/src/e1_input.c
+++ b/src/e1_input.c
@@ -1156,6 +1156,9 @@
void e1inp_ipaccess_init(void);
void e1inp_rs232_init(void);
void e1inp_unixsocket_init(void);
+#ifdef ENABLE_WEBSDR
+int e1inp_websdr_init(void);
+#endif /* ENABLE_WEBSDR */
void e1inp_init(void)
{
@@ -1174,4 +1177,7 @@
e1inp_ipaccess_init();
e1inp_rs232_init();
e1inp_unixsocket_init();
+#ifdef ENABLE_WEBSDR
+ e1inp_websdr_init();
+#endif /* ENABLE_WEBSDR */
}
diff --git a/src/e1_input_vty.c b/src/e1_input_vty.c
index de92359..291b7b9 100644
--- a/src/e1_input_vty.c
+++ b/src/e1_input_vty.c
@@ -42,11 +42,17 @@
#include <osmocom/abis/e1_input.h>
+#include "config.h"
+
#define X(x) (1 << x)
/* CONFIG */
+#ifndef ENABLE_WEBSDR
#define E1_DRIVER_NAMES "(misdn|misdn_lapd|dahdi|e1d|ipa|unixsocket)"
+#else /* ENABLE_WEBSDR */
+#define E1_DRIVER_NAMES "(misdn|misdn_lapd|dahdi|e1d|ipa|unixsocket|websdr)"
+#endif /* ENABLE_WEBSDR */
#define E1_DRIVER_HELP "mISDN supported E1 Card (kernel LAPD)\n" \
"mISDN supported E1 Card (userspace LAPD)\n" \
"DAHDI supported E1/T1/J1 Card\n" \
diff --git a/src/input/ipaccess.c b/src/input/ipaccess.c
index 6f3c980..28fd11f 100644
--- a/src/input/ipaccess.c
+++ b/src/input/ipaccess.c
@@ -25,6 +25,7 @@
*/
#include "internal.h"
+#include "config.h"
#include <stdio.h>
#include <unistd.h>
@@ -56,6 +57,10 @@
#include <osmocom/netif/ipa.h>
#include <osmocom/abis/e1_input.h>
+#ifdef ENABLE_WEBSDR
+#include <osmocom/abis/websdr.h>
+#endif /* ENABLE_WEBSDR */
+
/* global parameters of IPA input driver */
struct ipa_pars g_e1inp_ipaccess_pars;
@@ -1161,11 +1166,13 @@
const char *rem_addr, uint16_t rem_port,
uint8_t trx_nr)
{
- struct osmo_stream_cli *cli;
+ int rc;
struct e1inp_ts *e1i_ts = e1inp_line_ipa_rsl_ts(line, trx_nr);
+#ifndef ENABLE_WEBSDR
+ struct osmo_stream_cli *cli;
struct ipaccess_line *il;
char cli_name[128];
- int rc;
+#endif
if (E1INP_SIGN_RSL+trx_nr-1 >= NUM_E1_TS) {
LOGPITS(e1i_ts, DLINP, LOGL_ERROR, "cannot create RSL BTS link: "
@@ -1173,6 +1180,7 @@
return -EINVAL;
}
+#ifndef ENABLE_WEBSDR
/* Drop previous line */
if ((rc = e1inp_ipa_bts_rsl_close_n(line, trx_nr)) < 0)
return rc;
@@ -1219,6 +1227,14 @@
ipaccess_bts_keepalive_fsm_alloc(e1i_ts, cli, "rsl_bts_to_bsc");
il->ipa_cli[1 + trx_nr] = cli;
+#else /* ENABLE_WEBSDR */
+ rc = ws_rsl_connect_cb(E1INP_SIGN_RSL + trx_nr);
+ if (rc < 0) {
+ LOGP(DLINP, LOGL_ERROR, "cannot create RSL BTS link\n");
+ return -EIO;
+ }
+#endif /* ENABLE_WEBSDR */
+
return 0;
}
diff --git a/src/input/websdr.c b/src/input/websdr.c
new file mode 100644
index 0000000..1382f3b
--- /dev/null
+++ b/src/input/websdr.c
@@ -0,0 +1,667 @@
+/* OpenBSC Abis input driver for WebSDR */
+
+/* (C) 2026 by Timur Davydov <dtv.comp@gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "internal.h"
+#include "../config.h"
+
+#include <errno.h>
+
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/gsm/protocol/ipaccess.h>
+#include <osmocom/abis/e1_input.h>
+
+#include <osmocom/abis/websdr.h>
+
+#ifdef __EMSCRIPTEN__
+#include <emscripten.h>
+
+EM_JS(int, ws_osmux_deliver_cb_wrapper, (const void *msg, unsigned msglen), {
+ return ws_osmux_deliver_cb(msg, msglen);
+});
+
+EM_JS(int, ws_ipa_send_wrapper, (unsigned ts_nr, const void *msg, unsigned msglen), {
+ return ws_ipa_send(ts_nr, msg, msglen);
+});
+
+EM_JS(int, ws_rsl_connect_cb_wrapper, (unsigned ts_nr), {
+ return ws_rsl_connect_cb(ts_nr);
+});
+#endif /* __EMSCRIPTEN__ */
+
+#define CB_BUF_SZ 8192
+
+static struct e1inp_line *g_line_cb = NULL;
+static int write_suppressed = 0;
+
+static const uint8_t websdr_pong_msg[] = {
+ 0, 1, IPAC_PROTO_IPACCESS, IPAC_MSGT_PONG
+};
+
+static char cb_data[CB_BUF_SZ * NUM_E1_TS];
+static size_t cb_len[NUM_E1_TS] = {0};
+
+struct websdr_line {
+ bool line_already_initialized;
+ struct osmo_stream_cli *ipa_cli[NUM_E1_TS]; /* 0=OML, 1+N=TRX_N */
+};
+
+static inline struct e1inp_ts *websdr_line_ts(unsigned ts_nr, struct e1inp_line *line)
+{
+ if (ts_nr == E1INP_SIGN_OML)
+ return e1inp_line_ipa_oml_ts(line);
+ else
+ return e1inp_line_ipa_rsl_ts(line, ts_nr - E1INP_SIGN_RSL);
+}
+
+static int handle_ts1_write(unsigned int ts_nr, struct e1inp_line *line)
+{
+ struct e1inp_ts *e1i_ts;
+ struct e1inp_sign_link *sign_link;
+ struct msgb *msg;
+ int ret;
+
+ e1i_ts = websdr_line_ts(ts_nr, line);
+
+ /* get the next msg for this timeslot */
+ msg = e1inp_tx_ts(e1i_ts, &sign_link);
+ if (!msg) {
+ return 0;
+ }
+
+ switch (sign_link->type) {
+ case E1INP_SIGN_OML:
+ case E1INP_SIGN_RSL:
+ case E1INP_SIGN_OSMO:
+ break;
+ default:
+ /* leave WRITE flag enabled, come back for more msg */
+ ret = -EINVAL;
+ goto out;
+ }
+
+ msg->l2h = msg->data;
+ ipa_prepend_header(msg, sign_link->tei);
+
+ LOGPITS(e1i_ts, DLMI, LOGL_DEBUG, "TX %u: %s\n", ts_nr,
+ osmo_hexdump(msg->l2h, msgb_l2len(msg)));
+
+ ret = ws_ipa_send(ts_nr, msg->data, msg->len);
+out:
+ msgb_free(msg);
+ return ret;
+}
+
+static int ts_want_write(struct e1inp_ts *e1i_ts)
+{
+ struct e1inp_line *line = e1i_ts->line;
+ unsigned ts_nr = e1i_ts - line->ts + 1;
+ int res;
+
+ if (write_suppressed)
+ return 0;
+
+ do {
+ res = handle_ts1_write(ts_nr, line);
+ } while (res > 0);
+
+ return 0;
+}
+
+static int websdr_line_update(struct e1inp_line *line)
+{
+ int ret = -ENOENT;
+ struct websdr_line *il;
+
+ if (!line->driver_data)
+ line->driver_data = talloc_zero(line, struct websdr_line);
+
+ if (!line->driver_data) {
+ LOGP(DLINP, LOGL_ERROR, "WebSDR: OOM in line update\n");
+ return -ENOMEM;
+ }
+ il = line->driver_data;
+
+ switch(line->ops->cfg.ipa.role) {
+ case E1INP_LINE_R_BSC: {
+ g_line_cb = line;
+ LOGP(DLINP, LOGL_NOTICE, "enabling WebSDR BSC mode, OML connecting\n");
+ ret = 0;
+ break;
+ }
+ case E1INP_LINE_R_BTS: {
+ g_line_cb = line;
+ LOGP(DLINP, LOGL_NOTICE, "enabling WebSDR BTS mode, OML connecting\n");
+ ret = 0;
+ break;
+ }
+ default:
+ break;
+ }
+
+ il->line_already_initialized = true;
+ return ret;
+}
+
+static void websdr_close(struct e1inp_sign_link *sign_link)
+{
+ struct e1inp_ts *e1i_ts = sign_link->ts;
+
+ e1inp_int_snd_event(e1i_ts, sign_link, S_L_INP_TEI_DN);
+
+ g_line_cb = NULL;
+}
+
+static int cb_push_data(unsigned ts_nr, const void *buf, size_t buflen)
+{
+ if (cb_len[ts_nr] + buflen > CB_BUF_SZ)
+ return -EOVERFLOW;
+
+ memcpy(cb_data + CB_BUF_SZ * ts_nr + cb_len[ts_nr], buf, buflen);
+ cb_len[ts_nr] += buflen;
+ return 0;
+}
+
+static ssize_t cb_recv_data(unsigned ts_nr, void *buf, size_t buflen)
+{
+ size_t avail = buflen > cb_len[ts_nr] ? cb_len[ts_nr] : buflen;
+ memcpy(buf, cb_data + CB_BUF_SZ * ts_nr, avail);
+
+ cb_len[ts_nr] -= avail;
+ memmove(cb_data + CB_BUF_SZ * ts_nr, cb_data + avail + CB_BUF_SZ * ts_nr, cb_len[ts_nr]);
+
+ if (avail == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+ return (ssize_t)avail;
+}
+
+static int websdr_ccm_send_pong(unsigned ts_nr)
+{
+ return ws_ipa_send(ts_nr, websdr_pong_msg, sizeof(websdr_pong_msg));
+}
+
+static int websdr_ccm_rcvmsg_bts_base(struct msgb *msg, unsigned ts_nr)
+{
+ uint8_t msg_type = *(msg->l2h);
+ int ret = 0;
+
+ switch (msg_type) {
+ case IPAC_MSGT_PING:
+ ret = websdr_ccm_send_pong(ts_nr);
+ if (ret < 0) {
+ LOGP(DLINP, LOGL_ERROR, "Cannot send PONG "
+ "message. Reason: %s\n", strerror(errno));
+ }
+ break;
+ case IPAC_MSGT_PONG:
+ DEBUGP(DLMI, "PONG!\n");
+ break;
+ case IPAC_MSGT_ID_ACK:
+ DEBUGP(DLMI, "ID_ACK\n");
+ break;
+ }
+ return ret;
+}
+
+void websdr_msg_push_header(struct msgb *msg, uint8_t proto)
+{
+ struct ipaccess_head *hh;
+
+ msg->l2h = msg->data;
+ hh = (struct ipaccess_head *) msgb_push(msg, sizeof(*hh));
+ hh->proto = proto;
+ hh->len = htons(msgb_l2len(msg));
+}
+
+#define IPA_STRING_MAX 64
+
+static struct msgb *websdr_bts_id_resp(const struct ipaccess_unit *dev, uint8_t *data, int len, int trx_nr)
+{
+ struct msgb *nmsg;
+ char str[IPA_STRING_MAX];
+ uint8_t *tag;
+
+ memset(str, 0, sizeof(str));
+
+ nmsg = ipa_msg_alloc(0);
+ if (!nmsg)
+ return NULL;
+
+ *msgb_put(nmsg, 1) = IPAC_MSGT_ID_RESP;
+ while (len) {
+ if (len < 2) {
+ LOGP(DLINP, LOGL_NOTICE,
+ "Short read of ipaccess tag\n");
+ msgb_free(nmsg);
+ return NULL;
+ }
+ switch (data[1]) {
+ case IPAC_IDTAG_UNIT:
+ snprintf(str, sizeof(str), "%u/%u/%u",
+ dev->site_id, dev->bts_id, trx_nr);
+ break;
+ case IPAC_IDTAG_MACADDR:
+ snprintf(str, sizeof(str),
+ "%02x:%02x:%02x:%02x:%02x:%02x",
+ dev->mac_addr[0], dev->mac_addr[1],
+ dev->mac_addr[2], dev->mac_addr[3],
+ dev->mac_addr[4], dev->mac_addr[5]);
+ break;
+ case IPAC_IDTAG_LOCATION1:
+ if (dev->location1)
+ osmo_strlcpy(str, dev->location1, sizeof(str));
+ break;
+ case IPAC_IDTAG_LOCATION2:
+ if (dev->location2)
+ osmo_strlcpy(str, dev->location2, sizeof(str));
+ break;
+ case IPAC_IDTAG_EQUIPVERS:
+ if (dev->equipvers)
+ osmo_strlcpy(str, dev->equipvers, sizeof(str));
+ break;
+ case IPAC_IDTAG_SWVERSION:
+ if (dev->swversion)
+ osmo_strlcpy(str, dev->swversion, sizeof(str));
+ break;
+ case IPAC_IDTAG_UNITNAME:
+ snprintf(str, sizeof(str),
+ "%s-%02x-%02x-%02x-%02x-%02x-%02x",
+ dev->unit_name,
+ dev->mac_addr[0], dev->mac_addr[1],
+ dev->mac_addr[2], dev->mac_addr[3],
+ dev->mac_addr[4], dev->mac_addr[5]);
+ break;
+ case IPAC_IDTAG_SERNR:
+ if (dev->serno)
+ osmo_strlcpy(str, dev->serno, sizeof(str));
+ break;
+ default:
+ LOGP(DLINP, LOGL_NOTICE,
+ "Unknown ipaccess tag 0x%02x\n", *data);
+ msgb_free(nmsg);
+ return NULL;
+ }
+
+ LOGP(DLINP, LOGL_INFO, " tag %d: %s\n", data[1], str);
+ tag = msgb_put(nmsg, 3 + strlen(str) + 1);
+ tag[0] = 0x00;
+ tag[1] = 1 + strlen(str) + 1;
+ tag[2] = data[1];
+ memcpy(tag + 3, str, strlen(str) + 1);
+ data += 2;
+ len -= 2;
+ }
+ websdr_msg_push_header(nmsg, IPAC_PROTO_IPACCESS);
+ return nmsg;
+}
+
+static struct msgb *websdr_bts_id_ack(void)
+{
+ struct msgb *nmsg2;
+
+ nmsg2 = ipa_msg_alloc(0);
+ if (!nmsg2)
+ return NULL;
+
+ *msgb_put(nmsg2, 1) = IPAC_MSGT_ID_ACK;
+ websdr_msg_push_header(nmsg2, IPAC_PROTO_IPACCESS);
+
+ return nmsg2;
+}
+
+/* handle incoming message to BTS, check if it is an IPA CCM, and if yes,
+ * handle it accordingly (PING/PONG/ID_REQ/ID_RESP/ID_ACK) */
+static int websdr_bts_handle_ccm(struct e1inp_line *line, unsigned ts_nr,
+ struct ipaccess_unit *dev, struct msgb *msg)
+{
+ struct ipaccess_head *hh = (struct ipaccess_head *)msg->data;
+ struct msgb *rmsg;
+ int ret = 0;
+ /* line might not exist if != bsc||bts */
+
+ /* special handling for IPA CCM. */
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ uint8_t msg_type = *(msg->l2h);
+ // struct osmo_fsm_inst* ka_fsm = NULL;
+
+ /* peek the pong for our keepalive fsm */
+ if (line && msg_type == IPAC_MSGT_PONG) {
+ //ka_fsm = websdr_line_ts(link->ofd, line)->driver.ipaccess.ka_fsm;
+ //ipa_keepalive_fsm_pong_received(ka_fsm);
+ }
+
+ /* ping, pong and acknowledgment cases. */
+ ret = websdr_ccm_rcvmsg_bts_base(msg, ts_nr);
+ if (ret < 0)
+ goto err;
+
+ /* this is a request for identification from the BSC. */
+ if (msg_type == IPAC_MSGT_ID_GET) {
+ uint8_t *data = msgb_l2(msg);
+ int len = msgb_l2len(msg);
+ int trx_nr = 0;
+
+ if (ts_nr >= E1INP_SIGN_RSL)
+ trx_nr = ts_nr - E1INP_SIGN_RSL;
+
+ LOGP(DLINP, LOGL_NOTICE, "received ID_GET for unit ID %u/%u/%u\n",
+ dev->site_id, dev->bts_id, trx_nr);
+ // dev->trx_id = trx_nr;
+ rmsg = websdr_bts_id_resp(dev, data + 1, len - 1, trx_nr);
+ ret = ws_ipa_send(ts_nr, rmsg->data, rmsg->len);
+ if (ret != rmsg->len) {
+ LOGP(DLINP, LOGL_ERROR, "cannot send ID_RESP "
+ "message. Reason: %s\n", strerror(errno));
+ goto err_rmsg;
+ }
+ msgb_free(rmsg);
+
+ /* send ID_ACK. */
+ rmsg = websdr_bts_id_ack();
+ ret = ws_ipa_send(ts_nr, rmsg->data, rmsg->len);
+ if (ret != rmsg->len) {
+ LOGP(DLINP, LOGL_ERROR, "cannot send ID_ACK "
+ "message. Reason: %s\n", strerror(errno));
+ goto err_rmsg;
+ }
+ msgb_free(rmsg);
+ }
+ return 1;
+ }
+
+ return 0;
+
+err_rmsg:
+ msgb_free(rmsg);
+err:
+ //ipa_client_conn_close(link);
+ return -1;
+}
+
+static int websdr_bts_read_cb(unsigned ts_nr, struct msgb *msg)
+{
+ struct e1inp_line* line = g_line_cb;
+ struct e1inp_ts *e1i_ts = NULL;
+ struct e1inp_sign_link *sign_link;
+ int ret = 0;
+ struct ipaccess_head *hh = (struct ipaccess_head *) msg->data;
+ uint8_t msg_type = *(msg->l2h);
+
+ //fprintf(stderr, "IPA PROTP=%d SZ=%d e1i_ts=%p\n",
+ // hh->proto, msgb_l1len(msg), websdr_line_ts(ts_nr, line));
+
+ /* special handling for IPA CCM. */
+ if (hh->proto == IPAC_PROTO_IPACCESS) {
+ /* this is a request for identification from the BSC. */
+ if (msg_type == IPAC_MSGT_ID_GET) {
+ if (!line->ops->sign_link_up) {
+ LOGP(DLINP, LOGL_ERROR,
+ "Unable to set signal link, "
+ "closing socket.\n");
+ goto err;
+ }
+ }
+ }
+
+ /* core CCM handling */
+ ret = websdr_bts_handle_ccm(line, ts_nr, line->ops->cfg.ipa.dev, msg);
+ if (ret < 0)
+ goto err;
+
+ if (ret == 1 && hh->proto == IPAC_PROTO_IPACCESS) {
+ if (msg_type == IPAC_MSGT_ID_GET) {
+ sign_link = line->ops->sign_link_up(msg,
+ line,
+ ts_nr);
+ if (sign_link == NULL) {
+ LOGP(DLINP, LOGL_ERROR,
+ "Unable to set signal link, "
+ "closing socket.\n");
+ goto err;
+ }
+ }
+ msgb_free(msg);
+ return ret;
+ } else {
+ e1i_ts = websdr_line_ts(ts_nr, line);
+ }
+
+ OSMO_ASSERT(e1i_ts != NULL);
+
+ if (e1i_ts->type == E1INP_TS_TYPE_NONE) {
+ LOGP(DLINP, LOGL_ERROR, "Signalling link not initialized. Discarding."
+ " msg_type=%u\n", msg_type);
+ goto err;
+ }
+
+ /* look up for some existing signaling link. */
+ sign_link = e1inp_lookup_sign_link(e1i_ts, hh->proto, 0);
+ if (sign_link == NULL) {
+ LOGP(DLINP, LOGL_ERROR, "no matching signalling link for "
+ "hh->proto=0x%02x\n", hh->proto);
+ goto err;
+ }
+ msg->dst = sign_link;
+
+ /* XXX better use e1inp_ts_rx? */
+ if (!line->ops->sign_link) {
+ LOGP(DLINP, LOGL_ERROR, "Fix your application, "
+ "no action set for signalling messages.\n");
+ goto err;
+ }
+ ret = line->ops->sign_link(msg);
+ return ret;
+err:
+ // ipa_client_conn_close(link);
+ msgb_free(msg);
+ return -EBADF;
+}
+
+static int websdr_msg_recv_buffered(unsigned ts_nr, struct msgb **rmsg, struct msgb **tmp_msg)
+{
+ struct msgb *msg = tmp_msg ? *tmp_msg : NULL;
+ struct ipaccess_head *hh;
+ int len, ret;
+ int needed;
+
+ if (msg == NULL) {
+ msg = ipa_msg_alloc(0);
+ if (msg == NULL) {
+ ret = -ENOMEM;
+ goto discard_msg;
+ }
+ msg->l1h = msg->tail;
+ }
+
+ if (msg->l2h == NULL) {
+ /* first read our 3-byte header */
+ needed = sizeof(*hh) - msg->len;
+ ret = cb_recv_data(ts_nr, msg->tail, needed);
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ if (msg->len == 0) {
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message header (%d/%zu)\n",
+ msg->len, sizeof(*hh));
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
+
+ msg->l2h = msg->tail;
+ }
+
+ hh = (struct ipaccess_head *) msg->data;
+
+ /* then read the length as specified in header */
+ len = osmo_ntohs(hh->len);
+
+ if (len < 0 || CB_BUF_SZ < len + sizeof(*hh)) {
+ LOGP(DLINP, LOGL_ERROR, "bad message length of %d bytes, "
+ "received %d bytes\n", len, msg->len);
+ ret = -EIO;
+ goto discard_msg;
+ }
+
+ needed = len - msgb_l2len(msg);
+
+ if (needed > 0) {
+ ret = cb_recv_data(ts_nr, msg->tail, needed);
+
+ if (ret == 0)
+ goto discard_msg;
+
+ if (ret < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ ret = 0;
+ else {
+ ret = -errno;
+ goto discard_msg;
+ }
+ }
+
+ msgb_put(msg, ret);
+
+ if (ret < needed) {
+ LOGP(DLINP, LOGL_INFO,
+ "Received part of IPA message L2 data (%d/%d)\n",
+ msgb_l2len(msg), len);
+ if (!tmp_msg) {
+ ret = -EIO;
+ goto discard_msg;
+ }
+ *tmp_msg = msg;
+ return -EAGAIN;
+ }
+ }
+
+ ret = msgb_l2len(msg);
+
+ if (ret == 0) {
+ LOGP(DLINP, LOGL_INFO,
+ "Discarding IPA message without payload\n");
+ ret = -EAGAIN;
+ goto discard_msg;
+ }
+
+ if (tmp_msg)
+ *tmp_msg = NULL;
+ *rmsg = msg;
+ return ret;
+
+discard_msg:
+ if (tmp_msg)
+ *tmp_msg = NULL;
+ msgb_free(msg);
+ return ret;
+}
+
+int ws_ipa_push_raw_data(unsigned ts_nr, const char* data, unsigned sz)
+{
+ static struct msgb *tmpmsg[NUM_E1_TS] = { NULL };
+ struct msgb *msg;
+ int ret;
+
+ cb_push_data(ts_nr, data, sz);
+ write_suppressed = 1;
+
+ for (;;) {
+ ret = websdr_msg_recv_buffered(ts_nr, &msg, &tmpmsg[ts_nr]);
+ if (ret < 0)
+ break;
+
+ //fprintf(stderr, "ws_ipa_push_raw_data(TS_NR=%d) => buff %d REM %p\n", ts_nr, ret, tmpmsg[ts_nr]);
+ ret = websdr_bts_read_cb(ts_nr, msg);
+ }
+
+ write_suppressed = 0;
+ ts_want_write(websdr_line_ts(ts_nr, g_line_cb));
+ return ret;
+}
+
+int ws_osmux_deliver_cb(const void *msg, unsigned msglen)
+{
+ LOGP(DLIO, LOGL_DEBUG, "WebSDR: ws_osmux_deliver_cb called with msg=%p msglen=%u", msg, msglen);
+#ifdef __EMSCRIPTEN__
+ return ws_osmux_deliver_cb_wrapper(msg, msglen);
+#else /* __EMSCRIPTEN__ */
+ /* For non-WebAssembly builds, we just return success. */
+ return 0;
+#endif /* __EMSCRIPTEN__ */
+}
+
+int ws_ipa_send(unsigned ts_nr, const void *msg, unsigned msglen)
+{
+ LOGP(DLIO, LOGL_DEBUG, "WebSDR: ws_ipa_send called with ts_nr=%u msg=%p msglen=%u", ts_nr, msg, msglen);
+#ifdef __EMSCRIPTEN__
+ return ws_ipa_send_wrapper(ts_nr, msg, msglen);
+#else /* __EMSCRIPTEN__ */
+ /* For non-WebAssembly builds, we just return success. */
+ return 0;
+#endif /* __EMSCRIPTEN__ */
+}
+
+int ws_rsl_connect_cb(unsigned ts_nr)
+{
+ LOGP(DLIO, LOGL_DEBUG, "WebSDR: ws_rsl_connect_cb called with ts_nr=%u", ts_nr);
+#ifdef __EMSCRIPTEN__
+ return ws_rsl_connect_cb_wrapper(ts_nr);
+#else /* __EMSCRIPTEN__ */
+ /* For non-WebAssembly builds, we just return success. */
+ return 0;
+#endif /* __EMSCRIPTEN__ */
+}
+
+static struct e1inp_driver websdr_driver = {
+ .name = "websdr",
+ .want_write = ts_want_write,
+ .line_update = websdr_line_update,
+ .close = websdr_close,
+};
+
+int e1inp_websdr_init(void)
+{
+ return e1inp_driver_register(&websdr_driver);
+}
To view, visit change 42652. To unsubscribe, or for help writing mail filters, visit settings.