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 <axilirator(a)gmail.com>
- * Contributions by sysmocom - s.f.m.c. GmbH
+ * (C) 2021-2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
*
* 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 <axilirator(a)gmail.com>
* (C) 2018 by Harald Welte <laforge(a)gnumonks.org>
- * Contributions by sysmocom - s.f.m.c. GmbH
+ * (C) 2020-2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de>
*
* 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 <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: Hoernchen <ewild(a)sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <vyanitskiy(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: osmith <osmith(a)sysmocom.de>
Gerrit-Reviewer: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: merged