pespin has submitted this change. ( https://gerrit.osmocom.org/c/libosmo-gprs/+/31325 )
Change subject: rlcmac: Introduce initial poll answer infrastructure ......................................................................
rlcmac: Introduce initial poll answer infrastructure
This commit imports pdch_ul_controller.{c,h} from osmo-pcu.git d8cea3c618dbd2a343e6c012e5545006d44fc244 and adapts it to be of use on the MS side.
Related: OS#5500 Change-Id: I9d38a8de4240e65585cc8bbe3c044473af5a83e5 --- M include/osmocom/gprs/rlcmac/Makefile.am A include/osmocom/gprs/rlcmac/pdch_ul_controller.h M include/osmocom/gprs/rlcmac/rlcmac_private.h M include/osmocom/gprs/rlcmac/sched.h M include/osmocom/gprs/rlcmac/tbf_dl.h M include/osmocom/gprs/rlcmac/types_private.h M src/rlcmac/Makefile.am A src/rlcmac/pdch_ul_controller.c M src/rlcmac/rlcmac.c M src/rlcmac/sched.c M src/rlcmac/tbf_dl.c M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err 13 files changed, 439 insertions(+), 14 deletions(-)
Approvals: laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved
diff --git a/include/osmocom/gprs/rlcmac/Makefile.am b/include/osmocom/gprs/rlcmac/Makefile.am index 7a95174..81cfecd 100644 --- a/include/osmocom/gprs/rlcmac/Makefile.am +++ b/include/osmocom/gprs/rlcmac/Makefile.am @@ -3,6 +3,7 @@ coding_scheme.h \ gre.h \ llc_queue.h \ + pdch_ul_controller.h \ rlc.h \ rlc_window.h \ rlc_window_dl.h \ diff --git a/include/osmocom/gprs/rlcmac/pdch_ul_controller.h b/include/osmocom/gprs/rlcmac/pdch_ul_controller.h new file mode 100644 index 0000000..52970c4 --- /dev/null +++ b/include/osmocom/gprs/rlcmac/pdch_ul_controller.h @@ -0,0 +1,60 @@ +/* pdch_ul_controller.h + * + * Copyright (C) 2021-2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * Author: Pau Espin Pedrol pespin@sysmocom.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +#pragma once + +#include <stdint.h> +#include <stdbool.h> + +#include <osmocom/core/linuxrbtree.h> +#include <osmocom/core/utils.h> + +struct gprs_rlcmac_dl_tbf; + +struct gprs_rlcmac_pdch_ulc { + uint8_t ts_nr; + struct rb_root tree_root; + void *pool_ctx; /* talloc pool of struct pdch_ulc_node */ +}; + + +enum gprs_rlcmac_pdch_ulc_poll_reason { + GPRS_RLCMAC_PDCH_ULC_POLL_UL_ASS, /* Tx CTRL ACK for received UL ASS */ + GPRS_RLCMAC_PDCH_ULC_POLL_DL_ASS, /* Tx CTRL ACK for received DL ASS */ + GPRS_RLCMAC_PDCH_ULC_POLL_UL_ACK, /* Tx CTRL ACK (or PKT RES REQ on final UL ACK/NACK) for received UL ACK/NACK */ + GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK, /* Tx DL ACK/NACK (or others 8.1.2.2) for received data block */ + GPRS_RLCMAC_PDCH_ULC_POLL_CELL_CHG_CONTINUE, /* Tx CTRL ACK for received Pkt cell Change Continue */ +}; +extern const struct value_string gprs_rlcmac_pdch_ulc_poll_reason_names[]; + +struct gprs_rlcmac_pdch_ulc_node { + struct rb_node node; /*! entry in gprs_rlcmac_pdch_ulc->tree_root */ + uint32_t fn; + enum gprs_rlcmac_pdch_ulc_poll_reason reason; + struct gprs_rlcmac_tbf *tbf; +}; + +struct gprs_rlcmac_pdch_ulc *gprs_rlcmac_pdch_ulc_alloc(void *ctx, uint8_t ts_nr); + +int gprs_rlcmac_pdch_ulc_reserve(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn, enum gprs_rlcmac_pdch_ulc_poll_reason reason, struct gprs_rlcmac_tbf *tbf); + +struct gprs_rlcmac_pdch_ulc_node *gprs_rlcmac_pdch_ulc_get_node(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn); +struct gprs_rlcmac_pdch_ulc_node *gprs_rlcmac_pdch_ulc_pop_node(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn); + +void gprs_rlcmac_pdch_ulc_release_node(struct gprs_rlcmac_pdch_ulc *ulc, struct gprs_rlcmac_pdch_ulc_node *item); +void gprs_rlcmac_pdch_ulc_release_tbf(struct gprs_rlcmac_pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf); +int gprs_rlcmac_pdch_ulc_release_fn(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn); + +void gprs_rlcmac_pdch_ulc_expire_fn(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn); diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h index fc65753..f5bbfb0 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_private.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h @@ -69,6 +69,10 @@
uint8_t next_ul_tbf_nr; uint8_t next_dl_tbf_nr; + + struct { + struct gprs_rlcmac_pdch_ulc *ulc[8]; + } sched; };
extern struct gprs_rlcmac_ctx *g_ctx; diff --git a/include/osmocom/gprs/rlcmac/sched.h b/include/osmocom/gprs/rlcmac/sched.h index d344aea..c7fa82a 100644 --- a/include/osmocom/gprs/rlcmac/sched.h +++ b/include/osmocom/gprs/rlcmac/sched.h @@ -9,4 +9,6 @@ uint8_t usf; };
+uint32_t rrbp2fn(uint32_t cur_fn, uint8_t rrbp); + int gprs_rlcmac_rcv_rts_block(struct gprs_rlcmac_rts_block_ind *bi); diff --git a/include/osmocom/gprs/rlcmac/tbf_dl.h b/include/osmocom/gprs/rlcmac/tbf_dl.h index 5d7941d..facae81 100644 --- a/include/osmocom/gprs/rlcmac/tbf_dl.h +++ b/include/osmocom/gprs/rlcmac/tbf_dl.h @@ -42,7 +42,7 @@
int gprs_rlcmac_dl_tbf_rcv_data_block(struct gprs_rlcmac_dl_tbf *dl_tbf, const struct gprs_rlcmac_rlc_data_info *rlc, - uint8_t *data); + uint8_t *data, uint32_t fn, uint8_t ts_nr);
static inline struct gprs_rlcmac_tbf *dl_tbf_as_tbf(struct gprs_rlcmac_dl_tbf *dl_tbf) { diff --git a/include/osmocom/gprs/rlcmac/types_private.h b/include/osmocom/gprs/rlcmac/types_private.h index 8450d08..77a0504 100644 --- a/include/osmocom/gprs/rlcmac/types_private.h +++ b/include/osmocom/gprs/rlcmac/types_private.h @@ -3,6 +3,14 @@
#include <osmocom/gprs/rlcmac/types.h>
+/* TS 44.060 able 10.4.5.1 RRBP offsets */ +enum gprs_rlcmac_rrbp_field { + GPRS_RLCMAC_RRBP_N_plus_13 = 0x0, + GPRS_RLCMAC_RRBP_N_plus_17_18 = 0x1, + GPRS_RLCMAC_RRBP_N_plus_21_22 = 0x2, + GPRS_RLCMAC_RRBP_N_plus_26 = 0x3, +}; + /* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */ enum gprs_rlcmac_payload_type { GPRS_RLCMAC_PT_DATA_BLOCK = 0x0, diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am index 68ab832..9133df0 100644 --- a/src/rlcmac/Makefile.am +++ b/src/rlcmac/Makefile.am @@ -30,6 +30,7 @@ csn1_ts_44_018.c \ csn1_ts_44_060.c \ gre.c \ + pdch_ul_controller.c \ llc_queue.c \ rlc_window.c \ rlc_window_dl.c \ diff --git a/src/rlcmac/pdch_ul_controller.c b/src/rlcmac/pdch_ul_controller.c new file mode 100644 index 0000000..7a3b753 --- /dev/null +++ b/src/rlcmac/pdch_ul_controller.c @@ -0,0 +1,212 @@ +/* pdch_ul_controller.c + * + * Copyright (C) 2021-2023 by sysmocom - s.f.m.c. GmbH info@sysmocom.de + * Author: Pau Espin Pedrol pespin@sysmocom.de + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <inttypes.h> +#include <unistd.h> +#include <talloc.h> + +#include <osmocom/core/logging.h> +#include <osmocom/core/linuxlist.h> +#include <osmocom/core/linuxrbtree.h> +#include <osmocom/gsm/gsm_utils.h> + +#include <osmocom/gprs/rlcmac/pdch_ul_controller.h> +#include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/types_private.h> + +/* TS 44.060 Table 10.4.5.1 states maximum RRBP is N + 26. Give extra space for time diff between Tx and Rx? */ +#define MAX_FN_RESERVED (27 + 50) + +const struct value_string gprs_rlcmac_pdch_ulc_poll_reason_names[] = { + { GPRS_RLCMAC_PDCH_ULC_POLL_UL_ASS, "UL_ASS" }, + { GPRS_RLCMAC_PDCH_ULC_POLL_DL_ASS, "DL_ASS" }, + { GPRS_RLCMAC_PDCH_ULC_POLL_UL_ACK, "UL_ACK" }, + { GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK, "DL_ACK" }, + { GPRS_RLCMAC_PDCH_ULC_POLL_CELL_CHG_CONTINUE, "CELL_CHG_CONTINUE" }, + { 0, NULL } +}; + +#define GSM_MAX_FN_THRESH (GSM_MAX_FN >> 1) +/* 0: equal, -1: fn1 BEFORE fn2, 1: fn1 AFTER fn2 */ +static inline int fn_cmp(uint32_t fn1, uint32_t fn2) +{ + if (fn1 == fn2) + return 0; + /* FN1 goes before FN2: */ + if ((fn1 < fn2 && (fn2 - fn1) < GSM_MAX_FN_THRESH) || + (fn1 > fn2 && (fn1 - fn2) > GSM_MAX_FN_THRESH)) + return -1; + /* FN1 goes after FN2: */ + return 1; +} + +struct gprs_rlcmac_pdch_ulc *gprs_rlcmac_pdch_ulc_alloc(void *ctx, uint8_t ts_nr) +{ + struct gprs_rlcmac_pdch_ulc *ulc; + ulc = talloc_zero(ctx, struct gprs_rlcmac_pdch_ulc); + if (!ulc) + return ulc; + + ulc->ts_nr = ts_nr; + ulc->pool_ctx = talloc_pool(ulc, sizeof(struct gprs_rlcmac_pdch_ulc_node) * MAX_FN_RESERVED); + return ulc; +} + +struct gprs_rlcmac_pdch_ulc_node *gprs_rlcmac_pdch_ulc_get_node(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn) +{ + OSMO_ASSERT(ulc); + struct rb_node *node = ulc->tree_root.rb_node; + struct gprs_rlcmac_pdch_ulc_node *it; + int res; + + while (node) { + it = rb_entry(node, struct gprs_rlcmac_pdch_ulc_node, node); + res = fn_cmp(it->fn, fn); + if (res > 0) /* it->fn AFTER fn */ + node = node->rb_left; + else if (res < 0) /* it->fn BEFORE fn */ + node = node->rb_right; + else /* it->fn == fn */ + return it; + } + return NULL; +} +struct gprs_rlcmac_pdch_ulc_node *gprs_rlcmac_pdch_ulc_pop_node(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn) +{ + struct gprs_rlcmac_pdch_ulc_node *item = gprs_rlcmac_pdch_ulc_get_node(ulc, fn); + if (!item) + return NULL; + rb_erase(&item->node, &ulc->tree_root); + return item; +} + +struct rrbp_opt { + uint8_t offset; + enum gprs_rlcmac_rrbp_field coding; +}; + +static struct gprs_rlcmac_pdch_ulc_node *_alloc_node(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn) +{ + struct gprs_rlcmac_pdch_ulc_node *node; + node = talloc_zero(ulc->pool_ctx, struct gprs_rlcmac_pdch_ulc_node); + node->fn = fn; + return node; +} + +static int gprs_rlcmac_pdch_ulc_add_node(struct gprs_rlcmac_pdch_ulc *ulc, struct gprs_rlcmac_pdch_ulc_node *item) +{ + struct rb_node **n = &(ulc->tree_root.rb_node); + struct rb_node *parent = NULL; + + while (*n) { + struct gprs_rlcmac_pdch_ulc_node *it; + int res; + + it = container_of(*n, struct gprs_rlcmac_pdch_ulc_node, node); + + parent = *n; + res = fn_cmp(item->fn, it->fn); + if (res < 0) { /* item->fn "BEFORE" it->fn */ + n = &((*n)->rb_left); + } else if (res > 0) { /* item->fn "AFTER" it->fn */ + n = &((*n)->rb_right); + } else { + LOGRLCMAC(LOGL_ERROR, "TS=%" PRIu8 " Trying to reserve already reserved FN %u\n", + ulc->ts_nr, item->fn); + return -EEXIST; + } + } + + rb_link_node(&item->node, parent, n); + rb_insert_color(&item->node, &ulc->tree_root); + return 0; +} + +int gprs_rlcmac_pdch_ulc_reserve(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn, + enum gprs_rlcmac_pdch_ulc_poll_reason reason, + struct gprs_rlcmac_tbf *tbf) +{ + struct gprs_rlcmac_pdch_ulc_node *item = _alloc_node(ulc, fn); + item->reason = reason; + item->tbf = tbf; + LOGRLCMAC(LOGL_DEBUG, "Register POLL (TS=%u FN=%u, reason=%s)\n", + ulc->ts_nr, item->fn, + get_value_string(gprs_rlcmac_pdch_ulc_poll_reason_names, item->reason)); + return gprs_rlcmac_pdch_ulc_add_node(ulc, item); +} + +void gprs_rlcmac_pdch_ulc_release_node(struct gprs_rlcmac_pdch_ulc *ulc, struct gprs_rlcmac_pdch_ulc_node *item) +{ + rb_erase(&item->node, &ulc->tree_root); + talloc_free(item); +} + +int gprs_rlcmac_pdch_ulc_release_fn(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn) +{ + struct gprs_rlcmac_pdch_ulc_node *item = gprs_rlcmac_pdch_ulc_get_node(ulc, fn); + if (!item) + return -ENOKEY; + gprs_rlcmac_pdch_ulc_release_node(ulc, item); + return 0; +} + +void gprs_rlcmac_pdch_ulc_release_tbf(struct gprs_rlcmac_pdch_ulc *ulc, const struct gprs_rlcmac_tbf *tbf) +{ + bool tree_modified; + do { + struct rb_node *node; + struct gprs_rlcmac_pdch_ulc_node *item; + + tree_modified = false; + for (node = rb_first(&ulc->tree_root); node; node = rb_next(node)) { + item = container_of(node, struct gprs_rlcmac_pdch_ulc_node, node); + if (item->tbf != tbf) + continue; + /* One entry found, remove it from tree and restart + * search from start (to avoid traverse continue from + * no-more existent node */ + tree_modified = true; + gprs_rlcmac_pdch_ulc_release_node(ulc, item); + break; + } + } while (tree_modified); +} + +void gprs_rlcmac_pdch_ulc_expire_fn(struct gprs_rlcmac_pdch_ulc *ulc, uint32_t fn) +{ + struct gprs_rlcmac_pdch_ulc_node *item; + int res; + + struct rb_node *first; + while ((first = rb_first(&ulc->tree_root))) { + item = container_of(first, struct gprs_rlcmac_pdch_ulc_node, node); + res = fn_cmp(item->fn, fn); + if (res > 0) /* item->fn AFTER fn */ + break; + if (res < 0) { /* item->fn BEFORE fn */ + /* Sanity check: */ + LOGRLCMAC(LOGL_ERROR, + "TS=%" PRIu8 " Expiring FN=%" PRIu32 " but previous FN=%" PRIu32 " is still reserved!\n", + ulc->ts_nr, fn, item->fn); + } + rb_erase(&item->node, &ulc->tree_root); + + LOGRLCMAC(LOGL_NOTICE, "TS=%u Timeout for registered POLL (FN=%u, reason=%s)\n", + ulc->ts_nr, item->fn, get_value_string(gprs_rlcmac_pdch_ulc_poll_reason_names, item->reason)); + + talloc_free(item); + } +} diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c index 5b72690..1e28bac 100644 --- a/src/rlcmac/rlcmac.c +++ b/src/rlcmac/rlcmac.c @@ -27,6 +27,7 @@ #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> #include <osmocom/gprs/rlcmac/rlcmac_dec.h> +#include <osmocom/gprs/rlcmac/pdch_ul_controller.h> #include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> #include <osmocom/gprs/rlcmac/gre.h> @@ -58,6 +59,7 @@ { bool first_init = true; int rc; + unsigned int i; OSMO_ASSERT(location == OSMO_GPRS_RLCMAC_LOCATION_MS || location == OSMO_GPRS_RLCMAC_LOCATION_PCU)
if (g_ctx) { @@ -94,6 +96,11 @@ } }
+ for (i = 0; i < ARRAY_SIZE(g_ctx->sched.ulc); i++) { + g_ctx->sched.ulc[i] = gprs_rlcmac_pdch_ulc_alloc(g_ctx, i); + OSMO_ASSERT(g_ctx->sched.ulc[i]); + } + return 0; }
@@ -324,7 +331,8 @@ return -ENOENT; } LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Rx new DL data\n"); - rc = gprs_rlcmac_dl_tbf_rcv_data_block(dl_tbf, &rlc_dec, rlcmac_prim->l1ctl.pdch_data_ind.data); + rc = gprs_rlcmac_dl_tbf_rcv_data_block(dl_tbf, &rlc_dec, rlcmac_prim->l1ctl.pdch_data_ind.data, + rlcmac_prim->l1ctl.pdch_data_ind.fn, rlcmac_prim->l1ctl.pdch_data_ind.ts_nr); return rc; }
diff --git a/src/rlcmac/sched.c b/src/rlcmac/sched.c index ea133c5..f229b7a 100644 --- a/src/rlcmac/sched.c +++ b/src/rlcmac/sched.c @@ -25,19 +25,85 @@ #include <osmocom/gprs/rlcmac/rlcmac_private.h> #include <osmocom/gprs/rlcmac/sched.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_dl.h> #include <osmocom/gprs/rlcmac/tbf_ul.h> #include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> +#include <osmocom/gprs/rlcmac/types_private.h> +#include <osmocom/gprs/rlcmac/pdch_ul_controller.h>
struct tbf_sched_ctrl_candidates { + struct gprs_rlcmac_dl_tbf *poll_dl_ack_final_ack; /* 8.1.2.2 1) */ + struct gprs_rlcmac_dl_tbf *poll_dl_ack; /* 8.1.2.2 7) */ struct gprs_rlcmac_ul_tbf *ul_ass; };
+static inline uint32_t next_fn(uint32_t fn, uint32_t offset) +{ + return (fn + offset) % GSM_MAX_FN; +} + +static inline bool fn_valid(uint32_t fn) +{ + uint32_t f = fn % 13; + return f == 0 || f == 4 || f == 8; +} + +uint32_t rrbp2fn(uint32_t cur_fn, uint8_t rrbp) +{ + uint32_t poll_fn; + static const uint8_t rrbp_list[] = { + 13, /* GPRS_RLCMAC_RRBP_N_plus_13 */ + 17, /* GPRS_RLCMAC_RRBP_N_plus_17_18 */ + 21, /* GPRS_RLCMAC_RRBP_N_plus_21_22 */ + 26, /* GPRS_RLCMAC_RRBP_N_plus_26 */ + }; + + OSMO_ASSERT(rrbp < ARRAY_SIZE(rrbp_list)); + + poll_fn = next_fn(cur_fn, rrbp_list[rrbp]); + if (!fn_valid(poll_fn)) { + /* 17 -> 18, 21 -> 22: */ + poll_fn = next_fn(poll_fn, 1); + OSMO_ASSERT(fn_valid(poll_fn)); + } + return poll_fn; +} + static void get_ctrl_msg_tbf_candidates(const struct gprs_rlcmac_rts_block_ind *bi, struct tbf_sched_ctrl_candidates *tbfs) {
struct gprs_rlcmac_entity *gre; + struct gprs_rlcmac_dl_tbf *dl_tbf; struct gprs_rlcmac_ul_tbf *ul_tbf; + struct gprs_rlcmac_pdch_ulc_node *node; + + node = gprs_rlcmac_pdch_ulc_get_node(g_ctx->sched.ulc[bi->ts], bi->fn); + if (node) { + switch (node->reason) { + case GPRS_RLCMAC_PDCH_ULC_POLL_UL_ASS: + /* TODO */ + break; + case GPRS_RLCMAC_PDCH_ULC_POLL_DL_ASS: + /* TODO */ + break; + case GPRS_RLCMAC_PDCH_ULC_POLL_UL_ACK: + /* TODO */ + break; + case GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK: + dl_tbf = tbf_as_dl_tbf(node->tbf); + /* 8.1.2.2 Polling for Packet Downlink Ack/Nack */ + if (gprs_rlcmac_tbf_dl_state(dl_tbf) == GPRS_RLCMAC_TBF_DL_ST_FINISHED) + tbfs->poll_dl_ack_final_ack = dl_tbf; + else + tbfs->poll_dl_ack = dl_tbf; + break; + case GPRS_RLCMAC_PDCH_ULC_POLL_CELL_CHG_CONTINUE: + /* TODO */ + break; + } + gprs_rlcmac_pdch_ulc_release_node(g_ctx->sched.ulc[bi->ts], node); + }
/* Iterate over UL TBFs: */ llist_for_each_entry(gre, &g_ctx->gre_list, entry) { @@ -75,13 +141,38 @@ return NULL; }
+ static struct msgb *sched_select_ctrl_msg(const struct gprs_rlcmac_rts_block_ind *bi, struct tbf_sched_ctrl_candidates *tbfs) { struct msgb *msg = NULL; - if (tbfs->ul_ass) + /* 8.1.2.2 1) (EGPRS) PACKET DOWNLINK ACK/NACK w/ FinalAckInd=1 */ + if (tbfs->poll_dl_ack_final_ack) { + LOGRLCMAC(LOGL_DEBUG, "(ts=%u,fn=%u,usf=%u) Tx DL ACK/NACK FinalAck=1\n", + bi->ts, bi->fn, bi->usf); + msg = NULL; /* TODO: generate DL ACK/NACK ctrl block */ + if (msg) + return msg; + } + + /* 8.1.2.2 5) Any other RLC/MAC control message, other than a (EGPRS) PACKET DOWNLINK ACK/NACK */ + if (tbfs->ul_ass) { msg = gprs_rlcmac_tbf_ul_ass_create_rlcmac_msg(tbfs->ul_ass, bi); - return msg; + if (msg) + return msg; + } + + /* 8.1.2.2 7) A (EGPRS) PACKET DOWNLINK ACK/NACK message not containing a Final Ack Indicator or a + * Channel Request Description IE */ + if (tbfs->poll_dl_ack) { + LOGRLCMAC(LOGL_DEBUG, "(ts=%u,fn=%u,usf=%u) Tx DL ACK/NACK\n", + bi->ts, bi->fn, bi->usf); + msg = NULL; /* TODO: generate DL ACK/NACK ctrl block */ + if (msg) + return msg; + } + + return NULL; }
static struct msgb *sched_select_ul_data_msg(const struct gprs_rlcmac_rts_block_ind *bi) @@ -116,7 +207,7 @@ struct msgb *msg = NULL; struct tbf_sched_ctrl_candidates tbf_cand = {0}; struct osmo_gprs_rlcmac_prim *rlcmac_prim_tx; - int rc; + int rc = 0;
get_ctrl_msg_tbf_candidates(bi, &tbf_cand);
@@ -126,17 +217,20 @@ if ((msg = sched_select_ul_data_msg(bi))) goto tx_msg;
- /* Prio 3: send dummy control message (or nothing depending on EXT_UTBF_NODATA) */ + /* Lowest prio: send dummy control message (or nothing depending on EXT_UTBF_NODATA) */ if ((msg = sched_select_ul_dummy_ctrl_blk(bi))) goto tx_msg;
/* Nothing to transmit */ - return 0; + goto ret_rc;
tx_msg: rlcmac_prim_tx = gprs_rlcmac_prim_alloc_l1ctl_pdch_data_req(bi->ts, bi->fn, msgb_data(msg), 0); rlcmac_prim_tx->l1ctl.pdch_data_req.data_len = msgb_length(msg); rc = gprs_rlcmac_prim_call_down_cb(rlcmac_prim_tx); msgb_free(msg); + +ret_rc: + gprs_rlcmac_pdch_ulc_expire_fn(g_ctx->sched.ulc[bi->ts], bi->fn); return rc; } diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c index a5b342a..da9b871 100644 --- a/src/rlcmac/tbf_dl.c +++ b/src/rlcmac/tbf_dl.c @@ -24,6 +24,7 @@ #include <osmocom/gprs/rlcmac/gre.h> #include <osmocom/gprs/rlcmac/rlc_window_dl.h> #include <osmocom/gprs/rlcmac/rlcmac_dec.h> +#include <osmocom/gprs/rlcmac/pdch_ul_controller.h>
struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre) { @@ -167,7 +168,7 @@
int gprs_rlcmac_dl_tbf_rcv_data_block(struct gprs_rlcmac_dl_tbf *dl_tbf, const struct gprs_rlcmac_rlc_data_info *rlc, - uint8_t *data) + uint8_t *data, uint32_t fn, uint8_t ts_nr) { const struct gprs_rlcmac_rlc_block_info *rdbi; struct gprs_rlcmac_rlc_block *block; @@ -257,7 +258,13 @@ } }
- /* TODO: if RRBP contains valid data, schedule a DL ACK/NACK. */ + /* If RRBP contains valid data, schedule a DL ACK/NACK. */ + if (rlc->es_p) { + uint32_t poll_fn = rrbp2fn(fn, rlc->rrbp); + gprs_rlcmac_pdch_ulc_reserve(g_ctx->sched.ulc[ts_nr], poll_fn, + GPRS_RLCMAC_PDCH_ULC_POLL_DL_ACK, + dl_tbf_as_tbf(dl_tbf)); + }
return 0; } diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index 283227b..c80ce60 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -25,6 +25,8 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/gre.h> #include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/types_private.h> +#include <osmocom/gprs/rlcmac/sched.h>
static void *tall_ctx = NULL;
@@ -241,7 +243,7 @@ 0x43, 0xc0, 0x01, 0x2b, 0x2b, 0x2b };
-static struct msgb *create_dl_data_block(uint8_t dl_tfi, uint8_t usf, enum gprs_rlcmac_coding_scheme cs, uint8_t bsn, bool fbi) +static struct msgb *create_dl_data_block(uint8_t dl_tfi, uint8_t usf, enum gprs_rlcmac_coding_scheme cs, uint8_t bsn, bool fbi, bool s_p, uint8_t rrbp) { struct msgb *msg = msgb_alloc(128, __func__); struct gprs_rlcmac_rlc_dl_data_header *hdr; @@ -249,8 +251,8 @@
hdr = (struct gprs_rlcmac_rlc_dl_data_header *)msgb_put(msg, gprs_rlcmac_mcs_size_dl(cs)); hdr->pt = 0; /* RLC/MAC block contains an RLC data block */ - hdr->rrbp = 0; - hdr->s_p = 0; + hdr->rrbp = rrbp; + hdr->s_p = s_p ? 1 : 0; hdr->usf = usf; hdr->pr = 0; hdr->tfi = dl_tfi; @@ -330,6 +332,7 @@ uint8_t usf = 0; uint32_t rts_fn = 4; uint8_t dl_tfi = 0; + uint8_t rrbp = GPRS_RLCMAC_RRBP_N_plus_17_18;
/* Notify RLCMAC about our TLLI */ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(tlli); @@ -341,7 +344,7 @@ OSMO_ASSERT(rc == 0);
/* Transmit some DL LLC data MS<-PCU */ - dl_data_msg = create_dl_data_block(dl_tfi, usf, GPRS_RLCMAC_CS_1, 0, 1); + dl_data_msg = create_dl_data_block(dl_tfi, usf, GPRS_RLCMAC_CS_1, 0, true, true, rrbp); rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(ts_nr, rts_fn, 0, 0, 0, msgb_data(dl_data_msg), msgb_length(dl_data_msg)); @@ -349,6 +352,12 @@ OSMO_ASSERT(rc == 0); msgb_free(dl_data_msg);
+ /* Trigger transmission of DL ACK/NACK */ + rts_fn = rrbp2fn(rts_fn, rrbp); + rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_rts_ind(ts_nr, rts_fn, usf); + rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); + OSMO_ASSERT(rc == 0); + printf("=== %s end ===\n", __func__); cleanup_test(); } diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index 41f88a7..2d1b804 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -38,7 +38,7 @@ DLGLOBAL INFO Rx from lower layers: L1CTL-PDCH_DATA.indication DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Rx new DL data DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) DL DATA TFI=0 received (V(Q)=0 .. V(R)=0) -DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Got CS-1 RLC data block: FBI=1, BSN=0, SPB=0, S/P=0 RRBP=0, E=0, bitoffs=24 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Got CS-1 RLC data block: FBI=1, BSN=0, SPB=0, S/P=1 RRBP=1, E=0, bitoffs=24 DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) BSN 0 storing in window (0..63) DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) data_length=20, data=19 43 c0 01 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b DLGLOBAL DEBUG - Raising V(R) to 1 @@ -51,3 +51,8 @@ DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Finished with DL TBF DLGLOBAL INFO DL_TBF{FLOW}: Received Event LAST_DL_DATA_RECVD DLGLOBAL INFO DL_TBF{FLOW}: state_chg to FINISHED +DLGLOBAL DEBUG Register POLL (TS=7 FN=21, reason=DL_ACK) +DLGLOBAL INFO Rx from lower layers: L1CTL-PDCH_RTS.indication +DLGLOBAL DEBUG (ts=7,fn=21,usf=0) Tx DL ACK/NACK FinalAck=1 +DLGLOBAL DEBUG (ts=7,fn=21,usf=0) No Uplink TBF available to transmit RLC/MAC Ul Data Block +DLGLOBAL DEBUG (ts=7,fn=21,usf=0) No Uplink TBF available to transmit RLC/MAC Ul Dummy Ctrl Block