fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmocom-bb/+/32504 )
Change subject: [WIP] l1gprs: implement queueing of Uplink blocks ......................................................................
[WIP] l1gprs: implement queueing of Uplink blocks
Change-Id: If9638a670f97f602c922ea8d3375674d03c15f53 Related: OS#5500 --- M include/l1ctl_proto.h M include/l1gprs.h M src/host/layer23/include/osmocom/bb/common/l1ctl.h M src/host/layer23/src/common/l1ctl.c M src/host/layer23/src/modem/rlcmac.c M src/host/trxcon/src/l1ctl.c M src/host/trxcon/src/trxcon_fsm.c M src/host/virt_phy/src/virt_prim_pdch.c M src/shared/l1gprs.c 9 files changed, 187 insertions(+), 100 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/04/32504/1
diff --git a/include/l1ctl_proto.h b/include/l1ctl_proto.h index 3512424..b1f59f9 100644 --- a/include/l1ctl_proto.h +++ b/include/l1ctl_proto.h @@ -345,19 +345,15 @@ uint8_t data[0]; } __attribute__((packed));
-/* payload of L1CTL_GPRS_UL_TBF_CFG_REQ */ -struct l1ctl_gprs_ul_tbf_cfg_req { +/* payload of L1CTL_GPRS_{UL,DL}_TBF_CFG_REQ */ +struct l1ctl_gprs_tbf_cfg_req { uint8_t tbf_ref; uint8_t slotmask; uint8_t padding[2]; -} __attribute__((packed)); - -/* payload of L1CTL_GPRS_DL_TBF_CFG_REQ */ -struct l1ctl_gprs_dl_tbf_cfg_req { - uint8_t tbf_ref; - uint8_t slotmask; - uint8_t dl_tfi; - uint8_t padding[1]; + union { + uint8_t ul_usf[8]; + uint8_t dl_tfi; + } __attribute__((packed)); } __attribute__((packed));
/* part of L1CTL_GPRS_{UL,DL}_BLOCK_{REQ,IND} */ @@ -370,6 +366,7 @@ /* payload of L1CTL_GPRS_UL_BLOCK_REQ */ struct l1ctl_gprs_ul_block_req { struct l1ctl_gprs_block_hdr hdr; + uint8_t tbf_ref; uint8_t data[0]; } __attribute__((packed));
diff --git a/include/l1gprs.h b/include/l1gprs.h index bbd654d..f7f49c2 100644 --- a/include/l1gprs.h +++ b/include/l1gprs.h @@ -4,6 +4,13 @@ #include <stdbool.h>
#include <osmocom/core/linuxlist.h> +#include <osmocom/gsm/gsm0502.h> + +/* Calculate TDMA Fn of the next block from TDMA Fn of the current block. + * Every fn % 13 == 12 we have either a PTCCH or an IDLE slot, thus + * every fn % 13 == 8 we add 5 frames, or 4 frames othrwise. */ +#define L1GPRS_NEXT_BLOCK_FN(fn) \ + GSM_TDMA_FN_SUM(fn, (fn % 13 == 8) ? 5 : 4)
struct l1gprs_state; struct msgb; @@ -17,8 +24,13 @@ 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; + /*! UL/DL specific parameters */ + union { + /*! USF for each PDCH indicated in the slotmask */ + uint8_t ul_usf[8]; + /*! DL TFI for all PDCHs indicated in the slotmask */ + uint8_t dl_tfi; + }; };
struct l1gprs_pdch { @@ -32,6 +44,12 @@ uint8_t dl_tbf_count; /*! DL TFI mask */ uint32_t dl_tfi_mask; + /*! last USF received on this PDCH */ + uint8_t last_usf; + /*! Tx block queue: blocks sent whenever USF matches */ + struct llist_head tx_queue_usf; + /*! Tx block queue: blocks with TDMA Fn, sorted */ + struct llist_head tx_queue_abs; };
struct l1gprs_state { @@ -74,8 +92,7 @@ const uint8_t *data; };
-int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, - struct l1gprs_prim_ul_block_req *req, - const struct msgb *msg); +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, struct msgb *msg); struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, const struct l1gprs_prim_dl_block_ind *ind); +struct msgb *l1gprs_pull_ul_block(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn); diff --git a/src/host/layer23/include/osmocom/bb/common/l1ctl.h b/src/host/layer23/include/osmocom/bb/common/l1ctl.h index 25bc6e8..2ad19e7 100644 --- a/src/host/layer23/include/osmocom/bb/common/l1ctl.h +++ b/src/host/layer23/include/osmocom/bb/common/l1ctl.h @@ -81,7 +81,7 @@
/* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */ int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, - uint8_t slotmask); + uint8_t slotmask, const uint8_t usf[8]);
/* Transmit L1CTL_GPRS_DL_TBF_CFG_REQ */ int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, diff --git a/src/host/layer23/src/common/l1ctl.c b/src/host/layer23/src/common/l1ctl.c index 8e55b7b..affb80d 100644 --- a/src/host/layer23/src/common/l1ctl.c +++ b/src/host/layer23/src/common/l1ctl.c @@ -1009,34 +1009,11 @@ return 0; }
-/* Transmit L1CTL_GPRS_UL_BLOCK_REQ */ -int l1ctl_tx_gprs_ul_block_req(struct osmocom_ms *ms, uint32_t fn, uint8_t tn, - const uint8_t *data, size_t data_len) -{ - struct l1ctl_gprs_ul_block_req *req; - struct msgb *msg; - - msg = osmo_l1_alloc(L1CTL_GPRS_UL_BLOCK_REQ); - if (!msg) - return -ENOMEM; - - req = (void *)msgb_put(msg, sizeof(*req)); - req->hdr.fn = htonl(fn); - req->hdr.tn = tn; - if (data_len > 0) - memcpy(msgb_put(msg, data_len), data, data_len); - - DEBUGP(DL1C, "Tx GPRS UL block (fn=%u, tn=%u, len=%zu): %s\n", - fn, tn, data_len, osmo_hexdump(data, data_len)); - - return osmo_send_l1(ms, msg); -} - /* Transmit L1CTL_GPRS_UL_TBF_CFG_REQ */ int l1ctl_tx_gprs_ul_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, - uint8_t slotmask) + uint8_t slotmask, const uint8_t usf[8]) { - struct l1ctl_gprs_ul_tbf_cfg_req *req; + struct l1ctl_gprs_tbf_cfg_req *req; struct msgb *msg;
msg = osmo_l1_alloc(L1CTL_GPRS_UL_TBF_CFG_REQ); @@ -1044,13 +1021,12 @@ return -ENOMEM;
req = (void *)msgb_put(msg, sizeof(*req)); - *req = (struct l1ctl_gprs_ul_tbf_cfg_req) { - .tbf_ref = tbf_ref, - .slotmask = slotmask, - }; + req->tbf_ref = tbf_ref; + req->slotmask = slotmask; + memcpy(&req->ul_usf[0], &usf[0], sizeof(req->ul_usf));
- DEBUGP(DL1C, "Tx GPRS UL TBF CFG (tbf_ref=%u, slotmask=0x%02x)\n", - tbf_ref, slotmask); + DEBUGP(DL1C, "Tx GPRS UL TBF CFG (tbf_ref=%u, slotmask=0x%02x): %s\n", + tbf_ref, slotmask, osmo_hexdump(&req->ul_usf[0], sizeof(req->ul_usf)));
return osmo_send_l1(ms, msg); } @@ -1059,7 +1035,7 @@ int l1ctl_tx_gprs_dl_tbf_cfg_req(struct osmocom_ms *ms, uint8_t tbf_ref, uint8_t slotmask, uint8_t dl_tfi) { - struct l1ctl_gprs_dl_tbf_cfg_req *req; + struct l1ctl_gprs_tbf_cfg_req *req; struct msgb *msg;
msg = osmo_l1_alloc(L1CTL_GPRS_DL_TBF_CFG_REQ); @@ -1067,11 +1043,9 @@ return -ENOMEM;
req = (void *)msgb_put(msg, sizeof(*req)); - *req = (struct l1ctl_gprs_dl_tbf_cfg_req) { - .tbf_ref = tbf_ref, - .slotmask = slotmask, - .dl_tfi = dl_tfi, - }; + req->tbf_ref = tbf_ref; + req->slotmask = slotmask; + req->dl_tfi = dl_tfi;
DEBUGP(DL1C, "Tx GPRS DL TBF CFG (tbf_ref=%u, slotmask=0x%02x, dl_tfi=%u)\n", tbf_ref, slotmask, dl_tfi); diff --git a/src/host/layer23/src/modem/rlcmac.c b/src/host/layer23/src/modem/rlcmac.c index deb366a..68d3c43 100644 --- a/src/host/layer23/src/modem/rlcmac.c +++ b/src/host/layer23/src/modem/rlcmac.c @@ -143,6 +143,7 @@ return modem_grr_tx_chan_req(ms, lp->rach_req.ra); case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_PDCH_DATA, PRIM_OP_REQUEST): return l1ctl_tx_gprs_ul_block_req(ms, + 0, /* XXX: lp->pdch_data_req.tbf_ref, */ lp->pdch_data_req.fn, lp->pdch_data_req.ts_nr, lp->pdch_data_req.data, @@ -150,7 +151,8 @@ case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_UL_TBF, PRIM_OP_REQUEST): return l1ctl_tx_gprs_ul_tbf_cfg_req(ms, lp->cfg_ul_tbf_req.ul_tbf_nr, - lp->cfg_ul_tbf_req.ul_slotmask); + lp->cfg_ul_tbf_req.ul_slotmask, + lp->cfg_ul_tbf_req.ul_usf); case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST): return l1ctl_tx_gprs_dl_tbf_cfg_req(ms, lp->cfg_dl_tbf_req.dl_tbf_nr, diff --git a/src/host/trxcon/src/l1ctl.c b/src/host/trxcon/src/l1ctl.c index a45bc2c..5ddb5b2 100644 --- a/src/host/trxcon/src/l1ctl.c +++ b/src/host/trxcon/src/l1ctl.c @@ -828,8 +828,10 @@ msgb_free(msg); return rc; case L1CTL_GPRS_UL_BLOCK_REQ: + /* pass ownership of the msgb to trxcon_fsm/l1gprs */ rc = osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_GPRS_UL_BLOCK_REQ, msg); - msgb_free(msg); + if (rc != 0) + msgb_free(msg); return rc; /* Not (yet) handled messages */ case L1CTL_NEIGH_PM_REQ: diff --git a/src/host/trxcon/src/trxcon_fsm.c b/src/host/trxcon/src/trxcon_fsm.c index 219f0c0..83f971a 100644 --- a/src/host/trxcon/src/trxcon_fsm.c +++ b/src/host/trxcon/src/trxcon_fsm.c @@ -29,7 +29,6 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/logging.h> #include <osmocom/gsm/gsm0502.h> -#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/bb/trxcon/trxcon.h> #include <osmocom/bb/trxcon/trxcon_fsm.h> @@ -556,31 +555,17 @@ break; case TRXCON_EV_GPRS_UL_BLOCK_REQ: { - struct l1gprs_prim_ul_block_req block_req; - struct l1sched_prim *prim; struct msgb *msg = data;
- if (l1gprs_handle_ul_block_req(trxcon->gprs, &block_req, msg) != 0) - return; - - msg = l1sched_prim_alloc(L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST, block_req.data_len); - OSMO_ASSERT(msg != NULL); - - prim = l1sched_prim_from_msgb(msg); - prim->data_req = (struct l1sched_prim_chdr) { - .frame_nr = block_req.hdr.fn, - .chan_nr = RSL_CHAN_OSMO_PDCH | block_req.hdr.tn, - .link_id = 0x00, - }; - - memcpy(msgb_put(msg, block_req.data_len), block_req.data, block_req.data_len); - l1sched_prim_from_user(trxcon->sched, msg); + if (l1gprs_handle_ul_block_req(trxcon->gprs, msg) != 0) + msgb_free(msg); break; } case TRXCON_EV_RX_DATA_IND: { const struct trxcon_param_rx_data_ind *ind = data; struct l1gprs_prim_dl_block_ind block_ind; + struct l1sched_prim *prim; struct msgb *msg;
block_ind = (struct l1gprs_prim_dl_block_ind) { @@ -602,9 +587,27 @@ else block_ind.meas.ber10k = 10000 * ind->n_errors / ind->n_bits_total;
+ /* feed l1gprs with the received data */ msg = l1gprs_handle_dl_block_ind(trxcon->gprs, &block_ind); - if (msg != NULL) + if (msg != NULL) /* pass ownership of msgb to l1ctl */ trxcon_l1ctl_send(trxcon, msg); + + /* pull an Uplink block from l1gprs */ + const uint32_t next_block_fn = L1GPRS_NEXT_BLOCK_FN(ind->frame_nr); + msg = l1gprs_pull_ul_block(trxcon->gprs, next_block_fn, ind->chan_nr & 0x07); + if (msg == NULL) + return; + + /* push a l1sched_prim header in front */ + l1sched_prim_init(msg, L1SCHED_PRIM_T_DATA, PRIM_OP_REQUEST); + prim = l1sched_prim_from_msgb(msg); + prim->data_req = (struct l1sched_prim_chdr) { + .frame_nr = next_block_fn, + .chan_nr = ind->chan_nr, + }; + + /* enqueue an Uplink block for transmission */ + l1sched_prim_from_user(trxcon->sched, msg); break; } case TRXCON_EV_DCH_EST_REQ: diff --git a/src/host/virt_phy/src/virt_prim_pdch.c b/src/host/virt_phy/src/virt_prim_pdch.c index 65551d3..23bd1e6 100644 --- a/src/host/virt_phy/src/virt_prim_pdch.c +++ b/src/host/virt_phy/src/virt_prim_pdch.c @@ -51,7 +51,6 @@ void l1ctl_rx_gprs_ul_block_req(struct l1_model_ms *ms, struct msgb *msg) { const struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data; - struct l1gprs_prim_ul_block_req req;
if (OSMO_UNLIKELY(ms->gprs == NULL)) { LOGPMS(DL1P, LOGL_ERROR, ms, "l1gprs is not initialized\n"); @@ -60,14 +59,10 @@ }
msg->l1h = (void *)l1h->data; - if (l1gprs_handle_ul_block_req(ms->gprs, &req, msg) != 0) { + if (l1gprs_handle_ul_block_req(ms->gprs, msg) != 0) { msgb_free(msg); return; } - msg->l2h = (void *)&req.data[0]; - - virt_l1_sched_schedule(ms, msg, req.hdr.fn, req.hdr.tn, - &gsmtapl1_tx_to_virt_um_inst); }
void l1ctl_tx_gprs_dl_block_ind(struct l1_model_ms *ms, const struct msgb *msg, @@ -93,7 +88,14 @@ .data_len = msgb_length(msg), };
+ /* feed l1gprs with the received data */ nmsg = l1gprs_handle_dl_block_ind(ms->gprs, &ind); - if (nmsg != NULL) + if (nmsg != NULL) /* pass ownership of msgb to l1ctl */ l1ctl_sap_tx_to_l23_inst(ms, nmsg); + + /* pull an Uplink block from l1gprs */ + nmsg = l1gprs_pull_ul_block(ms->gprs, L1GPRS_NEXT_BLOCK_FN(fn), tn); + if (nmsg == NULL) /* schedule for transmission (pass ownership of msgb) */ + virt_l1_sched_schedule(ms, nmsg, next_block_fn, tn, + &gsmtapl1_tx_to_virt_um_inst); } diff --git a/src/shared/l1gprs.c b/src/shared/l1gprs.c index 5874b48..769c9c0 100644 --- a/src/shared/l1gprs.c +++ b/src/shared/l1gprs.c @@ -35,6 +35,9 @@ #include <osmocom/bb/l1ctl_proto.h> #include <osmocom/bb/l1gprs.h>
+#define FN_INVALID 0xffffffff +#define USF_INVALID 0xff + #define LOGP_GPRS(gprs, level, fmt, args...) \ LOGP(l1gprs_log_cat, level, "%s" fmt, \ (gprs)->log_prefix, ## args) @@ -239,6 +242,9 @@
pdch->tn = tn; pdch->gprs = gprs; + pdch->last_usf = USF_INVALID; + INIT_LLIST_HEAD(&pdch->tx_queue_usf); + INIT_LLIST_HEAD(&pdch->tx_queue_abs); }
INIT_LLIST_HEAD(&gprs->tbf_list); @@ -257,6 +263,23 @@ if (gprs == NULL) return;
+ for (unsigned int tn = 0; tn < ARRAY_SIZE(gprs->pdch); tn++) { + struct l1gprs_pdch *pdch = &gprs->pdch[tn]; + struct msgb *msg; + + while (!llist_empty(&pdch->tx_queue_usf)) { + msg = llist_first_entry(&pdch->tx_queue_usf, struct msgb, list); + llist_del(&msg->list); + msgb_free(msg); + } + + while (!llist_empty(&pdch->tx_queue_abs)) { + msg = llist_first_entry(&pdch->tx_queue_abs, struct msgb, list); + llist_del(&msg->list); + msgb_free(msg); + } + } + while (!llist_empty(&gprs->tbf_list)) { struct l1gprs_tbf *tbf;
@@ -335,13 +358,10 @@ return 0; }
-int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, - struct l1gprs_prim_ul_block_req *req, - const struct msgb *msg) +int l1gprs_handle_ul_block_req(struct l1gprs_state *gprs, struct msgb *msg) { - const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; - const struct l1gprs_pdch *pdch = NULL; - size_t data_len; + struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; + struct l1gprs_pdch *pdch;
OSMO_ASSERT(l1br != NULL);
@@ -358,12 +378,20 @@ return -EINVAL; }
+ msg->l2h = &l1br->data[0]; + l1br->hdr.fn = ntohl(l1br->hdr.fn); + pdch = &gprs->pdch[l1br->hdr.tn]; - data_len = msgb_l1len(msg) - sizeof(*l1br);
LOGP_PDCH(pdch, LOGL_DEBUG, - "Rx UL BLOCK.req (fn=%u, len=%zu): %s\n", - ntohl(l1br->hdr.fn), data_len, osmo_hexdump(l1br->data, data_len)); + "Rx UL BLOCK.req (tbf_ref=%u, fn=%u, len=%u): %s\n", + l1br->tbf_ref, l1br->hdr.fn, msgb_l2len(msg), msgb_hexdump_l2(msg)); + + if (OSMO_UNLIKELY(msgb_l2len(msg) == 0)) { + LOGP_PDCH(pdch, LOGL_ERROR, + "Rx UL BLOCK.req with empty payload\n"); + return -EINVAL; + }
if ((pdch->ul_tbf_count == 0) && (pdch->dl_tbf_count == 0)) { LOGP_PDCH(pdch, LOGL_ERROR, @@ -371,14 +399,10 @@ return -EINVAL; }
- *req = (struct l1gprs_prim_ul_block_req) { - .hdr = { - .fn = ntohl(l1br->hdr.fn), - .tn = l1br->hdr.tn, - }, - .data = &l1br->data[0], - .data_len = data_len, - }; + if (l1br->hdr.fn == FN_INVALID) + msgb_enqueue(&pdch->tx_queue_usf, msg); + else /* TODO: sorted insertion */ + msgb_enqueue(&pdch->tx_queue_abs, msg);
return 0; } @@ -386,8 +410,8 @@ struct msgb *l1gprs_handle_dl_block_ind(struct l1gprs_state *gprs, const struct l1gprs_prim_dl_block_ind *ind) { - const struct l1gprs_pdch *pdch = NULL; struct l1ctl_gprs_dl_block_ind *l1bi; + struct l1gprs_pdch *pdch = NULL; enum osmo_gprs_cs cs; struct msgb *msg;
@@ -400,6 +424,9 @@
pdch = &gprs->pdch[ind->hdr.tn];
+ /* reset last seen USF */ + pdch->last_usf = USF_INVALID; + LOGP_PDCH(pdch, LOGL_DEBUG, "Rx DL BLOCK.ind (fn=%u, len=%zu): %s\n", ind->hdr.fn, ind->data_len, osmo_hexdump(ind->data, ind->data_len)); @@ -427,7 +454,7 @@ .ci_cb = htons(ind->meas.ci_cb), .rx_lev = ind->meas.rx_lev, }, - .usf = 0xff, + .usf = USF_INVALID, };
if (ind->data_len == 0) @@ -440,6 +467,7 @@ case OSMO_GPRS_CS3: case OSMO_GPRS_CS4: l1bi->usf = ind->data[0] & 0x07; + pdch->last_usf = l1bi->usf; /* Determine whether to include the payload or not */ if (l1gprs_pdch_filter_dl_block(pdch, ind->data)) memcpy(msgb_put(msg, ind->data_len), ind->data, ind->data_len); @@ -455,3 +483,55 @@
return msg; } + +struct msgb *l1gprs_pull_ul_block(struct l1gprs_state *gprs, uint32_t fn, uint8_t tn) +{ + struct l1gprs_pdch *pdch; + struct msgb *msg; + + OSMO_ASSERT(tn < ARRAY_SIZE(gprs->pdch)); + pdch = &gprs->pdch[tn]; + + /* check if we have a block scheduled for this TDMA Fn */ + llist_for_each_entry(msg, &pdch->tx_queue_abs, list) { + const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; + + if (l1br->hdr.fn != fn) + continue; + + LOGP_PDCH(pdch, LOGL_DEBUG, + "%s(fn=%u): found an UL block with matching TDMA Fn: %s\n", + __func__, fn, msgb_hexdump_l2(msg)); + + llist_del(&msg->list); + msgb_pull_to_l2(msg); + return msg; + } + + /* USF of the last received DL block must be known */ + if (pdch->last_usf == USF_INVALID) + return NULL; + + /* check if we have a block with matching USF */ + llist_for_each_entry(msg, &pdch->tx_queue_usf, list) { + const struct l1ctl_gprs_ul_block_req *l1br = (void *)msg->l1h; + const struct l1gprs_tbf *tbf; + + /* find an Uplink TBF for this block */ + tbf = l1gprs_find_tbf(gprs, true, l1br->tbf_ref); + if (tbf == NULL) + continue; + if (tbf->ul_usf != pdch->last_usf) + continue; + + LOGP_PDCH(pdch, LOGL_DEBUG, + "%s(fn=%u): found an UL block with matching USF %u: %s\n", + __func__, fn, pdch->last_usf, msgb_hexdump_l2(msg)); + + llist_del(&msg->list); + msgb_pull_to_l2(msg); + return msg; + } + + return NULL; +}