lists.osmocom.org
Sign In
Sign Up
Sign In
Sign Up
Manage this list
×
Keyboard Shortcuts
Thread View
j
: Next unread message
k
: Previous unread message
j a
: Jump to all threads
j l
: Jump to MailingList overview
2025
June
May
April
March
February
January
2024
December
November
October
September
August
July
June
May
April
March
February
January
2023
December
November
October
September
August
July
June
May
April
March
February
January
2022
December
November
October
September
August
July
June
May
April
March
February
January
List overview
Download
gerrit-log
February 2023
----- 2025 -----
June 2025
May 2025
April 2025
March 2025
February 2025
January 2025
----- 2024 -----
December 2024
November 2024
October 2024
September 2024
August 2024
July 2024
June 2024
May 2024
April 2024
March 2024
February 2024
January 2024
----- 2023 -----
December 2023
November 2023
October 2023
September 2023
August 2023
July 2023
June 2023
May 2023
April 2023
March 2023
February 2023
January 2023
----- 2022 -----
December 2022
November 2022
October 2022
September 2022
August 2022
July 2022
June 2022
May 2022
April 2022
March 2022
February 2022
January 2022
gerrit-log@lists.osmocom.org
1 participants
2601 discussions
Start a n
N
ew thread
[L] Change in libosmo-gprs[master]: rlcmac: Introduce initial poll answer infrastructure
by pespin
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(a)sysmocom.de> + * Author: Pau Espin Pedrol <pespin(a)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(a)sysmocom.de> + * Author: Pau Espin Pedrol <pespin(a)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 -- 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 <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[XL] Change in libosmo-gprs[master]: rlcmac: Implement initial DL rx data path
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31320
) Change subject: rlcmac: Implement initial DL rx data path ...................................................................... rlcmac: Implement initial DL rx data path This code imports code from osmo-pcu.git d8cea3c618dbd2a343e6c012e5545006d44fc244 (heavy modified to adapt to current code base). With this patch the RLCMAC layer is already capable of decoding RLC/MAC GPRS data blocks and submit them as LLC frames to the upper layer. Related: OS#5500 Change-Id: Ie7535606916c0800c0e1bd9555be022c81ea257d --- M include/osmocom/gprs/rlcmac/Makefile.am M include/osmocom/gprs/rlcmac/rlc.h A include/osmocom/gprs/rlcmac/rlc_window_dl.h A include/osmocom/gprs/rlcmac/rlcmac_dec.h M include/osmocom/gprs/rlcmac/rlcmac_private.h M include/osmocom/gprs/rlcmac/tbf_dl.h M src/rlcmac/Makefile.am A src/rlcmac/rlc_window_dl.c M src/rlcmac/rlcmac.c A src/rlcmac/rlcmac_dec.c M src/rlcmac/rlcmac_enc.c M src/rlcmac/tbf_dl.c M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err M tests/rlcmac/rlcmac_prim_test.ok 15 files changed, 997 insertions(+), 12 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 e91f342..7a95174 100644 --- a/include/osmocom/gprs/rlcmac/Makefile.am +++ b/include/osmocom/gprs/rlcmac/Makefile.am @@ -5,7 +5,9 @@ llc_queue.h \ rlc.h \ rlc_window.h \ + rlc_window_dl.h \ rlc_window_ul.h \ + rlcmac_dec.h \ rlcmac_enc.h \ rlcmac_private.h \ sched.h \ diff --git a/include/osmocom/gprs/rlcmac/rlc.h b/include/osmocom/gprs/rlcmac/rlc.h index c16df4d..4e7882e 100644 --- a/include/osmocom/gprs/rlcmac/rlc.h +++ b/include/osmocom/gprs/rlcmac/rlc.h @@ -68,7 +68,7 @@ #endif } __attribute__ ((packed)); -struct gprs_rlcmacrlc_li_field_egprs { +struct gprs_rlcmac_rlc_li_field_egprs { #if OSMO_IS_LITTLE_ENDIAN uint8_t e:1, li:7; @@ -105,6 +105,7 @@ unsigned int es_p; unsigned int rrbp; unsigned int pr; + unsigned int fbi; uint8_t num_data_blocks; /* this can actually be only 0, 1, 2: enforced in gprs_rlcmac_rlc_data_header_init() */ unsigned int with_padding; unsigned int data_offs_bits[2]; diff --git a/include/osmocom/gprs/rlcmac/rlc_window_dl.h b/include/osmocom/gprs/rlcmac/rlc_window_dl.h new file mode 100644 index 0000000..58faaa4 --- /dev/null +++ b/include/osmocom/gprs/rlcmac/rlc_window_dl.h @@ -0,0 +1,90 @@ +/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060 */ +#pragma once + +#include <stdint.h> + +#include <osmocom/core/bitvec.h> + +#include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/rlc_window.h> + +struct gprs_rlcmac_dl_tbf; + +enum gprs_rlcmac_rlc_dl_bsn_state { + GPRS_RLCMAC_RLC_DL_BSN_INVALID, + GPRS_RLCMAC_RLC_DL_BSN_RECEIVED, + GPRS_RLCMAC_RLC_DL_BSN_MISSING, + GPRS_RLCMAC_RLC_DL_BSN_MAX, +}; + +struct gprs_rlcmac_rlc_v_n { + enum gprs_rlcmac_rlc_dl_bsn_state v_n[RLC_MAX_SNS/2]; /* receive state array */ +}; + +void gprs_rlcmac_rlc_v_n_reset(struct gprs_rlcmac_rlc_v_n *v_n); +enum gprs_rlcmac_rlc_dl_bsn_state gprs_rlcmac_rlc_v_n_get_state(const struct gprs_rlcmac_rlc_v_n *v_n, int bsn); +bool gprs_rlcmac_rlc_v_n_is_received(const struct gprs_rlcmac_rlc_v_n *v_n, int bsn); + +/* Mark a RLC frame for something */ +void gprs_rlcmac_rlc_v_n_mark_received(struct gprs_rlcmac_rlc_v_n *v_n, int bsn); +void gprs_rlcmac_rlc_v_n_mark_missing(struct gprs_rlcmac_rlc_v_n *v_n, int bsn); + + +struct gprs_rlcmac_rlc_dl_window { + struct gprs_rlcmac_rlc_window window; /* parent */ + struct gprs_rlcmac_dl_tbf *dl_tbf; /* backpointer */ + + uint16_t v_r; /* send state */ + uint16_t v_q; /* ack state */ + struct gprs_rlcmac_rlc_v_n v_n; +}; + +struct gprs_rlcmac_rlc_dl_window *gprs_rlcmac_rlc_dl_window_alloc(struct gprs_rlcmac_dl_tbf *dl_tbf); +void gprs_rlcmac_rlc_dl_window_free(struct gprs_rlcmac_rlc_dl_window *dlw); + +void gprs_rlcmac_rlc_dl_window_reset(struct gprs_rlcmac_rlc_dl_window *dlw); + +uint16_t gprs_rlcmac_rlc_dl_window_v_r(const struct gprs_rlcmac_rlc_dl_window *dlw); +uint16_t gprs_rlcmac_rlc_dl_window_v_q(const struct gprs_rlcmac_rlc_dl_window *dlw); + +void gprs_rlcmac_rlc_dl_window_set_v_r(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t v_r); +void gprs_rlcmac_rlc_dl_window_set_v_q(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t v_q); + +bool gprs_rlcmac_rlc_dl_window_is_in_window(const struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn); +bool gprs_rlcmac_rlc_dl_window_is_received(const struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn); + +void gprs_rlcmac_rlc_dl_window_update_rbb(const struct gprs_rlcmac_rlc_dl_window *dlw, char *rbb); +uint16_t gprs_rlcmac_rlc_dl_window_update_rbb_egprs(const struct gprs_rlcmac_rlc_dl_window *dlw, uint8_t *rbb); + +void gprs_rlcmac_rlc_dl_window_raise_v_r_to(struct gprs_rlcmac_rlc_dl_window *dlw, int moves); +void gprs_rlcmac_rlc_dl_window_raise_v_q_to(struct gprs_rlcmac_rlc_dl_window *dlw, int incr); +void gprs_rlcmac_rlc_dl_window_raise_v_r(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn); +uint16_t gprs_rlcmac_rlc_dl_window_raise_v_q(struct gprs_rlcmac_rlc_dl_window *dlw); + +void gprs_rlcmac_rlc_dl_window_receive_bsn(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn); +bool gprs_rlcmac_rlc_dl_window_invalidate_bsn(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn); + +static inline uint16_t gprs_rlcmac_rlc_dl_window_ssn(const struct gprs_rlcmac_rlc_dl_window *dlw) +{ + return gprs_rlcmac_rlc_dl_window_v_r(dlw); +} + +static inline struct gprs_rlcmac_rlc_window *rlc_dlw_as_w(struct gprs_rlcmac_rlc_dl_window *dlw) +{ + return &dlw->window; +} + +static inline const struct gprs_rlcmac_rlc_window *rlc_dlw_as_w_const(const struct gprs_rlcmac_rlc_dl_window *dlw) +{ + return &dlw->window; +} + +static inline struct gprs_rlcmac_rlc_dl_window *rlc_w_as_dlw(struct gprs_rlcmac_rlc_window *w) +{ + return (struct gprs_rlcmac_rlc_dl_window *)w; +} + +static inline const struct gprs_rlcmac_rlc_dl_window *rcl_w_as_dlw_const(struct gprs_rlcmac_rlc_window *w) +{ + return (const struct gprs_rlcmac_rlc_dl_window *)w; +} diff --git a/include/osmocom/gprs/rlcmac/rlcmac_dec.h b/include/osmocom/gprs/rlcmac/rlcmac_dec.h new file mode 100644 index 0000000..c973917 --- /dev/null +++ b/include/osmocom/gprs/rlcmac/rlcmac_dec.h @@ -0,0 +1,40 @@ +#pragma once + +/* RLCMAC decoding support functions */ + +#include <stdint.h> +#include <osmocom/core/msgb.h> + +#include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/coding_scheme.h> + + +/**************** + * DATA BLOCKS: + ****************/ + +/* represents (parts) LLC PDUs within one RLC Data block */ +struct gprs_rlcmac_rlc_llc_chunk { + uint8_t offset; + uint8_t length; + bool is_complete; /* if this PDU ends in this block */ +}; + +int gprs_rlcmac_rlc_data_from_dl_data(const struct gprs_rlcmac_rlc_block_info *rdbi, + enum gprs_rlcmac_coding_scheme cs, + const uint8_t *data, + struct gprs_rlcmac_rlc_llc_chunk *chunks, + unsigned int chunks_size); + +int gprs_rlcmac_rlc_parse_dl_data_header(struct gprs_rlcmac_rlc_data_info *rlc, + const uint8_t *data, + enum gprs_rlcmac_coding_scheme cs); + +unsigned int gprs_rlcmac_rlc_copy_to_aligned_buffer(const struct gprs_rlcmac_rlc_data_info *rlc, + unsigned int data_block_idx, + const uint8_t *src, uint8_t *buffer); + + +/**************** + * CONTROL BLOCKS: + ****************/ diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h index 36c634c..fc65753 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_private.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h @@ -17,6 +17,8 @@ #define GPRS_RLCMAC_USF_UNUSED 0x07 +#define GPRS_RLCMAC_LLC_PDU_MAX_LEN 1543 + struct gprs_rlcmac_ul_tbf_allocation_ts { bool allocated; uint8_t usf; diff --git a/include/osmocom/gprs/rlcmac/tbf_dl.h b/include/osmocom/gprs/rlcmac/tbf_dl.h index 56b0e8c..5d7941d 100644 --- a/include/osmocom/gprs/rlcmac/tbf_dl.h +++ b/include/osmocom/gprs/rlcmac/tbf_dl.h @@ -10,6 +10,10 @@ #include <osmocom/gprs/rlcmac/coding_scheme.h> #include <osmocom/gprs/rlcmac/sched.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/rlc.h> + +struct gprs_rlcmac_rlc_window; +struct gprs_rlcmac_rlc_ul_window; struct gprs_rlcmac_dl_tbf { struct gprs_rlcmac_tbf tbf; @@ -17,6 +21,18 @@ /* Current TS/TFI/USF allocated by the PCU: */ struct gprs_rlcmac_dl_tbf_allocation cur_alloc; + + /* Currently LLC frame being filled from RLC blocks */ + struct msgb *llc_rx_msg; + + /* Holds state of all generated in-transit RLC blocks */ + struct gprs_rlcmac_rlc_block_store *blkst; + + /* Downlink RLC Window, holds ACK state */ + union { /* easy access to parent and child */ + struct gprs_rlcmac_rlc_window *w; + struct gprs_rlcmac_rlc_dl_window *dlw; + }; }; struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre); @@ -24,6 +40,10 @@ int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf); +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); + static inline struct gprs_rlcmac_tbf *dl_tbf_as_tbf(struct gprs_rlcmac_dl_tbf *dl_tbf) { return &dl_tbf->tbf; diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am index 0612eaa..68ab832 100644 --- a/src/rlcmac/Makefile.am +++ b/src/rlcmac/Makefile.am @@ -32,9 +32,11 @@ gre.c \ llc_queue.c \ rlc_window.c \ + rlc_window_dl.c \ rlc_window_ul.c \ rlc.c \ rlcmac.c \ + rlcmac_dec.c \ rlcmac_enc.c \ rlcmac_prim.c \ sched.c \ diff --git a/src/rlcmac/rlc_window_dl.c b/src/rlcmac/rlc_window_dl.c new file mode 100644 index 0000000..a2bef5f --- /dev/null +++ b/src/rlcmac/rlc_window_dl.c @@ -0,0 +1,265 @@ +/* Uplink RLC Window as per 3GPP TS 44.060 */ +/* + * (C) 2012 Ivan Klyuchnikov + * (C) 2012 Andreas Eversberg <jolly(a)eversberg.eu> + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * + * All Rights Reserved + * + * 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 <stdint.h> +#include <stdbool.h> +#include <osmocom/core/bitvec.h> +#include <osmocom/core/logging.h> + +#include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/rlcmac_dec.h> +#include <osmocom/gprs/rlcmac/rlc_window_dl.h> +#include <osmocom/gprs/rlcmac/tbf_dl.h> + + +static inline bool gprs_rlcmac_rlc_v_n_is_state(const struct gprs_rlcmac_rlc_v_n *v_n, + int bsn, enum gprs_rlcmac_rlc_dl_bsn_state type) +{ + return v_n->v_n[bsn & mod_sns_half()] == type; +} + +static inline void gprs_rlcmac_rlc_v_n_mark(struct gprs_rlcmac_rlc_v_n *v_n, + int bsn, enum gprs_rlcmac_rlc_dl_bsn_state type) +{ + v_n->v_n[bsn & mod_sns_half()] = type; +} + +void gprs_rlcmac_rlc_v_n_reset(struct gprs_rlcmac_rlc_v_n *v_n) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(v_n->v_n); i++) + v_n->v_n[i] = GPRS_RLCMAC_RLC_DL_BSN_INVALID; +} + +/* Check for an individual frame */ +bool gprs_rlcmac_rlc_v_n_is_received(const struct gprs_rlcmac_rlc_v_n *v_n, int bsn) +{ + return gprs_rlcmac_rlc_v_n_is_state(v_n, bsn, GPRS_RLCMAC_RLC_DL_BSN_RECEIVED); +} + +enum gprs_rlcmac_rlc_dl_bsn_state gprs_rlcmac_rlc_v_n_get_state(const struct gprs_rlcmac_rlc_v_n *v_n, int bsn) +{ + return v_n->v_n[bsn & mod_sns_half()]; +} + +/* Mark a RLC frame for something */ +void gprs_rlcmac_rlc_v_n_mark_received(struct gprs_rlcmac_rlc_v_n *v_n, int bsn) +{ + return gprs_rlcmac_rlc_v_n_mark(v_n, bsn, GPRS_RLCMAC_RLC_DL_BSN_RECEIVED); +} + +void gprs_rlcmac_rlc_v_n_mark_missing(struct gprs_rlcmac_rlc_v_n *v_n, int bsn) +{ + return gprs_rlcmac_rlc_v_n_mark(v_n, bsn, GPRS_RLCMAC_RLC_DL_BSN_MISSING); +} + +/************* + * UL WINDOW +*************/ +struct gprs_rlcmac_rlc_dl_window *gprs_rlcmac_rlc_dl_window_alloc(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct gprs_rlcmac_rlc_dl_window *dlw; + + dlw = talloc_zero(dl_tbf, struct gprs_rlcmac_rlc_dl_window); + if (!dlw) + return NULL; + + gprs_rlcmac_rlc_window_constructor(rlc_dlw_as_w(dlw)); + + dlw->dl_tbf = dl_tbf; + gprs_rlcmac_rlc_dl_window_reset(dlw); + + return dlw; +} + +void gprs_rlcmac_rlc_dl_window_free(struct gprs_rlcmac_rlc_dl_window *dlw) +{ + if (!dlw) + return; + + gprs_rlcmac_rlc_window_destructor(rlc_dlw_as_w(dlw)); + talloc_free(dlw); +} + +static void gprs_rlcmac_rlc_dl_window_reset_state(struct gprs_rlcmac_rlc_dl_window *dlw) +{ + dlw->v_r = 0; + dlw->v_q = 0; +} + +void gprs_rlcmac_rlc_dl_window_reset(struct gprs_rlcmac_rlc_dl_window *dlw) +{ + gprs_rlcmac_rlc_dl_window_reset_state(dlw); + gprs_rlcmac_rlc_v_n_reset(&dlw->v_n); +} + +uint16_t gprs_rlcmac_rlc_dl_window_v_r(const struct gprs_rlcmac_rlc_dl_window *dlw) +{ + return dlw->v_r; +} + +uint16_t gprs_rlcmac_rlc_dl_window_v_q(const struct gprs_rlcmac_rlc_dl_window *dlw) +{ + return dlw->v_q; +} + +void gprs_rlcmac_rlc_dl_window_set_v_r(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t v_r) +{ + dlw->v_r = v_r; +} + +void gprs_rlcmac_rlc_dl_window_set_v_q(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t v_q) +{ + dlw->v_q = v_q; +} + +bool gprs_rlcmac_rlc_dl_window_is_in_window(const struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn) +{ + const struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w_const(dlw); + uint16_t offset_v_q; + + /* current block relative to lowest unreceived block */ + offset_v_q = (bsn - dlw->v_q) & gprs_rlcmac_rlc_window_mod_sns(w); + /* If out of window (may happen if blocks below V(Q) are received + * again. */ + return offset_v_q < gprs_rlcmac_rlc_window_ws(w); +} + +bool gprs_rlcmac_rlc_dl_window_is_received(const struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn) +{ + const struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w_const(dlw); + uint16_t offset_v_r; + + /* Offset to the end of the received window */ + offset_v_r = (dlw->v_r - 1 - bsn) & gprs_rlcmac_rlc_window_mod_sns(w); + return gprs_rlcmac_rlc_dl_window_is_in_window(dlw, bsn) && + gprs_rlcmac_rlc_v_n_is_received(&dlw->v_n, bsn) && + offset_v_r < gprs_rlcmac_rlc_window_ws(w); +} + +void gprs_rlcmac_rlc_dl_window_update_rbb(const struct gprs_rlcmac_rlc_dl_window *dlw, char *rbb) +{ + const struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w_const(dlw); + uint16_t mod_sns = gprs_rlcmac_rlc_window_mod_sns(w); + uint16_t ws = gprs_rlcmac_rlc_window_ws(w); + uint16_t ssn = gprs_rlcmac_rlc_dl_window_ssn(dlw); + + unsigned int i; + + for (i = 0; i < ws; i++) { + if (gprs_rlcmac_rlc_v_n_is_received(&dlw->v_n, ssn - 1 - i) & mod_sns) + rbb[ws - 1 - i] = 'R'; + else + rbb[ws - 1 - i] = 'I'; + } +} + +/* Update the receive block bitmap */ +uint16_t gprs_rlcmac_rlc_dl_window_update_rbb_egprs(const struct gprs_rlcmac_rlc_dl_window *dlw, uint8_t *rbb) +{ + const struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w_const(dlw); + uint16_t ws = gprs_rlcmac_rlc_window_ws(w); + uint16_t i; + uint16_t bsn; + uint16_t bitmask = 0x80; + int8_t pos = 0; + int8_t bit_pos = 0; + + for (i = 0, bsn = (dlw->v_q + 1); ((bsn < (dlw->v_r)) && (i < ws)); i++, + bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) { + if (gprs_rlcmac_rlc_v_n_is_received(&dlw->v_n, bsn)) + rbb[pos] = rbb[pos] | bitmask; + else + rbb[pos] = rbb[pos] & (~bitmask); + bitmask = bitmask >> 1; + bit_pos++; + bit_pos = bit_pos % 8; + if (bit_pos == 0) { + pos++; + bitmask = 0x80; + } + } + return i; +} + +void gprs_rlcmac_rlc_dl_window_raise_v_r_to(struct gprs_rlcmac_rlc_dl_window *dlw, int moves) +{ + struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w(dlw); + dlw->v_r = gprs_rlcmac_rlc_window_mod_sns_bsn(w, dlw->v_r + moves); +} + +void gprs_rlcmac_rlc_dl_window_raise_v_q_to(struct gprs_rlcmac_rlc_dl_window *dlw, int incr) +{ + struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w(dlw); + dlw->v_q = gprs_rlcmac_rlc_window_mod_sns_bsn(w, dlw->v_q + incr); +} + +/* Raise V(R) to highest received sequence number not received. */ +void gprs_rlcmac_rlc_dl_window_raise_v_r(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn) +{ + struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w(dlw); + uint16_t offset_v_r; + + offset_v_r = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1 - gprs_rlcmac_rlc_dl_window_v_r(dlw)); + /* Positive offset, so raise. */ + if (offset_v_r < (gprs_rlcmac_rlc_window_sns(w) >> 1)) { + while (offset_v_r--) { + if (offset_v_r) /* all except the received block */ + gprs_rlcmac_rlc_v_n_mark_missing(&dlw->v_n, gprs_rlcmac_rlc_dl_window_v_r(dlw)); + gprs_rlcmac_rlc_dl_window_raise_v_r_to(dlw, 1); + } + LOGRLCMAC(LOGL_DEBUG, "- Raising V(R) to %d\n", gprs_rlcmac_rlc_dl_window_v_r(dlw)); + } +} + +/* + * Raise V(Q) if possible. This is looped until there is a gap + * (non received block) or the window is empty. + */ +uint16_t gprs_rlcmac_rlc_dl_window_raise_v_q(struct gprs_rlcmac_rlc_dl_window *dlw) +{ + struct gprs_rlcmac_rlc_window *w = rlc_dlw_as_w(dlw); + uint16_t count = 0; + + while (gprs_rlcmac_rlc_dl_window_v_q(dlw) != gprs_rlcmac_rlc_dl_window_v_r(dlw)) { + if (!gprs_rlcmac_rlc_v_n_is_received(&dlw->v_n, gprs_rlcmac_rlc_dl_window_v_q(dlw))) + break; + LOGRLCMAC(LOGL_DEBUG, "- Taking block %d out, raising V(Q) to %d\n", + gprs_rlcmac_rlc_dl_window_v_q(dlw), + gprs_rlcmac_rlc_window_mod_sns_bsn(w, gprs_rlcmac_rlc_dl_window_v_q(dlw) + 1)); + gprs_rlcmac_rlc_dl_window_raise_v_q_to(dlw, 1); + count += 1; + } + + return count; +} + +void gprs_rlcmac_rlc_dl_window_receive_bsn(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn) +{ + gprs_rlcmac_rlc_v_n_mark_received(&dlw->v_n, bsn); + gprs_rlcmac_rlc_dl_window_raise_v_r(dlw, bsn); +} + +bool gprs_rlcmac_rlc_dl_window_invalidate_bsn(struct gprs_rlcmac_rlc_dl_window *dlw, uint16_t bsn) +{ + bool was_valid = gprs_rlcmac_rlc_v_n_is_received(&dlw->v_n, bsn); + gprs_rlcmac_rlc_v_n_mark_missing(&dlw->v_n, bsn); + + return was_valid; +} diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c index 19d9944..5b72690 100644 --- a/src/rlcmac/rlcmac.c +++ b/src/rlcmac/rlcmac.c @@ -26,6 +26,7 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #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/tbf_ul_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> #include <osmocom/gprs/rlcmac/gre.h> @@ -302,29 +303,40 @@ return rc; } -static int gprs_rlcmac_handle_gprs_dl_data_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim) +static int gprs_rlcmac_handle_gprs_dl_data_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, + enum gprs_rlcmac_coding_scheme cs) { const struct gprs_rlcmac_rlc_dl_data_header *data_hdr = (const struct gprs_rlcmac_rlc_dl_data_header *)rlcmac_prim->l1ctl.pdch_data_ind.data; struct gprs_rlcmac_dl_tbf *dl_tbf; + struct gprs_rlcmac_rlc_data_info rlc_dec; + int rc; - dl_tbf = gprs_rlcmac_find_dl_tbf_by_tfi(data_hdr->tfi); + rc = gprs_rlcmac_rlc_parse_dl_data_header(&rlc_dec, rlcmac_prim->l1ctl.pdch_data_ind.data, cs); + if (rc < 0) { + LOGRLCMAC(LOGL_ERROR, "Got %s DL data block but header parsing has failed\n", + gprs_rlcmac_mcs_name(cs)); + return rc; + } + + dl_tbf = gprs_rlcmac_find_dl_tbf_by_tfi(rlc_dec.tfi); if (!dl_tbf) { LOGPTBFDL(dl_tbf, LOGL_INFO, "Rx DL data for unknown dl_tfi=%u\n", data_hdr->tfi); return -ENOENT; } LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Rx new DL data\n"); - return 0; + rc = gprs_rlcmac_dl_tbf_rcv_data_block(dl_tbf, &rlc_dec, rlcmac_prim->l1ctl.pdch_data_ind.data); + return rc; } int gprs_rlcmac_handle_gprs_dl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, - enum gprs_rlcmac_coding_scheme cs) + enum gprs_rlcmac_coding_scheme cs) { const struct gprs_rlcmac_rlc_dl_data_header *data_hdr = (const struct gprs_rlcmac_rlc_dl_data_header *)rlcmac_prim->l1ctl.pdch_data_ind.data; /* Check block content (data vs ctrl) based on Payload Type: TS 44.060 10.4.7 */ switch ((enum gprs_rlcmac_payload_type)data_hdr->pt) { case GPRS_RLCMAC_PT_DATA_BLOCK: /* "Contains an RLC data block" */ - return gprs_rlcmac_handle_gprs_dl_data_block(rlcmac_prim); + return gprs_rlcmac_handle_gprs_dl_data_block(rlcmac_prim, cs); case GPRS_RLCMAC_PT_CONTROL_BLOCK: /* "Contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC * control header" */ diff --git a/src/rlcmac/rlcmac_dec.c b/src/rlcmac/rlcmac_dec.c new file mode 100644 index 0000000..cd44fc8 --- /dev/null +++ b/src/rlcmac/rlcmac_dec.c @@ -0,0 +1,343 @@ +/* RLC/MAC encoding helpers, 3GPP TS 44.060 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <
http://www.gnu.org/licenses/
>. + * + */ + +#include <stdint.h> + +#include <osmocom/core/logging.h> + +#include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/rlcmac_dec.h> +#include <osmocom/gprs/rlcmac/rlc.h> + +#define LENGTH_TO_END 255 +/*! + * \returns num extensions fields (num frames == offset) on success, + * -errno otherwise. + */ +static int parse_extensions_egprs(const uint8_t *data, unsigned int data_len, + unsigned int *offs, bool is_last_block, + struct gprs_rlcmac_rlc_llc_chunk *chunks, + unsigned int chunks_size) +{ + const struct gprs_rlcmac_rlc_li_field_egprs *li; + uint8_t e; + unsigned int num_chunks = 0; + + e = 0; + while (!e) { + if (*offs > data_len) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI extended, but no more data\n"); + return -EINVAL; + } + + /* get new E */ + li = (struct gprs_rlcmac_rlc_li_field_egprs *)&data[*offs]; + e = li->e; + *offs += 1; + + if (!chunks) + continue; + + if (num_chunks == chunks_size) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI extended, but no more chunks possible\n"); + return -ENOSPC; + } + if (li->li == 0 && num_chunks == 0) { + /* TS 44.060, table 10.4.14a.1, row 2a */ + /* TS 44.060, table 10.4.14a.1, row 4 */ + chunks[num_chunks].length = 0; + chunks[num_chunks].is_complete = true; + } else if (li->li == 127 && li->e == 1) { + /* TS 44.060, table 10.4.14a.1, row 3 & 5 */ + /* only filling bytes left */ + LOGRLCMAC(LOGL_DEBUG, "DL DATA LI contains only filling bytes with extension octet: LI=%d, E=%d, count=%d\n", + li->li, li->e, num_chunks); + break; + } else if (li->li > 0) { + /* TS 44.060, table 10.4.14a.1, row 1 & 2b */ + chunks[num_chunks].length = li->li; + chunks[num_chunks].is_complete = true; + } else { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI contains invalid extension octet: LI=%d, E=%d, count=%d\n", + li->li, li->e, num_chunks); + return -EINVAL; + } + + LOGRLCMAC(LOGL_DEBUG, "DL DATA LI contains extension octet: LI=%d, E=%d, count=%d\n", + li->li, li->e, num_chunks); + num_chunks += 1; + + if (e == 1) { + /* There is space after the last chunk, add a final one */ + if (num_chunks == chunks_size) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI possibly extended, but no more chunks possible\n"); + return -ENOSPC; + } + + chunks[num_chunks].length = LENGTH_TO_END; + chunks[num_chunks].is_complete = is_last_block; + num_chunks += 1; + } + } + + return num_chunks; +} + +static int parse_extensions_gprs(const uint8_t *data, unsigned int data_len, + unsigned int *offs, bool is_last_block, + struct gprs_rlcmac_rlc_llc_chunk *chunks, + unsigned int chunks_size) +{ + const struct gprs_rlcmac_rlc_li_field *li; + uint8_t m, e; + unsigned int num_chunks = 0; + + e = 0; + while (!e) { + if (*offs > data_len) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI extended, but no more data\n"); + return -EINVAL; + } + + /* get new E */ + li = (const struct gprs_rlcmac_rlc_li_field *)&data[*offs]; + e = li->e; + m = li->m; + *offs += 1; + + if (li->li == 0) { + /* TS 44.060, 10.4.14, par 6 */ + e = 1; + m = 0; + } + + /* TS 44.060, table 10.4.13.1 */ + if (m == 0 && e == 0) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA ignored, because M='0' and E='0'.\n"); + return 0; + } + + if (!chunks) + continue; + + if (num_chunks == chunks_size) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI extended, but no more chunks possible\n"); + return -ENOSPC; + } + + if (li->li == 0) + /* e is 1 here */ + chunks[num_chunks].length = LENGTH_TO_END; + else + chunks[num_chunks].length = li->li; + + chunks[num_chunks].is_complete = li->li || is_last_block; + + LOGRLCMAC(LOGL_DEBUG, "DL DATA LI contains extension octet: LI=%d, M=%d, E=%d, count=%d\n", + li->li, li->m, li->e, num_chunks); + num_chunks += 1; + + if (e == 1 && m == 1) { + if (num_chunks == chunks_size) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA LI extended, but no more chunks possible\n"); + return -ENOSPC; + } + /* TS 44.060, 10.4.13.1, row 4 */ + chunks[num_chunks].length = LENGTH_TO_END; + chunks[num_chunks].is_complete = is_last_block; + num_chunks += 1; + } + } + + return num_chunks; +} + +int gprs_rlcmac_rlc_data_from_dl_data(const struct gprs_rlcmac_rlc_block_info *rdbi, + enum gprs_rlcmac_coding_scheme cs, + const uint8_t *data, + struct gprs_rlcmac_rlc_llc_chunk *chunks, + unsigned int chunks_size) +{ + uint8_t e; + unsigned int data_len = rdbi->data_len; + int num_chunks = 0, i; + unsigned int offs = 0; + bool is_last_block = (rdbi->cv == 0); + + if (!chunks) + chunks_size = 0; + + e = rdbi->e; + if (e) { + if (chunks_size > 0) { + /* Block without LI means it only contains data of one LLC PDU */ + chunks[num_chunks].offset = offs; + chunks[num_chunks].length = LENGTH_TO_END; + chunks[num_chunks].is_complete = is_last_block; + num_chunks += 1; + } else if (chunks) { + LOGRLCMAC(LOGL_NOTICE, "No extension, but no more chunks possible\n"); + return -ENOSPC; + } + } else if (gprs_rlcmac_mcs_is_edge(cs)) { + /* if E is not set (LI follows), EGPRS */ + num_chunks = parse_extensions_egprs(data, data_len, &offs, + is_last_block, + chunks, chunks_size); + } else { + /* if E is not set (LI follows), GPRS */ + num_chunks = parse_extensions_gprs(data, data_len, &offs, + is_last_block, + chunks, chunks_size); + } + + if (num_chunks < 0) + return num_chunks; + + if (chunks_size == 0) + return num_chunks; + + /* LLC */ + for (i = 0; i < num_chunks; i++) { + chunks[i].offset = offs; + if (chunks[i].length == LENGTH_TO_END) { + if (offs == data_len) { + /* There is no place for an additional chunk, + * so drop it (this may happen with EGPRS since + * there is no M flag. */ + num_chunks -= 1; + break; + } + chunks[i].length = data_len - offs; + } + offs += chunks[i].length; + if (offs > data_len) { + LOGRLCMAC(LOGL_NOTICE, "DL DATA out of block border, " + "chunk idx: %d, offset: %u, size: %d, data_len: %u\n", + i, offs, chunks[i].length, data_len); + return -EINVAL; + } + } + + return num_chunks; +} + +int gprs_rlcmac_rlc_parse_dl_data_header_gprs(struct gprs_rlcmac_rlc_data_info *rlc, + const uint8_t *data, enum gprs_rlcmac_coding_scheme cs) +{ + const struct gprs_rlcmac_rlc_dl_data_header *gprs = (const struct gprs_rlcmac_rlc_dl_data_header *)data; + unsigned int cur_bit = 0; + + gprs_rlcmac_rlc_data_info_init_dl(rlc, cs, false); + + rlc->usf = gprs->usf; + rlc->es_p = gprs->s_p; + rlc->rrbp = gprs->rrbp; + rlc->tfi = gprs->tfi; + rlc->pr = gprs->pr; + + rlc->num_data_blocks = 1; + rlc->block_info[0].cv = gprs->fbi ? 0 : 0xff; + rlc->block_info[0].pi = 0; + rlc->block_info[0].bsn = gprs->bsn; + rlc->block_info[0].e = gprs->e; + rlc->block_info[0].ti = 0; + rlc->block_info[0].spb = 0; + cur_bit += rlc->data_offs_bits[0]; + /* skip data area */ + cur_bit += gprs_rlcmac_mcs_max_data_block_bytes(cs) * 8; + + return cur_bit; +} + +int gprs_rlcmac_rlc_parse_dl_data_header(struct gprs_rlcmac_rlc_data_info *rlc, + const uint8_t *data, + enum gprs_rlcmac_coding_scheme cs) +{ + unsigned int cur_bit = 0; + + switch (gprs_rlcmac_mcs_header_type(cs)) { + case GPRS_RLCMAC_HEADER_GPRS_DATA: + cur_bit = gprs_rlcmac_rlc_parse_dl_data_header_gprs(rlc, data, cs); + break; + case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3: + case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2: + case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1: + /* TODO: EGPRS. See osmo-pcu.git rlc_parse_ul_data_header_egprs_type_*() */ + default: + LOGRLCMAC(LOGL_ERROR, "Decoding of DL %s data blocks not yet supported.\n", + gprs_rlcmac_mcs_name(cs)); + return -ENOTSUP; + }; + + return cur_bit; +} + +/** + * Copy LSB bitstream RLC data block to byte aligned buffer. + * + * Note that the bitstream is encoded in LSB first order, so the two octets + * 654321xx xxxxxx87 contain the octet 87654321 starting at bit position 3 + * (LSB has bit position 1). This is a different order than the one used by + * CSN.1. + * + * \param data_block_idx The block index, 0..1 for header type 1, 0 otherwise + * \param src A pointer to the start of the RLC block (incl. the header) + * \param buffer A data area of a least the size of the RLC block + * \returns the number of bytes copied + */ +unsigned int gprs_rlcmac_rlc_copy_to_aligned_buffer( + const struct gprs_rlcmac_rlc_data_info *rlc, + unsigned int data_block_idx, + const uint8_t *src, uint8_t *buffer) +{ + unsigned int hdr_bytes; + unsigned int extra_bits; + unsigned int i; + + uint8_t c, last_c; + uint8_t *dst; + const struct gprs_rlcmac_rlc_block_info *rdbi; + + OSMO_ASSERT(data_block_idx < rlc->num_data_blocks); + rdbi = &rlc->block_info[data_block_idx]; + + hdr_bytes = rlc->data_offs_bits[data_block_idx] >> 3; + extra_bits = (rlc->data_offs_bits[data_block_idx] & 7); + + if (extra_bits == 0) { + /* It is aligned already */ + memmove(buffer, src + hdr_bytes, rdbi->data_len); + return rdbi->data_len; + } + + dst = buffer; + src = src + hdr_bytes; + last_c = *(src++); + + for (i = 0; i < rdbi->data_len; i++) { + c = src[i]; + *(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits)); + last_c = c; + } + + return rdbi->data_len; +} diff --git a/src/rlcmac/rlcmac_enc.c b/src/rlcmac/rlcmac_enc.c index 723fdc9..e1ea990 100644 --- a/src/rlcmac/rlcmac_enc.c +++ b/src/rlcmac/rlcmac_enc.c @@ -200,15 +200,15 @@ void gprs_rlcmac_rlc_data_to_ul_append_egprs_li_padding(const struct gprs_rlcmac_rlc_block_info *rdbi, int *offset, int *num_chunks, uint8_t *data_block) { - struct gprs_rlcmacrlc_li_field_egprs *li; - struct gprs_rlcmacrlc_li_field_egprs *prev_li; + struct gprs_rlcmac_rlc_li_field_egprs *li; + struct gprs_rlcmac_rlc_li_field_egprs *prev_li; uint8_t *delimiter, *data; LOGRLCMAC(LOGL_DEBUG, "Adding LI=127 to signal padding\n"); data = data_block + *offset; delimiter = data_block + *num_chunks; - prev_li = (struct gprs_rlcmacrlc_li_field_egprs *)(*num_chunks ? delimiter - 1 : NULL); + prev_li = (struct gprs_rlcmac_rlc_li_field_egprs *)(*num_chunks ? delimiter - 1 : NULL); /* we don't have more LLC frames */ /* We will have to add another chunk with filling octets */ @@ -217,7 +217,7 @@ memmove(delimiter + 1, delimiter, data - delimiter); /* set filling bytes extension */ - li = (struct gprs_rlcmacrlc_li_field_egprs *)delimiter; + li = (struct gprs_rlcmac_rlc_li_field_egprs *)delimiter; li->e = 1; li->li = 127; diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c index ae6e7ac..a5b342a 100644 --- a/src/rlcmac/tbf_dl.c +++ b/src/rlcmac/tbf_dl.c @@ -22,6 +22,8 @@ #include <osmocom/gprs/rlcmac/tbf_dl.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/rlc_window_dl.h> +#include <osmocom/gprs/rlcmac/rlcmac_dec.h> struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre) { @@ -40,7 +42,15 @@ dl_tbf->tbf.nr = g_ctx->next_dl_tbf_nr++; + dl_tbf->dlw = gprs_rlcmac_rlc_dl_window_alloc(dl_tbf); + OSMO_ASSERT(dl_tbf->dlw); + + + dl_tbf->blkst = gprs_rlcmac_rlc_block_store_alloc(dl_tbf); + OSMO_ASSERT(dl_tbf->blkst); + return dl_tbf; + err_tbf_destruct: gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf)); talloc_free(dl_tbf); @@ -52,6 +62,15 @@ if (!dl_tbf) return; + msgb_free(dl_tbf->llc_rx_msg); + dl_tbf->llc_rx_msg = NULL; + + gprs_rlcmac_rlc_block_store_free(dl_tbf->blkst); + dl_tbf->blkst = NULL; + + gprs_rlcmac_rlc_dl_window_free(dl_tbf->dlw); + dl_tbf->dlw = NULL; + //gprs_rlcmac_tbf_dl_fsm_destructor(dl_tbf); gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf)); @@ -84,3 +103,161 @@ dl_tbf->cur_alloc.dl_tfi); return gprs_rlcmac_prim_call_down_cb(rlcmac_prim); } + +/* + * Store received block data in LLC message(s) and forward to SGSN + * if complete. + */ +static int gprs_rlcmac_dl_tbf_assemble_forward_llc(struct gprs_rlcmac_dl_tbf *dl_tbf, + const struct gprs_rlcmac_rlc_block *blk) +{ + const uint8_t *data = &blk->buf[0]; + uint8_t len = blk->len; + const struct gprs_rlcmac_rlc_block_info *rdbi = &blk->block_info; + enum gprs_rlcmac_coding_scheme cs = blk->cs_last; + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + + struct gprs_rlcmac_rlc_llc_chunk frames[16], *frame; + int i, num_frames = 0; + int rc = 0; + + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Assembling frames: (len=%d)\n", len); + + if (!dl_tbf->llc_rx_msg) { + rlcmac_prim = gprs_rlcmac_prim_alloc_grr_unitdata_ind( + dl_tbf->tbf.gre->tlli, NULL, GPRS_RLCMAC_LLC_PDU_MAX_LEN); + dl_tbf->llc_rx_msg = rlcmac_prim->oph.msg; + dl_tbf->llc_rx_msg->l3h = dl_tbf->llc_rx_msg->tail; + } else { + rlcmac_prim = msgb_rlcmac_prim(dl_tbf->llc_rx_msg); + } + + num_frames = gprs_rlcmac_rlc_data_from_dl_data(rdbi, cs, data, + &frames[0], ARRAY_SIZE(frames)); + + /* create LLC frames */ + for (i = 0; i < num_frames; i++) { + frame = frames + i; + + if (frame->length) { + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Frame %d " + "starts at offset %d, " + "length=%d, is_complete=%d\n", + i + 1, frame->offset, frame->length, + frame->is_complete); + + memcpy(msgb_put(dl_tbf->llc_rx_msg, frame->length), + data + frame->offset, frame->length); + } + + if (frame->is_complete) { + /* send frame to upper layers: */ + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "complete UL frame len=%d\n", msgb_l3len(dl_tbf->llc_rx_msg)); + rlcmac_prim->grr.ll_pdu = msgb_l3(dl_tbf->llc_rx_msg); + rlcmac_prim->grr.ll_pdu_len = msgb_l3len(dl_tbf->llc_rx_msg); + + /* ownserhsip of dl_tbf->llc_rx_msg transferred here: */ + rc = gprs_rlcmac_prim_call_up_cb(rlcmac_prim); + dl_tbf->llc_rx_msg = NULL; + } + } + + return rc; +} + +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) +{ + const struct gprs_rlcmac_rlc_block_info *rdbi; + struct gprs_rlcmac_rlc_block *block; + + const uint16_t ws = gprs_rlcmac_rlc_window_ws(dl_tbf->w); + + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "DL DATA TFI=%d received (V(Q)=%d .. V(R)=%d)\n", + rlc->tfi, + gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw), + gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw)); + + unsigned int block_idx; + + /* Loop over num_blocks */ + for (block_idx = 0; block_idx < rlc->num_data_blocks; block_idx++) { + uint8_t *rlc_data; + rdbi = &rlc->block_info[block_idx]; + + LOGPTBFDL(dl_tbf, LOGL_DEBUG, + "Got %s RLC data block: FBI=%u, BSN=%d, SPB=%d, S/P=%d RRBP=%u, E=%d, bitoffs=%d\n", + gprs_rlcmac_mcs_name(rlc->cs), + rdbi->cv == 0, rdbi->bsn, rdbi->spb, + rlc->es_p, rlc->rrbp, rdbi->e, + rlc->data_offs_bits[block_idx]); + + /* Check whether the block needs to be decoded */ + + if (!gprs_rlcmac_rlc_dl_window_is_in_window(dl_tbf->dlw, rdbi->bsn)) { + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d out of window %d..%d (it's normal)\n", + rdbi->bsn, + gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw), + gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) + ws - 1)); + continue; + } else if (gprs_rlcmac_rlc_v_n_is_received(&dl_tbf->dlw->v_n, rdbi->bsn)) { + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d already received\n", rdbi->bsn); + continue; + } + + /* Store block and meta info to BSN buffer */ + + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "BSN %d storing in window (%d..%d)\n", + rdbi->bsn, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw), + gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) + ws - 1)); + block = gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, rdbi->bsn); + OSMO_ASSERT(rdbi->data_len <= sizeof(block->buf)); + rlc_data = &(block->buf[0]); + + block->block_info = *rdbi; + block->cs_last = rlc->cs; + block->len = gprs_rlcmac_rlc_copy_to_aligned_buffer(rlc, block_idx, + data, rlc_data); + + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "data_length=%d, data=%s\n", + block->len, osmo_hexdump(rlc_data, block->len)); + + gprs_rlcmac_rlc_dl_window_receive_bsn(dl_tbf->dlw, rdbi->bsn); + } + + /* Raise V(Q) if possible, and retrieve LLC frames from blocks. + * This is looped until there is a gap (non received block) or + * the window is empty.*/ + const uint16_t v_q_beg = gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw); + const uint16_t count = gprs_rlcmac_rlc_dl_window_raise_v_q(dl_tbf->dlw); + + /* Retrieve LLC frames from blocks that are ready */ + for (uint16_t i = 0; i < count; ++i) { + uint16_t index = gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, v_q_beg + i); + gprs_rlcmac_dl_tbf_assemble_forward_llc(dl_tbf, gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, index)); + } + + /* Last frame in buffer: */ + uint16_t last = gprs_rlcmac_rlc_window_mod_sns_bsn(dl_tbf->w, + gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw) - 1); + block = gprs_rlcmac_rlc_block_store_get_block(dl_tbf->blkst, last); + rdbi = &block->block_info; + + /* Check if we already received all data TBF had to send: */ + if (//this->state_is(TBF_ST_FLOW) && /* still in flow state */ + (gprs_rlcmac_rlc_dl_window_v_q(dl_tbf->dlw) == gprs_rlcmac_rlc_dl_window_v_r(dl_tbf->dlw)) && /* if complete */ + block->len) { /* if there was ever a last block received */ + LOGPTBFDL(dl_tbf, LOGL_DEBUG, + "No gaps in received block, last block: BSN=%d FBI=%d\n", + rdbi->bsn, rdbi->cv == 0); + if (rdbi->cv == 0) { + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Finished with DL TBF\n"); + osmo_fsm_inst_dispatch(dl_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD, NULL); + } + } + + /* TODO: if RRBP contains valid data, schedule a DL ACK/NACK. */ + + return 0; +} diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index 00f1740..283227b 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -186,9 +186,8 @@ printf("%s(): Rx %s TLLI=0x%08x\n", __func__, pdu_name, rlcmac_prim->gmmrr.page_ind.tlli); break; case OSMO_GPRS_RLCMAC_SAP_GRR: - printf("%s(): Rx %s TLLI=0x%08x SAPI=%s ll=[%s]\n", __func__, pdu_name, + printf("%s(): Rx %s TLLI=0x%08x ll=[%s]\n", __func__, pdu_name, rlcmac_prim->grr.tlli, - get_value_string(osmo_gprs_rlcmac_llc_sapi_names, rlcmac_prim->grr.unitdata_req.sapi), osmo_hexdump(rlcmac_prim->grr.ll_pdu, rlcmac_prim->grr.ll_pdu_len)); break; default: diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index 89a970c..41f88a7 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -37,3 +37,17 @@ DLGLOBAL INFO DL_TBF{NEW}: state_chg to FLOW 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) 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 +DLGLOBAL DEBUG - Taking block 0 out, raising V(Q) to 1 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Assembling frames: (len=20) +DLGLOBAL DEBUG DL DATA LI contains extension octet: LI=6, M=0, E=1, count=0 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Frame 1 starts at offset 1, length=6, is_complete=1 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) complete UL frame len=6 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) No gaps in received block, last block: BSN=0 FBI=1 +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 diff --git a/tests/rlcmac/rlcmac_prim_test.ok b/tests/rlcmac/rlcmac_prim_test.ok index 808ce21..49b02b9 100644 --- a/tests/rlcmac/rlcmac_prim_test.ok +++ b/tests/rlcmac/rlcmac_prim_test.ok @@ -5,4 +5,5 @@ === test_ul_tbf_attach end === === test_dl_tbf_ccch_assign start === test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_DL_TBF.request dl_tbf_nr=0 dl_slotmask=0x80 dl_tfi=0 +test_rlcmac_prim_up_cb(): Rx GRR-UNITDATA.indication TLLI=0x00000001 ll=[43 c0 01 2b 2b 2b ] === test_dl_tbf_ccch_assign end === -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31320
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: Ie7535606916c0800c0e1bd9555be022c81ea257d Gerrit-Change-Number: 31320 Gerrit-PatchSet: 3 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[L] Change in libosmo-gprs[master]: rlcmac: Introduce DL TBF creation through PCH ImmAss
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31286
) Change subject: rlcmac: Introduce DL TBF creation through PCH ImmAss ...................................................................... rlcmac: Introduce DL TBF creation through PCH ImmAss This patch only introduces the gprs_rlcmac_dl_tbf subclass and allocates/frees it based on PCH ImmAss, and adds initial paths for received blocks. It also provides a unit test to showcase the scenario. Change-Id: I7f98e3456ef35d80becdad3481afeb771457b0ef --- M include/osmocom/gprs/rlcmac/Makefile.am M include/osmocom/gprs/rlcmac/coding_scheme.h M include/osmocom/gprs/rlcmac/gre.h M include/osmocom/gprs/rlcmac/rlcmac.h M include/osmocom/gprs/rlcmac/rlcmac_private.h A include/osmocom/gprs/rlcmac/tbf_dl.h A include/osmocom/gprs/rlcmac/tbf_dl_fsm.h M include/osmocom/gprs/rlcmac/types_private.h M src/rlcmac/Makefile.am M src/rlcmac/coding_scheme.c M src/rlcmac/gre.c M src/rlcmac/misc.c M src/rlcmac/rlcmac.c M src/rlcmac/rlcmac_prim.c A src/rlcmac/tbf_dl.c A src/rlcmac/tbf_dl_fsm.c M tests/rlcmac/Makefile.am M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err M tests/rlcmac/rlcmac_prim_test.ok 20 files changed, 722 insertions(+), 8 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 3f04ee3..e91f342 100644 --- a/include/osmocom/gprs/rlcmac/Makefile.am +++ b/include/osmocom/gprs/rlcmac/Makefile.am @@ -10,6 +10,8 @@ rlcmac_private.h \ sched.h \ tbf.h \ + tbf_dl.h \ + tbf_dl_fsm.h \ tbf_ul.h \ tbf_ul_fsm.h \ tbf_ul_ass_fsm.h \ diff --git a/include/osmocom/gprs/rlcmac/coding_scheme.h b/include/osmocom/gprs/rlcmac/coding_scheme.h index 31692a5..5b71e73 100644 --- a/include/osmocom/gprs/rlcmac/coding_scheme.h +++ b/include/osmocom/gprs/rlcmac/coding_scheme.h @@ -61,6 +61,7 @@ uint8_t gprs_rlcmac_mcs_chan_code(enum gprs_rlcmac_coding_scheme cs); enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_by_size_ul(unsigned size); +enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_by_size_dl(unsigned size); enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_gprs_by_num(unsigned num); enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_egprs_by_num(unsigned num); bool gprs_rlcmac_mcs_is_valid(enum gprs_rlcmac_coding_scheme cs); diff --git a/include/osmocom/gprs/rlcmac/gre.h b/include/osmocom/gprs/rlcmac/gre.h index ea50ddb..ca5f773 100644 --- a/include/osmocom/gprs/rlcmac/gre.h +++ b/include/osmocom/gprs/rlcmac/gre.h @@ -4,6 +4,7 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/llc_queue.h> +struct gprs_rlcmac_dl_tbf; struct gprs_rlcmac_ul_tbf; struct gprs_rlcmac_entity { @@ -12,6 +13,7 @@ struct gprs_rlcmac_llc_queue *llc_queue; + struct gprs_rlcmac_dl_tbf *dl_tbf; struct gprs_rlcmac_ul_tbf *ul_tbf; }; diff --git a/include/osmocom/gprs/rlcmac/rlcmac.h b/include/osmocom/gprs/rlcmac/rlcmac.h index 2de5687..64a2073 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac.h +++ b/include/osmocom/gprs/rlcmac/rlcmac.h @@ -18,6 +18,7 @@ enum osmo_gprs_rlcmac_log_cat { OSMO_GPRS_RLCMAC_LOGC_RLCMAC, OSMO_GPRS_RLCMAC_LOGC_TBFUL, + OSMO_GPRS_RLCMAC_LOGC_TBFDL, _OSMO_GPRS_RLCMAC_LOGC_MAX, }; diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h index f35b69a..36c634c 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_private.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h @@ -28,6 +28,16 @@ struct gprs_rlcmac_ul_tbf_allocation_ts ts[8]; }; +struct gprs_rlcmac_dl_tbf_allocation_ts { + bool allocated; +}; + +struct gprs_rlcmac_dl_tbf_allocation { + uint8_t dl_tfi; + uint8_t num_ts; /* number of allocated TS */ + struct gprs_rlcmac_dl_tbf_allocation_ts ts[8]; +}; + extern int g_rlcmac_log_cat[_OSMO_GPRS_RLCMAC_LOGC_MAX]; #define LOGRLCMAC(lvl, fmt, args...) LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_RLCMAC], lvl, fmt, ## args) @@ -56,13 +66,17 @@ struct llist_head gre_list; /* contains (struct gprs_rlcmac_entity)->entry */ uint8_t next_ul_tbf_nr; + uint8_t next_dl_tbf_nr; }; extern struct gprs_rlcmac_ctx *g_ctx; /* rlcmac.c */ struct gprs_rlcmac_entity *gprs_rlcmac_find_entity_by_tlli(uint32_t tlli); +struct gprs_rlcmac_dl_tbf *gprs_rlcmac_find_dl_tbf_by_tfi(uint8_t dl_tfi); int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia); +int gprs_rlcmac_handle_gprs_dl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, + enum gprs_rlcmac_coding_scheme cs); /* rlcmac_prim.c */ int gprs_rlcmac_prim_call_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim); diff --git a/include/osmocom/gprs/rlcmac/tbf_dl.h b/include/osmocom/gprs/rlcmac/tbf_dl.h new file mode 100644 index 0000000..56b0e8c --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf_dl.h @@ -0,0 +1,52 @@ +/* Downlink TBF, 3GPP TS 44.060 */ +#pragma once + +#include <inttypes.h> + +#include <osmocom/core/msgb.h> + +#include <osmocom/gprs/rlcmac/tbf.h> +#include <osmocom/gprs/rlcmac/tbf_dl_fsm.h> +#include <osmocom/gprs/rlcmac/coding_scheme.h> +#include <osmocom/gprs/rlcmac/sched.h> +#include <osmocom/gprs/rlcmac/rlcmac_private.h> + +struct gprs_rlcmac_dl_tbf { + struct gprs_rlcmac_tbf tbf; + struct gprs_rlcmac_tbf_dl_fsm_ctx state_fsm; + + /* Current TS/TFI/USF allocated by the PCU: */ + struct gprs_rlcmac_dl_tbf_allocation cur_alloc; +}; + +struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre); +void gprs_rlcmac_dl_tbf_free(struct gprs_rlcmac_dl_tbf *dl_tbf); + +int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf); + +static inline struct gprs_rlcmac_tbf *dl_tbf_as_tbf(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + return &dl_tbf->tbf; +} + +static inline const struct gprs_rlcmac_tbf *dl_tbf_as_tbf_const(const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + return &dl_tbf->tbf; +} + +static inline struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf(struct gprs_rlcmac_tbf *tbf) +{ + OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_DL); + return (struct gprs_rlcmac_dl_tbf *)tbf; +} + +static inline const struct gprs_rlcmac_dl_tbf *tbf_as_dl_tbf_const(struct gprs_rlcmac_tbf *tbf) +{ + OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_TBF_DIR_DL); + return (const struct gprs_rlcmac_dl_tbf *)tbf; +} + +#define LOGPTBFDL(dl_tbf, lvl, fmt, args...) \ + LOGP(g_rlcmac_log_cat[OSMO_GPRS_RLCMAC_LOGC_TBFUL], lvl, "TBF(DL:NR-%" PRIu8 ":TLLI-%08x) " fmt, \ + (dl_tbf)->tbf.nr, (dl_tbf)->tbf.gre->tlli, \ + ## args) diff --git a/include/osmocom/gprs/rlcmac/tbf_dl_fsm.h b/include/osmocom/gprs/rlcmac/tbf_dl_fsm.h new file mode 100644 index 0000000..5458b68 --- /dev/null +++ b/include/osmocom/gprs/rlcmac/tbf_dl_fsm.h @@ -0,0 +1,35 @@ +/* Uplink TBF, 3GPP TS 44.060 */ +#pragma once + +#include <osmocom/core/fsm.h> + +#include <osmocom/gprs/rlcmac/rlcmac_private.h> + +struct gprs_rlcmac_dl_tbf; + +enum gprs_rlcmac_tbf_dl_fsm_states { + GPRS_RLCMAC_TBF_DL_ST_NEW = 0, /* new created TBF */ + GPRS_RLCMAC_TBF_DL_ST_FLOW, /* RLC/MAC flow, resource needed */ + GPRS_RLCMAC_TBF_DL_ST_FINISHED, /* flow finished, wait for release */ +}; + +struct gprs_rlcmac_tbf_dl_fsm_ctx { + struct osmo_fsm_inst *fi; + union { /* back pointer. union used to easily access superclass from ctx */ + struct gprs_rlcmac_tbf *tbf; + struct gprs_rlcmac_dl_tbf *dl_tbf; + }; +}; + +enum tbf_dl_fsm_event { + GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD, + GPRS_RLCMAC_TBF_UL_EV_DL_ASS_COMPL, +}; + +int gprs_rlcmac_tbf_dl_fsm_init(void); +void gprs_rlcmac_tbf_dl_fsm_set_log_cat(int logcat); + +int gprs_rlcmac_tbf_dl_fsm_constructor(struct gprs_rlcmac_dl_tbf *dl_tbf); +void gprs_rlcmac_tbf_dl_fsm_destructor(struct gprs_rlcmac_dl_tbf *dl_tbf); + +enum gprs_rlcmac_tbf_dl_fsm_states gprs_rlcmac_tbf_dl_state(const 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 10852cf..8450d08 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 Section 10.4.7 Table 10.4.7.1: Payload Type field */ +enum gprs_rlcmac_payload_type { + GPRS_RLCMAC_PT_DATA_BLOCK = 0x0, + GPRS_RLCMAC_PT_CONTROL_BLOCK = 0x1, + GPRS_RLCMAC_PT_CONTROL_BLOCK_OPT = 0x2, + GPRS_RLCMAC_PT_RESERVED = 0x3 +}; + /* TS 44.060 Table 11.2.16.2 "ACCESS_TYPE" */ enum gprs_rlcmac_access_type { GPRS_RLCMAC_ACCESS_TYPE_2PHASE_ACC_REQ = 0, /* Two Phase Access Request */ diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am index 6fca2b0..0612eaa 100644 --- a/src/rlcmac/Makefile.am +++ b/src/rlcmac/Makefile.am @@ -39,6 +39,8 @@ rlcmac_prim.c \ sched.c \ tbf.c \ + tbf_dl.c \ + tbf_dl_fsm.c \ tbf_ul.c \ tbf_ul_fsm.c \ tbf_ul_ass_fsm.c \ diff --git a/src/rlcmac/coding_scheme.c b/src/rlcmac/coding_scheme.c index 889e0ff..350cb0b 100644 --- a/src/rlcmac/coding_scheme.c +++ b/src/rlcmac/coding_scheme.c @@ -144,6 +144,27 @@ } } +/* Same as UL. Sizes only change in EGPRS2 blocks, which we don't support */ +enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_by_size_dl(unsigned size) +{ + switch (size) { + case 23: return GPRS_RLCMAC_CS_1; + case 27: return GPRS_RLCMAC_MCS_1; + case 33: return GPRS_RLCMAC_MCS_2; + case 34: return GPRS_RLCMAC_CS_2; + case 40: return GPRS_RLCMAC_CS_3; + case 42: return GPRS_RLCMAC_MCS_3; + case 49: return GPRS_RLCMAC_MCS_4; + case 54: return GPRS_RLCMAC_CS_4; + case 61: return GPRS_RLCMAC_MCS_5; + case 79: return GPRS_RLCMAC_MCS_6; + case 119: return GPRS_RLCMAC_MCS_7; + case 143: return GPRS_RLCMAC_MCS_8; + case 155: return GPRS_RLCMAC_MCS_9; + default: return GPRS_RLCMAC_CS_UNKNOWN; + } +} + enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_gprs_by_num(unsigned num) { if (num < 1 || num > 4) diff --git a/src/rlcmac/gre.c b/src/rlcmac/gre.c index 3d8ba08..40cd585 100644 --- a/src/rlcmac/gre.c +++ b/src/rlcmac/gre.c @@ -24,6 +24,7 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.h> +#include <osmocom/gprs/rlcmac/tbf_dl.h> #include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul.h> #include <osmocom/gprs/rlcmac/gre.h> @@ -58,6 +59,7 @@ if (!gre) return; + gprs_rlcmac_dl_tbf_free(gre->dl_tbf); gprs_rlcmac_ul_tbf_free(gre->ul_tbf); gprs_rlcmac_llc_queue_free(gre->llc_queue); llist_del(&gre->entry); diff --git a/src/rlcmac/misc.c b/src/rlcmac/misc.c index 4edc2b9..7317bd1 100644 --- a/src/rlcmac/misc.c +++ b/src/rlcmac/misc.c @@ -18,6 +18,7 @@ #include <osmocom/core/utils.h> #include <osmocom/core/logging.h> #include <osmocom/gprs/rlcmac/rlcmac.h> +#include <osmocom/gprs/rlcmac/tbf_dl_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.h> @@ -30,8 +31,15 @@ OSMO_ASSERT(logc < _OSMO_GPRS_RLCMAC_LOGC_MAX); g_rlcmac_log_cat[logc] = logc_num; - if (logc == OSMO_GPRS_RLCMAC_LOGC_TBFUL) { + switch (logc) { + case OSMO_GPRS_RLCMAC_LOGC_TBFUL: gprs_rlcmac_tbf_ul_fsm_set_log_cat(logc_num); gprs_rlcmac_tbf_ul_ass_fsm_set_log_cat(logc_num); + break; + case OSMO_GPRS_RLCMAC_LOGC_TBFDL: + gprs_rlcmac_tbf_dl_fsm_set_log_cat(logc_num); + break; + default: + break; } } diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c index 3310950..19d9944 100644 --- a/src/rlcmac/rlcmac.c +++ b/src/rlcmac/rlcmac.c @@ -29,8 +29,11 @@ #include <osmocom/gprs/rlcmac/tbf_ul_fsm.h> #include <osmocom/gprs/rlcmac/tbf_ul_ass_fsm.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/csn1_defs.h> +#include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/types_private.h> #define GPRS_CODEL_SLOW_INTERVAL_MS 4000 @@ -73,6 +76,11 @@ osmo_tdefs_reset(g_ctx->T_defs); if (first_init) { + rc = gprs_rlcmac_tbf_dl_fsm_init(); + if (rc != 0) { + TALLOC_FREE(g_ctx); + return rc; + } rc = gprs_rlcmac_tbf_ul_fsm_init(); if (rc != 0) { TALLOC_FREE(g_ctx); @@ -114,6 +122,20 @@ return NULL; } +struct gprs_rlcmac_dl_tbf *gprs_rlcmac_find_dl_tbf_by_tfi(uint8_t dl_tfi) +{ + struct gprs_rlcmac_entity *gre; + + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + if (!gre->dl_tbf) + continue; + if (gre->dl_tbf->cur_alloc.dl_tfi != dl_tfi) + continue; + return gre->dl_tbf; + } + return NULL; +} + static int gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(uint8_t ts_nr, const struct gsm48_imm_ass *ia, const IA_RestOctets_t *iaro) { int rc = -ENOENT; @@ -139,6 +161,40 @@ return rc; } +static int gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(uint8_t ts_nr, const struct gsm48_imm_ass *ia, const IA_RestOctets_t *iaro) +{ + int rc; + struct gprs_rlcmac_entity *gre; + struct gprs_rlcmac_dl_tbf *dl_tbf; + const Packet_Downlink_ImmAssignment_t *pkdlass; + + if (iaro->UnionType == 1) { + /* TODO */ + return -ENOENT; + } + + pkdlass = &iaro->u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Downlink_ImmAssignment; + + gre = gprs_rlcmac_find_entity_by_tlli(pkdlass->TLLI); + if (!gre) { + LOGRLCMAC(LOGL_NOTICE, "Got IMM_ASS (DL_TBF) for unknown TLLI=0x%08x\n", pkdlass->TLLI); + return -ENOENT; + } + + LOGGRE(gre, LOGL_INFO, "Got PCH IMM_ASS (DL_TBF): DL_TFI=%u TS=%u\n", + pkdlass->TFI_ASSIGNMENT, ts_nr); + dl_tbf = gprs_rlcmac_dl_tbf_alloc(gre); + dl_tbf->cur_alloc.dl_tfi = pkdlass->TFI_ASSIGNMENT; + dl_tbf->cur_alloc.ts[ts_nr].allocated = true; + + /* replace old DL TBF with new one: */ + gprs_rlcmac_dl_tbf_free(gre->dl_tbf); + gre->dl_tbf = dl_tbf; + + rc = osmo_fsm_inst_dispatch(dl_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_UL_EV_DL_ASS_COMPL, NULL); + return rc; +} + int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia) { int rc; @@ -171,7 +227,7 @@ rc = gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(ch_ts, ia, &iaro); break; case 1: /* iaro.u.ll.lh0x.MultiBlock_PktDlAss.* (IA_MultiBlock_PktDlAss_t) */ - /* TODO: Alloc DL TBF */ + rc = gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(ch_ts, ia, &iaro); break; } /* TODO: iaro.u.lh.AdditionsR13.* (IA_AdditionsR13_t) */ @@ -204,7 +260,7 @@ } break; case 1: /* iaro.u.hh.u.UplinkDownlinkAssignment.ul_dl.Packet_Downlink_ImmAssignment* (Packet_Downlink_ImmAssignment_t) */ - /* TODO: Alloc DL TBF */ + rc = gprs_rlcmac_handle_ccch_imm_ass_dl_tbf(ch_ts, ia, &iaro); break; } break; @@ -216,3 +272,71 @@ return rc; } + +static int gprs_rlcmac_handle_gprs_dl_ctrl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim) +{ + struct bitvec *bv; + RlcMacDownlink_t *dl_ctrl_block; + size_t max_len = gprs_rlcmac_mcs_max_bytes_dl(GPRS_RLCMAC_CS_1); + int rc; + + bv = bitvec_alloc(max_len, g_ctx); + OSMO_ASSERT(bv); + bitvec_unpack(bv, rlcmac_prim->l1ctl.pdch_data_ind.data); + + dl_ctrl_block = (RlcMacDownlink_t *)talloc_zero(g_ctx, RlcMacDownlink_t); + OSMO_ASSERT(dl_ctrl_block); + rc = osmo_gprs_rlcmac_decode_downlink(bv, dl_ctrl_block); + if (rc < 0) { + LOGRLCMAC(LOGL_NOTICE, "Failed decoding dl ctrl block: %s\n", + osmo_hexdump(rlcmac_prim->l1ctl.pdch_data_ind.data, + rlcmac_prim->l1ctl.pdch_data_ind.data_len)); + goto free_ret; + } + + LOGRLCMAC(LOGL_NOTICE, "TODO: handle decoded dl ctrl block!\n"); + +free_ret: + talloc_free(dl_ctrl_block); + bitvec_free(bv); + return rc; +} + +static int gprs_rlcmac_handle_gprs_dl_data_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim) +{ + const struct gprs_rlcmac_rlc_dl_data_header *data_hdr = (const struct gprs_rlcmac_rlc_dl_data_header *)rlcmac_prim->l1ctl.pdch_data_ind.data; + struct gprs_rlcmac_dl_tbf *dl_tbf; + + dl_tbf = gprs_rlcmac_find_dl_tbf_by_tfi(data_hdr->tfi); + if (!dl_tbf) { + LOGPTBFDL(dl_tbf, LOGL_INFO, "Rx DL data for unknown dl_tfi=%u\n", data_hdr->tfi); + return -ENOENT; + } + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "Rx new DL data\n"); + return 0; +} + +int gprs_rlcmac_handle_gprs_dl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, + enum gprs_rlcmac_coding_scheme cs) +{ + const struct gprs_rlcmac_rlc_dl_data_header *data_hdr = (const struct gprs_rlcmac_rlc_dl_data_header *)rlcmac_prim->l1ctl.pdch_data_ind.data; + /* Check block content (data vs ctrl) based on Payload Type: TS 44.060 10.4.7 */ + switch ((enum gprs_rlcmac_payload_type)data_hdr->pt) { + case GPRS_RLCMAC_PT_DATA_BLOCK: + /* "Contains an RLC data block" */ + return gprs_rlcmac_handle_gprs_dl_data_block(rlcmac_prim); + case GPRS_RLCMAC_PT_CONTROL_BLOCK: + /* "Contains an RLC/MAC control block that does not include the optional octets of the RLC/MAC + * control header" */ + return gprs_rlcmac_handle_gprs_dl_ctrl_block(rlcmac_prim); + case GPRS_RLCMAC_PT_CONTROL_BLOCK_OPT: + /* Contains an RLC/MAC control block that includes the optional first octet of the RLC/MAC + * control header" */ + return gprs_rlcmac_handle_gprs_dl_ctrl_block(rlcmac_prim); + case GPRS_RLCMAC_PT_RESERVED: /* Reserved. In this version of the protocol, the mobile station shall ignore all fields of the + * RLC/MAC block except for the USF field */ + return 0; + default: + OSMO_ASSERT(0); + } +} diff --git a/src/rlcmac/rlcmac_prim.c b/src/rlcmac/rlcmac_prim.c index 954f954..1707578 100644 --- a/src/rlcmac/rlcmac_prim.c +++ b/src/rlcmac/rlcmac_prim.c @@ -37,6 +37,7 @@ #include <osmocom/gprs/rlcmac/rlcmac_prim.h> #include <osmocom/gprs/rlcmac/rlcmac_private.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> @@ -466,9 +467,27 @@ static int rlcmac_prim_handle_l1ctl_pdch_data_ind(struct osmo_gprs_rlcmac_prim *rlcmac_prim) { - int rc = gprs_rlcmac_prim_handle_unsupported(rlcmac_prim); - rc = 1; /* msg owned (freed) */ - return rc; + enum gprs_rlcmac_coding_scheme cs = gprs_rlcmac_mcs_get_by_size_dl(rlcmac_prim->l1ctl.pdch_data_ind.data_len); + + if (cs == GPRS_RLCMAC_CS_UNKNOWN) { + LOGRLCMAC(LOGL_ERROR, "Dropping DL data block with invalid length %u: %s\n", + rlcmac_prim->l1ctl.pdch_data_ind.data_len, + osmo_hexdump(rlcmac_prim->l1ctl.pdch_data_ind.data, + rlcmac_prim->l1ctl.pdch_data_ind.data_len)); + return -EINVAL; + } + + if (gprs_rlcmac_mcs_is_gprs(cs)) + return gprs_rlcmac_handle_gprs_dl_block(rlcmac_prim, cs); + + if (gprs_rlcmac_mcs_is_edge(cs)) { + LOGRLCMAC(LOGL_NOTICE, "RX EGPRS DL data block NOT SUPPORTED\n"); + return -ENOTSUP; + } + + /* Should never be reached. */ + OSMO_ASSERT(0); + return -EINVAL; } static int rlcmac_prim_handle_l1ctl_ccch_data_ind(struct osmo_gprs_rlcmac_prim *rlcmac_prim) diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c new file mode 100644 index 0000000..ae6e7ac --- /dev/null +++ b/src/rlcmac/tbf_dl.c @@ -0,0 +1,86 @@ +/* Downlink TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <
http://www.gnu.org/licenses/
>. + * + */ +#include <osmocom/core/logging.h> + +#include <osmocom/gprs/rlcmac/tbf_dl.h> +#include <osmocom/gprs/rlcmac/gre.h> + +struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre) +{ + struct gprs_rlcmac_dl_tbf *dl_tbf; + int rc; + + dl_tbf = talloc_zero(gre, struct gprs_rlcmac_dl_tbf); + if (!dl_tbf) + return NULL; + + gprs_rlcmac_tbf_constructor(dl_tbf_as_tbf(dl_tbf), GPRS_RLCMAC_TBF_DIR_DL, gre); + + rc = gprs_rlcmac_tbf_dl_fsm_constructor(dl_tbf); + if (rc < 0) + goto err_tbf_destruct; + + dl_tbf->tbf.nr = g_ctx->next_dl_tbf_nr++; + + return dl_tbf; +err_tbf_destruct: + gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf)); + talloc_free(dl_tbf); + return NULL; +} + +void gprs_rlcmac_dl_tbf_free(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + if (!dl_tbf) + return; + + //gprs_rlcmac_tbf_dl_fsm_destructor(dl_tbf); + + gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf)); + talloc_free(dl_tbf); +} + + +static uint8_t dl_tbf_dl_slotmask(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + uint8_t i; + uint8_t dl_slotmask = 0; + + for (i = 0; i < 8; i++) { + if (dl_tbf->cur_alloc.ts[i].allocated) + dl_slotmask |= (1 << i); + } + + return dl_slotmask; +} + +int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + uint8_t dl_slotmask = dl_tbf_dl_slotmask(dl_tbf); + + LOGPTBFDL(dl_tbf, LOGL_INFO, "Send L1CTL-CF_DL_TBF.req dl_slotmask=0x%02x dl_tfi=%u\n", + dl_slotmask, dl_tbf->cur_alloc.dl_tfi); + rlcmac_prim = gprs_rlcmac_prim_alloc_l1ctl_cfg_dl_tbf_req(dl_tbf->tbf.nr, + dl_slotmask, + dl_tbf->cur_alloc.dl_tfi); + return gprs_rlcmac_prim_call_down_cb(rlcmac_prim); +} diff --git a/src/rlcmac/tbf_dl_fsm.c b/src/rlcmac/tbf_dl_fsm.c new file mode 100644 index 0000000..e4a4cf2 --- /dev/null +++ b/src/rlcmac/tbf_dl_fsm.c @@ -0,0 +1,164 @@ +/* TBF as per 3GPP TS 44.064 */ +/* + * (C) 2023 by sysmocom - s.f.m.c. GmbH <info(a)sysmocom.de> + * + * All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation; either version 3 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 Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <
http://www.gnu.org/licenses/
>. + * + */ + +#include <talloc.h> +#include <osmocom/core/tdef.h> +#include <osmocom/core/fsm.h> + +#include <osmocom/gprs/rlcmac/tbf_dl_fsm.h> +#include <osmocom/gprs/rlcmac/tbf_dl.h> +#include <osmocom/gprs/rlcmac/gre.h> + +#define X(s) (1 << (s)) + +static const struct value_string tbf_dl_fsm_event_names[] = { + { GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD, "LAST_DL_DATA_RECVD" }, + { GPRS_RLCMAC_TBF_UL_EV_DL_ASS_COMPL, "DL_ASS_COMPL" }, + { 0, NULL } +}; + +static const struct osmo_tdef_state_timeout tbf_dl_fsm_timeouts[32] = { + [GPRS_RLCMAC_TBF_DL_ST_NEW] = { }, + [GPRS_RLCMAC_TBF_DL_ST_FLOW] = { }, + [GPRS_RLCMAC_TBF_DL_ST_FINISHED] = { }, +}; + +/* Transition to a state, using the T timer defined in tbf_fsm_timeouts. + * The actual timeout value is in turn obtained from conn->T_defs. + * Assumes local variable fi exists. */ + #define tbf_dl_fsm_state_chg(fi, NEXT_STATE) \ + osmo_tdef_fsm_inst_state_chg(fi, NEXT_STATE, \ + tbf_dl_fsm_timeouts, \ + g_ctx->T_defs, \ + -1) + +static void st_new(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_dl_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_UL_EV_DL_ASS_COMPL: + /* Configure DL TBF on the lower MAC side: */ + gprs_rlcmac_dl_tbf_configure_l1ctl(ctx->dl_tbf); + tbf_dl_fsm_state_chg(fi, GPRS_RLCMAC_TBF_DL_ST_FLOW); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_flow(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_dl_fsm_ctx *)fi->priv; + switch (event) { + case GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD: + tbf_dl_fsm_state_chg(fi, GPRS_RLCMAC_TBF_DL_ST_FINISHED); + break; + default: + OSMO_ASSERT(0); + } +} + +static void st_finished(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + //struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_dl_fsm_ctx *)fi->priv; + switch (event) { + default: + OSMO_ASSERT(0); + } +} + +static struct osmo_fsm_state tbf_dl_fsm_states[] = { + [GPRS_RLCMAC_TBF_DL_ST_NEW] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_UL_EV_DL_ASS_COMPL), + .out_state_mask = + X(GPRS_RLCMAC_TBF_DL_ST_FLOW), + .name = "NEW", + .action = st_new, + }, + [GPRS_RLCMAC_TBF_DL_ST_FLOW] = { + .in_event_mask = + X(GPRS_RLCMAC_TBF_DL_EV_LAST_DL_DATA_RECVD), + .out_state_mask = + X(GPRS_RLCMAC_TBF_DL_ST_FINISHED), + .name = "FLOW", + .action = st_flow, + }, + [GPRS_RLCMAC_TBF_DL_ST_FINISHED] = { + .in_event_mask = 0, + .out_state_mask = 0, + .name = "FINISHED", + .action = st_finished, + }, +}; + +static int tbf_dl_fsm_timer_cb(struct osmo_fsm_inst *fi) +{ + //struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_dl_fsm_ctx *)fi->priv; + switch (fi->T) { + default: + OSMO_ASSERT(0); + } + return 0; +} + +static struct osmo_fsm tbf_dl_fsm = { + .name = "DL_TBF", + .states = tbf_dl_fsm_states, + .num_states = ARRAY_SIZE(tbf_dl_fsm_states), + .timer_cb = tbf_dl_fsm_timer_cb, + .log_subsys = DLGLOBAL, /* updated dynamically through gprs_rlcmac_tbf_dl_fsm_set_log_cat() */ + .event_names = tbf_dl_fsm_event_names, +}; + +int gprs_rlcmac_tbf_dl_fsm_init(void) +{ + return osmo_fsm_register(&tbf_dl_fsm); +} + +void gprs_rlcmac_tbf_dl_fsm_set_log_cat(int logcat) +{ + tbf_dl_fsm.log_subsys = logcat; +} + +int gprs_rlcmac_tbf_dl_fsm_constructor(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = &dl_tbf->state_fsm; + ctx->dl_tbf = dl_tbf; + ctx->fi = osmo_fsm_inst_alloc(&tbf_dl_fsm, dl_tbf, ctx, LOGL_INFO, NULL); + if (!ctx->fi) + return -ENODATA; + + return 0; +} + +void gprs_rlcmac_tbf_dl_fsm_destructor(struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = &dl_tbf->state_fsm; + osmo_fsm_inst_free(ctx->fi); + ctx->fi = NULL; +} + +enum gprs_rlcmac_tbf_dl_fsm_states gprs_rlcmac_tbf_dl_state(const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + const struct gprs_rlcmac_tbf_dl_fsm_ctx *ctx = &dl_tbf->state_fsm; + return ctx->fi->state; +} diff --git a/tests/rlcmac/Makefile.am b/tests/rlcmac/Makefile.am index 949aed6..8d19656 100644 --- a/tests/rlcmac/Makefile.am +++ b/tests/rlcmac/Makefile.am @@ -1,5 +1,6 @@ AM_CFLAGS = \ -Wall \ + $(LIBOSMOGSM_CFLAGS) \ $(LIBOSMOCORE_CFLAGS) \ -I$(top_srcdir)/include/ \ $(NULL) @@ -24,10 +25,12 @@ $(NULL) # Common LDADD entries +# libosmo-gprs-rlcmac.a is used below to access non-exported private symbols used in the test: LDADD = \ - $(LIBOSMOCORE_LIBS) \ + $(top_builddir)/src/rlcmac/.libs/libosmo-gprs-rlcmac.a \ $(top_builddir)/src/csn1/libosmo-csn1.la \ - $(top_builddir)/src/rlcmac/libosmo-gprs-rlcmac.la \ + $(LIBOSMOGSM_LIBS) \ + $(LIBOSMOCORE_LIBS) \ $(NULL) csn1_ts_44_018_test_SOURCES = csn1_ts_44_018_test.c diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index fa864f9..00f1740 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -24,6 +24,7 @@ #include <osmocom/gprs/rlcmac/rlcmac.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/rlc.h> static void *tall_ctx = NULL; @@ -118,6 +119,64 @@ 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b }; +/* +GSM CCCH - Immediate Assignment + L2 Pseudo Length + 0010 11.. = L2 Pseudo Length value: 11 + .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) + .... 0110 = Protocol discriminator: Radio Resources Management messages (0x6) + 0000 .... = Skip Indicator: No indication of selected PLMN (0) + Message Type: Immediate Assignment + Page Mode + .... 0000 = Page Mode: Normal paging (0) + Dedicated mode or TBF + 0011 .... = Dedicated mode or TBF: This message assigns a downlink TBF to the mobile station identified in the IA Rest Octets IE (3) + Packet Channel Description + 0000 1... = Channel Type: 1 + .... .111 = Timeslot: 7 + 111. .... = Training Sequence: 7 + .... .0.. = Spare: 0x00 + .... ..11 0110 0111 = Single channel ARFCN: 871 + Request Reference + Random Access Information (RA): 125 + 1000 0... = T1': 16 + .... .000 000. .... = T3: 0 + ...0 0000 = T2: 0 + [RFN: 21216] + Timing Advance + Timing advance value: 28 + Mobile Allocation + Length: 0 + IA Rest Octets + H... .... = First Discriminator Bit: High + .H.. .... = Second Discriminator Bit: High + ..0. .... = Discriminator Bit: Packet Assignment + ...1 .... = Discriminator Bit: Packet Downlink Assignment + Packet Downlink Assignment + .... 0000 0000 0000 0000 0000 0000 0000 0001 .... = TLLI: 0x00000001 + .... 1... = TFI Assignment (etc): Present + .... .000 00.. .... = TFI_Assignment: 0 + ..0. .... = RLC_Mode: RLC acknowledged mode + ...0 .... = Alpha: Not Present + .... 0000 0... .... = Gamma: 0 dB (0) + .0.. .... = Polling: no action is required from MS + ..0. .... = TA_Valid: the timing advance value is not valid + ...0 .... = Timing Advance Index: Not Present + .... 0... = TBF Starting Time: Not Present + .... .0.. = P0: Not Present + .... ..L. = Additions in R99: Not Present + .... ...L = Additions in Rel-6: Not Present + L... .... = Additions in Rel-7: Not Present + .L.. .... = Additions in Rel-10: Not Present + ..L. .... = Additions in Rel-13: Not Present + Padding Bits: default padding +*/ +static uint8_t ccch_imm_ass_pkt_dl_tbf[] = { + 0x2d, 0x06, 0x3f, 0x30, 0x0f, 0xe3, 0x67, 0x7d, 0x80, 0x00, + 0x1c, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x18, 0x00, 0x03, + 0x2b, 0x2b, 0x2b, 0x2b +}; + static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) { const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); @@ -162,6 +221,12 @@ rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_tbf_nr, rlcmac_prim->l1ctl.cfg_ul_tbf_req.ul_slotmask); break; + case OSMO_PRIM(OSMO_GPRS_RLCMAC_L1CTL_CFG_DL_TBF, PRIM_OP_REQUEST): + printf("%s(): Rx %s dl_tbf_nr=%u dl_slotmask=0x%02x dl_tfi=%u\n", __func__, pdu_name, + rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tbf_nr, + rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_slotmask, + rlcmac_prim->l1ctl.cfg_dl_tbf_req.dl_tfi); + break; default: printf("%s(): Rx %s\n", __func__, pdu_name); } @@ -173,6 +238,37 @@ return 0; } +static const uint8_t llc_dummy_command[] = { + 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) +{ + struct msgb *msg = msgb_alloc(128, __func__); + struct gprs_rlcmac_rlc_dl_data_header *hdr; + struct gprs_rlcmac_rlc_li_field *lime; + + 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->usf = usf; + hdr->pr = 0; + hdr->tfi = dl_tfi; + hdr->fbi = fbi ? 1 : 0; + hdr->tfi = dl_tfi; + hdr->bsn = bsn; + hdr->e = 0; + lime = &hdr->lime[0]; + lime->li = sizeof(llc_dummy_command); + lime->m = 0; + lime->e = 1; + msg->l3h = &lime->ll_pdu[0]; + memset(msg->l3h, 0x2b, msgb_l3len(msg)); + memcpy(msg->l3h, llc_dummy_command, sizeof(llc_dummy_command)); + return msg; +} + void prepare_test(void) { int rc; @@ -183,6 +279,12 @@ osmo_gprs_rlcmac_prim_set_down_cb(test_rlcmac_prim_down_cb, NULL); } +void cleanup_test(void) +{ + /* Reinit the RLCMAC layer so that data generated during the test is freed within the test context: */ + osmo_gprs_rlcmac_init(OSMO_GPRS_RLCMAC_LOCATION_MS); +} + static void test_ul_tbf_attach(void) { struct osmo_gprs_rlcmac_prim *rlcmac_prim; @@ -210,7 +312,46 @@ rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); + printf("=== %s end ===\n", __func__); + cleanup_test(); +} + +/* PCU allocates a DL TBF through PCH ImmAss for MS (when in packet-idle) */ +static void test_dl_tbf_ccch_assign(void) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + int rc; + struct msgb *dl_data_msg; + + printf("=== %s start ===\n", __func__); + prepare_test(); + uint32_t tlli = 0x0000001; + uint8_t ts_nr = 7; + uint8_t usf = 0; + uint32_t rts_fn = 4; + uint8_t dl_tfi = 0; + + /* Notify RLCMAC about our TLLI */ + rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_gmmrr_assign_req(tlli); + rc = osmo_gprs_rlcmac_prim_upper_down(rlcmac_prim); + + OSMO_ASSERT(sizeof(ccch_imm_ass_pkt_dl_tbf) == GSM_MACBLOCK_LEN); + rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_dl_tbf); + rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); + 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); + 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)); + rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); + OSMO_ASSERT(rc == 0); + msgb_free(dl_data_msg); + + printf("=== %s end ===\n", __func__); + cleanup_test(); } static const struct log_info_cat test_log_categories[] = { }; @@ -234,6 +375,7 @@ log_set_use_color(osmo_stderr_target, 0); test_ul_tbf_attach(); + test_dl_tbf_ccch_assign(); talloc_free(tall_ctx); } diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index e6d6b30..89a970c 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -25,3 +25,15 @@ DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying 1 RLC blocks, 1 BSNs DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying data unit 0 (BSN 0) DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) msg block (BSN 0, CS-2): 3c 00 01 01 c0 00 08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 00 +DLGLOBAL INFO UL_TBF_ASS{IDLE}: Deallocated +DLGLOBAL INFO UL_TBF{FLOW}: Deallocated +DLGLOBAL INFO Rx from upper layers: GMMRR-ASSIGN.request +DLGLOBAL INFO GMMRR-ASSIGN.req: creating new entity TLLI=0x00000001 +DLGLOBAL INFO Rx from lower layers: L1CTL-CCCH_DATA.indication +DLGLOBAL INFO GRE(00000001) Got PCH IMM_ASS (DL_TBF): DL_TFI=0 TS=7 +DLGLOBAL INFO DL_TBF{NEW}: Allocated +DLGLOBAL INFO DL_TBF{NEW}: Received Event DL_ASS_COMPL +DLGLOBAL INFO TBF(DL:NR-0:TLLI-00000001) Send L1CTL-CF_DL_TBF.req dl_slotmask=0x80 dl_tfi=0 +DLGLOBAL INFO DL_TBF{NEW}: state_chg to FLOW +DLGLOBAL INFO Rx from lower layers: L1CTL-PDCH_DATA.indication +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) Rx new DL data diff --git a/tests/rlcmac/rlcmac_prim_test.ok b/tests/rlcmac/rlcmac_prim_test.ok index 6e0db40..808ce21 100644 --- a/tests/rlcmac/rlcmac_prim_test.ok +++ b/tests/rlcmac/rlcmac_prim_test.ok @@ -3,3 +3,6 @@ test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_UL_TBF.request ul_tbf_nr=0 ul_slotmask=0x80 test_rlcmac_prim_down_cb(): Rx L1CTL-PDCH_DATA.request fn=4 ts=7 data_len=34 data=[3c 00 01 01 c0 00 08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 00 ] === test_ul_tbf_attach end === +=== test_dl_tbf_ccch_assign start === +test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_DL_TBF.request dl_tbf_nr=0 dl_slotmask=0x80 dl_tfi=0 +=== test_dl_tbf_ccch_assign end === -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31286
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I7f98e3456ef35d80becdad3481afeb771457b0ef Gerrit-Change-Number: 31286 Gerrit-PatchSet: 10 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-CC: msuraev <msuraev(a)sysmocom.de> Gerrit-CC: neels <nhofmeyr(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[M] Change in libosmo-gprs[master]: rlcmac: Implement Tx of DL ACK/NACK
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31328
) Change subject: rlcmac: Implement Tx of DL ACK/NACK ...................................................................... rlcmac: Implement Tx of DL ACK/NACK Measurement related functionality is not yet implemented and hence the related fields cannot be filled in yet. Related: OS#5500 Change-Id: I6ae2df07929fb6c4733e87b18cebe75a6f24f520 --- M include/osmocom/gprs/rlcmac/rlcmac_enc.h M include/osmocom/gprs/rlcmac/tbf_dl.h M src/rlcmac/rlcmac_enc.c M src/rlcmac/sched.c M src/rlcmac/tbf_dl.c M tests/rlcmac/rlcmac_prim_test.err M tests/rlcmac/rlcmac_prim_test.ok 7 files changed, 114 insertions(+), 4 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/rlcmac_enc.h b/include/osmocom/gprs/rlcmac/rlcmac_enc.h index d091d53..1a0f586 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_enc.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_enc.h @@ -46,3 +46,5 @@ void gprs_rlcmac_enc_prepare_pkt_ul_dummy_block(RlcMacUplink_t *block, uint32_t tlli); void gprs_rlcmac_enc_prepare_pkt_resource_req(RlcMacUplink_t *block, struct gprs_rlcmac_ul_tbf *ul_tbf, enum gprs_rlcmac_access_type acc_type); + +void gprs_rlcmac_enc_prepare_pkt_downlink_ack_nack(RlcMacUplink_t *block, const struct gprs_rlcmac_dl_tbf *dl_tbf); diff --git a/include/osmocom/gprs/rlcmac/tbf_dl.h b/include/osmocom/gprs/rlcmac/tbf_dl.h index facae81..f398c0a 100644 --- a/include/osmocom/gprs/rlcmac/tbf_dl.h +++ b/include/osmocom/gprs/rlcmac/tbf_dl.h @@ -40,6 +40,8 @@ int gprs_rlcmac_dl_tbf_configure_l1ctl(struct gprs_rlcmac_dl_tbf *dl_tbf); +struct msgb *gprs_rlcmac_dl_tbf_create_pkt_dl_ack_nack(const struct gprs_rlcmac_dl_tbf *dl_tbf); + 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, uint32_t fn, uint8_t ts_nr); diff --git a/src/rlcmac/rlcmac_enc.c b/src/rlcmac/rlcmac_enc.c index e1ea990..1f1ebc3 100644 --- a/src/rlcmac/rlcmac_enc.c +++ b/src/rlcmac/rlcmac_enc.c @@ -24,6 +24,8 @@ #include <osmocom/gprs/rlcmac/csn1_defs.h> #include <osmocom/gprs/rlcmac/rlcmac_enc.h> #include <osmocom/gprs/rlcmac/gre.h> +#include <osmocom/gprs/rlcmac/tbf_dl.h> +#include <osmocom/gprs/rlcmac/rlc_window_dl.h> #include <osmocom/gprs/rlcmac/tbf_ul.h> int gprs_rlcmac_rlc_write_ul_data_header(const struct gprs_rlcmac_rlc_data_info *rlc, uint8_t *data) @@ -352,3 +354,59 @@ req->Exist_AdditionsR99 = 0; /* TODO: no req->AdditionsR99 yet */ } + +static void gprs_rlcmac_enc_prepare_pkt_ack_nack_desc_gprs(Ack_Nack_Description_t *ack_desc, const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct bitvec bv = { + .data = &ack_desc->RECEIVED_BLOCK_BITMAP[0], + .data_len = sizeof(ack_desc->RECEIVED_BLOCK_BITMAP), + }; + char rbb[65]; + + gprs_rlcmac_rlc_dl_window_update_rbb(dl_tbf->dlw, rbb); + rbb[64] = 0; + LOGPTBFDL(dl_tbf, LOGL_DEBUG, "- V(N): \"%s\" R=Received I=Invalid\n", rbb); + + ack_desc->FINAL_ACK_INDICATION = (gprs_rlcmac_tbf_dl_state(dl_tbf) == GPRS_RLCMAC_TBF_DL_ST_FINISHED); + ack_desc->STARTING_SEQUENCE_NUMBER = gprs_rlcmac_rlc_dl_window_ssn(dl_tbf->dlw); + for (int i = 0; i < 64; i++) { + /* Set bit at the appropriate position (see 3GPP TS 44.060 9.1.8.1) */ + bool is_ack = (rbb[i] == 'R'); + bitvec_set_bit(&bv, is_ack == 1 ? ONE : ZERO); + } +} + +/* Channel Quality Report struct, TS 44.060 Table 11.2.6. */ +static void gprs_rlcmac_enc_prepare_channel_quality_report(Channel_Quality_Report_t *cqr, const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + /* TODO: fill cqr from info stored probably in the gre object. */ +} + +void gprs_rlcmac_enc_prepare_pkt_downlink_ack_nack(RlcMacUplink_t *block, const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + Packet_Downlink_Ack_Nack_t *ack = &block->u.Packet_Downlink_Ack_Nack; + struct gprs_rlcmac_entity *gre = dl_tbf->tbf.gre; + + memset(block, 0, sizeof(*block)); + ack->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_UL_MSGT_PACKET_RESOURCE_REQUEST; + ack->PayloadType = GPRS_RLCMAC_PT_CONTROL_BLOCK; + ack->R = 0; /* MS sent channel request message once */ + + ack->DOWNLINK_TFI = dl_tbf->cur_alloc.dl_tfi; + gprs_rlcmac_enc_prepare_pkt_ack_nack_desc_gprs(&ack->Ack_Nack_Description, dl_tbf); + + /* Channel Request Description */ + if (gre->ul_tbf && gprs_rlcmac_tbf_ul_ass_pending(gre->ul_tbf)) { + Channel_Request_Description_t *chan_req = &ack->Channel_Request_Description; + ack->Exist_Channel_Request_Description = 1; + chan_req->PEAK_THROUGHPUT_CLASS = 0; /* TODO */ + chan_req->RADIO_PRIORITY = 0; /* TODO */ + chan_req->RLC_MODE = GPRS_RLCMAC_RLC_MODE_ACKNOWLEDGED; + chan_req->LLC_PDU_TYPE = GPRS_RLCMAC_LLC_PDU_TYPE_ACKNOWLEDGED; + chan_req->RLC_OCTET_COUNT = 0; /* TODO */ + } else { + ack->Exist_Channel_Request_Description = 0; + } + + gprs_rlcmac_enc_prepare_channel_quality_report(&ack->Channel_Quality_Report, dl_tbf); +} diff --git a/src/rlcmac/sched.c b/src/rlcmac/sched.c index f229b7a..6a31f83 100644 --- a/src/rlcmac/sched.c +++ b/src/rlcmac/sched.c @@ -150,7 +150,7 @@ 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 */ + msg = gprs_rlcmac_dl_tbf_create_pkt_dl_ack_nack(tbfs->poll_dl_ack_final_ack); if (msg) return msg; } @@ -167,7 +167,7 @@ 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 */ + msg = gprs_rlcmac_dl_tbf_create_pkt_dl_ack_nack(tbfs->poll_dl_ack); if (msg) return msg; } diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c index da9b871..49f8187 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/rlcmac_enc.h> #include <osmocom/gprs/rlcmac/pdch_ul_controller.h> struct gprs_rlcmac_dl_tbf *gprs_rlcmac_dl_tbf_alloc(struct gprs_rlcmac_entity *gre) @@ -105,6 +106,40 @@ return gprs_rlcmac_prim_call_down_cb(rlcmac_prim); } +struct msgb *gprs_rlcmac_dl_tbf_create_pkt_dl_ack_nack(const struct gprs_rlcmac_dl_tbf *dl_tbf) +{ + struct msgb *msg; + struct bitvec bv; + RlcMacUplink_t ul_block; + int rc; + + OSMO_ASSERT(dl_tbf); + + msg = msgb_alloc(GSM_MACBLOCK_LEN, "pkt_dl_ack_nack"); + if (!msg) + return NULL; + + /* Initialize a bit vector that uses allocated msgb as the data buffer. */ + bv = (struct bitvec){ + .data = msgb_put(msg, GSM_MACBLOCK_LEN), + .data_len = GSM_MACBLOCK_LEN, + }; + bitvec_unhex(&bv, GPRS_RLCMAC_DUMMY_VEC); + + gprs_rlcmac_enc_prepare_pkt_downlink_ack_nack(&ul_block, dl_tbf); + rc = osmo_gprs_rlcmac_encode_uplink(&bv, &ul_block); + if (rc < 0) { + LOGPTBFDL(dl_tbf, LOGL_ERROR, "Encoding of Packet Downlink ACK/NACK failed (%d)\n", rc); + goto free_ret; + } + + return msg; + +free_ret: + msgb_free(msg); + return NULL; +} + /* * Store received block data in LLC message(s) and forward to SGSN * if complete. diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index 2d1b804..d4c8250 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -54,5 +54,4 @@ 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 +DLGLOBAL DEBUG TBF(DL:NR-0:TLLI-00000001) - V(N): "IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIR" R=Received I=Invalid diff --git a/tests/rlcmac/rlcmac_prim_test.ok b/tests/rlcmac/rlcmac_prim_test.ok index 49b02b9..7823636 100644 --- a/tests/rlcmac/rlcmac_prim_test.ok +++ b/tests/rlcmac/rlcmac_prim_test.ok @@ -6,4 +6,5 @@ === test_dl_tbf_ccch_assign start === test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_DL_TBF.request dl_tbf_nr=0 dl_slotmask=0x80 dl_tfi=0 test_rlcmac_prim_up_cb(): Rx GRR-UNITDATA.indication TLLI=0x00000001 ll=[43 c0 01 2b 2b 2b ] +test_rlcmac_prim_down_cb(): Rx L1CTL-PDCH_DATA.request fn=21 ts=7 data_len=23 data=[40 14 00 00 00 00 00 00 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b ] === test_dl_tbf_ccch_assign end === -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31328
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I6ae2df07929fb6c4733e87b18cebe75a6f24f520 Gerrit-Change-Number: 31328 Gerrit-PatchSet: 3 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[S] Change in libosmo-gprs[master]: rlcmac: llc_queue: Fix access to null msgb during dequeue
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31342
) ( 1 is the latest approved patch-set. No files were changed between the latest approved patch-set and the submitted one. )Change subject: rlcmac: llc_queue: Fix access to null msgb during dequeue ...................................................................... rlcmac: llc_queue: Fix access to null msgb during dequeue Change-Id: I513aaef6ad0f44b5a6d32be512bdd8b4fdabb4e7 --- M src/rlcmac/llc_queue.c 1 file changed, 12 insertions(+), 1 deletion(-) Approvals: laforge: Looks good to me, approved Jenkins Builder: Verified diff --git a/src/rlcmac/llc_queue.c b/src/rlcmac/llc_queue.c index 2643b66..2c1aeea 100644 --- a/src/rlcmac/llc_queue.c +++ b/src/rlcmac/llc_queue.c @@ -207,7 +207,9 @@ "new_queue_size=%zu\n", frames, octets, gprs_rlcmac_llc_queue_size(q)); } - msgb_pull_to_l2(msg); + if (!msg) + return NULL; + msgb_pull_to_l2(msg); return msg; } -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31342
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I513aaef6ad0f44b5a6d32be512bdd8b4fdabb4e7 Gerrit-Change-Number: 31342 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[S] Change in libosmo-gprs[master]: rlcmac: tests: Validate Tx of second RLC/MAC block containing GMM Attach
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31343
) Change subject: rlcmac: tests: Validate Tx of second RLC/MAC block containing GMM Attach ...................................................................... rlcmac: tests: Validate Tx of second RLC/MAC block containing GMM Attach Change-Id: I9a5870d2972c70233c6e8859bc6d694ff180b690 --- M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err M tests/rlcmac/rlcmac_prim_test.ok 3 files changed, 43 insertions(+), 2 deletions(-) Approvals: laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index c80ce60..2711889 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -179,6 +179,19 @@ 0x2b, 0x2b, 0x2b, 0x2b }; +static inline unsigned fn2bn(unsigned fn) +{ + return (fn % 52) / 4; +} + +static inline unsigned fn_next_block(unsigned fn) +{ + unsigned bn = fn2bn(fn) + 1; + fn = fn - (fn % 52); + fn += bn * 4 + bn / 3; + return fn % GSM_MAX_FN; +} + static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) { const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); @@ -308,7 +321,13 @@ rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_ccch_data_ind(0, ccch_imm_ass_pkt_ul_tbf_normal); rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); - /* Trigger transmission of LLC data (GMM Attach) */ + /* Trigger transmission of LLC data (GMM Attach) (first part) */ + 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); + + /* Trigger transmission of LLC data (GMM Attach) (second part) */ + rts_fn = fn_next_block(rts_fn); 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); diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index d4c8250..2e82b2e 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -25,8 +25,20 @@ DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying 1 RLC blocks, 1 BSNs DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying data unit 0 (BSN 0) DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) msg block (BSN 0, CS-2): 3c 00 01 01 c0 00 08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 00 +DLGLOBAL INFO Rx from lower layers: L1CTL-PDCH_RTS.indication +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Sending new block at BSN 1, CS=CS-2 +DLGLOBAL DEBUG -- Chunk with length 3 is less than remaining space (30): add length header to delimit LLC frame +DLGLOBAL DEBUG -- Final block, so we done. +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Complete UL frame, len=0 +DLGLOBAL INFO UL_TBF{FLOW}: Received Event LAST_UL_DATA_SENT +DLGLOBAL INFO UL_TBF{FLOW}: state_chg to FINISHED +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) data block (BSN 1, CS-2): 0d e2 18 f2 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) need_padding 0 spb_status 0 spb 0 (BSN1 1 BSN2 -1) +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying 1 RLC blocks, 1 BSNs +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying data unit 0 (BSN 1) +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) msg block (BSN 1, CS-2): 00 00 02 0d e2 18 f2 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 00 DLGLOBAL INFO UL_TBF_ASS{IDLE}: Deallocated -DLGLOBAL INFO UL_TBF{FLOW}: Deallocated +DLGLOBAL INFO UL_TBF{FINISHED}: Deallocated DLGLOBAL INFO Rx from upper layers: GMMRR-ASSIGN.request DLGLOBAL INFO GMMRR-ASSIGN.req: creating new entity TLLI=0x00000001 DLGLOBAL INFO Rx from lower layers: L1CTL-CCCH_DATA.indication diff --git a/tests/rlcmac/rlcmac_prim_test.ok b/tests/rlcmac/rlcmac_prim_test.ok index 7823636..7792932 100644 --- a/tests/rlcmac/rlcmac_prim_test.ok +++ b/tests/rlcmac/rlcmac_prim_test.ok @@ -2,6 +2,7 @@ test_rlcmac_prim_down_cb(): Rx L1CTL-RACH.request ra=0x7e test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_UL_TBF.request ul_tbf_nr=0 ul_slotmask=0x80 test_rlcmac_prim_down_cb(): Rx L1CTL-PDCH_DATA.request fn=4 ts=7 data_len=34 data=[3c 00 01 01 c0 00 08 01 01 d5 71 00 00 08 29 26 24 00 00 00 00 71 62 f2 24 6c 84 44 04 11 e5 10 00 00 ] +test_rlcmac_prim_down_cb(): Rx L1CTL-PDCH_DATA.request fn=8 ts=7 data_len=34 data=[00 00 02 0d e2 18 f2 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 00 ] === test_ul_tbf_attach end === === test_dl_tbf_ccch_assign start === test_rlcmac_prim_down_cb(): Rx L1CTL-CFG_DL_TBF.request dl_tbf_nr=0 dl_slotmask=0x80 dl_tfi=0 -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31343
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I9a5870d2972c70233c6e8859bc6d694ff180b690 Gerrit-Change-Number: 31343 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[S] Change in libosmo-gprs[master]: rlcmac: tbf_ul: Fix wrong function called looking up msg size
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31345
) Change subject: rlcmac: tbf_ul: Fix wrong function called looking up msg size ...................................................................... rlcmac: tbf_ul: Fix wrong function called looking up msg size Change-Id: I9d5afee36a633c311b5657181d0de7cbddd593e0 --- M src/rlcmac/tbf_ul.c 1 file changed, 10 insertions(+), 1 deletion(-) Approvals: laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved diff --git a/src/rlcmac/tbf_ul.c b/src/rlcmac/tbf_ul.c index ae6b156..06daf36 100644 --- a/src/rlcmac/tbf_ul.c +++ b/src/rlcmac/tbf_ul.c @@ -672,7 +672,7 @@ rlc.tfi = ul_tbf->cur_alloc.ul_tfi; /* TFI */ /* return data block(s) as message */ - msg_len = gprs_rlcmac_mcs_size_dl(cs); + msg_len = gprs_rlcmac_mcs_size_ul(cs); msg = msgb_alloc(msg_len, "rlcmac_ul_data"); if (!msg) return NULL; -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31345
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I9d5afee36a633c311b5657181d0de7cbddd593e0 Gerrit-Change-Number: 31345 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[S] Change in libosmo-gprs[master]: rlcmac: tbf_*: Unlink the freeing tbf from its gre
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31346
) Change subject: rlcmac: tbf_*: Unlink the freeing tbf from its gre ...................................................................... rlcmac: tbf_*: Unlink the freeing tbf from its gre Change-Id: I2079c4440a05f4b44c9b7bece383c69e84198302 --- M src/rlcmac/tbf_dl.c M src/rlcmac/tbf_ul.c 2 files changed, 15 insertions(+), 0 deletions(-) Approvals: laforge: Looks good to me, but someone else must approve Jenkins Builder: Verified pespin: Looks good to me, approved diff --git a/src/rlcmac/tbf_dl.c b/src/rlcmac/tbf_dl.c index 49f8187..7fb4be9 100644 --- a/src/rlcmac/tbf_dl.c +++ b/src/rlcmac/tbf_dl.c @@ -64,6 +64,9 @@ if (!dl_tbf) return; + if (dl_tbf->tbf.gre->dl_tbf == dl_tbf) + dl_tbf->tbf.gre->dl_tbf = NULL; + msgb_free(dl_tbf->llc_rx_msg); dl_tbf->llc_rx_msg = NULL; diff --git a/src/rlcmac/tbf_ul.c b/src/rlcmac/tbf_ul.c index 06daf36..4a6f9ce 100644 --- a/src/rlcmac/tbf_ul.c +++ b/src/rlcmac/tbf_ul.c @@ -72,6 +72,9 @@ if (!ul_tbf) return; + if (ul_tbf->tbf.gre->ul_tbf == ul_tbf) + ul_tbf->tbf.gre->ul_tbf = NULL; + talloc_free(ul_tbf->llc_tx_msg); gprs_rlcmac_rlc_block_store_free(ul_tbf->blkst); -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31346
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I2079c4440a05f4b44c9b7bece383c69e84198302 Gerrit-Change-Number: 31346 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[L] Change in libosmo-gprs[master]: rlcmac: Implement Rx of UL ACK/NACK
by pespin
pespin has submitted this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31344
) Change subject: rlcmac: Implement Rx of UL ACK/NACK ...................................................................... rlcmac: Implement Rx of UL ACK/NACK Related: OS#5500 Change-Id: I5e3d8e77042d3ad1618e6b62bc1a377a93239580 --- M include/osmocom/gprs/rlcmac/rlc_window.h M include/osmocom/gprs/rlcmac/rlcmac_dec.h M include/osmocom/gprs/rlcmac/rlcmac_private.h M include/osmocom/gprs/rlcmac/tbf_ul.h M include/osmocom/gprs/rlcmac/tbf_ul_fsm.h M include/osmocom/gprs/rlcmac/types_private.h M src/rlcmac/rlc_window.c M src/rlcmac/rlcmac.c M src/rlcmac/rlcmac_dec.c M src/rlcmac/tbf_ul.c M src/rlcmac/tbf_ul_fsm.c M tests/rlcmac/rlcmac_prim_test.c M tests/rlcmac/rlcmac_prim_test.err 13 files changed, 341 insertions(+), 11 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/rlc_window.h b/include/osmocom/gprs/rlcmac/rlc_window.h index a5d89ab..09a2739 100644 --- a/include/osmocom/gprs/rlcmac/rlc_window.h +++ b/include/osmocom/gprs/rlcmac/rlc_window.h @@ -3,6 +3,12 @@ #include <stdint.h> +#define GPRS_RLCMAC_GPRS_WS 64 /* max window size */ +#define GPRS_RLCMAC_EGPRS_MIN_WS 64 /* min window size */ +#define GPRS_RLCMAC_EGPRS_MAX_WS 1024 /* min window size */ +#define GPRS_RLCMAC_EGPRS_MAX_BSN_DELTA 512 +#define GPRS_RLCMAC_MAX_WS RLC_EGPRS_MAX_WS + struct gprs_rlcmac_rlc_window { uint16_t sns; uint16_t ws; diff --git a/include/osmocom/gprs/rlcmac/rlcmac_dec.h b/include/osmocom/gprs/rlcmac/rlcmac_dec.h index c973917..ea92a0f 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_dec.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_dec.h @@ -4,10 +4,13 @@ #include <stdint.h> #include <osmocom/core/msgb.h> +#include <osmocom/core/bitvec.h> +#include <osmocom/gprs/rlcmac/csn1_defs.h> #include <osmocom/gprs/rlcmac/rlc.h> #include <osmocom/gprs/rlcmac/coding_scheme.h> +struct gprs_rlcmac_rlc_ul_window; /**************** * DATA BLOCKS: @@ -38,3 +41,8 @@ /**************** * CONTROL BLOCKS: ****************/ + +void gprs_rlcmac_extract_rbb(const struct bitvec *rbb, char *show_rbb); +int gprs_rlcmac_decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc, + struct bitvec *bits, int *bsn_begin, int *bsn_end, + struct gprs_rlcmac_rlc_ul_window *ulw); diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h b/include/osmocom/gprs/rlcmac/rlcmac_private.h index f5bbfb0..80461da 100644 --- a/include/osmocom/gprs/rlcmac/rlcmac_private.h +++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h @@ -80,6 +80,7 @@ /* rlcmac.c */ struct gprs_rlcmac_entity *gprs_rlcmac_find_entity_by_tlli(uint32_t tlli); struct gprs_rlcmac_dl_tbf *gprs_rlcmac_find_dl_tbf_by_tfi(uint8_t dl_tfi); +struct gprs_rlcmac_ul_tbf *gprs_rlcmac_find_ul_tbf_by_tfi(uint8_t ul_tfi); int gprs_rlcmac_handle_ccch_imm_ass(const struct gsm48_imm_ass *ia); int gprs_rlcmac_handle_gprs_dl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, enum gprs_rlcmac_coding_scheme cs); diff --git a/include/osmocom/gprs/rlcmac/tbf_ul.h b/include/osmocom/gprs/rlcmac/tbf_ul.h index efe03b4..f9c5a7f 100644 --- a/include/osmocom/gprs/rlcmac/tbf_ul.h +++ b/include/osmocom/gprs/rlcmac/tbf_ul.h @@ -49,6 +49,8 @@ struct msgb *gprs_rlcmac_ul_tbf_data_create(struct gprs_rlcmac_ul_tbf *ul_tbf, const struct gprs_rlcmac_rts_block_ind *bi); struct msgb *gprs_rlcmac_ul_tbf_dummy_create(const struct gprs_rlcmac_ul_tbf *ul_tbf); +int gprs_rlcmac_ul_tbf_handle_pkt_ul_ack_nack(struct gprs_rlcmac_ul_tbf *ul_tbf, bool final_ack, + unsigned first_bsn, struct bitvec *rbb); static inline struct gprs_rlcmac_tbf *ul_tbf_as_tbf(struct gprs_rlcmac_ul_tbf *ul_tbf) { diff --git a/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h index dc50a66..1d7ff88 100644 --- a/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h +++ b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h @@ -26,7 +26,7 @@ GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START, GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT, - GPRS_RLCMAC_TBF_UL_EV_FOOBAR, + GPRS_RLCMAC_TBF_UL_EV_FINAL_ACK_RECVD, }; int gprs_rlcmac_tbf_ul_fsm_init(void); diff --git a/include/osmocom/gprs/rlcmac/types_private.h b/include/osmocom/gprs/rlcmac/types_private.h index 77a0504..8f985ba 100644 --- a/include/osmocom/gprs/rlcmac/types_private.h +++ b/include/osmocom/gprs/rlcmac/types_private.h @@ -46,3 +46,11 @@ GPRS_RLCMAC_LLC_PDU_TYPE_ACKNOWLEDGED = 0, GPRS_RLCMAC_LLC_PDU_TYPE_UNACKNOWLEDGED = 1, }; + +/* TS 44.060 12.20 "PAGE_MODE" */ +enum gprs_rlcmac_page_mode { + GPRS_RLCMAC_PAGE_MODE_NORMAL = 0, + GPRS_RLCMAC_PAGE_MODE_EXTENDED = 1, + GPRS_RLCMAC_PAGE_MODE_REORGANIZATION = 2, + GPRS_RLCMAC_PAGE_MODE_SAME_BEFORE = 3, +}; diff --git a/src/rlcmac/rlc_window.c b/src/rlcmac/rlc_window.c index d17307b..2a37e97 100644 --- a/src/rlcmac/rlc_window.c +++ b/src/rlcmac/rlc_window.c @@ -22,16 +22,10 @@ #include <osmocom/gprs/rlcmac/rlc.h> #include <osmocom/gprs/rlcmac/rlc_window.h> -#define RLC_GPRS_WS 64 /* max window size */ -#define RLC_EGPRS_MIN_WS 64 /* min window size */ -#define RLC_EGPRS_MAX_WS 1024 /* min window size */ -#define RLC_EGPRS_MAX_BSN_DELTA 512 -#define RLC_MAX_WS RLC_EGPRS_MAX_WS - void gprs_rlcmac_rlc_window_constructor(struct gprs_rlcmac_rlc_window *w) { w->sns = RLC_GPRS_SNS; - w->ws = RLC_GPRS_WS; + w->ws = GPRS_RLCMAC_GPRS_WS; } void gprs_rlcmac_rlc_window_destructor(struct gprs_rlcmac_rlc_window *w) diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c index 1e28bac..c6c23e6 100644 --- a/src/rlcmac/rlcmac.c +++ b/src/rlcmac/rlcmac.c @@ -36,6 +36,7 @@ #include <osmocom/gprs/rlcmac/csn1_defs.h> #include <osmocom/gprs/rlcmac/rlc.h> #include <osmocom/gprs/rlcmac/types_private.h> +#include <osmocom/gprs/rlcmac/rlc_window.h> #define GPRS_CODEL_SLOW_INTERVAL_MS 4000 @@ -144,6 +145,20 @@ return NULL; } +struct gprs_rlcmac_ul_tbf *gprs_rlcmac_find_ul_tbf_by_tfi(uint8_t ul_tfi) +{ + struct gprs_rlcmac_entity *gre; + + llist_for_each_entry(gre, &g_ctx->gre_list, entry) { + if (!gre->ul_tbf) + continue; + if (gre->ul_tbf->cur_alloc.ul_tfi != ul_tfi) + continue; + return gre->ul_tbf; + } + return NULL; +} + static int gprs_rlcmac_handle_ccch_imm_ass_ul_tbf(uint8_t ts_nr, const struct gsm48_imm_ass *ia, const IA_RestOctets_t *iaro) { int rc = -ENOENT; @@ -281,6 +296,45 @@ return rc; } +static int gprs_rlcmac_handle_pkt_ul_ack_nack(const struct osmo_gprs_rlcmac_prim *rlcmac_prim, const RlcMacDownlink_t *dl_block) +{ + const Packet_Uplink_Ack_Nack_t *ack = &dl_block->u.Packet_Uplink_Ack_Nack; + const PU_AckNack_GPRS_t *gprs = &ack->u.PU_AckNack_GPRS_Struct; + const Ack_Nack_Description_t *ack_desc = &gprs->Ack_Nack_Description; + struct gprs_rlcmac_ul_tbf *ul_tbf; + int bsn_begin, bsn_end; + int num_blocks; + uint8_t bits_data[GPRS_RLCMAC_GPRS_WS/8]; + char show_bits[GPRS_RLCMAC_GPRS_WS + 1]; + struct bitvec bits = { + .data = bits_data, + .data_len = sizeof(bits_data), + .cur_bit = 0, + }; + int rc; + + ul_tbf = gprs_rlcmac_find_ul_tbf_by_tfi(dl_block->TFI); + if (!ul_tbf) { + LOGRLCMAC(LOGL_INFO, "TS=%u FN=%u Rx Pkt UL ACK/NACK: UL_TBF TFI=%u not found\n", + rlcmac_prim->l1ctl.pdch_data_ind.ts_nr, + rlcmac_prim->l1ctl.pdch_data_ind.fn, + dl_block->TFI); + return -ENOENT; + } + + num_blocks = gprs_rlcmac_decode_gprs_acknack_bits( + ack_desc, &bits, &bsn_begin, &bsn_end, ul_tbf->ulw); + + LOGPTBFUL(ul_tbf, LOGL_DEBUG, + "Got GPRS UL ACK bitmap: SSN: %d, BSN %d to %d - 1 (%d blocks), \"%s\"\n", + ack_desc->STARTING_SEQUENCE_NUMBER, + bsn_begin, bsn_end, num_blocks, + (gprs_rlcmac_extract_rbb(&bits, show_bits), show_bits)); + + rc = gprs_rlcmac_ul_tbf_handle_pkt_ul_ack_nack(ul_tbf, ack_desc->FINAL_ACK_INDICATION, bsn_begin, &bits); + return rc; +} + static int gprs_rlcmac_handle_gprs_dl_ctrl_block(const struct osmo_gprs_rlcmac_prim *rlcmac_prim) { struct bitvec *bv; @@ -302,7 +356,21 @@ goto free_ret; } - LOGRLCMAC(LOGL_NOTICE, "TODO: handle decoded dl ctrl block!\n"); + LOGRLCMAC(LOGL_INFO, "TS=%u FN=%u Rx %s\n", + rlcmac_prim->l1ctl.pdch_data_ind.ts_nr, + rlcmac_prim->l1ctl.pdch_data_ind.fn, + get_value_string(osmo_gprs_rlcmac_dl_msg_type_names, dl_ctrl_block->u.MESSAGE_TYPE)); + + switch (dl_ctrl_block->u.MESSAGE_TYPE) { + case OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK: + rc = gprs_rlcmac_handle_pkt_ul_ack_nack(rlcmac_prim, dl_ctrl_block); + break; + default: + LOGRLCMAC(LOGL_ERROR, "TS=%u FN=%u Rx %s NOT SUPPORTED! ignoring\n", + rlcmac_prim->l1ctl.pdch_data_ind.ts_nr, + rlcmac_prim->l1ctl.pdch_data_ind.fn, + get_value_string(osmo_gprs_rlcmac_dl_msg_type_names, dl_ctrl_block->u.MESSAGE_TYPE)); + } free_ret: talloc_free(dl_ctrl_block); diff --git a/src/rlcmac/rlcmac_dec.c b/src/rlcmac/rlcmac_dec.c index cd44fc8..fa88c26 100644 --- a/src/rlcmac/rlcmac_dec.c +++ b/src/rlcmac/rlcmac_dec.c @@ -26,6 +26,7 @@ #include <osmocom/gprs/rlcmac/rlcmac_private.h> #include <osmocom/gprs/rlcmac/rlcmac_dec.h> #include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/rlc_window_ul.h> #define LENGTH_TO_END 255 /*! @@ -341,3 +342,83 @@ return rdbi->data_len; } + +/** + * show_rbb needs to be an array with 65 elements + * The index of the array is the bit position in the rbb + * (show_rbb[63] relates to BSN ssn-1) + */ +void gprs_rlcmac_extract_rbb(const struct bitvec *rbb, char *show_rbb) +{ + unsigned int i; + for (i = 0; i < rbb->cur_bit; i++) { + uint8_t bit; + bit = bitvec_get_bit_pos(rbb, i); + show_rbb[i] = bit == 1 ? 'R' : 'I'; + } + + show_rbb[i] = '\0'; +} + +static int handle_final_ack(struct bitvec *bits, int *bsn_begin, int *bsn_end, + struct gprs_rlcmac_rlc_ul_window *ulw) +{ + int num_blocks, i; + uint16_t v_a = gprs_rlcmac_rlc_ul_window_v_a(ulw); + + num_blocks = gprs_rlcmac_rlc_window_mod_sns_bsn(rlc_ulw_as_w(ulw), + gprs_rlcmac_rlc_ul_window_v_s(ulw) - v_a); + for (i = 0; i < num_blocks; i++) + bitvec_set_bit(bits, ONE); + + *bsn_begin = v_a; + *bsn_end = gprs_rlcmac_rlc_window_mod_sns_bsn(rlc_ulw_as_w(ulw), *bsn_begin + num_blocks); + return num_blocks; +} + +int gprs_rlcmac_decode_gprs_acknack_bits(const Ack_Nack_Description_t *desc, + struct bitvec *bits, int *bsn_begin, int *bsn_end, + struct gprs_rlcmac_rlc_ul_window *ulw) +{ + int urbb_len = GPRS_RLCMAC_GPRS_WS; + int num_blocks; + struct bitvec urbb; + + if (desc->FINAL_ACK_INDICATION) + return handle_final_ack(bits, bsn_begin, bsn_end, ulw); + + *bsn_begin = gprs_rlcmac_rlc_ul_window_v_a(ulw); + *bsn_end = desc->STARTING_SEQUENCE_NUMBER; + + num_blocks = gprs_rlcmac_rlc_window_mod_sns_bsn(rlc_ulw_as_w(ulw), *bsn_end - *bsn_begin); + + if (num_blocks < 0 || num_blocks > urbb_len) { + *bsn_end = *bsn_begin; + LOGRLCMAC(LOGL_NOTICE, "Invalid GPRS Ack/Nack window %d:%d (length %d)\n", + *bsn_begin, *bsn_end, num_blocks); + return -EINVAL; + } + + urbb.cur_bit = 0; + urbb.data = (uint8_t *)desc->RECEIVED_BLOCK_BITMAP; + urbb.data_len = sizeof(desc->RECEIVED_BLOCK_BITMAP); + + /* + * TS 44.060, 12.3: + * BSN = (SSN - bit_number) modulo 128, for bit_number = 1 to 64. + * The BSN values represented range from (SSN - 1) mod 128 to (SSN - 64) mod 128. + * + * We are only interested in the range from V(A) to SSN-1 which is + * num_blocks large. The RBB is laid out as + * [SSN-1] [SSN-2] ... [V(A)] ... [SSN-64] + * so we want to start with [V(A)] and go backwards until we reach + * [SSN-1] to get the needed BSNs in an increasing order. Note that + * the bit numbers are counted from the end of the buffer. + */ + for (int i = num_blocks; i > 0; i--) { + int is_ack = bitvec_get_bit_pos(&urbb, urbb_len - i); + bitvec_set_bit(bits, is_ack == 1 ? ONE : ZERO); + } + + return num_blocks; +} diff --git a/src/rlcmac/tbf_ul.c b/src/rlcmac/tbf_ul.c index 906efbe..ae6b156 100644 --- a/src/rlcmac/tbf_ul.c +++ b/src/rlcmac/tbf_ul.c @@ -22,6 +22,7 @@ #include <osmocom/core/bitvec.h> #include <osmocom/gprs/rlcmac/tbf_ul.h> +#include <osmocom/gprs/rlcmac/rlcmac_dec.h> #include <osmocom/gprs/rlcmac/rlcmac_enc.h> #include <osmocom/gprs/rlcmac/gre.h> #include <osmocom/gprs/rlcmac/coding_scheme.h> @@ -110,6 +111,74 @@ return (st == GPRS_RLCMAC_TBF_UL_ST_FLOW); } +static int gprs_rlcmac_ul_tbf_update_window(struct gprs_rlcmac_ul_tbf *ul_tbf, + unsigned first_bsn, struct bitvec *rbb) +{ + unsigned dist; + uint16_t lost = 0, received = 0; + char show_v_b[RLC_MAX_SNS + 1]; + char show_rbb[RLC_MAX_SNS + 1]; + dist = gprs_rlcmac_rlc_ul_window_distance(ul_tbf->ulw); + unsigned num_blocks = rbb->cur_bit > dist + ? dist : rbb->cur_bit; + unsigned behind_last_bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(ul_tbf->w, first_bsn + num_blocks); + + gprs_rlcmac_extract_rbb(rbb, show_rbb); + /* show received array in debug */ + LOGPTBFUL(ul_tbf, LOGL_DEBUG, + "ack: (BSN=%d)\"%s\"(BSN=%d) R=ACK I=NACK\n", + first_bsn, show_rbb, + gprs_rlcmac_rlc_window_mod_sns_bsn(ul_tbf->w, behind_last_bsn - 1)); + + gprs_rlcmac_rlc_ul_window_update(ul_tbf->ulw, rbb, first_bsn, &lost, &received); + + /* raise V(A), if possible */ + gprs_rlcmac_rlc_ul_window_raise(ul_tbf->ulw, + gprs_rlcmac_rlc_ul_window_move_window(ul_tbf->ulw)); + + /* show receive state array in debug (V(A)..V(S)-1) */ + gprs_rlcmac_rlc_ul_window_show_state(ul_tbf->ulw, show_v_b); + LOGPTBFUL(ul_tbf, LOGL_DEBUG, + "V(B): (V(A)=%d)\"%s\"(V(S)-1=%d) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid\n", + gprs_rlcmac_rlc_ul_window_v_a(ul_tbf->ulw), show_v_b, + gprs_rlcmac_rlc_ul_window_v_s_mod(ul_tbf->ulw, -1)); + return 0; +} + +int gprs_rlcmac_ul_tbf_handle_final_ack(struct gprs_rlcmac_ul_tbf *ul_tbf) +{ + int rc = 0; + + osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi, GPRS_RLCMAC_TBF_UL_EV_FINAL_ACK_RECVD, NULL); + + /* range V(A)..V(S)-1 */ + //received = gprs_rlcmac_rlc_ul_window_count_unacked(ul_tbf->ulw); + /* report all outstanding packets as received */ + //gprs_rlcmac_received_lost(this, received, 0); + gprs_rlcmac_rlc_ul_window_reset(ul_tbf->ulw); + + /* TODO: check for RRBP and attempt to create a new UL TBF if + * gprs_rlcmac_ul_tbf_have_data(ul_tbf) */ + return rc; +} + +int gprs_rlcmac_ul_tbf_handle_pkt_ul_ack_nack(struct gprs_rlcmac_ul_tbf *ul_tbf, bool final_ack, + unsigned first_bsn, struct bitvec *rbb) +{ + int rc; + rc = gprs_rlcmac_ul_tbf_update_window(ul_tbf, first_bsn, rbb); + + if (final_ack) { + LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Final ACK received.\n"); + rc = gprs_rlcmac_ul_tbf_handle_final_ack(ul_tbf); + } else if (gprs_rlcmac_tbf_ul_state(ul_tbf) && + gprs_rlcmac_rlc_ul_window_window_empty(ul_tbf->ulw)) { + LOGPTBFUL(ul_tbf, LOGL_NOTICE, + "Received acknowledge of all blocks, but without final ack indication (don't worry)\n"); + } + return rc; +} + struct msgb *gprs_rlcmac_ul_tbf_dummy_create(const struct gprs_rlcmac_ul_tbf *ul_tbf) { struct msgb *msg; diff --git a/src/rlcmac/tbf_ul_fsm.c b/src/rlcmac/tbf_ul_fsm.c index 5095605..3cff899 100644 --- a/src/rlcmac/tbf_ul_fsm.c +++ b/src/rlcmac/tbf_ul_fsm.c @@ -33,7 +33,7 @@ { GPRS_RLCMAC_TBF_UL_EV_UL_ASS_START, "UL_ASS_START" }, { GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, "UL_ASS_COMPL" }, { GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT, "LAST_UL_DATA_SENT" }, - { GPRS_RLCMAC_TBF_UL_EV_FOOBAR, "FOOBAR" }, + { GPRS_RLCMAC_TBF_UL_EV_FINAL_ACK_RECVD, "FINAL_ACK_RECVD" }, { 0, NULL } }; @@ -118,6 +118,8 @@ { //struct gprs_rlcmac_tbf_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx *)fi->priv; switch (event) { + case GPRS_RLCMAC_TBF_UL_EV_FINAL_ACK_RECVD: + break; default: OSMO_ASSERT(0); } @@ -151,7 +153,7 @@ }, [GPRS_RLCMAC_TBF_UL_ST_FINISHED] = { .in_event_mask = - X(GPRS_RLCMAC_TBF_UL_EV_FOOBAR), + X(GPRS_RLCMAC_TBF_UL_EV_FINAL_ACK_RECVD), .out_state_mask = X(GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN), .name = "FINISHED", diff --git a/tests/rlcmac/rlcmac_prim_test.c b/tests/rlcmac/rlcmac_prim_test.c index 2711889..658ddb3 100644 --- a/tests/rlcmac/rlcmac_prim_test.c +++ b/tests/rlcmac/rlcmac_prim_test.c @@ -23,8 +23,10 @@ #include <osmocom/core/fsm.h> #include <osmocom/gprs/rlcmac/rlcmac.h> +#include <osmocom/gprs/rlcmac/csn1_defs.h> #include <osmocom/gprs/rlcmac/gre.h> #include <osmocom/gprs/rlcmac/rlc.h> +#include <osmocom/gprs/rlcmac/rlc_window.h> #include <osmocom/gprs/rlcmac/types_private.h> #include <osmocom/gprs/rlcmac/sched.h> @@ -192,6 +194,64 @@ return fn % GSM_MAX_FN; } +static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block_buf(uint8_t *buf, int num_bytes, uint8_t tn, uint32_t fn) +{ + struct osmo_gprs_rlcmac_prim *rlcmac_prim; + + + rlcmac_prim = osmo_gprs_rlcmac_prim_alloc_l1ctl_pdch_data_ind(tn, fn, 0, 0, 0, + NULL, num_bytes); + rlcmac_prim->l1ctl.pdch_data_ind.data = msgb_put(rlcmac_prim->oph.msg, num_bytes); + memcpy(rlcmac_prim->l1ctl.pdch_data_ind.data, buf, num_bytes); + return rlcmac_prim; +} + +static struct osmo_gprs_rlcmac_prim *create_dl_ctrl_block(RlcMacDownlink_t *dl_block, uint8_t tn, uint32_t fn) +{ + struct bitvec *rlc_block; + uint8_t buf[64]; + int num_bytes; + + rlc_block = bitvec_alloc(23, tall_ctx); + + OSMO_ASSERT(osmo_gprs_rlcmac_encode_downlink(rlc_block, dl_block) == 0); + num_bytes = bitvec_pack(rlc_block, &buf[0]); + OSMO_ASSERT((size_t)num_bytes < sizeof(buf)); + bitvec_free(rlc_block); + + return create_dl_ctrl_block_buf(&buf[0], num_bytes, tn, fn); +} + +static void ul_ack_nack_init(RlcMacDownlink_t *dl_block, uint8_t ul_tfi, enum gprs_rlcmac_coding_scheme cs) +{ + Packet_Uplink_Ack_Nack_t *ack = &dl_block->u.Packet_Uplink_Ack_Nack; + PU_AckNack_GPRS_t *gprs = &ack->u.PU_AckNack_GPRS_Struct; + + memset(dl_block, 0, sizeof(*dl_block)); + dl_block->PAYLOAD_TYPE = GPRS_RLCMAC_PT_CONTROL_BLOCK; + dl_block->RRBP = 0; + dl_block->SP = 0; + dl_block->USF = 0x00; + dl_block->u.MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK; + + ack->MESSAGE_TYPE = OSMO_GPRS_RLCMAC_DL_MSGT_PACKET_UPLINK_ACK_NACK; + ack->PAGE_MODE = GPRS_RLCMAC_PAGE_MODE_NORMAL; + ack->UPLINK_TFI = ul_tfi; + ack->UnionType = 0; /* GPRS */ + + gprs->CHANNEL_CODING_COMMAND = cs; +} + +static void ul_ack_nack_mark(Ack_Nack_Description_t *ack_desc, unsigned int idx, bool received) +{ + //ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - 1] = 0xff; + //memset(ack_desc->RECEIVED_BLOCK_BITMAP, 0xff, sizeof(ack_desc->RECEIVED_BLOCK_BITMAP)); + if (received) + ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] |= (1 << (idx & 0x03)); + else + ack_desc->RECEIVED_BLOCK_BITMAP[sizeof(ack_desc->RECEIVED_BLOCK_BITMAP) - idx/8 - 1] &= ~(1 << (idx & 0x03)); +} + static int test_rlcmac_prim_up_cb(struct osmo_gprs_rlcmac_prim *rlcmac_prim, void *user_data) { const char *pdu_name = osmo_gprs_rlcmac_prim_name(rlcmac_prim); @@ -306,7 +366,10 @@ printf("=== %s start ===\n", __func__); prepare_test(); + RlcMacDownlink_t dl_block; + Ack_Nack_Description_t *ack_desc = &dl_block.u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description; uint32_t tlli = 0x2342; + uint8_t ul_tfi = 0; uint8_t ts_nr = 7; uint8_t usf = 0; uint32_t rts_fn = 4; @@ -330,7 +393,16 @@ rts_fn = fn_next_block(rts_fn); 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); + /* PCU acks it: */ + ul_ack_nack_init(&dl_block, ul_tfi, GPRS_RLCMAC_CS_2); + ack_desc->STARTING_SEQUENCE_NUMBER = 1; + ack_desc->FINAL_ACK_INDICATION = 1; + ul_ack_nack_mark(ack_desc, 0, true); + ul_ack_nack_mark(ack_desc, 1, true); + rlcmac_prim = create_dl_ctrl_block(&dl_block, ts_nr, rts_fn); + rc = osmo_gprs_rlcmac_prim_lower_up(rlcmac_prim); OSMO_ASSERT(rc == 0); printf("=== %s end ===\n", __func__); diff --git a/tests/rlcmac/rlcmac_prim_test.err b/tests/rlcmac/rlcmac_prim_test.err index 2e82b2e..8f7283d 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -37,6 +37,15 @@ DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying 1 RLC blocks, 1 BSNs DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Copying data unit 0 (BSN 1) DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) msg block (BSN 1, CS-2): 00 00 02 0d e2 18 f2 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 2b 00 +DLGLOBAL INFO Rx from lower layers: L1CTL-PDCH_DATA.indication +DLGLOBAL INFO TS=7 FN=8 Rx Pkt UL ACK/NACK +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Got GPRS UL ACK bitmap: SSN: 1, BSN 0 to 2 - 1 (2 blocks), "RR" +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) ack: (BSN=0)"RR"(BSN=1) R=ACK I=NACK +DLGLOBAL DEBUG - got ack for BSN=0 +DLGLOBAL DEBUG - got ack for BSN=1 +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) V(B): (V(A)=2)""(V(S)-1=1) A=Acked N=Nacked U=Unacked X=Resend-Unacked I=Invalid +DLGLOBAL DEBUG TBF(UL:NR-0:TLLI-00002342) Final ACK received. +DLGLOBAL INFO UL_TBF{FINISHED}: Received Event FINAL_ACK_RECVD DLGLOBAL INFO UL_TBF_ASS{IDLE}: Deallocated DLGLOBAL INFO UL_TBF{FINISHED}: Deallocated DLGLOBAL INFO Rx from upper layers: GMMRR-ASSIGN.request -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31344
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I5e3d8e77042d3ad1618e6b62bc1a377a93239580 Gerrit-Change-Number: 31344 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-MessageType: merged
2 years, 4 months
1
0
0
0
[L] Change in libosmo-gprs[master]: rlcmac: Implement T3166
by pespin
pespin has posted comments on this change. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31350
) Change subject: rlcmac: Implement T3166 ...................................................................... Patch Set 2: Code-Review+2 -- To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31350
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs Gerrit-Branch: master Gerrit-Change-Id: I58dd757cbb5d8279fdf7fbdfb6f38ec0119c6156 Gerrit-Change-Number: 31350 Gerrit-PatchSet: 2 Gerrit-Owner: pespin <pespin(a)sysmocom.de> Gerrit-Reviewer: Jenkins Builder Gerrit-Reviewer: laforge <laforge(a)osmocom.org> Gerrit-Reviewer: pespin <pespin(a)sysmocom.de> Gerrit-Comment-Date: Mon, 20 Feb 2023 12:49:40 +0000 Gerrit-HasComments: No Gerrit-Has-Labels: Yes Gerrit-MessageType: comment
2 years, 4 months
1
0
0
0
← Newer
1
...
103
104
105
106
107
108
109
...
261
Older →
Jump to page:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
Results per page:
10
25
50
100
200