Timur Davydov has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/libosmo-abis/+/42652?usp=email )


Change subject: abis: add optional WebSDR E1 input driver
......................................................................

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 <[email protected]>
+ *
+ * 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 <[email protected]>
+ *
+ * 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 https://gerrit.osmocom.org/c/libosmo-abis/+/42652?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: libosmo-abis
Gerrit-Branch: master
Gerrit-Change-Id: Ib804dc8eb67a91678603f54ea0beccd5c61e4a80
Gerrit-Change-Number: 42652
Gerrit-PatchSet: 1
Gerrit-Owner: Timur Davydov <[email protected]>

Reply via email to