trxcon/scheduler: transmit dummy frames on CBTX lchans If at the moment of transmission there are no frames in TX buffer, then either a dummy LAPDm frame (0x01, 0x03, 0x01, 0x2b ...) or a silence frame (depending on a codec in use) shall be transmitted. This is required for proper measurements on the BTS side.
Change-Id: Ie590990f2274ea476678f6b2079f90eeadab6501 --- M src/host/trxcon/sched_prim.c M src/host/trxcon/sched_trx.c M src/host/trxcon/sched_trx.h M src/host/trxcon/trxcon.c 4 files changed, 118 insertions(+), 0 deletions(-) git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/06/7206/3 diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index fb5f0a0..c17fb2a 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -23,6 +23,7 @@ */ #include <errno.h> +#include <stdlib.h> #include <string.h> #include <talloc.h> @@ -31,6 +32,7 @@ #include <osmocom/core/linuxlist.h> #include <osmocom/gsm/protocol/gsm_04_08.h> +#include <osmocom/gsm/protocol/gsm_08_58.h> #include "scheduler.h" #include "sched_trx.h" @@ -227,6 +229,93 @@ } /** + * Assigns a dummy primitive to a lchan depending on its type. + * Could be used when there is nothing to transmit, but + * CBTX (Continuous Burst Transmission) is assumed. + * + * @param lchan lchan to assign a primitive + * @return zero in case of success, otherwise a error code + */ +int sched_prim_dummy(struct trx_lchan_state *lchan) +{ + enum trx_lchan_type chan = lchan->type; + uint8_t tch_mode = lchan->tch_mode; + struct trx_ts_prim *prim; + uint8_t prim_buffer[40]; + size_t prim_len = 0; + int i; + + /** + * TS 144.006, section 8.4.2.3 "Fill frames" + * A fill frame is a UI command frame for SAPI 0, P=0 + * and with an information field of 0 octet length. + */ + static const uint8_t lapdm_fill_frame[] = { + 0x01, 0x03, 0x01, + /* Pending part is to be randomized */ + }; + + /* Make sure that there is no existing primitive */ + OSMO_ASSERT(lchan->prim == NULL); + + /** + * Determine what actually should be generated: + * TCH in GSM48_CMODE_SIGN: LAPDm fill frame; + * TCH in other modes: silence frame; + * other channels: LAPDm fill frame. + */ + if (CHAN_IS_TCH(chan) && TCH_MODE_IS_SPEECH(tch_mode)) { + /** + * Silence frame indication + * HACK: use actual rsl_cmode! + */ + prim_len = sched_bad_frame_ind(prim_buffer, + RSL_CMOD_SPD_SPEECH, tch_mode); + } else if (CHAN_IS_TCH(chan) && TCH_MODE_IS_DATA(tch_mode)) { + /* FIXME: should we do anything for CSD? */ + return 0; + } else { + /** + * TS 144.006, section 8.1.2.3 "Fill frames" + * A fill frame is a UI command frame for SAPI 0, P=0 + * and with an information field of 0 octet length. + */ + memcpy(prim_buffer, lapdm_fill_frame, 3); + + /* Randomize pending unused bytes */ + for (i = 3; i < GSM_MACBLOCK_LEN; i++) + prim_buffer[i] = (uint8_t) rand(); + + /* Define a prim length */ + prim_len = GSM_MACBLOCK_LEN; + } + + /* Nothing to allocate / assign */ + if (!prim_len) + return 0; + + /* Allocate a new primitive */ + prim = talloc_zero_size(lchan, sizeof(struct trx_ts_prim) + prim_len); + if (prim == NULL) + return -ENOMEM; + + /* Init primitive header */ + prim->payload_len = prim_len; + prim->chan = lchan->type; + + /* Fill in the payload */ + memcpy(prim->payload, prim_buffer, prim_len); + + /* Assign the current prim */ + lchan->prim = prim; + + LOGP(DSCHD, LOGL_DEBUG, "Transmitting a dummy / silence frame " + "on lchan=%s\n", trx_lchan_desc[chan].name); + + return 0; +} + +/** * Flushes a queue of primitives * * @param list list of prims going to be flushed diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index 8c859de..168c4ef 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -97,6 +97,19 @@ if (lchan->prim == NULL) lchan->prim = sched_prim_dequeue(&ts->tx_prims, chan); + /* TODO: report TX buffers health to the higher layers */ + + /* If CBTX (Continuous Burst Transmission) is assumed */ + if (trx_lchan_desc[chan].flags & TRX_CH_FLAG_CBTX) { + /** + * Probably, a TX buffer is empty. Nevertheless, + * we shall continuously transmit anything on + * CBTX channels. + */ + if (lchan->prim == NULL) + sched_prim_dummy(lchan); + } + /* If there is no primitive, do nothing */ if (lchan->prim == NULL) continue; diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 6a02543..d9e0c8e 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -275,6 +275,17 @@ int sched_prim_push(struct trx_instance *trx, struct trx_ts_prim *prim, uint8_t chan_nr); +#define TCH_MODE_IS_SPEECH(mode) \ + (mode == GSM48_CMODE_SPEECH_V1 \ + || mode == GSM48_CMODE_SPEECH_EFR \ + || mode == GSM48_CMODE_SPEECH_AMR) + +#define TCH_MODE_IS_DATA(mode) \ + (mode == GSM48_CMODE_DATA_14k5 \ + || mode == GSM48_CMODE_DATA_12k0 \ + || mode == GSM48_CMODE_DATA_6k0 \ + || mode == GSM48_CMODE_DATA_3k6) + #define CHAN_IS_TCH(chan) \ (chan == TRXC_TCHF || chan == TRXC_TCHH_0 || chan == TRXC_TCHH_1) @@ -286,6 +297,7 @@ struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, enum trx_lchan_type lchan_type); +int sched_prim_dummy(struct trx_lchan_state *lchan); void sched_prim_drop(struct trx_lchan_state *lchan); void sched_prim_flush_queue(struct llist_head *list); diff --git a/src/host/trxcon/trxcon.c b/src/host/trxcon/trxcon.c index 5542240..43c98a5 100644 --- a/src/host/trxcon/trxcon.c +++ b/src/host/trxcon/trxcon.c @@ -28,6 +28,7 @@ #include <getopt.h> #include <unistd.h> #include <signal.h> +#include <time.h> #include <arpa/inet.h> @@ -293,6 +294,9 @@ } } + /* Initialize pseudo-random generator */ + srand(time(NULL)); + while (!app_data.quit) osmo_select_main(0); -- To view, visit https://gerrit.osmocom.org/7206 To unsubscribe, visit https://gerrit.osmocom.org/settings Gerrit-MessageType: newpatchset Gerrit-Change-Id: Ie590990f2274ea476678f6b2079f90eeadab6501 Gerrit-PatchSet: 3 Gerrit-Project: osmocom-bb Gerrit-Branch: fixeria/trx Gerrit-Owner: Vadim Yanitskiy <axilira...@gmail.com> Gerrit-Reviewer: Jenkins Builder