fixeria has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-bts/+/33832 )


Change subject: l1sap: proper rate adaptation for CSD (RFC4040 'clearmode')
......................................................................

l1sap: proper rate adaptation for CSD (RFC4040 'clearmode')

Since 95407f3f osmo-bts-trx supports scheduling all CSD specific
channel rates, however the rate adaptation was missing.

On the radio interface we deal with CSD-modified V.110 frames, which
need to be converted to normal 80-bit V.110 frames (RA1'/RA1), which
in turn need to be batched and sent in RFC4040 "clearmode" 160 octet
RTP payloads (RA1/RA2 as per I.460).

Change-Id: I5e3701ad52d5d428fd02caff037881045f2d0a02
Related: OS#1572
---
M include/osmo-bts/Makefile.am
A include/osmo-bts/csd_v110.h
M src/common/Makefile.am
A src/common/csd_v110.c
M src/common/l1sap.c
5 files changed, 250 insertions(+), 11 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/32/33832/1

diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index 1a07287..3e54035 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -23,6 +23,7 @@
        tx_power.h \
        control_if.h \
        cbch.h \
+       csd_v110.h \
        l1sap.h \
        lchan.h \
        power_control.h \
diff --git a/include/osmo-bts/csd_v110.h b/include/osmo-bts/csd_v110.h
new file mode 100644
index 0000000..b80f95d
--- /dev/null
+++ b/include/osmo-bts/csd_v110.h
@@ -0,0 +1,11 @@
+#pragma once
+
+/* RFC4040 "clearmode" RTP payload length */
+#define RFC4040_RTP_PLEN 160
+
+struct gsm_lchan;
+
+int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
+                       const uint8_t *data, size_t data_len);
+int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
+                       const uint8_t *rtp, size_t rtp_len);
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index 270139c..beae875 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -50,6 +50,7 @@
        bts_ctrl_commands.c \
        bts_ctrl_lookup.c \
        bts_shutdown_fsm.c \
+       csd_v110.c \
        l1sap.c \
        cbch.c \
        power_control.c \
diff --git a/src/common/csd_v110.c b/src/common/csd_v110.c
new file mode 100644
index 0000000..f800f4c
--- /dev/null
+++ b/src/common/csd_v110.c
@@ -0,0 +1,166 @@
+/*
+ * (C) 2023 by sysmocom - s.f.m.c. GmbH <[email protected]>
+ * Author: Vadim Yanitskiy <[email protected]>
+ *
+ * All Rights Reserved
+ *
+ * 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 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 <stdint.h>
+#include <errno.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm44021.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/protocol/gsm_04_08.h>
+#include <osmocom/isdn/v110.h>
+
+#include <osmo-bts/csd_v110.h>
+#include <osmo-bts/lchan.h>
+
+#define DATA_RATE(type, mode) ((type << 8) | mode)
+
+static int lchan_data_rate(const struct gsm_lchan *lchan,
+                          size_t *num_blocks,
+                          size_t *num_bits)
+{
+       switch (DATA_RATE(lchan->type, lchan->tch_mode)) {
+       /* TCH/F9.6: 4 * 60 bits every 20 ms (12.0 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_F, GSM48_CMODE_DATA_12k0):
+               *num_blocks = 4;
+               *num_bits = 60;
+               break;
+       /* TCH/F4.8: 2 * 60 bits every 20 ms (6.0 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_F, GSM48_CMODE_DATA_6k0):
+               *num_blocks = 2;
+               *num_bits = 60;
+               break;
+       /* TCH/H4.8: 4 * 60 bits every 40 ms (6.0 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_H, GSM48_CMODE_DATA_6k0):
+               *num_blocks = 4;
+               *num_bits = 60;
+               break;
+       /* TCH/F2.4: 2 * 36 bits every 20 ms (3.6 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_F, GSM48_CMODE_DATA_3k6):
+               *num_blocks = 2;
+               *num_bits = 36;
+               break;
+       /* TCH/H2.4: 4 * 36 bits every 40 ms (3.6 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_H, GSM48_CMODE_DATA_3k6):
+               *num_blocks = 4;
+               *num_bits = 36;
+               break;
+       /* TODO: TCH/F14.4: 290 bits every 20 ms (14.5 kbit/s) */
+       case DATA_RATE(GSM_LCHAN_TCH_F, GSM48_CMODE_DATA_14k5):
+       default:
+               return -ENOTSUP;
+       }
+
+       return 0;
+}
+
+int csd_v110_rtp_encode(const struct gsm_lchan *lchan, uint8_t *rtp,
+                       const uint8_t *data, size_t data_len)
+{
+       size_t num_blocks, num_bits;
+       ubit_t ra_bits[80 * 4];
+       int rc;
+
+       rc = lchan_data_rate(lchan, &num_blocks, &num_bits);
+       if (rc)
+               return rc;
+
+       /* handle empty/incomplete frames gracefully */
+       if (OSMO_UNLIKELY(data_len < (num_blocks * num_bits))) {
+               /* reuse ra_bits[]: fake a frame with all bits set to zero */
+               memset(&ra_bits[0], 0x00, sizeof(ra_bits));
+               data_len = num_blocks * num_bits;
+               data = &ra_bits[0];
+       }
+
+       /* RA1'/RA1: convert to an intermediate data rate */
+       for (unsigned int i = 0; i < num_blocks; i++) {
+               struct osmo_v110_decoded_frame df;
+
+               /* convert modified V.110 frames to normal V.110 frames */
+               if (num_bits == 60)
+                       osmo_csd_12k_6k_decode_frame(&df, &data[i * 60], 60);
+               else /* num_bits == 36 */
+                       osmo_csd_3k6_decode_frame(&df, &data[i * 36], 36);
+               osmo_v110_encode_frame(&ra_bits[i * 80], 80, &df);
+       }
+
+       /* RA1/RA2: convert from an intermediate rate to 64 kbit/s */
+       if (num_blocks == 4) {
+               /* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
+               for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
+                       rtp[i] = (0xff >> 2);
+                       rtp[i] |= ra_bits[j++] << 7;
+                       rtp[i] |= ra_bits[j++] << 6;
+               }
+       } else {
+               /* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
+               for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++) {
+                       rtp[i] = (0xff >> 1);
+                       rtp[i] |= ra_bits[i] << 7;
+               }
+       }
+
+       return RFC4040_RTP_PLEN;
+}
+
+int csd_v110_rtp_decode(const struct gsm_lchan *lchan, uint8_t *data,
+                       const uint8_t *rtp, size_t rtp_len)
+{
+       size_t num_blocks, num_bits;
+       ubit_t ra_bits[80 * 4];
+       int rc;
+
+       rc = lchan_data_rate(lchan, &num_blocks, &num_bits);
+       if (rc)
+               return rc;
+
+       if (OSMO_UNLIKELY(rtp_len != RFC4040_RTP_PLEN))
+               return -EINVAL;
+
+       /* RA1/RA2: convert from 64 kbit/s to an intermediate rate */
+       if (num_blocks == 4) {
+               /* 4 * 80 bits (16 kbit/s) => 2 bits per octet */
+               for (unsigned int i = 0, j = 0; i < RFC4040_RTP_PLEN; i++) {
+                       ra_bits[j++] = rtp[i] >> 7;
+                       ra_bits[j++] = rtp[i] >> 6;
+               }
+       } else {
+               /* 2 * 80 bits (8 kbit/s) => 1 bit per octet */
+               for (unsigned int i = 0; i < RFC4040_RTP_PLEN; i++)
+                       ra_bits[i] = rtp[i] >> 7;
+       }
+
+       /* RA1'/RA1: convert to an intermediate data rate */
+       for (unsigned int i = 0; i < num_blocks; i++) {
+               struct osmo_v110_decoded_frame df;
+
+               /* convert modified V.110 frames to normal V.110 frames */
+               osmo_v110_decode_frame(&df, &ra_bits[i * 80], 80);
+               if (num_bits == 60)
+                       osmo_csd_12k_6k_encode_frame(&data[i * 60], 60, &df);
+               else /* num_bits == 36 */
+                       osmo_csd_3k6_encode_frame(&data[i * 36], 36, &df);
+       }
+
+       return num_blocks * num_bits;
+}
diff --git a/src/common/l1sap.c b/src/common/l1sap.c
index 7f61f2c..14074f0 100644
--- a/src/common/l1sap.c
+++ b/src/common/l1sap.c
@@ -57,6 +57,7 @@
 #include <osmo-bts/rtp_input_preen.h>
 #include <osmo-bts/pcuif_proto.h>
 #include <osmo-bts/cbch.h>
+#include <osmo-bts/csd_v110.h>

 /* determine the CCCH block number based on the frame number */
 unsigned int l1sap_fn2ccch_block(uint32_t fn)
@@ -1780,8 +1781,26 @@
        return 1;
 }

+static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, uint32_t fn,
+                                   const uint8_t *data, uint16_t data_len)
+{
+       uint8_t rtp_pl[RFC4040_RTP_PLEN];
+       int rc;
+
+       rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len);
+       if (rc < 0)
+               return;
+
+       osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket,
+                               &rtp_pl[0], sizeof(rtp_pl),
+                               fn_ms_adj(fn, lchan),
+                               lchan->rtp_tx_marker);
+       /* Only clear the marker bit once we have sent a RTP packet with it */
+       lchan->rtp_tx_marker = false;
+}
+
 /* a helper function for the logic in l1sap_tch_ind() */
-static void send_ul_rtp_packet(struct gsm_lchan *lchan, uint32_t fn,
+static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn,
                               const uint8_t *rtp_pl, uint16_t rtp_pl_len)
 {
        if (lchan->abis_ip.osmux.use) {
@@ -1810,7 +1829,7 @@
        else
                toc = 0x00;
        msgb_push_u8(msg, toc);
-       send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
+       send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len);
 }

 /* A helper function for l1sap_tch_ind(): handling BFI
@@ -1835,7 +1854,7 @@
                /* did it actually give us some output? */
                if (rc > 0) {
                        /* yes, send it out in RTP */
-                       send_ul_rtp_packet(lchan, fn, ecu_out, rc);
+                       send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc);
                        return;
                }
        }
@@ -1843,7 +1862,7 @@
        /* Are we in rtp continuous-streaming special mode? If so, send out
         * a BFI packet as zero-length RTP payload. */
        if (lchan->ts->trx->bts->rtp_nogaps_mode) {
-               send_ul_rtp_packet(lchan, fn, NULL, 0);
+               send_ul_rtp_packet_speech(lchan, fn, NULL, 0);
                return;
        }

@@ -1900,11 +1919,21 @@
                if (lchan->ecu_state)
                        osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, 
msg->len);
                /* hand msg to RTP code for transmission */
-               if (bts->emit_hr_rfc5993 && lchan->type == GSM_LCHAN_TCH_H &&
-                   lchan->tch_mode == GSM48_CMODE_SPEECH_V1)
-                       send_rtp_rfc5993(lchan, fn, msg);
-               else
-                       send_ul_rtp_packet(lchan, fn, msg->data, msg->len);
+               switch (lchan->rsl_cmode) {
+               case RSL_CMOD_SPD_SPEECH:
+                       if (bts->emit_hr_rfc5993 && lchan->type == 
GSM_LCHAN_TCH_H &&
+                           lchan->tch_mode == GSM48_CMODE_SPEECH_V1)
+                               send_rtp_rfc5993(lchan, fn, msg);
+                       else
+                               send_ul_rtp_packet_speech(lchan, fn, msg->data, 
msg->len);
+                       break;
+               case RSL_CMOD_SPD_DATA:
+                       send_ul_rtp_packet_data(lchan, fn, msg->data, msg->len);
+                       break;
+               case RSL_CMOD_SPD_SIGN:
+               default: /* shall not happen */
+                       OSMO_ASSERT(0);
+               }
                /* if loopback is enabled, also queue received RTP data */
                if (lchan->loopback) {
                        /* add new frame to queue, make sure the queue doesn't 
get too long */
@@ -2191,10 +2220,23 @@
                OSMO_ASSERT(0);
        }

-       msg = l1sap_msgb_alloc(rtp_pl_len);
+       msg = l1sap_msgb_alloc(512);
        if (!msg)
                return;
-       memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
+
+       if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) {
+               int rc = csd_v110_rtp_decode(lchan, msg->tail,
+                                            rtp_pl, rtp_pl_len);
+               if (rc > 0) {
+                       msgb_put(msg, rc);
+               } else {
+                       msgb_free(msg);
+                       return;
+               }
+       } else {
+               memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len);
+       }
+
        msgb_pull(msg, sizeof(struct osmo_phsap_prim));

        /* Store RTP header Marker bit in control buffer */

--
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/33832
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I5e3701ad52d5d428fd02caff037881045f2d0a02
Gerrit-Change-Number: 33832
Gerrit-PatchSet: 1
Gerrit-Owner: fixeria <[email protected]>
Gerrit-MessageType: newchange

Reply via email to