pespin has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmocom-bb/+/29264 )
Change subject: trxcon: Initial support for forwarding AMR
......................................................................
trxcon: Initial support for forwarding AMR
This allows TTCN3 L1CTL module (used in BTS_Tests) to transmit and
receive AMR payloads towards osmo-bts-trx.
Related: SYS#5987
Change-Id: Ia20bc96e39726a919a556c83c8be48cb31af7331
---
M include/l1ctl_proto.h
M src/host/layer23/src/common/l1ctl.c
M src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
M src/host/trxcon/include/osmocom/bb/l1sched/logging.h
M src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
M src/host/trxcon/src/l1ctl.c
M src/host/trxcon/src/sched_lchan_tchf.c
M src/host/trxcon/src/trxcon_fsm.c
M src/host/virt_phy/src/l1ctl_sap.c
M src/target/firmware/layer1/l23_api.c
10 files changed, 170 insertions(+), 55 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/64/29264/1
diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h
index 3720f82..a15f905 100644
--- a/include/l1ctl_proto.h
+++ b/include/l1ctl_proto.h
@@ -162,7 +162,10 @@
uint8_t tch_mode; /* enum tch_mode */
uint8_t audio_mode;
uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
- uint8_t padding[1];
+ struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
} __attribute__((packed));
/* data on the CCCH was found. This is following the header */
@@ -241,7 +244,10 @@
#define AUDIO_RX_TRAFFIC_IND (1<<3)
uint8_t audio_mode;
uint8_t tch_loop_mode; /* enum l1ctl_tch_loop_mode */
- uint8_t padding[1];
+ struct { /* 3GPP TS 08.58 9.3.52, 3GPP TS 44.018 10.5.2.21aa */
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
} __attribute__((packed));
/* the l1_info_ul header is in front */
diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c
index e33074a..d520403 100644
--- a/src/host/layer23/src/common/l1ctl.c
+++ b/src/host/layer23/src/common/l1ctl.c
@@ -472,6 +472,7 @@
req->tch_mode = tch_mode;
req->audio_mode = audio_mode;
req->tch_loop_mode = tch_loop_mode;
+ /* TODO: Set AMR codec in req if req->tch_mode==GSM48_CMODE_SPEECH_AMR */
return osmo_send_l1(ms, msg);
}
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
index 04303f0..9686562 100644
--- a/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/l1sched.h
@@ -297,6 +297,8 @@
uint8_t ber_num;
/*! Sum of bit error rates */
float ber_sum;
+ /* last received dtx frame type */
+ uint8_t last_dtx;
} amr;
/*! A5/X encryption state */
diff --git a/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
index fb1c018..d2b80e3 100644
--- a/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
+++ b/src/host/trxcon/include/osmocom/bb/l1sched/logging.h
@@ -17,12 +17,13 @@
LOGP_SCHED_CAT(sched, common, level, fmt, ## args)
+#define LOGP_LCHAN_NAME_FMT "TS%u-%s"
#define LOGP_LCHAN_NAME_ARGS(lchan) \
(lchan)->ts->index, l1sched_lchan_desc[(lchan)->type].name
/* Messages using l1sched_lchan_state as the context */
#define LOGP_LCHAN_CAT(lchan, cat, level, fmt, args...) \
- LOGP_SCHED_CAT((lchan)->ts->sched, cat, level, "TS%u-%s " fmt, \
+ LOGP_SCHED_CAT((lchan)->ts->sched, cat, level, LOGP_LCHAN_NAME_FMT " "
fmt, \
LOGP_LCHAN_NAME_ARGS(lchan), ## args)
/* Common messages using l1sched_lchan_state as the context */
diff --git a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
index 2a6f7d5..6344733 100644
--- a/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
+++ b/src/host/trxcon/include/osmocom/bb/trxcon/trxcon.h
@@ -58,6 +58,10 @@
/* param of TRXCON_EV_SET_{CCCH,TCH}_MODE_REQ */
struct trxcon_param_set_ccch_tch_mode_req {
uint8_t mode;
+ struct {
+ uint8_t start_codec;
+ uint8_t codecs_bitmask;
+ } amr;
bool applied;
};
diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c
index 4d6bed2..033b93d 100644
--- a/src/host/trxcon/src/l1ctl.c
+++ b/src/host/trxcon/src/l1ctl.c
@@ -702,6 +702,10 @@
struct trxcon_param_set_ccch_tch_mode_req req = {
.mode = mode_req->tch_mode,
};
+ if (mode_req->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ req.amr.start_codec = mode_req->amr.start_codec;
+ req.amr.codecs_bitmask = mode_req->amr.codecs_bitmask;
+ }
rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_SET_TCH_MODE_REQ, &req);
if (rc != 0 || !req.applied) {
diff --git a/src/host/trxcon/src/sched_lchan_tchf.c
b/src/host/trxcon/src/sched_lchan_tchf.c
index 96acf0e..0474df7 100644
--- a/src/host/trxcon/src/sched_lchan_tchf.c
+++ b/src/host/trxcon/src/sched_lchan_tchf.c
@@ -30,11 +30,63 @@
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_amr_dtx.h>
#include <osmocom/codec/codec.h>
#include <osmocom/bb/l1sched/l1sched.h>
#include <osmocom/bb/l1sched/logging.h>
+/* 3GPP TS 45.009, table 3.2.1.3-{1,3}: AMR on Uplink TCH/F.
+ *
+ * +---+---+---+---+---+---+---+---+
+ * | a | b | c | d | e | f | g | h | Burst 'a' received first
+ * +---+---+---+---+---+---+---+---+
+ * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Speech/FACCH frame (bursts 'a' ..
'h')
+ *
+ * TDMA frame number of burst 'h' is always used as the table index. */
+static const uint8_t sched_tchf_ul_amr_cmi_map[26] = {
+ [7] = 1, /* TCH/F: a=0 / h=7 */
+ [16] = 1, /* TCH/F: a=8 / h=16 */
+ [24] = 1, /* TCH/F: a=17 / h=24 */
+};
+
+/*! determine whether an uplink AMR block is CMI according to 3GPP TS 45.009.
+ * \param[in] fn_begin frame number of the beginning of the block.
+ * \returns true in case of CMI; false otherwise. */
+static inline bool ul_amr_fn_is_cmi(uint32_t fn_begin)
+{
+ switch (fn_begin % 26) {
+ /*! See also: 3GPP TS 45.009, section 3.2.1.3 Transmitter/Receiver Synchronisation */
+ /* valid for AHS subslot 0 and AFS: */
+ case 0:
+ case 8:
+ case 17:
+ /* valid for AHS subslot 1: */
+ case 1:
+ case 9:
+ case 18:
+ return true;
+ break;
+ /* Complementary values for sanity check */
+ /* valid for AHS subslot 0 and AFS: */
+ case 4:
+ case 13:
+ case 21:
+ /* valid for AHS subslot 1: */
+ case 5:
+ case 14:
+ case 22:
+ return false;
+ break;
+ default:
+ LOGP(l1sched_log_cat_data, LOGL_DEBUG,
+ "uplink frame number fn_begin=%u does not mark the beginning of a voice
block!\n", fn_begin);
+ OSMO_ASSERT(false);
+ return false;
+ break;
+ }
+}
+
int rx_tchf_fn(struct l1sched_lchan_state *lchan,
uint32_t fn, uint8_t bid, const sbit_t *bits,
const struct l1sched_meas_set *meas)
@@ -43,6 +95,9 @@
sbit_t *buffer, *offset;
uint8_t l2[128], *mask;
size_t l2_len;
+ int amr = 0;
+ uint8_t ft;
+ bool amr_is_cmr;
/* Set up pointers */
mask = &lchan->rx_burst_mask;
@@ -97,12 +152,36 @@
1, 1, &n_errors, &n_bits_total);
break;
case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
+ /* the first FN 0,8,17 defines that CMI is included in frame,
+ * the first FN 4,13,21 defines that CMR is included in frame.
+ * NOTE: A frame ends 7 FN after start.
*/
- LOGP_LCHAND(lchan, LOGL_ERROR, "AMR isn't supported yet\n");
- return -ENOTSUP;
+ amr_is_cmr = !sched_tchf_ul_amr_cmi_map[fn % 26];
+
+ /* we store tch_data + 2 header bytes, the amr variable set to
+ * 2 will allow us to skip the first 2 bytes in case we did
+ * 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(l2 + amr, buffer,
+ amr_is_cmr, lchan->amr.codec, lchan->amr.codecs, &lchan->amr.ul_ft,
+ &lchan->amr.ul_cmr, &n_errors, &n_bits_total,
&lchan->amr.last_dtx);
+
+ /* only good speech frames get rtp header */
+ if (rc != GSM_MACBLOCK_LEN && rc >= 4) {
+ if (lchan->amr.last_dtx == AMR_OTHER) {
+ ft = lchan->amr.codec[lchan->amr.ul_ft];
+ } else {
+ /* SID frames will always get Frame Type Index 8 (AMR_SID) */
+ ft = AMR_SID;
+ }
+ rc = osmo_amr_rtp_enc(l2,
+ lchan->amr.codec[lchan->amr.ul_cmr],
+ ft, AMR_GOOD);
+ LOGP_LCHAND(lchan, LOGL_ERROR, "osmo_amr_rtp_enc() returned rc=%d\n", rc);
+ }
+
+ break;
default:
LOGP_LCHAND(lchan, LOGL_ERROR, "Invalid TCH mode: %u\n",
lchan->tch_mode);
return -EINVAL;
@@ -186,55 +265,51 @@
if (br->bid > 0)
return 0;
- /* Check the current TCH mode */
- switch (lchan->tch_mode) {
- case GSM48_CMODE_SIGN:
- case GSM48_CMODE_SPEECH_V1: /* FR */
- l2_len = GSM_FR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_EFR: /* EFR */
- l2_len = GSM_EFR_BYTES;
- break;
- case GSM48_CMODE_SPEECH_AMR: /* AMR */
- /**
- * TODO: AMR requires a dedicated loop,
- * which will be implemented later...
- */
- LOGP_LCHAND(lchan, LOGL_ERROR,
- "AMR isn't supported yet, dropping frame...\n");
-
- /* Forget this primitive */
- l1sched_prim_drop(lchan);
-
- return -ENOTSUP;
- default:
- LOGP_LCHAND(lchan, LOGL_ERROR,
- "Invalid TCH mode: %u, dropping frame...\n",
- lchan->tch_mode);
-
- /* Forget this primitive */
- l1sched_prim_drop(lchan);
-
- return -EINVAL;
- }
-
- /* Determine and check the payload length */
- if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) {
- l2_len = GSM_MACBLOCK_LEN; /* FACCH */
- } else if (lchan->prim->payload_len != l2_len) {
- LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
- "(expected %zu for TCH or %u for FACCH), so dropping...\n",
- lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
-
- l1sched_prim_drop(lchan);
- return -EINVAL;
- }
-
/* Shift buffer by 4 bursts back for interleaving */
memcpy(buffer, buffer + 464, 464);
- /* Encode payload */
- rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
+ /* populate the buffer with bursts */
+ if (lchan->prim->payload_len == GSM_MACBLOCK_LEN) { /* FACCH */
+ /* Encode payload */
+ rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, GSM_MACBLOCK_LEN, 1);
+ } else if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) {
+ /* the first FN 0,8,17 defines that CMI is included in frame,
+ * the first FN 4,13,21 defines that CMR is included in frame.
+ */
+ rc = gsm0503_tch_afs_encode(buffer, lchan->prim->payload + 2,
+ lchan->prim->payload_len - 2, !ul_amr_fn_is_cmi(br->fn),
+ lchan->amr.codec, lchan->amr.codecs,
+ lchan->amr.ul_ft,
+ lchan->amr.ul_cmr);
+ } else {
+ /* Determine and check the payload length */
+ switch (lchan->tch_mode) {
+ case GSM48_CMODE_SIGN:
+ case GSM48_CMODE_SPEECH_V1: /* FR */
+ l2_len = GSM_FR_BYTES;
+ break;
+ case GSM48_CMODE_SPEECH_EFR: /* EFR */
+ l2_len = GSM_EFR_BYTES;
+ break;
+ default:
+ LOGP_LCHAND(lchan, LOGL_ERROR,
+ "Invalid TCH mode: %u, dropping frame...\n",
+ lchan->tch_mode);
+ /* Forget this primitive */
+ l1sched_prim_drop(lchan);
+ return -EINVAL;
+ }
+ if (lchan->prim->payload_len != l2_len) {
+ LOGP_LCHAND(lchan, LOGL_ERROR, "Primitive has odd length %zu "
+ "(expected %zu for TCH or %u for FACCH), so dropping...\n",
+ lchan->prim->payload_len, l2_len, GSM_MACBLOCK_LEN);
+
+ l1sched_prim_drop(lchan);
+ return -EINVAL;
+ }
+ rc = gsm0503_tch_fr_encode(buffer, lchan->prim->payload, l2_len, 1);
+ }
+
if (rc) {
LOGP_LCHAND(lchan, LOGL_ERROR, "Failed to encode L2 payload (len=%zu):
%s\n",
lchan->prim->payload_len, osmo_hexdump(lchan->prim->payload,
diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c
index aa7193a..1713539 100644
--- a/src/host/trxcon/src/trxcon_fsm.c
+++ b/src/host/trxcon/src/trxcon_fsm.c
@@ -36,6 +36,7 @@
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/bb/trxcon/l1ctl_proto.h>
#include <osmocom/bb/l1sched/l1sched.h>
+#include <osmocom/bb/l1sched/logging.h>
#define S(x) (1 << (x))
@@ -340,6 +341,26 @@
if (!lchan->active)
continue;
lchan->tch_mode = req->mode;
+ if (req->mode == GSM48_CMODE_SPEECH_AMR) {
+ int pos, n, acum;
+ uint8_t bmask = req->amr.codecs_bitmask;
+ n = 0, acum = 0;
+ while ((pos = ffs(bmask)) != 0) {
+ acum += pos;
+ LOGPFSML(fi, LOGL_DEBUG,
+ LOGP_LCHAN_NAME_FMT " AMR codec[%u] = %u\n",
+ LOGP_LCHAN_NAME_ARGS(lchan), n, acum - 1);
+ lchan->amr.codec[n++] = acum - 1;
+ bmask >>= pos;
+ }
+ if (n == 0) {
+ LOGPFSML(fi, LOGL_ERROR,
+ LOGP_LCHAN_NAME_FMT " Empty AMR codec mode bitmask!\n",
+ LOGP_LCHAN_NAME_ARGS(lchan));
+ continue;
+ }
+ lchan->amr.codecs = n;
+ }
req->applied = true;
}
}
diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c
index bab6259..a0e8c0c 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -486,6 +486,7 @@
l1_model_tch_mode_set(ms, tch_mode_req->tch_mode);
ms->state.audio_mode = tch_mode_req->audio_mode;
+ /* TODO: Handle AMR codecs from tch_mode_req if
tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */
LOGPMS(DL1C, LOGL_INFO, ms, "Rx L1CTL_TCH_MODE_REQ (tch_mode=0x%02x
audio_mode=0x%02x)\n",
tch_mode_req->tch_mode, tch_mode_req->audio_mode);
diff --git a/src/target/firmware/layer1/l23_api.c b/src/target/firmware/layer1/l23_api.c
index a133c62..599272f 100644
--- a/src/target/firmware/layer1/l23_api.c
+++ b/src/target/firmware/layer1/l23_api.c
@@ -553,6 +553,7 @@
l1s.tch_sync = 1; /* Needed for audio to work */
l1s.tch_loop_mode = tch_mode_req->tch_loop_mode;
+ /* TODO: Handle AMR codecs from tch_mode_req if
tch_mode_req->tch_mode==GSM48_CMODE_SPEECH_AMR */
l1ctl_tx_tch_mode_conf(tch_mode, audio_mode);
}
@@ -726,4 +727,3 @@
{
sercomm_register_rx_cb(SC_DLCI_L1A_L23, l1a_l23_rx);
}
-
--
To view, visit
https://gerrit.osmocom.org/c/osmocom-bb/+/29264
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: Ia20bc96e39726a919a556c83c8be48cb31af7331
Gerrit-Change-Number: 29264
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: newchange