pespin has uploaded this change for review. ( 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, 978 insertions(+), 12 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-gprs refs/changes/20/31320/1
diff --git a/include/osmocom/gprs/rlcmac/Makefile.am b/include/osmocom/gprs/rlcmac/Makefile.am index 02d1a70..e061bf0 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 8ca5370..c255a24 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 ce97ea8..6421030 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 bdbb6a4..a940b97 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@eversberg.eu + * (C) 2023 by sysmocom - s.f.m.c. GmbH info@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 1b9d7a8..c9d9634 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> @@ -297,29 +298,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@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 82a3847..0eb58ee 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; + #if 0 err_tbf_destruct: gprs_rlcmac_tbf_destructor(dl_tbf_as_tbf(dl_tbf)); @@ -54,6 +64,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)); @@ -83,3 +102,161 @@ 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); } + +/* + * 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(this->state_fi, TBF_EV_LAST_UL_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 9654fc4..8502314 100644 --- a/tests/rlcmac/rlcmac_prim_test.err +++ b/tests/rlcmac/rlcmac_prim_test.err @@ -34,3 +34,15 @@ DLGLOBAL INFO TBF(DL:NR-0:TLLI-00000001) Send L1CTL-CF_DL_TBF.req dl_slotmask=0x80 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 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 ===