fixeria has submitted this change. ( 
https://gerrit.osmocom.org/c/osmocom-bb/+/32922 )

Change subject: trxcon/l1sched: implement CSD scheduling support
......................................................................

trxcon/l1sched: implement CSD scheduling support

This patch adds support for TCH/[FH]2.4, TCH/[FH]4.8, TCH/F9.6 and
TCH/F14.4 (including FACCH).  Additional changes made:

* enlarge the maximum TCH burst buffer size to 24 * (2 * 58) bytes;
* enlarge per-l1cs UL/DL burst masks to hold up to 32 bits;
* enlarge per-l1cs DL meas ring buffer to 24 entries;
* enlarge L1SCHED_PRIM_TAILROOM from 256 to 512 bytes;
* enlarge L1CTL_LENGTH from 256 to 512 bytes;

Change-Id: I0d7389a9a5f7019b9316ab1c0115797ff54a0e41
Depends: libosmocore.git Ib482817b5f6a4e3c7299f6e0b3841143b60fc93d
Depends: libosmocore.git I0c7a9c180dcafe64e6aebe53518d3d11e1f29886
Depends: libosmocore.git I4685376c8deb04db670684c9ebf685ad6fc989fa
Related: OS#4396, OS#1572
---
M src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
M src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
M src/host/trxcon/src/l1ctl.c
M src/host/trxcon/src/sched_lchan_common.c
M src/host/trxcon/src/sched_lchan_desc.c
M src/host/trxcon/src/sched_lchan_pdtch.c
M src/host/trxcon/src/sched_lchan_tchf.c
M src/host/trxcon/src/sched_lchan_tchh.c
M src/host/trxcon/src/sched_lchan_xcch.c
M src/host/trxcon/src/sched_prim.c
10 files changed, 368 insertions(+), 103 deletions(-)

Approvals:
  fixeria: Looks good to me, approved
  osmith: Looks good to me, but someone else must approve
  Jenkins Builder: Verified




diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h 
b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
index 98282c1..7d9a5b5 100644
--- a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
@@ -195,9 +195,9 @@
        int8_t rssi;
 };

-/* Simple ring buffer (up to 8 unique measurements) */
+/* Simple ring buffer (up to 24 unique measurements) */
 struct l1sched_lchan_meas_hist {
-       struct l1sched_meas_set buf[8];
+       struct l1sched_meas_set buf[24];
        struct l1sched_meas_set *head;
 };

@@ -213,9 +213,9 @@
        /*! Burst type: GMSK or 8PSK */
        enum l1sched_burst_type burst_type;
        /*! Mask of received bursts */
-       uint8_t rx_burst_mask;
+       uint32_t rx_burst_mask;
        /*! Mask of transmitted bursts */
-       uint8_t tx_burst_mask;
+       uint32_t tx_burst_mask;
        /*! Burst buffer for RX */
        sbit_t *rx_bursts;
        /*! Burst buffer for TX */
@@ -386,7 +386,7 @@
 /* Shared declarations for lchan handlers */
 extern const uint8_t l1sched_nb_training_bits[8][26];

-const char *l1sched_burst_mask2str(const uint8_t *mask, int bits);
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits);

 /* Interleaved TCH/H block TDMA frame mapping */
 bool l1sched_tchh_block_map_fn(enum l1sched_lchan_type chan,
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h 
b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
index 8a7e48e..83c61f0 100644
--- a/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/l1ctl_server.h
@@ -7,7 +7,7 @@
 #include <osmocom/core/timer.h>
 #include <osmocom/core/msgb.h>
 
-#define L1CTL_LENGTH 256
+#define L1CTL_LENGTH 512
 #define L1CTL_HEADROOM 32

 /**
diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c
index a45bc2c..0f6bd1b 100644
--- a/src/host/trxcon/src/l1ctl.c
+++ b/src/host/trxcon/src/l1ctl.c
@@ -44,7 +44,7 @@
 #include <osmocom/bb/trxcon/trxcon.h>
 #include <osmocom/bb/trxcon/trxcon_fsm.h>

-#define L1CTL_LENGTH           256
+#define L1CTL_LENGTH           512
 #define L1CTL_HEADROOM         32

 /* Logging categories configurable via trxcon_set_log_cfg() */
diff --git a/src/host/trxcon/src/sched_lchan_common.c 
b/src/host/trxcon/src/sched_lchan_common.c
index 2308297..2b1729a 100644
--- a/src/host/trxcon/src/sched_lchan_common.c
+++ b/src/host/trxcon/src/sched_lchan_common.c
@@ -78,13 +78,12 @@
  * Examples: "  ****.." (incomplete, 4/6 bursts)
  *           "    ****" (complete, all 4 bursts)
  *           "**.***.." (incomplete, 5/8 bursts) */
-const char *l1sched_burst_mask2str(const uint8_t *mask, int bits)
+const char *l1sched_burst_mask2str(const uint32_t *mask, int bits)
 {
-       /* TODO: CSD is interleaved over 22 bursts, so the mask needs to be 
extended */
-       static char buf[8 + 1];
+       static char buf[32 + 1];
        char *ptr = buf;

-       OSMO_ASSERT(bits <= 8 && bits > 0);
+       OSMO_ASSERT(bits <= 32 && bits > 0);

        while (--bits >= 0)
                *(ptr++) = (*mask & (1 << bits)) ? '*' : '.';
diff --git a/src/host/trxcon/src/sched_lchan_desc.c 
b/src/host/trxcon/src/sched_lchan_desc.c
index 4943a71..db5446e 100644
--- a/src/host/trxcon/src/sched_lchan_desc.c
+++ b/src/host/trxcon/src/sched_lchan_desc.c
@@ -130,7 +130,7 @@
                 *
                 * The MS shall continuously transmit bursts, even if there is 
nothing
                 * to send, unless DTX (Discontinuous Transmission) is used. */
-               .burst_buf_size = 8 * GSM_NBITS_NB_GMSK_PAYLOAD,
+               .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
                .rx_fn = rx_tchf_fn,
                .tx_fn = tx_tchf_fn,
        },
@@ -156,7 +156,7 @@
                 *
                 * The MS shall continuously transmit bursts, even if there is 
nothing
                 * to send, unless DTX (Discontinuous Transmission) is used. */
-               .burst_buf_size = 6 * GSM_NBITS_NB_GMSK_PAYLOAD,
+               .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
                .rx_fn = rx_tchh_fn,
                .tx_fn = tx_tchh_fn,
        },
@@ -167,7 +167,7 @@
                .link_id = L1SCHED_CH_LID_DEDIC,

                /* Same as for L1SCHED_TCHH_0, see above. */
-               .burst_buf_size = 6 * GSM_NBITS_NB_GMSK_PAYLOAD,
+               .burst_buf_size = 24 * GSM_NBITS_NB_GMSK_PAYLOAD,
                .rx_fn = rx_tchh_fn,
                .tx_fn = tx_tchh_fn,
        },
diff --git a/src/host/trxcon/src/sched_lchan_pdtch.c 
b/src/host/trxcon/src/sched_lchan_pdtch.c
index 001a2d9..0f8a25f 100644
--- a/src/host/trxcon/src/sched_lchan_pdtch.c
+++ b/src/host/trxcon/src/sched_lchan_pdtch.c
@@ -36,10 +36,11 @@
 int rx_pdtch_fn(struct l1sched_lchan_state *lchan,
                const struct l1sched_burst_ind *bi)
 {
-       uint8_t l2[GPRS_L2_MAX_LEN], *mask;
+       uint8_t l2[GPRS_L2_MAX_LEN];
        int n_errors, n_bits_total, rc;
        sbit_t *bursts_p, *burst;
        size_t l2_len;
+       uint32_t *mask;

        /* Set up pointers */
        mask = &lchan->rx_burst_mask;
@@ -125,7 +126,7 @@
 {
        ubit_t *bursts_p, *burst;
        const uint8_t *tsc;
-       uint8_t *mask;
+       uint32_t *mask;
        int rc;

        /* Set up pointers */
diff --git a/src/host/trxcon/src/sched_lchan_tchf.c 
b/src/host/trxcon/src/sched_lchan_tchf.c
index 12bea57..ac9ee6b 100644
--- a/src/host/trxcon/src/sched_lchan_tchf.c
+++ b/src/host/trxcon/src/sched_lchan_tchf.c
@@ -3,7 +3,7 @@
  * TDMA scheduler: handlers for DL / UL bursts on logical channels
  *
  * (C) 2017-2022 by Vadim Yanitskiy <[email protected]>
- * Contributions by sysmocom - s.f.m.c. GmbH
+ * (C) 2021-2023 by sysmocom - s.f.m.c. GmbH <[email protected]>
  *
  * All Rights Reserved
  *
@@ -28,6 +28,7 @@

 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>

 #include <osmocom/coding/gsm0503_coding.h>
 #include <osmocom/coding/gsm0503_amr_dtx.h>
@@ -36,6 +37,16 @@
 #include <osmocom/bb/l1sched/l1sched.h>
 #include <osmocom/bb/l1sched/logging.h>

+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
 /* 3GPP TS 45.009, table 3.2.1.3-{1,3}: AMR on Downlink TCH/F.
  *
  * +---+---+---+---+---+---+---+---+
@@ -57,14 +68,34 @@
        [17] = 1, /* TCH/F: a=17 */
 };

+static int decode_fr_facch(struct l1sched_lchan_state *lchan)
+{
+       uint8_t data[GSM_MACBLOCK_LEN];
+       int n_errors, n_bits_total;
+       int rc;
+
+       rc = gsm0503_tch_fr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+                                        &n_errors, &n_bits_total);
+       if (rc != GSM_MACBLOCK_LEN)
+               return rc;
+
+       /* calculate AVG of the measurements (FACCH/F takes 8 bursts) */
+       l1sched_lchan_meas_avg(lchan, 8);
+
+       l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+                                   n_errors, n_bits_total, false);
+
+       return GSM_MACBLOCK_LEN;
+}
+
 int rx_tchf_fn(struct l1sched_lchan_state *lchan,
               const struct l1sched_burst_ind *bi)
 {
        int n_errors = -1, n_bits_total = 0, rc;
        sbit_t *bursts_p, *burst;
-       uint8_t tch_data[128];
+       uint8_t tch_data[290];
        size_t tch_data_len;
-       uint8_t *mask;
+       uint32_t *mask;
        int amr = 0;
        uint8_t ft;

@@ -77,8 +108,8 @@

        if (bi->bid == 0) {
                /* Shift the burst buffer by 4 bursts leftwards */
-               memcpy(&bursts_p[0], &bursts_p[464], 464);
-               memset(&bursts_p[464], 0, 464);
+               memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+               memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
                *mask = *mask << 4;
        } else {
                /* Align to the first burst of a block */
@@ -92,8 +123,8 @@
        /* Store the measurements */
        l1sched_lchan_meas_push(lchan, bi);

-       /* Copy burst to end of buffer of 8 bursts */
-       burst = bursts_p + bi->bid * 116 + 464;
+       /* Copy burst to end of buffer of 24 bursts */
+       burst = BUFPOS(bursts_p, 20 + bi->bid);
        memcpy(burst, bi->burst + 3, 58);
        memcpy(burst + 58, bi->burst + 87, 58);

@@ -102,7 +133,7 @@
                return 0;

        /* Calculate AVG of the measurements */
-       l1sched_lchan_meas_avg(lchan, 8);
+       l1sched_lchan_meas_avg(lchan, 8); // XXX

        /* Check for complete set of bursts */
        if ((*mask & 0xff) != 0xff) {
@@ -115,15 +146,18 @@

        }

+       /* TCH/F: speech and signalling frames are interleaved over 8 bursts, 
while
+        * CSD frames are interleaved over 22 bursts.  Unless we're in CSD mode,
+        * decode only the last 8 bursts to avoid introducing additional 
delays. */
        switch (lchan->tch_mode) {
        case GSM48_CMODE_SIGN:
        case GSM48_CMODE_SPEECH_V1: /* FR */
-               rc = gsm0503_tch_fr_decode(&tch_data[0], bursts_p,
-                       1, 0, &n_errors, &n_bits_total);
+               rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+                                          1, 0, &n_errors, &n_bits_total);
                break;
        case GSM48_CMODE_SPEECH_EFR: /* EFR */
-               rc = gsm0503_tch_fr_decode(&tch_data[0], bursts_p,
-                       1, 1, &n_errors, &n_bits_total);
+               rc = gsm0503_tch_fr_decode(&tch_data[0], BUFTAIL8(bursts_p),
+                                          1, 1, &n_errors, &n_bits_total);
                break;
        case GSM48_CMODE_SPEECH_AMR: /* AMR */
                /* we store tch_data + 2 header bytes, the amr variable set to
@@ -131,7 +165,7 @@
                 * receive an FACCH frame instead of a voice frame (we do not
                 * know this before we actually decode the frame) */
                amr = 2;
-               rc = gsm0503_tch_afs_decode_dtx(&tch_data[amr], bursts_p,
+               rc = gsm0503_tch_afs_decode_dtx(&tch_data[amr], 
BUFTAIL8(bursts_p),
                                                
!sched_tchf_dl_amr_cmi_map[bi->fn % 26],
                                                lchan->amr.codec,
                                                lchan->amr.codecs,
@@ -156,6 +190,36 @@
                                            "osmo_amr_rtp_enc() returned 
rc=%d\n", rc);
                }
                break;
+       /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_14k5:
+               /* FACCH/F does not steal TCH/F14.4 frames, but only disturbs 
some bits */
+               decode_fr_facch(lchan);
+               rc = gsm0503_tch_fr144_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+                                             &n_errors, &n_bits_total);
+               break;
+       /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_12k0:
+               /* FACCH/F does not steal TCH/F9.6 frames, but only disturbs 
some bits */
+               decode_fr_facch(lchan);
+               rc = gsm0503_tch_fr96_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+                                            &n_errors, &n_bits_total);
+               break;
+       /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_6k0:
+               /* FACCH/F does not steal TCH/F4.8 frames, but only disturbs 
some bits */
+               decode_fr_facch(lchan);
+               rc = gsm0503_tch_fr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+                                            &n_errors, &n_bits_total);
+               break;
+       /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_3k6:
+               /* TCH/F2.4 employs the same interleaving as TCH/FS (8 bursts),
+                * so FACCH/F *does* steal TCH/F2.4 frames completely. */
+               if (decode_fr_facch(lchan) == GSM_MACBLOCK_LEN)
+                       return 0; /* TODO: emit BFI? */
+               rc = gsm0503_tch_fr24_decode(&tch_data[0], BUFTAIL8(bursts_p),
+                                            &n_errors, &n_bits_total);
+               break;
        default:
                LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", 
lchan->tch_mode);
                return -EINVAL;
@@ -188,30 +252,13 @@
                                           n_errors, n_bits_total, true);
 }

-static struct msgb *prim_dequeue_tchf(struct l1sched_lchan_state *lchan)
-{
-       struct msgb *msg_facch;
-       struct msgb *msg_tch;
-
-       /* dequeue a pair of TCH and FACCH frames */
-       msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
-       msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
-
-       /* prioritize FACCH over TCH */
-       if (msg_facch != NULL) {
-               msgb_free(msg_tch); /* drop one TCH/FS block */
-               return msg_facch;
-       }
-
-       return msg_tch;
-}
-
 int tx_tchf_fn(struct l1sched_lchan_state *lchan,
               struct l1sched_burst_req *br)
 {
+       struct msgb *msg_facch, *msg_tch, *msg;
        ubit_t *bursts_p, *burst;
        const uint8_t *tsc;
-       uint8_t *mask;
+       uint32_t *mask;
        int rc;
 
        /* Set up pointers */
@@ -225,11 +272,15 @@
        }

        /* Shift the burst buffer by 4 bursts leftwards for interleaving */
-       memcpy(&bursts_p[0], &bursts_p[464], 464);
-       memset(&bursts_p[464], 0, 464);
+       memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 4), 20 * BPLEN);
+       memset(BUFPOS(bursts_p, 20), 0, 4 * BPLEN);
        *mask = *mask << 4;

-       struct msgb *msg = prim_dequeue_tchf(lchan);
+       /* dequeue a pair of TCH and FACCH frames */
+       msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+       msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+       /* prioritize FACCH over TCH */
+       msg = (msg_facch != NULL) ? msg_facch : msg_tch;

        /* populate the buffer with bursts */
        switch (lchan->tch_mode) {
@@ -244,7 +295,7 @@
                        gsm0503_tch_fr_encode(bursts_p, NULL, 0, 1);
                        goto send_burst;
                }
-               rc = gsm0503_tch_fr_encode(bursts_p,
+               rc = gsm0503_tch_fr_encode(BUFPOS(bursts_p, 0),
                                           msgb_l2(msg),
                                           msgb_l2len(msg), 1);
                break;
@@ -268,7 +319,7 @@
                        data += 2;
                }

-               rc = gsm0503_tch_afs_encode(bursts_p,
+               rc = gsm0503_tch_afs_encode(BUFPOS(bursts_p, 0),
                                            data, data_len,
                                            amr_fn_is_cmr,
                                            lchan->amr.codec,
@@ -277,6 +328,60 @@
                                            lchan->amr.ul_cmr);
                break;
        }
+       /* CSD (TCH/F14.4): 14.5 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_14k5:
+               if ((msg = msg_tch) != NULL) {
+                       OSMO_ASSERT(msgb_l2len(msg) == 290);
+                       gsm0503_tch_fr144_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm data sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               } /* else: all bits of this frame are set to zero */
+               if ((msg = msg_facch) != NULL) {
+                       gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm FACCH sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               }
+               goto send_burst;
+       /* CSD (TCH/F9.6): 12.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_12k0:
+               if ((msg = msg_tch) != NULL) {
+                       OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+                       gsm0503_tch_fr96_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm data sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               } /* else: all bits of this frame are set to zero */
+               if ((msg = msg_facch) != NULL) {
+                       gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm FACCH sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               }
+               goto send_burst;
+       /* CSD (TCH/F4.8): 6.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_6k0:
+               if ((msg = msg_tch) != NULL) {
+                       OSMO_ASSERT(msgb_l2len(msg) == 2 * 60);
+                       gsm0503_tch_fr48_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm data sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               } /* else: all bits of this frame are set to zero */
+               if ((msg = msg_facch) != NULL) {
+                       gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm FACCH sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               }
+               goto send_burst;
+       /* CSD (TCH/F2.4): 3.6 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_3k6:
+               if (msg == NULL)
+                       goto send_burst;
+               if (msg == msg_facch) {
+                       /* FACCH/F does steal a TCH/F2.4 frame completely */
+                       rc = gsm0503_tch_fr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+               } else {
+                       OSMO_ASSERT(msgb_l2len(msg) == 2 * 36);
+                       rc = gsm0503_tch_fr24_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+               }
+               break;
        default:
                LOGP_LCHAND(lchan, LOGL_ERROR,
                            "TCH mode %s is unknown or not supported\n",
@@ -288,16 +393,18 @@
                LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload 
(len=%u): %s\n",
                            msgb_l2len(msg), msgb_hexdump_l2(msg));
 free_bad_msg:
-               msgb_free(msg);
+               msgb_free(msg_facch);
+               msgb_free(msg_tch);
                return -EINVAL;
        }

        /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
        l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+       msgb_free((msg == msg_facch) ? msg_tch : msg_facch);

 send_burst:
        /* Determine which burst should be sent */
-       burst = bursts_p + br->bid * 116;
+       burst = BUFPOS(bursts_p, br->bid);

        /* Update mask */
        *mask |= (1 << br->bid);
diff --git a/src/host/trxcon/src/sched_lchan_tchh.c 
b/src/host/trxcon/src/sched_lchan_tchh.c
index 38aef34..0932cce 100644
--- a/src/host/trxcon/src/sched_lchan_tchh.c
+++ b/src/host/trxcon/src/sched_lchan_tchh.c
@@ -4,7 +4,7 @@
  *
  * (C) 2018-2022 by Vadim Yanitskiy <[email protected]>
  * (C) 2018 by Harald Welte <[email protected]>
- * Contributions by sysmocom - s.f.m.c. GmbH
+ * (C) 2020-2023 by sysmocom - s.f.m.c. GmbH <[email protected]>
  *
  * All Rights Reserved
  *
@@ -31,6 +31,7 @@

 #include <osmocom/gsm/protocol/gsm_04_08.h>
 #include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/gsm/gsm0502.h>

 #include <osmocom/coding/gsm0503_coding.h>
 #include <osmocom/coding/gsm0503_amr_dtx.h>
@@ -39,6 +40,16 @@
 #include <osmocom/bb/l1sched/l1sched.h>
 #include <osmocom/bb/l1sched/logging.h>

+/* Burst Payload LENgth (short alias) */
+#define BPLEN GSM_NBITS_NB_GMSK_PAYLOAD
+
+/* Burst BUFfer capacity (in BPLEN units) */
+#define BUFMAX 24
+
+/* Burst BUFfer position macros */
+#define BUFPOS(buf, n) &buf[(n) * BPLEN]
+#define BUFTAIL8(buf) BUFPOS(buf, (BUFMAX - 8))
+
 /* 3GPP TS 45.009, table 3.2.1.3-{2,4}: AMR on Downlink TCH/H.
  *
  * +---+---+---+---+---+---+
@@ -123,6 +134,41 @@
        [7]  = 1, /* FACCH/H(1): B2(22,24,1,3,5,7) */
 };

+/* 3GPP TS 45.002, table 2 in clause 7: Mapping tables for TCH/H2.4 and 
TCH/H4.8.
+ *
+ * 
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | i | j | k | l | m | n | o | p | q | r | s 
| t | u | v |
+ * 
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
+ *
+ * TCH/H(0): B0(0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19)
+ * TCH/H(1): B0(1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20)
+ * TCH/H(0): B1(8,10,13,15,17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2)
+ * TCH/H(1): B1(9,11,14,16,18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3)
+ * TCH/H(0): B2(17,19,21,23,0,2,4,6,8,10,13,15,17,19,21,23,0,2,4,6,8,10)
+ * TCH/H(1): B2(18,20,22,24,1,3,5,7,9,11,14,16,18,20,22,24,1,3,5,7,9,11)
+ *
+ * TDMA frame number of burst 'a' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_ul_csd_map[26] = {
+       [0]  = 1, /* TCH/H(0): B0(0  ... 19) */
+       [1]  = 1, /* TCH/H(1): B0(1  ... 20) */
+       [8]  = 1, /* TCH/H(0): B1(8  ... 2) */
+       [9]  = 1, /* TCH/H(1): B1(9  ... 3) */
+       [17] = 1, /* TCH/H(0): B2(17 ... 10) */
+       [18] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
+/* TDMA frame number of burst 'v' % 26 is the table index.
+ * This mapping is valid for both TCH/H(0) and TCH/H(1). */
+static const uint8_t sched_tchh_dl_csd_map[26] = {
+       [19] = 1, /* TCH/H(0): B0(0  ... 19) */
+       [20] = 1, /* TCH/H(1): B0(1  ... 20) */
+       [2]  = 1, /* TCH/H(0): B1(8  ... 2) */
+       [3]  = 1, /* TCH/H(1): B1(9  ... 3) */
+       [10] = 1, /* TCH/H(0): B2(17 ... 10) */
+       [11] = 1, /* TCH/H(1): B2(18 ... 11) */
+};
+
 /**
  * Can a TCH/H block transmission be initiated / finished
  * on a given frame number and a given channel type?
@@ -180,14 +226,34 @@
        return false;
 }

+static int decode_hr_facch(struct l1sched_lchan_state *lchan)
+{
+       uint8_t data[GSM_MACBLOCK_LEN];
+       int n_errors, n_bits_total;
+       int rc;
+
+       rc = gsm0503_tch_hr_facch_decode(&data[0], BUFTAIL8(lchan->rx_bursts),
+                                        &n_errors, &n_bits_total);
+       if (rc != GSM_MACBLOCK_LEN)
+               return rc;
+
+       /* calculate AVG of the measurements (FACCH/H takes 6 bursts) */
+       l1sched_lchan_meas_avg(lchan, 6);
+
+       l1sched_lchan_emit_data_ind(lchan, &data[0], GSM_MACBLOCK_LEN,
+                                   n_errors, n_bits_total, false);
+
+       return GSM_MACBLOCK_LEN;
+}
+
 int rx_tchh_fn(struct l1sched_lchan_state *lchan,
               const struct l1sched_burst_ind *bi)
 {
        int n_errors = -1, n_bits_total = 0, rc;
        sbit_t *bursts_p, *burst;
-       uint8_t tch_data[128];
+       uint8_t tch_data[240];
        size_t tch_data_len;
-       uint8_t *mask;
+       uint32_t *mask;
        int amr = 0;
        uint8_t ft;

@@ -200,9 +266,8 @@

        if (bi->bid == 0) {
                /* Shift the burst buffer by 2 bursts leftwards */
-               memcpy(&bursts_p[0], &bursts_p[232], 232);
-               memcpy(&bursts_p[232], &bursts_p[464], 232);
-               memset(&bursts_p[464], 0, 232);
+               memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+               memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
                *mask = *mask << 2;
        }

@@ -215,9 +280,6 @@
                if (lchan->tch_mode == GSM48_CMODE_SIGN) {
                        if (!l1sched_tchh_facch_start(lchan->type, bi->fn, 0))
                                return 0;
-               } else { /* or TCH/H traffic frame */
-                       if (!l1sched_tchh_traffic_start(lchan->type, bi->fn, 0))
-                               return 0;
                }
        }

@@ -227,8 +289,8 @@
        /* Store the measurements */
        l1sched_lchan_meas_push(lchan, bi);

-       /* Copy burst to the end of buffer of 6 bursts */
-       burst = bursts_p + bi->bid * 116 + 464;
+       /* Copy burst to the end of buffer of 24 bursts */
+       burst = BUFPOS(bursts_p, 20 + bi->bid);
        memcpy(burst, bi->burst + 3, 58);
        memcpy(burst + 58, bi->burst + 87, 58);

@@ -236,6 +298,28 @@
        if (bi->bid != 1)
                return 0;

+       /* Wait for complete set of bursts */
+       switch (lchan->tch_mode) {
+       case GSM48_CMODE_SIGN:
+               /* FACCH/H is interleaved over 6 bursts */
+               if ((*mask & 0x3f) != 0x3f)
+                       return 0;
+               break;
+       case GSM48_CMODE_DATA_6k0:
+       case GSM48_CMODE_DATA_3k6:
+               /* Data (CSD) is interleaved over 22 bursts */
+               if ((*mask & 0x3fffff) != 0x3fffff)
+                       return 0;
+               if (!sched_tchh_dl_csd_map[bi->fn % 26])
+                       return 0; /* CSD: skip decoding attempt, need 2 more 
bursts */
+               break;
+       default:
+               /* Speech is interleaved over 4 bursts */
+               if ((*mask & 0x0f) != 0x0f)
+                       return 0;
+               break;
+       }
+
        /* Skip decoding attempt in case of FACCH/H */
        if (lchan->dl_ongoing_facch) {
                /* Send BFI (DATA.ind without payload) for the 2nd stolen TCH 
frame */
@@ -245,17 +329,21 @@
                return 0;
        }

+       /* TCH/H: speech and signalling frames are interleaved over 4 and 6 
bursts,
+        * respectively, while CSD frames are interleaved over 22 bursts.  
Unless
+        * we're in CSD mode, decode only the last 6 bursts to avoid introducing
+        * additional delays. */
        switch (lchan->tch_mode) {
        case GSM48_CMODE_SIGN:
        case GSM48_CMODE_SPEECH_V1: /* HR */
-               rc = gsm0503_tch_hr_decode(&tch_data[0], bursts_p,
+               rc = gsm0503_tch_hr_decode(&tch_data[0], BUFTAIL8(bursts_p),
                                           !sched_tchh_dl_facch_map[bi->fn % 
26],
                                           &n_errors, &n_bits_total);
                break;
        case GSM48_CMODE_SPEECH_AMR: /* AMR */
                /* See comment in function rx_tchf_fn() */
                amr = 2;
-               rc = gsm0503_tch_ahs_decode_dtx(&tch_data[amr], bursts_p,
+               rc = gsm0503_tch_ahs_decode_dtx(&tch_data[amr], 
BUFTAIL8(bursts_p),
                                                !sched_tchh_dl_facch_map[bi->fn 
% 26],
                                                
!sched_tchh_dl_amr_cmi_map[bi->fn % 26],
                                                lchan->amr.codec,
@@ -281,6 +369,20 @@
                                            "osmo_amr_rtp_enc() returned 
rc=%d\n", rc);
                }
                break;
+       /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_6k0:
+               /* FACCH/H does not steal TCH/H4.8 frames, but only disturbs 
some bits */
+               decode_hr_facch(lchan);
+               rc = gsm0503_tch_hr48_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+                                            &n_errors, &n_bits_total);
+               break;
+       /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_3k6:
+               /* FACCH/H does not steal TCH/H2.4 frames, but only disturbs 
some bits */
+               decode_hr_facch(lchan);
+               rc = gsm0503_tch_hr24_decode(&tch_data[0], BUFPOS(bursts_p, 0),
+                                            &n_errors, &n_bits_total);
+               break;
        default:
                LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n", 
lchan->tch_mode);
                return -EINVAL;
@@ -322,33 +424,13 @@
                                           n_errors, n_bits_total, true);
 }

-static struct msgb *prim_dequeue_tchh(struct l1sched_lchan_state *lchan, 
uint32_t fn)
-{
-       struct msgb *msg_facch;
-       struct msgb *msg_tch;
-
-       /* dequeue a pair of TCH and FACCH frames */
-       msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
-       if (l1sched_tchh_facch_start(lchan->type, fn, true))
-               msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
-       else
-               msg_facch = NULL;
-
-       /* prioritize FACCH over TCH */
-       if (msg_facch != NULL) {
-               msgb_free(msg_tch); /* drop 1st TCH/HS block */
-               return msg_facch;
-       }
-
-       return msg_tch;
-}
-
 int tx_tchh_fn(struct l1sched_lchan_state *lchan,
               struct l1sched_burst_req *br)
 {
+       struct msgb *msg_facch, *msg_tch, *msg;
        ubit_t *bursts_p, *burst;
        const uint8_t *tsc;
-       uint8_t *mask;
+       uint32_t *mask;
        int rc;

        /* Set up pointers */
@@ -362,16 +444,23 @@
        }

        if (*mask == 0x00) {
-               /* Align transmission of the first FACCH/H frame */
-               if (lchan->tch_mode == GSM48_CMODE_SIGN)
+               /* Align transmission of the first frame */
+               switch (lchan->tch_mode) {
+               case GSM48_CMODE_SIGN:
                        if (!l1sched_tchh_facch_start(lchan->type, br->fn, 1))
                                return 0;
+                       break;
+               case GSM48_CMODE_DATA_6k0:
+               case GSM48_CMODE_DATA_3k6:
+                       if (!sched_tchh_ul_csd_map[br->fn % 26])
+                               return 0;
+                       break;
+               }
        }

        /* Shift the burst buffer by 2 bursts leftwards for interleaving */
-       memcpy(&bursts_p[0], &bursts_p[232], 232);
-       memcpy(&bursts_p[232], &bursts_p[464], 232);
-       memset(&bursts_p[464], 0, 232);
+       memmove(BUFPOS(bursts_p, 0), BUFPOS(bursts_p, 2), 20 * BPLEN);
+       memset(BUFPOS(bursts_p, 20), 0, 2 * BPLEN);
        *mask = *mask << 2;

        /* If FACCH/H blocks are still pending */
@@ -381,7 +470,23 @@
                goto send_burst;
        }

-       struct msgb *msg = prim_dequeue_tchh(lchan, br->fn);
+       switch (lchan->tch_mode) {
+       case GSM48_CMODE_DATA_6k0:
+       case GSM48_CMODE_DATA_3k6:
+               /* CSD: skip dequeueing/encoding, send 2 more bursts */
+               if (!sched_tchh_ul_csd_map[br->fn % 26])
+                       goto send_burst;
+               break;
+       }
+
+       /* dequeue a pair of TCH and FACCH frames */
+       msg_tch = l1sched_lchan_prim_dequeue_tch(lchan, false);
+       if (l1sched_tchh_facch_start(lchan->type, br->fn, true))
+               msg_facch = l1sched_lchan_prim_dequeue_tch(lchan, true);
+       else
+               msg_facch = NULL;
+       /* prioritize FACCH over TCH */
+       msg = (msg_facch != NULL) ? msg_facch : msg_tch;

        /* populate the buffer with bursts */
        switch (lchan->tch_mode) {
@@ -397,7 +502,7 @@
                        gsm0503_tch_hr_encode(bursts_p, NULL, 0);
                        goto send_burst;
                }
-               rc = gsm0503_tch_hr_encode(bursts_p,
+               rc = gsm0503_tch_hr_encode(BUFPOS(bursts_p, 0),
                                           msgb_l2(msg),
                                           msgb_l2len(msg));
                break;
@@ -421,7 +526,7 @@
                        data += 2;
                }

-               rc = gsm0503_tch_ahs_encode(bursts_p,
+               rc = gsm0503_tch_ahs_encode(BUFPOS(bursts_p, 0),
                                            data, data_len,
                                            amr_fn_is_cmr,
                                            lchan->amr.codec,
@@ -430,6 +535,34 @@
                                            lchan->amr.ul_cmr);
                break;
        }
+       /* CSD (TCH/H4.8): 6.0 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_6k0:
+               if ((msg = msg_tch) != NULL) {
+                       OSMO_ASSERT(msgb_l2len(msg) == 4 * 60);
+                       gsm0503_tch_hr48_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm data sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               } /* else: all bits of this frame are set to zero */
+               if ((msg = msg_facch) != NULL) {
+                       gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm FACCH sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               }
+               goto send_burst;
+       /* CSD (TCH/H2.4): 3.6 kbit/s radio interface rate */
+       case GSM48_CMODE_DATA_3k6:
+               if ((msg = msg_tch) != NULL) {
+                       OSMO_ASSERT(msgb_l2len(msg) == 4 * 36);
+                       gsm0503_tch_hr24_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm data sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               } /* else: all bits of this frame are set to zero */
+               if ((msg = msg_facch) != NULL) {
+                       gsm0503_tch_hr_facch_encode(BUFPOS(bursts_p, 0), 
msgb_l2(msg));
+                       /* Confirm FACCH sending (pass ownership of the 
msgb/prim) */
+                       l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+               }
+               goto send_burst;
        default:
                LOGP_LCHAND(lchan, LOGL_ERROR,
                            "TCH mode %s is unknown or not supported\n",
@@ -441,7 +574,8 @@
                LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload 
(len=%u): %s\n",
                            msgb_l2len(msg), msgb_hexdump_l2(msg));
 free_bad_msg:
-               msgb_free(msg);
+               msgb_free(msg_facch);
+               msgb_free(msg_tch);
                return -EINVAL;
        }

@@ -450,10 +584,11 @@

        /* Confirm data / traffic sending (pass ownership of the msgb/prim) */
        l1sched_lchan_emit_data_cnf(lchan, msg, br->fn);
+       msgb_free((msg == msg_facch) ? msg_tch : msg_facch);

 send_burst:
        /* Determine which burst should be sent */
-       burst = bursts_p + br->bid * 116;
+       burst = BUFPOS(bursts_p, br->bid);

        /* Update mask */
        *mask |= (1 << br->bid);
diff --git a/src/host/trxcon/src/sched_lchan_xcch.c 
b/src/host/trxcon/src/sched_lchan_xcch.c
index b3863d5..52b5d1e 100644
--- a/src/host/trxcon/src/sched_lchan_xcch.c
+++ b/src/host/trxcon/src/sched_lchan_xcch.c
@@ -36,9 +36,10 @@
 int rx_data_fn(struct l1sched_lchan_state *lchan,
               const struct l1sched_burst_ind *bi)
 {
-       uint8_t l2[GSM_MACBLOCK_LEN], *mask;
+       uint8_t l2[GSM_MACBLOCK_LEN];
        int n_errors, n_bits_total, rc;
        sbit_t *bursts_p, *burst;
+       uint32_t *mask;

        /* Set up pointers */
        mask = &lchan->rx_burst_mask;
@@ -124,7 +125,7 @@
 {
        ubit_t *bursts_p, *burst;
        const uint8_t *tsc;
-       uint8_t *mask;
+       uint32_t *mask;
        int rc;

        /* Set up pointers */
diff --git a/src/host/trxcon/src/sched_prim.c b/src/host/trxcon/src/sched_prim.c
index 2597d84..c4c80f8 100644
--- a/src/host/trxcon/src/sched_prim.c
+++ b/src/host/trxcon/src/sched_prim.c
@@ -36,7 +36,7 @@
 #include <osmocom/bb/l1sched/logging.h>

 #define L1SCHED_PRIM_HEADROOM  64
-#define L1SCHED_PRIM_TAILROOM  64
+#define L1SCHED_PRIM_TAILROOM  512

 osmo_static_assert(sizeof(struct l1sched_prim) <= L1SCHED_PRIM_HEADROOM, 
l1sched_prim_size);


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

Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I0d7389a9a5f7019b9316ab1c0115797ff54a0e41
Gerrit-Change-Number: 32922
Gerrit-PatchSet: 13
Gerrit-Owner: fixeria <[email protected]>
Gerrit-Reviewer: Hoernchen <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <[email protected]>
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: osmith <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-MessageType: merged

Reply via email to