pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmocom-bb/+/33223 )
Change subject: l1gprs: implement TBF starting time support ......................................................................
l1gprs: implement TBF starting time support
Change-Id: I174e3c43d2f4c828a528710b284e62c9bb794122 Related: OS#5500 --- M include/l1gprs.h M src/shared/l1gprs.c 2 files changed, 256 insertions(+), 20 deletions(-)
Approvals: Jenkins Builder: Verified pespin: Looks good to me, approved
diff --git a/include/l1gprs.h b/include/l1gprs.h index 060366e..eb15726 100644 --- a/include/l1gprs.h +++ b/include/l1gprs.h @@ -9,6 +9,21 @@ struct l1gprs_state; struct msgb;
+struct l1gprs_tbf_pending_req { + /*! Item in l1gprs_state->tbf_list_pending */ + struct llist_head list; + /*! Uplink or Downlink */ + bool uplink; + /*! TBF reference number (not index) */ + uint8_t tbf_ref; + /*! PDCH timeslots used by this TBF */ + uint8_t slotmask; + /*! (Downlink only) DL TFI (Temporary Flow Indentity): 0..31 */ + uint8_t dl_tfi; + /*! TBF starting time (absolute TDMA Fn) */ + uint32_t start_fn; +}; + struct l1gprs_tbf { /*! Item in l1gprs_state->tbf_list */ struct llist_head list; @@ -33,11 +48,16 @@ uint8_t dl_tbf_count; /*! DL TFI mask */ uint32_t dl_tfi_mask; + /*! Pending UL TBF count */ + uint8_t pending_ul_tbf_count; + /*! Pending DL TBF count */ + uint8_t pending_dl_tbf_count; };
static inline size_t l1gprs_pdch_use_count(const struct l1gprs_pdch *pdch) { - return pdch->ul_tbf_count + pdch->dl_tbf_count; + return pdch->ul_tbf_count + pdch->dl_tbf_count + + pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count; }
@@ -46,8 +66,10 @@ struct l1gprs_state { /*! PDCH state for each timeslot */ struct l1gprs_pdch pdch[8]; - /*! Uplink and Downlink TBFs */ + /*! Uplink and Downlink TBFs (active), struct l1gprs_pending_tbf */ struct llist_head tbf_list; + /*! Uplink and Downlink TBFs (pending), struct l1gprs_tbf_pending_req */ + struct llist_head tbf_list_pending; /*! Logging context (used as prefix for messages) */ char *log_prefix; /*! Some private data for API user */ diff --git a/src/shared/l1gprs.c b/src/shared/l1gprs.c index bae13d2..a9a6228 100644 --- a/src/shared/l1gprs.c +++ b/src/shared/l1gprs.c @@ -31,6 +31,7 @@
#include <osmocom/gsm/protocol/gsm_04_08.h> #include <osmocom/gsm/protocol/gsm_44_060.h> +#include <osmocom/gsm/gsm0502.h>
#include <osmocom/bb/l1ctl_proto.h> #include <osmocom/bb/l1gprs.h> @@ -43,10 +44,16 @@ LOGP_GPRS((pdch)->gprs, level, "(PDCH-%u) " fmt, \ (pdch)->tn, ## args)
+#define LOG_TBF_CFG_REQ_FMT "tbf_ref=%u, slotmask=0x%02x, start_fn=%u" +#define LOG_TBF_CFG_REQ_ARGS(req) \ + (req)->tbf_ref, (req)->slotmask, ntohl((req)->start_fn) + #define LOG_TBF_FMT "%cL-TBF#%03d" #define LOG_TBF_ARGS(tbf) \ (tbf)->uplink ? 'U' : 'D', tbf->tbf_ref
+#define TDMA_FN_INVALID 0xffffffff + static int l1gprs_log_cat = DLGLOBAL;
enum gprs_rlcmac_block_type { @@ -56,13 +63,39 @@ GPRS_RLCMAC_RESERVED = 0x03, };
-static struct l1gprs_tbf *l1gprs_tbf_alloc(struct l1gprs_state *gprs, +static struct l1gprs_tbf_pending_req *l1gprs_tbf_pending_req_alloc(void *talloc_ctx, + bool uplink, uint8_t tbf_ref, + uint8_t slotmask, uint32_t start_fn) +{ + struct l1gprs_tbf_pending_req *preq; + + preq = talloc(talloc_ctx, struct l1gprs_tbf_pending_req); + OSMO_ASSERT(preq != NULL); + + preq->uplink = uplink; + preq->tbf_ref = tbf_ref; + preq->slotmask = slotmask; + preq->start_fn = start_fn; + + return preq; +} + +static void l1gprs_tbf_pending_req_free(struct l1gprs_tbf_pending_req *preq) +{ + if (preq == NULL) + return; + llist_del(&preq->list); + talloc_free(preq); +} + + +static struct l1gprs_tbf *l1gprs_tbf_alloc(void *talloc_ctx, bool uplink, uint8_t tbf_ref, uint8_t slotmask) { struct l1gprs_tbf *tbf;
- tbf = talloc(gprs, struct l1gprs_tbf); + tbf = talloc(talloc_ctx, struct l1gprs_tbf); OSMO_ASSERT(tbf != NULL);
tbf->uplink = uplink; @@ -80,12 +113,12 @@ talloc_free(tbf); }
-static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs, - bool uplink, uint8_t tbf_ref) +static struct l1gprs_tbf *_l1gprs_find_tbf(const struct llist_head *tbf_list, + bool uplink, uint8_t tbf_ref) { struct l1gprs_tbf *tbf;
- llist_for_each_entry(tbf, &gprs->tbf_list, list) { + llist_for_each_entry(tbf, tbf_list, list) { if (tbf->uplink != uplink) continue; if (tbf->tbf_ref != tbf_ref) @@ -96,6 +129,16 @@ return NULL; }
+static struct l1gprs_tbf *l1gprs_find_tbf(struct l1gprs_state *gprs, + bool uplink, uint8_t tbf_ref) +{ + struct l1gprs_tbf *tbf; + + if ((tbf = _l1gprs_find_tbf(&gprs->tbf_list, uplink, tbf_ref)) != NULL) + return tbf; + return NULL; +} + static void l1gprs_register_tbf(struct l1gprs_state *gprs, struct l1gprs_tbf *tbf) { @@ -129,7 +172,7 @@ llist_add_tail(&tbf->list, &gprs->tbf_list);
LOGP_GPRS(gprs, LOGL_INFO, - LOG_TBF_FMT " is registered\n", + LOG_TBF_FMT " is registered as active\n", LOG_TBF_ARGS(tbf)); }
@@ -228,6 +271,122 @@ l1gprs_tbf_free(tbf); }
+static void l1gprs_add_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq) +{ + OSMO_ASSERT(preq->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~preq->slotmask & (1 << pdch->tn)) + continue; + + if (preq->uplink) { + pdch->pending_ul_tbf_count++; + } else { + pdch->pending_dl_tbf_count++; + /* We don't care about DL_TFI here, we don't want to activate it */ + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Linked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(preq)); + /* If just got first use: */ + if (l1gprs_pdch_use_count(pdch) == 1) { + if (gprs->pdch_changed_cb) + gprs->pdch_changed_cb(pdch, true); + } + } + + llist_add_tail(&preq->list, &gprs->tbf_list_pending); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is added as pending (fn=%u)\n", + LOG_TBF_ARGS(preq), preq->start_fn); +} + +static void l1gprs_remove_tbf_pending_req(struct l1gprs_state *gprs, struct l1gprs_tbf_pending_req *preq) +{ + + OSMO_ASSERT(preq->slotmask != 0x00); + + /* Update the PDCH states */ + for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + + if (~preq->slotmask & (1 << pdch->tn)) + continue; + + if (preq->uplink) { + OSMO_ASSERT(pdch->pending_ul_tbf_count > 0); + pdch->pending_ul_tbf_count--; + } else { + OSMO_ASSERT(pdch->pending_dl_tbf_count > 0); + pdch->pending_dl_tbf_count--; + /* We don't care about DL_TFI here, we didn't activate them in first place */ + } + + LOGP_PDCH(pdch, LOGL_DEBUG, + "Unlinked " LOG_TBF_FMT "\n", + LOG_TBF_ARGS(preq)); + /* Note: not calling gprs->pdch_changed_cb since no real + * activate / deactivate change can occur on lower layers as a + * consequence of moving a PDCH from pending to active, hence + * avoid triggering one active=false event here and immediately + * afterwards the opposite event when adding it as active: */ + } + + llist_del(&preq->list); + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " is removed as pending (fn=%u)\n", + LOG_TBF_ARGS(preq), preq->start_fn); +} + +/* Check if the current TDMA Fn is past the start TDMA Fn. + * Based on fn_cmp() implementation from osmo-pcu.git, simplified. */ +static bool l1gprs_check_fn(uint32_t current, uint32_t start) +{ + const uint32_t thresh = GSM_TDMA_HYPERFRAME / 2; + + if ((current < start && (start - current) < thresh) || + (current > start && (current - start) > thresh)) + return false; + + return true; +} + +/* Check the list of pending TBFs and move those with expired Fn to the active list */ +static void l1gprs_check_pending_tbfs(struct l1gprs_state *gprs, uint32_t fn) +{ + struct l1gprs_tbf_pending_req *preq, *tmp; + struct l1gprs_tbf *tbf; + + llist_for_each_entry_safe(preq, tmp, &gprs->tbf_list_pending, list) { + if (!l1gprs_check_fn(fn, preq->start_fn)) + continue; + + LOGP_GPRS(gprs, LOGL_INFO, + LOG_TBF_FMT " becomes active (current_fn=%u, start_fn=%u)\n", + LOG_TBF_ARGS(preq), fn, preq->start_fn); + + l1gprs_remove_tbf_pending_req(gprs, preq); + + /* If this tbf already exists in the main list, simply update its timeslot: */ + tbf = _l1gprs_find_tbf(&gprs->tbf_list, preq->uplink, preq->tbf_ref); + if (tbf) { + l1gprs_update_tbf(gprs, tbf, preq->slotmask); + tbf->dl_tfi = preq->dl_tfi; + } else { + tbf = l1gprs_tbf_alloc(gprs, preq->uplink, preq->tbf_ref, preq->slotmask); + tbf->dl_tfi = preq->dl_tfi; + l1gprs_register_tbf(gprs, tbf); + } + talloc_free(preq); + } +} + #define L1GPRS_L1CTL_MSGB_SIZE 256 #define L1GPRS_L1CTL_MSGB_HEADROOM 32
@@ -302,6 +461,7 @@ }
INIT_LLIST_HEAD(&gprs->tbf_list); + INIT_LLIST_HEAD(&gprs->tbf_list_pending);
if (log_prefix == NULL) gprs->log_prefix = talloc_asprintf(gprs, "l1gprs[0x%p]: ", gprs); @@ -327,6 +487,16 @@ l1gprs_tbf_free(tbf); }
+ while (!llist_empty(&gprs->tbf_list_pending)) { + struct l1gprs_tbf_pending_req *preq; + + preq = llist_first_entry(&gprs->tbf_list_pending, struct l1gprs_tbf_pending_req, list); + LOGP_GPRS(gprs, LOGL_DEBUG, + "%s(): " LOG_TBF_FMT " is free()d\n", + __func__, LOG_TBF_ARGS(preq)); + l1gprs_tbf_pending_req_free(preq); + } + talloc_free(gprs->log_prefix); talloc_free(gprs); } @@ -351,12 +521,23 @@ }
LOGP_GPRS(gprs, LOGL_INFO, - "Rx Uplink TBF config: tbf_ref=%u, slotmask=0x%02x\n", - req->tbf_ref, req->slotmask); - - tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref); + "Rx UL TBF config: " LOG_TBF_CFG_REQ_FMT "\n", + LOG_TBF_CFG_REQ_ARGS(req));
if (req->slotmask != 0x00) { + uint32_t start_fn = ntohl(req->start_fn); + if (start_fn != TDMA_FN_INVALID) { + /* Create a temporary tbf and keep it in a separate + * list. It will be moved/merged into the main list at + * start_fn time. */ + struct l1gprs_tbf_pending_req *preq; + preq = l1gprs_tbf_pending_req_alloc(gprs, true, req->tbf_ref, + req->slotmask, start_fn); + l1gprs_add_tbf_pending_req(gprs, preq); + return 0; + } + + tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref); if (tbf) { l1gprs_update_tbf(gprs, tbf, req->slotmask); } else { @@ -364,6 +545,7 @@ l1gprs_register_tbf(gprs, tbf); } } else { + tbf = l1gprs_find_tbf(gprs, true, req->tbf_ref); if (tbf == NULL) { LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n", __func__, 'U', req->tbf_ref); @@ -390,8 +572,8 @@ }
LOGP_GPRS(gprs, LOGL_INFO, - "Rx Downlink TBF config: tbf_ref=%u, slotmask=0x%02x, dl_tfi=%u\n", - req->tbf_ref, req->slotmask, req->dl_tfi); + "Rx DL TBF config: " LOG_TBF_CFG_REQ_FMT ", dl_tfi=%u\n", + LOG_TBF_CFG_REQ_ARGS(req), req->dl_tfi);
if (req->dl_tfi > 31) { LOGP_GPRS(gprs, LOGL_ERROR, @@ -400,17 +582,31 @@ return -EINVAL; }
- tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref); - if (req->slotmask != 0x00) { + uint32_t start_fn = ntohl(req->start_fn); + if (start_fn != TDMA_FN_INVALID) { + /* Create a temporary tbf and keep it in a separate + * list. It will be moved/merged into the main list at + * start_fn time. */ + struct l1gprs_tbf_pending_req *preq; + preq = l1gprs_tbf_pending_req_alloc(gprs, false, req->tbf_ref, + req->slotmask, start_fn); + preq->dl_tfi = req->dl_tfi; + l1gprs_add_tbf_pending_req(gprs, preq); + return 0; + } + + tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref); if (tbf) { l1gprs_update_tbf(gprs, tbf, req->slotmask); } else { - tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref, req->slotmask); + tbf = l1gprs_tbf_alloc(gprs, false, req->tbf_ref, + req->slotmask); tbf->dl_tfi = req->dl_tfi; l1gprs_register_tbf(gprs, tbf); } } else { + tbf = l1gprs_find_tbf(gprs, false, req->tbf_ref); if (tbf == NULL) { LOGP_GPRS(gprs, LOGL_ERROR, "%s(): " LOG_TBF_FMT " not found\n", __func__, 'D', req->tbf_ref); @@ -496,9 +692,17 @@ BLOCK_IND_IS_PTCCH(ind) ? "PTCCH" : "PDTCH", ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len));
- if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) { - LOGP_PDCH(pdch, LOGL_ERROR, - "Rx DL BLOCK.ind, but this PDCH has no configured TBFs\n"); + l1gprs_check_pending_tbfs(gprs, ind->hdr.fn); + + if (pdch->ul_tbf_count + pdch->dl_tbf_count == 0) { + if (pdch->pending_ul_tbf_count + pdch->pending_dl_tbf_count > 0) + LOGP_PDCH(pdch, LOGL_DEBUG, + "Rx DL BLOCK.ind (fn=%u), but this PDCH has no active TBFs yet\n", + ind->hdr.fn); + else + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx DL BLOCK.ind (fn=%u), but this PDCH has no configured TBFs\n", + ind->hdr.fn); return NULL; }