From 0cd4252ab580b3c1601c9e45353170d830cf374c Mon Sep 17 00:00:00 2001 From: Vadim Yanitskiy Date: Fri, 23 Mar 2018 01:40:03 +0700 Subject: trxcon/scheduler: fix Measurement Reporting on SACCH According to 3GPP TS 04.08, section 3.4.1, SACCH logical channel accompanies either a traffic or a signaling channel. It has the particularity that continuous transmission must occur in both directions, so on the Uplink direction measurement result messages are sent at each possible occasion when nothing else has to be sent. The LAPDm fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not applicable on SACCH channels! Unfortunately, 3GPP TS 04.08 doesn't clearly state which "else messages" besides Measurement Reports can be send by the MS on SACCH channels. However, in sub-clause 3.4.1 it's stated that the interval between two successive measurement result messages shall not exceed one L2 frame. This change introduces a separate handler for SACCH primitives, which dequeues a SACCH primitive from transmit queue, if present. Otherwise it dequeues a cached Measurement Report (the last received one). Finally, if the cache is empty, a "dummy" measurement report is used. When it's possible, a non-MR primitive is prioritized. Change-Id: If1b8dc74ced746d6270676fdde75fcda32f91a3d Related: OS#2988 --- src/host/trxcon/sched_prim.c | 210 ++++++++++++++++++++++++++++++++++++++++--- src/host/trxcon/sched_trx.c | 5 +- src/host/trxcon/sched_trx.h | 10 +++ 3 files changed, 211 insertions(+), 14 deletions(-) diff --git a/src/host/trxcon/sched_prim.c b/src/host/trxcon/sched_prim.c index 20679009..275a0509 100644 --- a/src/host/trxcon/sched_prim.c +++ b/src/host/trxcon/sched_prim.c @@ -122,6 +122,194 @@ int sched_prim_push(struct trx_instance *trx, return 0; } +/** + * Composes a new primitive using either cached (if populated), + * or "dummy" Measurement Report message. + * + * @param lchan lchan to assign a primitive + * @return SACCH primitive to be transmitted + */ +static struct trx_ts_prim *prim_compose_mr(struct trx_lchan_state *lchan) +{ + struct trx_ts_prim *prim; + uint8_t *mr_src_ptr; + bool cached; + int rc; + + /* "Dummy" Measurement Report */ + static const uint8_t meas_rep_dummy[] = { + /* L1 SACCH pseudo-header */ + 0x0f, 0x00, + + /* LAPDm header */ + 0x01, 0x03, 0x49, + + /* Measurement report */ + 0x06, 0x15, 0x36, 0x36, 0x01, 0xC0, + + /* TODO: Padding? Randomize if so */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + }; + + /* Allocate a new primitive */ + rc = sched_prim_init(lchan, &prim, GSM_MACBLOCK_LEN, + trx_lchan_desc[lchan->type].chan_nr, TRX_CH_LID_SACCH); + OSMO_ASSERT(rc == 0); + + /* Check if the MR cache is populated (verify LAPDm header) */ + cached = (lchan->sacch.mr_cache[2] != 0x00 + && lchan->sacch.mr_cache[3] != 0x00 + && lchan->sacch.mr_cache[4] != 0x00); + if (cached) { /* Use the cached one */ + mr_src_ptr = lchan->sacch.mr_cache; + lchan->sacch.mr_cache_usage++; + } else { /* Use "dummy" one */ + mr_src_ptr = (uint8_t *) meas_rep_dummy; + } + + /* Compose a new Measurement Report primitive */ + memcpy(prim->payload, mr_src_ptr, GSM_MACBLOCK_LEN); + +#if 0 + /** + * Update the L1 SACCH pseudo-header (only for cached MRs) + * + * FIXME: this would require having access to the trx_instance, + * what can be achieved either by chain-passing the pointer + * through sched_prim_dequeue(), or by adding some + * back-pointers to the logical channel state. + * + * TODO: filling of the actual values into cached Measurement + * Reports would break the distance spoofing feature. If it + * were known whether the spoofing is enabled or not, we could + * decide whether to update the cached L1 SACCH header here. + */ + if (!cached) { + prim->payload[0] = trx->tx_power; + prim->payload[1] = trx->ta; + } +#endif + + /* Inform about the cache usage count */ + if (cached && lchan->sacch.mr_cache_usage > 5) { + LOGP(DSCHD, LOGL_NOTICE, "SACCH MR cache usage count=%u > 5 " + "on lchan=%s => ancient measurements, please fix!\n", + lchan->sacch.mr_cache_usage, + trx_lchan_desc[lchan->type].name); + } + + LOGP(DSCHD, LOGL_NOTICE, "Using a %s Measurement Report " + "on lchan=%s\n", (cached ? "cached" : "dummy"), + trx_lchan_desc[lchan->type].name); + + return prim; +} + +/** + * Dequeues a SACCH primitive from transmit queue, if present. + * Otherwise dequeues a cached Measurement Report (the last + * received one). Finally, if the cache is empty, a "dummy" + * measurement report is used. + * + * According to 3GPP TS 04.08, section 3.4.1, SACCH channel + * accompanies either a traffic or a signaling channel. It + * has the particularity that continuous transmission must + * occur in both directions, so on the Uplink direction + * measurement result messages are sent at each possible + * occasion when nothing else has to be sent. The LAPDm + * fill frames (0x01, 0x03, 0x01, 0x2b, ...) are not + * applicable on SACCH channels! + * + * Unfortunately, 3GPP TS 04.08 doesn't clearly state + * which "else messages" besides Measurement Reports + * can be send by the MS on SACCH channels. However, + * in sub-clause 3.4.1 it's stated that the interval + * between two successive measurement result messages + * shall not exceed one L2 frame. + * + * @param queue transmit queue to take a prim from + * @param lchan lchan to assign a primitive + * @return SACCH primitive to be transmitted + */ +static struct trx_ts_prim *prim_dequeue_sacch(struct llist_head *queue, + struct trx_lchan_state *lchan) +{ + struct trx_ts_prim *prim_nmr = NULL; + struct trx_ts_prim *prim_mr = NULL; + struct trx_ts_prim *prim; + bool mr_now; + + /* Shall we transmit MR now? */ + mr_now = !lchan->sacch.mr_tx_last; + +#define PRIM_IS_MR(prim) \ + (prim->payload[5] == GSM48_PDISC_RR \ + && prim->payload[6] == GSM48_MT_RR_MEAS_REP) + + /* Iterate over all primitives in the queue */ + llist_for_each_entry(prim, queue, list) { + /* We are looking for particular channel */ + if (prim->chan != lchan->type) + continue; + + /* Just to be sure... */ + if (prim->payload_len != GSM_MACBLOCK_LEN) + continue; + + /* Look for a Measurement Report */ + if (!prim_mr && PRIM_IS_MR(prim)) + prim_mr = prim; + + /* Look for anything else */ + if (!prim_nmr && !PRIM_IS_MR(prim)) + prim_nmr = prim; + + /* Should we look further? */ + if (mr_now && prim_mr) + break; /* MR was found */ + else if (!mr_now && prim_nmr) + break; /* something else was found */ + } + + LOGP(DSCHD, LOGL_DEBUG, "SACCH MR selection on lchan=%s: " + "mr_tx_last=%d prim_mr=%p prim_nmr=%p\n", + trx_lchan_desc[lchan->type].name, + lchan->sacch.mr_tx_last, + prim_mr, prim_nmr); + + /* Prioritize non-MR prim if possible */ + if (mr_now && prim_mr) + prim = prim_mr; + else if (!mr_now && prim_nmr) + prim = prim_nmr; + else if (!mr_now && prim_mr) + prim = prim_mr; + else /* Nothing was found */ + prim = NULL; + + /* Have we found what we were looking for? */ + if (prim) /* Dequeue if so */ + llist_del(&prim->list); + else /* Otherwise compose a new MR */ + prim = prim_compose_mr(lchan); + + /* Update the cached report */ + if (prim == prim_mr) { + memcpy(lchan->sacch.mr_cache, + prim->payload, GSM_MACBLOCK_LEN); + lchan->sacch.mr_cache_usage = 0; + + LOGP(DSCHD, LOGL_DEBUG, "SACCH MR cache has been updated " + "for lchan=%s\n", trx_lchan_desc[lchan->type].name); + } + + /* Update the MR transmission state */ + lchan->sacch.mr_tx_last = PRIM_IS_MR(prim); + + return prim; +} + /* Dequeues a primitive of a given channel type */ static struct trx_ts_prim *prim_dequeue_one(struct llist_head *queue, enum trx_lchan_type lchan_type) @@ -285,6 +473,10 @@ no_facch: struct trx_ts_prim *sched_prim_dequeue(struct llist_head *queue, uint32_t fn, struct trx_lchan_state *lchan) { + /* SACCH is unorthodox, see 3GPP TS 04.08, section 3.4.1 */ + if (CHAN_IS_SACCH(lchan->type)) + return prim_dequeue_sacch(queue, lchan); + /* There is nothing to dequeue */ if (llist_empty(queue)) return NULL; @@ -346,6 +538,8 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) /* Make sure that there is no existing primitive */ OSMO_ASSERT(lchan->prim == NULL); + /* Not applicable for SACCH! */ + OSMO_ASSERT(!CHAN_IS_SACCH(lchan->type)); /** * Determine what actually should be generated: @@ -360,18 +554,8 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) /* FIXME: should we do anything for CSD? */ return 0; } else { - uint8_t *cur = prim_buffer; - - if (CHAN_IS_SACCH(chan)) { - /* Add 2-byte SACCH header */ - /* FIXME: How to get TA and MS Tx Power from l1l->trx->tx_power + l1l->trx->ta? */ - cur[0] = cur[1] = 0x00; - cur += 2; - } - - /* Copy a fill frame payload */ - memcpy(cur, lapdm_fill_frame, sizeof(lapdm_fill_frame)); - cur += sizeof(lapdm_fill_frame); + /* Copy LAPDm fill frame's header */ + memcpy(prim_buffer, lapdm_fill_frame, sizeof(lapdm_fill_frame)); /** * TS 144.006, section 5.2 "Frame delimitation and fill bits" @@ -379,7 +563,7 @@ int sched_prim_dummy(struct trx_lchan_state *lchan) * be set to the binary value "00101011", each fill bit should * be set to a random value when sent by the network. */ - for (i = cur - prim_buffer; i < GSM_MACBLOCK_LEN; i++) + for (i = sizeof(lapdm_fill_frame); i < GSM_MACBLOCK_LEN; i++) prim_buffer[i] = (uint8_t) rand(); /* Define a prim length */ diff --git a/src/host/trxcon/sched_trx.c b/src/host/trxcon/sched_trx.c index fc29998d..0b83af34 100644 --- a/src/host/trxcon/sched_trx.c +++ b/src/host/trxcon/sched_trx.c @@ -460,7 +460,7 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) /* Forget the current prim */ sched_prim_drop(lchan); - /* TCH specific variables */ + /* Channel specific stuff */ if (CHAN_IS_TCH(lchan->type)) { lchan->dl_ongoing_facch = 0; lchan->ul_facch_blocks = 0; @@ -469,6 +469,9 @@ static void sched_trx_reset_lchan(struct trx_lchan_state *lchan) /* Reset AMR state */ memset(&lchan->amr, 0x00, sizeof(lchan->amr)); + } else if (CHAN_IS_SACCH(lchan->type)) { + /* Reset SACCH state */ + memset(&lchan->sacch, 0x00, sizeof(lchan->sacch)); } /* Reset ciphering state */ diff --git a/src/host/trxcon/sched_trx.h b/src/host/trxcon/sched_trx.h index 818c95a0..10ae2566 100644 --- a/src/host/trxcon/sched_trx.h +++ b/src/host/trxcon/sched_trx.h @@ -186,6 +186,16 @@ struct trx_lchan_state { int32_t toa256_sum; } meas; + /*! \brief SACCH state */ + struct { + /*! \brief Cached measurement report (last received) */ + uint8_t mr_cache[GSM_MACBLOCK_LEN]; + /*! \brief Cache usage counter */ + uint8_t mr_cache_usage; + /*! \brief Was a MR transmitted last time? */ + bool mr_tx_last; + } sacch; + /* AMR specific */ struct { /*! \brief 4 possible codecs for AMR */ -- cgit v1.2.3