fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/35582?usp=email )
Change subject: [WIP] mobile: integrate V.110 TA & soft-UART from libosmocore ......................................................................
[WIP] mobile: integrate V.110 TA & soft-UART from libosmocore
Change-Id: I7ac9c0e5010730fa4d8bc7a7a3c7ff85e11731c0 Depends: libosmocore.git I5716bd6fd0201ee7a7a29e72f775972cd374082f Related: OS#4396 --- M src/host/layer23/configure.ac M src/host/layer23/include/osmocom/bb/mobile/tch.h M src/host/layer23/src/mobile/Makefile.am M src/host/layer23/src/mobile/tch.c A src/host/layer23/src/mobile/tch_data.c 5 files changed, 375 insertions(+), 11 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/82/35582/1
diff --git a/src/host/layer23/configure.ac b/src/host/layer23/configure.ac index b4eb860..7fb8bf1 100644 --- a/src/host/layer23/configure.ac +++ b/src/host/layer23/configure.ac @@ -45,6 +45,7 @@ PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.5.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.10.0) PKG_CHECK_MODULES(LIBOSMOGSM, libosmogsm) +PKG_CHECK_MODULES(LIBOSMOISDN, libosmoisdn) PKG_CHECK_MODULES(LIBOSMOCODEC, libosmocodec) PKG_CHECK_MODULES(LIBOSMOGPRSRLCMAC, libosmo-gprs-rlcmac) PKG_CHECK_MODULES(LIBOSMOGPRSLLC, libosmo-gprs-llc) diff --git a/src/host/layer23/include/osmocom/bb/mobile/tch.h b/src/host/layer23/include/osmocom/bb/mobile/tch.h index d2cb4bc..a31655a 100644 --- a/src/host/layer23/include/osmocom/bb/mobile/tch.h +++ b/src/host/layer23/include/osmocom/bb/mobile/tch.h @@ -13,6 +13,8 @@ } voice; struct tch_data_state { enum tch_data_io_handler handler; + struct osmo_v110_ta *v110_ta; + struct osmo_soft_uart *suart; } data; }; }; diff --git a/src/host/layer23/src/mobile/Makefile.am b/src/host/layer23/src/mobile/Makefile.am index 0639916..b4e45e2 100644 --- a/src/host/layer23/src/mobile/Makefile.am +++ b/src/host/layer23/src/mobile/Makefile.am @@ -8,6 +8,7 @@ $(LIBOSMOCORE_CFLAGS) \ $(LIBOSMOVTY_CFLAGS) \ $(LIBOSMOGSM_CFLAGS) \ + $(LIBOSMOISDN_CFLAGS) \ $(LIBOSMOGPRSRLCMAC_CFLAGS) \ $(LIBOSMOGPRSLLC_CFLAGS) \ $(LIBOSMOGPRSSNDCP_CFLAGS) \ @@ -31,6 +32,7 @@ mncc_sock.c \ primitives.c \ tch.c \ + tch_data.c \ tch_voice.c \ transaction.c \ vty_interface.c \ @@ -45,6 +47,7 @@ $(LIBOSMOCORE_LIBS) \ $(LIBOSMOVTY_LIBS) \ $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOISDN_LIBS) \ $(LIBOSMOGPRSRLCMAC_LIBS) \ $(LIBOSMOGPRSLLC_LIBS) \ $(LIBOSMOGPRSSNDCP_LIBS) \ diff --git a/src/host/layer23/src/mobile/tch.c b/src/host/layer23/src/mobile/tch.c index e5599dd..fa457ac 100644 --- a/src/host/layer23/src/mobile/tch.c +++ b/src/host/layer23/src/mobile/tch.c @@ -37,9 +37,14 @@
int tch_voice_state_init(struct gsm_trans *trans, struct tch_voice_state *state); +int tch_data_state_init(struct gsm_trans *trans, + struct tch_data_state *state); + void tch_voice_state_free(struct tch_voice_state *state); +void tch_data_state_free(struct tch_data_state *state);
int tch_voice_recv(struct osmocom_ms *ms, struct msgb *msg); +int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg); int tch_voice_serve_ms(struct osmocom_ms *ms);
/* Receive a Downlink traffic (voice/data) frame from the lower layers */ @@ -54,10 +59,8 @@
if (state->voice_or_data) return tch_voice_recv(ms, msg); - else /* TODO: tch_recv_data() */ - msgb_free(msg); - - return 0; + else + return tch_data_recv(ms, msg); }
/* Send an Uplink voice frame to the lower layers */ @@ -139,14 +142,11 @@ case GSM48_CMODE_DATA_12k0: case GSM48_CMODE_DATA_6k0: case GSM48_CMODE_DATA_3k6: -#if 0 state->voice_or_data = false; state->data.handler = ms->settings.tch_data.io_handler; - /* TODO: tch_data_state_init() */ if (tch_data_state_init(trans, &state->data) != 0) goto exit_free; break; -#endif case GSM48_CMODE_SIGN: default: LOGP(DL1C, LOGL_ERROR, "Unhandled channel mode %s\n", @@ -168,10 +168,8 @@ return; if (state->voice_or_data) tch_voice_state_free(&state->voice); -#if 0 - else /* TODO: tch_state_free_data() */ - tch_state_free_data(&state->data); -#endif + else + tch_data_state_free(&state->data);
talloc_free(state); ms->tch_state = NULL; diff --git a/src/host/layer23/src/mobile/tch_data.c b/src/host/layer23/src/mobile/tch_data.c new file mode 100644 index 0000000..082c397 --- /dev/null +++ b/src/host/layer23/src/mobile/tch_data.c @@ -0,0 +1,349 @@ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * Author: Vadim Yanitskiy vyanitskiy@sysmocom.de + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 General Public License for more details. + * + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> + +#include <osmocom/core/msgb.h> +#include <osmocom/core/soft_uart.h> + +#include <osmocom/gsm/protocol/gsm_08_58.h> +#include <osmocom/gsm/gsm44021.h> + +#include <osmocom/isdn/v110.h> +#include <osmocom/isdn/v110_ta.h> + +#include <osmocom/bb/common/logging.h> +#include <osmocom/bb/common/osmocom_data.h> +#include <osmocom/bb/common/ms.h> +#include <osmocom/bb/mobile/mncc.h> +#include <osmocom/bb/mobile/transaction.h> +#include <osmocom/bb/mobile/tch.h> + +struct csd_v110_frame_desc { + uint16_t num_blocks; + uint16_t num_bits; +}; + +struct csd_v110_lchan_desc { + struct csd_v110_frame_desc fr; + struct csd_v110_frame_desc hr; +}; + +/* key is enum gsm48_chan_mode, so assuming a value in range 0..255 */ +const struct csd_v110_lchan_desc csd_v110_lchan_desc[256] = { +#if 0 + [GSM48_CMODE_DATA_14k5] = { + /* TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */ + .fr = { .num_blocks = 1, .num_bits = 290 }, + }, +#endif + [GSM48_CMODE_DATA_12k0] = { + /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */ + .fr = { .num_blocks = 4, .num_bits = 60 }, + }, + [GSM48_CMODE_DATA_6k0] = { + /* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */ + .fr = { .num_blocks = 2, .num_bits = 60 }, + /* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */ + .hr = { .num_blocks = 4, .num_bits = 60 }, + }, + [GSM48_CMODE_DATA_3k6] = { + /* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */ + .fr = { .num_blocks = 2, .num_bits = 36 }, + /* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */ + .hr = { .num_blocks = 4, .num_bits = 36 }, + }, +}; + +static void tch_soft_uart_rx_cb(void *priv, struct msgb *msg, unsigned int flags) +{ + LOGP(DL1C, LOGL_FATAL, "%s(): [flags=0x%08x] %s\n", + __func__, flags, msgb_hexdump(msg)); + msgb_free(msg); +} + +static void tch_soft_uart_tx_cb(void *priv, struct msgb *msg) +{ + const char *data = "TEST\r\n"; + size_t n_bytes; + + n_bytes = OSMO_MIN(msg->data_len, strlen(data)); + if (n_bytes > 0) + memcpy(msgb_put(msg, n_bytes), (void *)data, n_bytes); +} + +struct osmo_soft_uart *tch_soft_uart_alloc(struct osmocom_ms *ms) +{ + struct osmo_soft_uart *suart; + + /* FIXME: take the exact config from BCap */ + const struct osmo_soft_uart_cfg cfg = { + .num_data_bits = 8, + .num_stop_bits = 1, + .parity_mode = OSMO_SUART_PARITY_NONE, + .rx_buf_size = 1024, /* TODO: align with the current TCH mode */ + .rx_timeout_ms = 100, /* TODO: align with TCH framing interval */ + .priv = &ms->tch_state->data, + .rx_cb = &tch_soft_uart_rx_cb, + .tx_cb = &tch_soft_uart_tx_cb, + }; + + suart = osmo_soft_uart_alloc(ms, "csd_soft_uart", &cfg); + if (suart == NULL) + return NULL; + + osmo_soft_uart_set_rx(suart, true); + osmo_soft_uart_set_tx(suart, true); + + return suart; +} + +/*************************************************************************************/ + +static void tch_v110_ta_rx_cb(void *priv, const ubit_t *buf, size_t buf_size) +{ + struct tch_data_state *state = (struct tch_data_state *)priv; + + /* XXX: we're assuming async call here */ + osmo_soft_uart_rx_ubits(state->suart, buf, buf_size); +} + +static void tch_v110_ta_tx_cb(void *priv, ubit_t *buf, size_t buf_size) +{ + struct tch_data_state *state = (struct tch_data_state *)priv; + + /* XXX: we're assuming async call here */ + osmo_soft_uart_tx_ubits(state->suart, buf, buf_size); +} + +static void tch_v110_ta_status_update_cb(void *priv, unsigned int status) +{ + LOGP(DL1C, LOGL_DEBUG, "%s(): [status=0x%08x]\n", __func__, status); + /* TODO: update status lines of the soft-UART */ +} + +struct osmo_v110_ta *tch_v110_ta_alloc(struct osmocom_ms *ms, + enum osmo_v100_sync_ra1_rate rate) +{ + const struct osmo_v110_ta_cfg cfg = { + .rate = rate, + .priv = &ms->tch_state->data, + .rx_cb = &tch_v110_ta_rx_cb, + .tx_cb = &tch_v110_ta_tx_cb, + .status_update_cb = &tch_v110_ta_status_update_cb, + }; + + return osmo_v110_ta_alloc(ms, "csd_v110_ta", &cfg); +} + +/*************************************************************************************/ + +static void swap_words(uint8_t *data, size_t data_len) +{ + /* swap bytes in words */ + while (data_len >= 2) { + uint8_t tmp = data[0]; + data[0] = data[1]; + data[1] = tmp; + data_len -= 2; + data += 2; + } +} + +int tch_csd_rx_from_l1(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_data_state *state = &ms->tch_state->data; + const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now; + const struct csd_v110_frame_desc *desc; + ubit_t data[4 * 60]; + + if (msgb_l3len(msg) < 30) + return -EINVAL; + + if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) + desc = &csd_v110_lchan_desc[cd->mode].fr; + else /* RSL_CHAN_Lm_ACCHs */ + desc = &csd_v110_lchan_desc[cd->mode].hr; + if (OSMO_UNLIKELY(desc->num_blocks == 0)) + return -ENOTSUP; + + switch (ms->settings.tch_data.io_format) { + case TCH_DATA_IOF_OSMO: + break; + case TCH_DATA_IOF_TI: + /* the layer1 firmware emits frames with swapped words (LE ordering) */ + swap_words(msgb_l3(msg), msgb_l3len(msg)); + break; + } + + /* unpack packed bits (MSB goes first) */ + osmo_pbit2ubit_ext(data, 0, msgb_l3(msg), 0, sizeof(data), 1); + + for (unsigned int i = 0; i < desc->num_blocks; i++) { + struct osmo_v110_decoded_frame df; + + if (desc->num_bits == 60) + osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60); + else /* desc->num_bits == 36 */ + osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36); + + /* FIXME: properly set E1/E2/E3 depending on data rate */ + df.e_bits[0] = 0; + df.e_bits[1] = 1; + df.e_bits[2] = 1; + + osmo_v110_ta_frame_in(state->v110_ta, &df); + } + + return 0; +} + +int tch_csd_tx_to_l1(struct osmocom_ms *ms) +{ + struct tch_data_state *state = &ms->tch_state->data; + const struct gsm48_rr_cd *cd = &ms->rrlayer.cd_now; + const struct csd_v110_frame_desc *desc; + ubit_t data[60 * 4]; + struct msgb *nmsg; + + if ((cd->chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_Bm_ACCHs) + desc = &csd_v110_lchan_desc[cd->mode].fr; + else /* RSL_CHAN_Lm_ACCHs */ + desc = &csd_v110_lchan_desc[cd->mode].hr; + if (OSMO_UNLIKELY(desc->num_blocks == 0)) + return -ENOTSUP; + + for (unsigned int i = 0; i < desc->num_blocks; i++) { + struct osmo_v110_decoded_frame df; + + if (osmo_v110_ta_frame_out(state->v110_ta, &df) != 0) + memset(&df, 0x01, sizeof(df)); + + /* set E7 to binary 0 in every 4-th frame + * (as per 3GPP TS 44.021, subclause 10.2.1) + * FIXME: properly handle desc->num_blocks == 2 */ + df.e_bits[6] = (i > 0); + + if (desc->num_bits == 60) + osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df); + else /* desc->num_bits == 36 */ + osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df); + } + + nmsg = msgb_alloc_headroom(33 + 64, 64, __func__); + OSMO_ASSERT(nmsg != NULL); + + nmsg->l2h = msgb_put(nmsg, 33); /* XXX: proper size */ + + /* pack unpacked bits (MSB goes first) */ + osmo_ubit2pbit_ext(msgb_l2(nmsg), 0, &data[0], 0, sizeof(data), 1); + + switch (ms->settings.tch_data.io_format) { + case TCH_DATA_IOF_OSMO: + break; + case TCH_DATA_IOF_TI: + /* the layer1 firmware expects frames with swapped words (LE ordering) */ + swap_words(msgb_l2(nmsg), msgb_l2len(nmsg)); + break; + } + + return gsm48_rr_tx_traffic(ms, nmsg); +} + +/* TODO: set/unset OSMO_V110_TA_C_108 */ +/* TODO: call osmo_v110_ta_sync_ind() */ + +/*************************************************************************************/ + +int tch_data_recv(struct osmocom_ms *ms, struct msgb *msg) +{ + struct tch_data_state *state = &ms->tch_state->data; + + switch (state->handler) { + case TCH_DATA_IOH_LOOPBACK: + /* Remove the DL info header */ + msgb_pull_to_l2(msg); + /* Send data frame back */ + return tch_send_msg(ms, msg); + case TCH_DATA_IOH_UNIX_SOCK: + tch_csd_rx_from_l1(ms, msg); + tch_csd_tx_to_l1(ms); + msgb_free(msg); + break; + case TCH_DATA_IOH_NONE: + /* Drop voice frame */ + msgb_free(msg); + } + + return 0; +} + +int tch_data_state_init(struct gsm_trans *trans, + struct tch_data_state *state) +{ + struct osmocom_ms *ms = trans->ms; + + switch (state->handler) { + case TCH_DATA_IOH_UNIX_SOCK: + /* TODO: open listening socket */ + break; + case TCH_DATA_IOH_LOOPBACK: + case TCH_DATA_IOH_NONE: + /* we don't need V.110 TA / soft-UART */ + return 0; + default: + break; + } + +#if 1 + state->suart = tch_soft_uart_alloc(ms); + if (state->suart == NULL) + goto exit_free; +#endif + + state->v110_ta = tch_v110_ta_alloc(ms, OSMO_V110_SYNC_RA1_9600); /* XXX */ + if (state->v110_ta == NULL) + goto exit_free; + + return 0; + +exit_free: + if (state->suart != NULL) + osmo_soft_uart_free(state->suart); + if (state->v110_ta != NULL) + osmo_v110_ta_free(state->v110_ta); + return -1; +} + +void tch_data_state_free(struct tch_data_state *state) +{ + if (state->suart != NULL) + osmo_soft_uart_free(state->suart); + if (state->v110_ta != NULL) + osmo_v110_ta_free(state->v110_ta); + + switch (state->handler) { + case TCH_DATA_IOH_UNIX_SOCK: + /* TODO: close listening socket */ + break; + default: + break; + } +}