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 <[email protected]>
+ * Author: Pau Espin Pedrol <[email protected]>
+ *
+ * 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 <[email protected]>
+ * Author: Pau Espin Pedrol <[email protected]>
+ *
+ * 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

--
To view, visit https://gerrit.osmocom.org/c/libosmo-gprs/+/31325
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: libosmo-gprs
Gerrit-Branch: master
Gerrit-Change-Id: I9d38a8de4240e65585cc8bbe3c044473af5a83e5
Gerrit-Change-Number: 31325
Gerrit-PatchSet: 4
Gerrit-Owner: pespin <[email protected]>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <[email protected]>
Gerrit-Reviewer: pespin <[email protected]>
Gerrit-MessageType: merged

Reply via email to