diff options
Diffstat (limited to 'src/host/trxcon/sched_prim.c')
-rw-r--r-- | src/host/trxcon/sched_prim.c | 210 |
1 files changed, 197 insertions, 13 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 */ |