pespin submitted this change.

View Change


Approvals: Jenkins Builder: Verified pespin: Looks good to me, approved
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(-)

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;
}


To view, visit change 33223. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Change-Id: I174e3c43d2f4c828a528710b284e62c9bb794122
Gerrit-Change-Number: 33223
Gerrit-PatchSet: 13
Gerrit-Owner: fixeria <vyanitskiy@sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: fixeria <axilirator@gmail.com>
Gerrit-Reviewer: pespin <pespin@sysmocom.de>
Gerrit-MessageType: merged