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@gmail.com - * Contributions by sysmocom - s.f.m.c. GmbH + * (C) 2021-2023 by sysmocom - s.f.m.c. GmbH info@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@gmail.com * (C) 2018 by Harald Welte laforge@gnumonks.org - * Contributions by sysmocom - s.f.m.c. GmbH + * (C) 2020-2023 by sysmocom - s.f.m.c. GmbH info@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);