pespin submitted this change.
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(-)
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@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 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@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 change 31320. To unsubscribe, or for help writing mail filters, visit settings.