pespin has uploaded this change for review. (
https://gerrit.osmocom.org/c/libosmo-gprs/+/31170 )
Change subject: rlcmac: Initial ul_tbf data support
......................................................................
rlcmac: Initial ul_tbf data support
This is basically an import of relevant code from osmo-pcu.git
08523c2286dfe27402f8e2eb4b98da5a529c7915 which allows encoding LLC
frames into RLC/MAC uplink data blocks.
A 1-to-1 import was not possible for several reasons:
* osmo-pcu uses C++ in some places
* osmo-pcu implements encoding of DL data blocks, not UL data blocks
* Some osmo-pcu code is still not really polished and stuff is still
mixed (for instance rlc_window(_ul).c/h were split into their own
files here).
Hence this was not a "copy all files and leave as they are" import, but
rather looking at the encode path starting on osmo-pcu.git scheduler and
see which code had to be pulled in to encode the RLC/MAC blocks.
Due to the slightly different requerirements for UL and DL blocks, it is
well possible that further work in needed in order to have the code
produce correct blocks. This is only a first step to pull in all code so
that stuff can be fixed in smallish incremental steps.
Related: OS#5500
Change-Id: I0a01d79d16bbfc63aa88e6bb0f432f3772645730
---
M include/osmocom/gprs/rlcmac/Makefile.am
A include/osmocom/gprs/rlcmac/coding_scheme.h
A include/osmocom/gprs/rlcmac/rlc.h
A include/osmocom/gprs/rlcmac/rlc_window.h
A include/osmocom/gprs/rlcmac/rlc_window_ul.h
M include/osmocom/gprs/rlcmac/rlcmac_enc.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 src/rlcmac/Makefile.am
A src/rlcmac/coding_scheme.c
A src/rlcmac/rlc.c
A src/rlcmac/rlc_window.c
A src/rlcmac/rlc_window_ul.c
M src/rlcmac/rlcmac.c
M src/rlcmac/rlcmac_enc.c
M src/rlcmac/tbf_ul.c
M src/rlcmac/tbf_ul_fsm.c
18 files changed, 2,525 insertions(+), 8 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/libosmo-gprs refs/changes/70/31170/1
diff --git a/include/osmocom/gprs/rlcmac/Makefile.am
b/include/osmocom/gprs/rlcmac/Makefile.am
index c529a4e..3f04ee3 100644
--- a/include/osmocom/gprs/rlcmac/Makefile.am
+++ b/include/osmocom/gprs/rlcmac/Makefile.am
@@ -1,7 +1,11 @@
noinst_HEADERS = \
codel.h \
+ coding_scheme.h \
gre.h \
llc_queue.h \
+ rlc.h \
+ rlc_window.h \
+ rlc_window_ul.h \
rlcmac_enc.h \
rlcmac_private.h \
sched.h \
diff --git a/include/osmocom/gprs/rlcmac/coding_scheme.h
b/include/osmocom/gprs/rlcmac/coding_scheme.h
new file mode 100644
index 0000000..31692a5
--- /dev/null
+++ b/include/osmocom/gprs/rlcmac/coding_scheme.h
@@ -0,0 +1,106 @@
+/* coding_scheme.h
+ *
+ * Copyright (C) 2015-2023 by sysmocom s.f.m.c. GmbH
+ *
+ * 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 <osmocom/core/utils.h>
+
+#include <stdbool.h>
+
+enum gprs_rlcmac_coding_scheme {
+ GPRS_RLCMAC_CS_UNKNOWN,
+ /* GPRS Coding Schemes: */
+ GPRS_RLCMAC_CS_1,
+ GPRS_RLCMAC_CS_2,
+ GPRS_RLCMAC_CS_3,
+ GPRS_RLCMAC_CS_4,
+ /* EDGE/EGPRS Modulation and Coding Schemes: */
+ GPRS_RLCMAC_MCS_1,
+ GPRS_RLCMAC_MCS_2,
+ GPRS_RLCMAC_MCS_3,
+ GPRS_RLCMAC_MCS_4,
+ GPRS_RLCMAC_MCS_5,
+ GPRS_RLCMAC_MCS_6,
+ GPRS_RLCMAC_MCS_7,
+ GPRS_RLCMAC_MCS_8,
+ GPRS_RLCMAC_MCS_9,
+ GPRS_RLCMAC_NUM_SCHEMES
+};
+
+enum gprs_rlcmac_coding_scheme_kind {
+ GPRS_RLCMAC_SCHEME_GPRS,
+ GPRS_RLCMAC_SCHEME_EGPRS_GMSK,
+ GPRS_RLCMAC_SCHEME_EGPRS,
+};
+
+enum gprs_rlcmac_egprs_arq_type {
+ GPRS_RLCMAC_EGPRS_ARQ1 = 0,
+ GPRS_RLCMAC_EGPRS_ARQ2 = 1,
+};
+
+extern const struct value_string gprs_rlcmac_coding_scheme_names[];
+const char *gprs_rlcmac_mcs_name(enum gprs_rlcmac_coding_scheme val);
+enum gprs_rlcmac_coding_scheme gprs_rlcmac_get_retx_mcs(enum gprs_rlcmac_coding_scheme
initial_mcs, enum gprs_rlcmac_coding_scheme commanded_mcs, bool resegment_bit);
+
+bool gprs_rlcmac_mcs_is_gprs(enum gprs_rlcmac_coding_scheme cs);
+bool gprs_rlcmac_mcs_is_edge(enum gprs_rlcmac_coding_scheme cs);
+bool gprs_rlcmac_mcs_is_edge_gmsk(enum gprs_rlcmac_coding_scheme cs);
+
+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_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);
+bool gprs_rlcmac_mcs_is_compat(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme o);
+bool gprs_rlcmac_mcs_is_compat_kind(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme_kind mode);
+
+uint8_t gprs_rlcmac_mcs_size_ul(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_size_dl(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_used_size_ul(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_used_size_dl(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_max_bytes_ul(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_max_bytes_dl(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_spare_bits_ul(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_spare_bits_dl(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_max_data_block_bytes(enum gprs_rlcmac_coding_scheme cs);
+uint8_t gprs_rlcmac_mcs_opt_padding_bits(enum gprs_rlcmac_coding_scheme cs);
+
+void gprs_rlcmac_mcs_inc_kind(enum gprs_rlcmac_coding_scheme *cs, enum
gprs_rlcmac_coding_scheme_kind mode);
+void gprs_rlcmac_mcs_dec_kind(enum gprs_rlcmac_coding_scheme *cs, enum
gprs_rlcmac_coding_scheme_kind mode);
+void gprs_rlcmac_mcs_inc(enum gprs_rlcmac_coding_scheme *cs);
+void gprs_rlcmac_mcs_dec(enum gprs_rlcmac_coding_scheme *cs);
+
+bool gprs_rlcmac_mcs_is_family_compat(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme o);
+void gprs_rlcmac_mcs_dec_to_single_block(enum gprs_rlcmac_coding_scheme *cs, bool
*need_stuffing);
+
+enum gprs_rlcmac_header_type {
+ GPRS_RLCMAC_HEADER_INVALID,
+ GPRS_RLCMAC_HEADER_GPRS_CONTROL,
+ GPRS_RLCMAC_HEADER_GPRS_DATA,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3,
+ GPRS_RLCMAC_NUM_HEADER_TYPES
+};
+
+enum gprs_rlcmac_header_type gprs_rlcmac_mcs_header_type(enum gprs_rlcmac_coding_scheme
mcs);
+
+uint8_t gprs_rlcmac_num_data_blocks(enum gprs_rlcmac_header_type ht);
+uint8_t gprs_rlcmac_num_data_header_bits_UL(enum gprs_rlcmac_header_type ht);
+uint8_t gprs_rlcmac_num_data_header_bits_DL(enum gprs_rlcmac_header_type ht);
+uint8_t gprs_rlcmac_num_data_block_header_bits(enum gprs_rlcmac_header_type ht);
+
+const char *gprs_rlcmac_msc_kind_name(enum gprs_rlcmac_coding_scheme_kind val);
diff --git a/include/osmocom/gprs/rlcmac/rlc.h b/include/osmocom/gprs/rlcmac/rlc.h
new file mode 100644
index 0000000..107b32b
--- /dev/null
+++ b/include/osmocom/gprs/rlcmac/rlc.h
@@ -0,0 +1,208 @@
+/* RLC block manipulation, 3GPP TS 44.060 */
+#pragma once
+
+#include <stdint.h>
+
+#include <osmocom/gprs/rlcmac/coding_scheme.h>
+
+#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
+#define RLC_EGPRS_SNS 2048 /* EGPRS, must be power of 2 */
+#define RLC_EGPRS_MAX_BSN_DELTA 512
+#define RLC_MAX_SNS RLC_EGPRS_SNS
+#define RLC_MAX_LEN 74 /* MCS-9 data unit */
+
+static inline uint16_t mod_sns_half(void)
+{
+ return (RLC_MAX_SNS / 2) - 1;
+}
+
+struct gprs_rlcmac_rlc_block_info {
+ unsigned int data_len; /* EGPRS: N2, GPRS: N2-2, N-2 */
+ unsigned int bsn;
+ unsigned int ti;
+ unsigned int e;
+ unsigned int cv; /* FBI == 1 <=> CV == 0 */
+ unsigned int pi;
+ unsigned int spb;
+};
+void gprs_rlcmac_rlc_block_info_init(struct gprs_rlcmac_rlc_block_info *rdbi, enum
gprs_rlcmac_coding_scheme cs, bool with_padding, const unsigned int spb);
+
+struct gprs_rlcmac_rlc_data_info {
+ enum gprs_rlcmac_coding_scheme cs;
+ unsigned int r;
+ unsigned int si;
+ unsigned int tfi;
+ unsigned int cps;
+ unsigned int rsb;
+ unsigned int usf;
+ unsigned int es_p;
+ unsigned int rrbp;
+ unsigned int pr;
+ 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];
+ struct gprs_rlcmac_rlc_block_info block_info[2];
+};
+void gprs_rlcmac_rlc_data_info_init_ul(struct gprs_rlcmac_rlc_data_info *rlc,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding, unsigned int spb);
+void gprs_rlcmac_rlc_data_info_init_dl(struct gprs_rlcmac_rlc_data_info *rlc,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding);
+
+/*
+ * EGPRS resegment status information for UL
+ * When only first split block is received bsn state
+ * will be set to EGPRS_RESEG_FIRST_SEG_RXD and when
+ * only second segment is received the state will be
+ * set to EGPRS_RESEG_SECOND_SEG_RXD. When both Split
+ * blocks are received the state will be set to
+ * EGPRS_RESEG_DEFAULT
+ * The EGPRS resegmentation feature allows MS to retransmit
+ * RLC blocks of HeaderType1, HeaderType2 by segmenting
+ * them to 2 HeaderType3 blocks(Example MCS5 will be
+ * retransmitted as 2 MCS2 blocks). Table 10.4.8b.1 of 44.060
+ * explains the possible values of SPB in HeadrType3 for UL
+ * direction. When the MCS is changed at the PCU, PCU directs the
+ * changed MCS to MS by PUAN or UPLINK ASSIGNMENT message along
+ * with RESEGMENT flag, Then MS may decide to retransmit the
+ * blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
+ * The retransmission MCS is calculated based on current MCS of
+ * the Block and demanded MCS by PCU. Section 10.3a.4.3 of 44.060
+ * shows the HeadrType3 with SPB field present in it
+*/
+enum gprs_rlcmac_rlc_egprs_dl_reseg_bsn_state {
+ GPRS_RLCMAC_EGPRS_RESEG_DEFAULT = 0,
+ GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_RXD = 0x01,
+ GPRS_RLCMAC_EGPRS_RESEG_SECOND_SEG_RXD = 0x02,
+ GPRS_RLCMAC_EGPRS_RESEG_INVALID = 0x04
+};
+
+/*
+ * EGPRS resegment status information for DL
+ * When only first segment is sent, bsn state
+ * will be set to EGPRS_RESEG_FIRST_SEG_SENT and when
+ * second segment is sent the state will be
+ * set to EGPRS_RESEG_SECOND_SEG_SENT.
+ * EGPRS_RESEG_DL_INVALID is set to 8 considering there is a scope for
+ * 3rd segment according to Table 10.4.8b.2 of 44.060
+ * The EGPRS resegmentation feature allows PCU to retransmit
+ * RLC blocks of HeaderType1, HeaderType2 by segmenting
+ * them to 2 HeaderType3 blocks(Example MCS5 will be
+ * retransmitted as 2 MCS2 blocks). Table 10.4.8b.2 of 44.060
+ * explains the possible values of SPB in HeadrType3 for DL
+ * direction.The PCU decides to retransmit the
+ * blocks by resegmenting it based on Table 8.1.1.1 of 44.060.
+ * The retransmission MCS is calculated based on current MCS of
+ * the Block and demanded MCS by PCU. Section 10.3a.3.3 of 44.060
+ * shows the HeadrType3 with SPB field present in it
+ */
+enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state {
+ GPRS_RLCMAC_EGPRS_RESEG_UL_DEFAULT = 0,
+ GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_SENT = 0x01,
+ GPRS_RLCMAC_EGPRS_RESEG_SECOND_SEG_SENT = 0x02,
+ GPRS_RLCMAC_EGPRS_RESEG_UL_INVALID = 0x08
+};
+
+/* holds the current status of the block w.r.t UL/DL split blocks */
+union split_block_status {
+ enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state block_status_ul;
+ enum gprs_rlcmac_rlc_egprs_dl_reseg_bsn_state block_status_dl;
+};
+
+/* Table 10.4.8b.2 of 44.060 */
+enum gprs_rlcmac_rlc_egprs_ul_spb {
+ GPRS_RLCMAC_EGPRS_UL_SPB_NO_RETX = 0,
+ GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_10PAD = 1,
+ GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_6NOPAD = 2,
+ GPRS_RLCMAC_EGPRS_UL_SPB_SEC_SEG = 3,
+};
+
+#if 0
+/* Table 10.4.8b.2 of 44.060 */
+enum gprs_rlcmac_rlc_egprs_dl_spb {
+ GPRS_RLCMAC_EGPRS_DL_SPB_NO_RETX = 0,
+ GPRS_RLCMAC_EGPRS_DL_SPB_FIRST_SEG = 2,
+ GPRS_RLCMAC_EGPRS_DL_SPB_SEC_SEG = 3,
+};
+#endif
+
+/*
+ * Valid puncturing scheme values
+ * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
+ */
+enum gprs_rlcmac_egprs_puncturing_values {
+ GPRS_RLCMAC_EGPRS_PS_1,
+ GPRS_RLCMAC_EGPRS_PS_2,
+ GPRS_RLCMAC_EGPRS_PS_3,
+ GPRS_RLCMAC_EGPRS_PS_INVALID,
+};
+
+/*
+ * EGPRS_MAX_PS_NUM_2 is valid for MCS 1,2,5,6.
+ * And EGPRS_MAX_PS_NUM_3 is valid for MCS 3,4,7,8,9
+ * TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1
+ */
+enum gprs_rlcmac_egprs_puncturing_types {
+ GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2 = 2,
+ GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3,
+ GPRS_RLCMAC_EGPRS_MAX_PS_NUM_INVALID,
+};
+
+struct gprs_rlcmac_rlc_block {
+ /* block data including LI headers */
+ uint8_t buf[RLC_MAX_LEN];
+ /* block data len including LI headers*/
+ uint8_t len;
+
+ struct gprs_rlcmac_rlc_block_info block_info;
+ /*
+ * cs_current_trans is variable to hold the cs_last value for
+ * current transmission. cs_current_trans is same as cs_last during
+ * transmission case. during retransmission cs_current_trans is
+ * fetched from egprs_mcs_retx_tbl table based on
+ * cs and demanded cs.reference is 44.060 Table
+ * 8.1.1.1 and Table 8.1.1.2
+ * For UL. cs_last shall be used everywhere.
+ */
+ enum gprs_rlcmac_coding_scheme cs_current_trans;
+ enum gprs_rlcmac_coding_scheme cs_last;
+
+ /*
+ * The MCS of initial transmission of a BSN
+ * This variable is used for split block
+ * processing in DL
+ */
+ enum gprs_rlcmac_coding_scheme cs_init;
+
+ /* puncturing scheme value to be used for next transmission*/
+ enum gprs_rlcmac_egprs_puncturing_values next_ps;
+
+ /* holds the status of the block w.r.t UL/DL split blocks*/
+ union split_block_status spb_status;
+};
+uint8_t *gprs_rlcmac_rlc_block_prepare(struct gprs_rlcmac_rlc_block *blk, size_t
block_data_len);
+
+/*
+ * I hold the currently transferred blocks and will provide
+ * the routines to manipulate these arrays.
+ */
+struct gprs_rlcmac_rlc_block_store {
+ struct gprs_rlcmac_rlc_block blocks[RLC_MAX_SNS/2];
+};
+
+struct gprs_rlcmac_rlc_block_store *gprs_rlcmac_rlc_block_store_alloc(void *ctx);
+void gprs_rlcmac_rlc_block_store_free(struct gprs_rlcmac_rlc_block_store *blkst);
+
+struct gprs_rlcmac_rlc_block *gprs_rlcmac_rlc_block_store_get_block(struct
gprs_rlcmac_rlc_block_store *blkst, int bsn);
+
+unsigned int gprs_rlcmac_rlc_mcs_cps(enum gprs_rlcmac_coding_scheme cs,
+ enum gprs_rlcmac_egprs_puncturing_values punct,
+ enum gprs_rlcmac_egprs_puncturing_values punct2,
+ bool with_padding);
+enum gprs_rlcmac_egprs_puncturing_values gprs_rlcmac_get_punct_scheme(enum
gprs_rlcmac_egprs_puncturing_values punct,
+ enum gprs_rlcmac_coding_scheme cs,
+ enum gprs_rlcmac_coding_scheme cs_current_trans,
+ enum gprs_rlcmac_rlc_egprs_ul_spb spb);
+void gprs_rlcmac_update_punct_scheme(enum gprs_rlcmac_egprs_puncturing_values *punct,
+ enum gprs_rlcmac_coding_scheme cs);
diff --git a/include/osmocom/gprs/rlcmac/rlc_window.h
b/include/osmocom/gprs/rlcmac/rlc_window.h
new file mode 100644
index 0000000..a5d89ab
--- /dev/null
+++ b/include/osmocom/gprs/rlcmac/rlc_window.h
@@ -0,0 +1,22 @@
+/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060 */
+#pragma once
+
+#include <stdint.h>
+
+struct gprs_rlcmac_rlc_window {
+ uint16_t sns;
+ uint16_t ws;
+};
+
+struct gprs_rlcmac_rlc_window;
+
+void gprs_rlcmac_rlc_window_constructor(struct gprs_rlcmac_rlc_window *w);
+void gprs_rlcmac_rlc_window_destructor(struct gprs_rlcmac_rlc_window *w);
+
+uint16_t gprs_rlcmac_rlc_window_mod_sns(const struct gprs_rlcmac_rlc_window *w);
+uint16_t gprs_rlcmac_rlc_window_mod_sns_bsn(const struct gprs_rlcmac_rlc_window *w,
uint16_t bsn);
+uint16_t gprs_rlcmac_rlc_window_sns(const struct gprs_rlcmac_rlc_window *w);
+uint16_t gprs_rlcmac_rlc_window_ws(const struct gprs_rlcmac_rlc_window *w);
+
+void gprs_rlcmac_rlc_window_set_sns(struct gprs_rlcmac_rlc_window *w, uint16_t sns);
+void gprs_rlcmac_rlc_window_set_ws(struct gprs_rlcmac_rlc_window *w, uint16_t ws);
diff --git a/include/osmocom/gprs/rlcmac/rlc_window_ul.h
b/include/osmocom/gprs/rlcmac/rlc_window_ul.h
new file mode 100644
index 0000000..d36e650
--- /dev/null
+++ b/include/osmocom/gprs/rlcmac/rlc_window_ul.h
@@ -0,0 +1,98 @@
+/* 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_ul_tbf;
+
+enum gprs_rlcmac_rlc_ul_bsn_state {
+ GPRS_RLCMAC_RLC_UL_BSN_INVALID,
+ GPRS_RLCMAC_RLC_UL_BSN_NACKED,
+ GPRS_RLCMAC_RLC_UL_BSN_ACKED,
+ GPRS_RLCMAC_RLC_UL_BSN_UNACKED,
+ GPRS_RLCMAC_RLC_UL_BSN_RESEND,
+ GPRS_RLCMAC_RLC_UL_BSN_MAX,
+};
+
+struct gprs_rlcmac_rlc_v_b {
+ enum gprs_rlcmac_rlc_ul_bsn_state v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
+};
+
+void gprs_rlcmac_rlc_v_b_reset(struct gprs_rlcmac_rlc_v_b *v_b);
+/* Check for an individual frame */
+bool gprs_rlcmac_rlc_v_b_is_unacked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+bool gprs_rlcmac_rlc_v_b_is_nacked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+bool gprs_rlcmac_rlc_v_b_is_acked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+bool gprs_rlcmac_rlc_v_b_is_resend(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+bool gprs_rlcmac_rlc_v_b_is_invalid(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+enum gprs_rlcmac_rlc_ul_bsn_state gprs_rlcmac_rlc_v_b_get_state(const struct
gprs_rlcmac_rlc_v_b *v_b, int bsn);
+
+/* Mark a RLC frame for something */
+void gprs_rlcmac_rlc_v_b_mark_unacked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+void gprs_rlcmac_rlc_v_b_mark_nacked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+void gprs_rlcmac_rlc_v_b_mark_acked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+void gprs_rlcmac_rlc_v_b_mark_resend(struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+void gprs_rlcmac_rlc_v_b_mark_invalid(struct gprs_rlcmac_rlc_v_b *v_b, int bsn);
+
+
+struct gprs_rlcmac_rlc_ul_window {
+ struct gprs_rlcmac_rlc_window window; /* parent */
+ struct gprs_rlcmac_ul_tbf *ul_tbf; /* backpointer */
+
+ uint16_t v_s; /* send state */
+ uint16_t v_a; /* ack state */
+ struct gprs_rlcmac_rlc_v_b v_b;
+};
+
+struct gprs_rlcmac_rlc_ul_window *gprs_rlcmac_rlc_ul_window_alloc(struct
gprs_rlcmac_ul_tbf *ul_tbf);
+void gprs_rlcmac_rlc_ul_window_free(struct gprs_rlcmac_rlc_ul_window *ulw);
+
+void gprs_rlcmac_rlc_ul_window_reset(struct gprs_rlcmac_rlc_ul_window *ulw);
+
+bool gprs_rlcmac_rlc_ul_window_window_stalled(const struct gprs_rlcmac_rlc_ul_window
*ulw);
+bool gprs_rlcmac_rlc_ul_window_window_empty(const struct gprs_rlcmac_rlc_ul_window
*ulw);
+
+void gprs_rlcmac_rlc_ul_window_increment_send(struct gprs_rlcmac_rlc_ul_window *ulw);
+void gprs_rlcmac_rlc_ul_window_raise(struct gprs_rlcmac_rlc_ul_window *ulw, int moves);
+
+uint16_t gprs_rlcmac_rlc_ul_window_v_s(const struct gprs_rlcmac_rlc_ul_window *ulw);
+uint16_t gprs_rlcmac_rlc_ul_window_v_s_mod(const struct gprs_rlcmac_rlc_ul_window *ulw,
int offset);
+uint16_t gprs_rlcmac_rlc_ul_window_v_a(const struct gprs_rlcmac_rlc_ul_window *ulw);
+uint16_t gprs_rlcmac_rlc_ul_window_distance(const struct gprs_rlcmac_rlc_ul_window
*ulw);
+
+/* Methods to manage reception */
+int gprs_rlcmac_rlc_ul_window_resend_needed(const struct gprs_rlcmac_rlc_ul_window
*ulw);
+unsigned int gprs_rlcmac_rlc_ul_window_mark_for_resend(struct gprs_rlcmac_rlc_ul_window
*ulw);
+void gprs_rlcmac_rlc_ul_window_update_ssn(struct gprs_rlcmac_rlc_ul_window *ulw, char
*show_rbb,
+ uint16_t ssn, uint16_t *lost, uint16_t *received);
+void gprs_rlcmac_rlc_ul_window_update(struct gprs_rlcmac_rlc_ul_window *ulw, const struct
bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost, uint16_t *received);
+unsigned int gprs_rlcmac_rlc_ul_window_move_window(struct gprs_rlcmac_rlc_ul_window
*ulw);
+void gprs_rlcmac_rlc_ul_window_show_state(const struct gprs_rlcmac_rlc_ul_window *ulw,
char *show_v_b);
+unsigned int gprs_rlcmac_rlc_ul_window_count_unacked(const struct
gprs_rlcmac_rlc_ul_window *ulw);
+
+
+static inline struct gprs_rlcmac_rlc_window *rlc_ulw_as_w(struct
gprs_rlcmac_rlc_ul_window *ulw)
+{
+ return &ulw->window;
+}
+
+static inline const struct gprs_rlcmac_rlc_window *rlc_ulw_as_w_const(const struct
gprs_rlcmac_rlc_ul_window *ulw)
+{
+ return &ulw->window;
+}
+
+static inline struct gprs_rlcmac_rlc_ul_window *rlc_w_as_ulw(struct
gprs_rlcmac_rlc_window *w)
+{
+ return (struct gprs_rlcmac_rlc_ul_window *)w;
+}
+
+static inline const struct gprs_rlcmac_rlc_ul_window *rcl_w_as_ulw_const(struct
gprs_rlcmac_rlc_window *w)
+{
+ return (const struct gprs_rlcmac_rlc_ul_window *)w;
+}
diff --git a/include/osmocom/gprs/rlcmac/rlcmac_enc.h
b/include/osmocom/gprs/rlcmac/rlcmac_enc.h
index 67448c1..d091d53 100644
--- a/include/osmocom/gprs/rlcmac/rlcmac_enc.h
+++ b/include/osmocom/gprs/rlcmac/rlcmac_enc.h
@@ -3,13 +3,46 @@
/* RLCMAC encoding support functions */
#include <stdint.h>
+#include <osmocom/core/msgb.h>
#include <osmocom/gprs/rlcmac/csn1_defs.h>
#include <osmocom/gprs/rlcmac/types_private.h>
#include <osmocom/gprs/rlcmac/tbf_ul.h>
+#include <osmocom/gprs/rlcmac/rlc.h>
#define GPRS_RLCMAC_DUMMY_VEC "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b"
+
+/****************
+ * DATA BLOCKS:
+ ****************/
+
+enum gpr_rlcmac_append_result {
+ GPRS_RLCMAC_AR_NEED_MORE_BLOCKS,
+ GPRS_RLCMAC_AR_COMPLETED_SPACE_LEFT,
+ GPRS_RLCMAC_AR_COMPLETED_BLOCK_FILLED,
+};
+
+int gprs_rlcmac_rlc_write_ul_data_header(const struct gprs_rlcmac_rlc_data_info *rlc,
uint8_t *data);
+
+enum gpr_rlcmac_append_result gprs_rlcmac_enc_append_ul_data(
+ struct gprs_rlcmac_rlc_block_info *rdbi,
+ enum gprs_rlcmac_coding_scheme cs,
+ struct msgb *llc_msg, int *offset, int *num_chunks,
+ uint8_t *data_block, bool is_final, int *count_payload);
+
+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);
+
+unsigned int gprs_rlcmac_rlc_copy_from_aligned_buffer(const struct
gprs_rlcmac_rlc_data_info *rlc,
+ unsigned int data_block_idx,
+ uint8_t *dst, const uint8_t *buffer);
+
+
+/****************
+ * CONTROL BLOCKS:
+ ****************/
+
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);
diff --git a/include/osmocom/gprs/rlcmac/rlcmac_private.h
b/include/osmocom/gprs/rlcmac/rlcmac_private.h
index ef40972..909ef3b 100644
--- a/include/osmocom/gprs/rlcmac/rlcmac_private.h
+++ b/include/osmocom/gprs/rlcmac/rlcmac_private.h
@@ -10,6 +10,7 @@
#include <osmocom/gprs/rlcmac/rlcmac_prim.h>
#include <osmocom/gprs/rlcmac/rlcmac.h>
+#include <osmocom/gprs/rlcmac/coding_scheme.h>
/* 3GPP TS 44.064 § 8.3 TLLI assignment procedures */
#define GPRS_RLCMAC_TLLI_UNASSIGNED (0xffffffff)
@@ -42,6 +43,8 @@
bool use;
uint32_t interval_msec;
} codel;
+ enum gprs_rlcmac_egprs_arq_type egprs_arq_type;
+ bool ul_tbf_preemptive_retransmission;
} cfg;
osmo_gprs_rlcmac_prim_up_cb rlcmac_up_cb;
void *rlcmac_up_cb_user_data;
diff --git a/include/osmocom/gprs/rlcmac/tbf_ul.h b/include/osmocom/gprs/rlcmac/tbf_ul.h
index 643cc10..1f1a0f6 100644
--- a/include/osmocom/gprs/rlcmac/tbf_ul.h
+++ b/include/osmocom/gprs/rlcmac/tbf_ul.h
@@ -6,15 +6,39 @@
#include <osmocom/gprs/rlcmac/tbf.h>
#include <osmocom/gprs/rlcmac/tbf_ul_fsm.h>
#include <osmocom/gprs/rlcmac/tbf_ul_ass_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_rlc_window;
+struct gprs_rlcmac_rlc_ul_window;
+
struct gprs_rlcmac_ul_tbf {
struct gprs_rlcmac_tbf tbf;
struct gprs_rlcmac_tbf_ul_fsm_ctx state_fsm;
struct gprs_rlcmac_tbf_ul_ass_fsm_ctx ul_ass_fsm;
+ /* Current TS/TFI/USF allocated by the PCU: */
struct gprs_rlcmac_ul_tbf_allocation cur_alloc;
+
+ /* Currently selected LLC frame to be scheduled/transmitted */
+ struct msgb *llc_tx_msg;
+ int32_t last_ul_drained_fn;
+
+ /* Holds state of all generated in-transit RLC blocks */
+ struct gprs_rlcmac_rlc_block_store *blkst;
+
+ /* Uplink RLC Window, holds ACK state */
+ union { /* easy access to parent and child */
+ struct gprs_rlcmac_rlc_window *w;
+ struct gprs_rlcmac_rlc_ul_window *ulw;
+ };
+
+ /* (M)CS used to transmit uplink blocks, assigned by PCU: */
+ enum gprs_rlcmac_coding_scheme tx_cs;
+
+ /* Whether the TBF is EGPRS or not */
+ bool is_egprs;
};
struct gprs_rlcmac_ul_tbf *gprs_rlcmac_ul_tbf_alloc(struct gprs_rlcmac_entity *gre);
@@ -23,7 +47,7 @@
bool gprs_rlcmac_ul_tbf_data_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct
gprs_rlcmac_rts_block_ind *bi);
bool gprs_rlcmac_ul_tbf_dummy_rts(const struct gprs_rlcmac_ul_tbf *ul_tbf, const struct
gprs_rlcmac_rts_block_ind *bi);
-struct msgb *gprs_rlcmac_ul_tbf_data_create(const struct gprs_rlcmac_ul_tbf *ul_tbf,
const struct gprs_rlcmac_rts_block_ind *bi);
+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);
diff --git a/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h
b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h
index b616398..e97b09f 100644
--- a/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h
+++ b/include/osmocom/gprs/rlcmac/tbf_ul_fsm.h
@@ -11,6 +11,7 @@
GPRS_RLCMAC_TBF_UL_ST_NEW = 0, /* new created TBF */
GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN, /* wait for Immediate Assignment */
GPRS_RLCMAC_TBF_UL_ST_FLOW, /* RLC/MAC flow, resource needed */
+ GPRS_RLCMAC_TBF_UL_ST_FINISHED, /* flow finished, wait for release */
};
struct gprs_rlcmac_tbf_ul_fsm_ctx {
@@ -23,6 +24,7 @@
enum tbf_ul_fsm_event {
GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL,
+ GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT,
GPRS_RLCMAC_TBF_UL_EV_FOOBAR,
};
diff --git a/src/rlcmac/Makefile.am b/src/rlcmac/Makefile.am
index 161d6f8..e43f4aa 100644
--- a/src/rlcmac/Makefile.am
+++ b/src/rlcmac/Makefile.am
@@ -22,12 +22,16 @@
$(NULL)
libosmo_gprs_rlcmac_la_SOURCES = \
+ codel.c \
+ coding_scheme.c \
csn1_ts_24_008.c \
csn1_ts_44_018.c \
csn1_ts_44_060.c \
- codel.c \
gre.c \
llc_queue.c \
+ rlc_window.c \
+ rlc_window_ul.c \
+ rlc.c \
rlcmac.c \
rlcmac_enc.c \
rlcmac_prim.c \
diff --git a/src/rlcmac/coding_scheme.c b/src/rlcmac/coding_scheme.c
new file mode 100644
index 0000000..1fbd3b6
--- /dev/null
+++ b/src/rlcmac/coding_scheme.c
@@ -0,0 +1,407 @@
+/* coding_scheme.c
+ *
+ * Copyright (C) 2019-2023 by sysmocom s.f.m.c. GmbH
+ *
+ * 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/utils.h>
+
+#include <osmocom/gprs/rlcmac/coding_scheme.h>
+
+const struct value_string gprs_rlcmac_coding_scheme_names[] = {
+ { GPRS_RLCMAC_CS_UNKNOWN, "UNKNOWN" },
+ { GPRS_RLCMAC_CS_1, "CS-1" },
+ { GPRS_RLCMAC_CS_2, "CS-2" },
+ { GPRS_RLCMAC_CS_3, "CS-3" },
+ { GPRS_RLCMAC_CS_4, "CS-4" },
+ { GPRS_RLCMAC_MCS_1, "MCS-1" },
+ { GPRS_RLCMAC_MCS_2, "MCS-2" },
+ { GPRS_RLCMAC_MCS_3, "MCS-3" },
+ { GPRS_RLCMAC_MCS_4, "MCS-4" },
+ { GPRS_RLCMAC_MCS_5, "MCS-5" },
+ { GPRS_RLCMAC_MCS_6, "MCS-6" },
+ { GPRS_RLCMAC_MCS_7, "MCS-7" },
+ { GPRS_RLCMAC_MCS_8, "MCS-8" },
+ { GPRS_RLCMAC_MCS_9, "MCS-9" },
+ { 0, NULL }
+};
+
+enum gprs_rlcmac_family {
+ FAMILY_INVALID,
+ FAMILY_A,
+ FAMILY_B,
+ FAMILY_C,
+};
+
+const static struct {
+ struct {
+ uint8_t bytes;
+ uint8_t ext_bits;
+ uint8_t data_header_bits;
+ } uplink, downlink;
+ uint8_t data_bytes;
+ uint8_t optional_padding_bits;
+ enum gprs_rlcmac_header_type data_hdr;
+ enum gprs_rlcmac_family family;
+} mcs_info[GPRS_RLCMAC_NUM_SCHEMES] = {
+ {{0, 0}, {0, 0}, 0, 0,
+ GPRS_RLCMAC_HEADER_INVALID, FAMILY_INVALID},
+ {{23, 0}, {23, 0}, 20, 0,
+ GPRS_RLCMAC_HEADER_GPRS_DATA, FAMILY_INVALID},
+ {{33, 7}, {33, 7}, 30, 0,
+ GPRS_RLCMAC_HEADER_GPRS_DATA, FAMILY_INVALID},
+ {{39, 3}, {39, 3}, 36, 0,
+ GPRS_RLCMAC_HEADER_GPRS_DATA, FAMILY_INVALID},
+ {{53, 7}, {53, 7}, 50, 0,
+ GPRS_RLCMAC_HEADER_GPRS_DATA, FAMILY_INVALID},
+
+ {{26, 1}, {26, 1}, 22, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
+ {{32, 1}, {32, 1}, 28, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3, FAMILY_B},
+ {{41, 1}, {41, 1}, 37, 48,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3, FAMILY_A},
+ {{48, 1}, {48, 1}, 44, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3, FAMILY_C},
+
+ {{60, 7}, {59, 6}, 56, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2, FAMILY_B},
+ {{78, 7}, {77, 6}, 74, 48,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2, FAMILY_A},
+ {{118, 2}, {117, 4}, 56, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1, FAMILY_B},
+ {{142, 2}, {141, 4}, 68, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
+ {{154, 2}, {153, 4}, 74, 0,
+ GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1, FAMILY_A},
+};
+
+const char *gprs_rlcmac_mcs_name(enum gprs_rlcmac_coding_scheme val)
+{
+ return get_value_string(gprs_rlcmac_coding_scheme_names, val);
+}
+
+bool gprs_rlcmac_mcs_is_gprs(enum gprs_rlcmac_coding_scheme cs)
+{
+ return GPRS_RLCMAC_CS_1 <= cs && cs <= GPRS_RLCMAC_CS_4;
+}
+
+bool gprs_rlcmac_mcs_is_edge(enum gprs_rlcmac_coding_scheme cs)
+{
+ return GPRS_RLCMAC_MCS_1 <= cs && cs <= GPRS_RLCMAC_MCS_9;
+}
+
+bool gprs_rlcmac_mcs_is_edge_gmsk(enum gprs_rlcmac_coding_scheme cs)
+{
+ if (gprs_rlcmac_mcs_is_edge(cs))
+ return cs <= GPRS_RLCMAC_MCS_4;
+
+ return false;
+}
+
+/* Return 3GPP TS 44.060 §12.10d (EDGE) or Table 11.2.28.2 (GPRS) Channel Coding Command
value */
+uint8_t gprs_rlcmac_mcs_chan_code(enum gprs_rlcmac_coding_scheme cs)
+{
+ if (gprs_rlcmac_mcs_is_gprs(cs))
+ return cs - GPRS_RLCMAC_CS_1;
+
+ if (gprs_rlcmac_mcs_is_edge(cs))
+ return cs - GPRS_RLCMAC_MCS_1;
+
+ /* Defaults to (M)GPRS_RLCMAC_CS_1 */
+ return 0;
+}
+
+enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_by_size_ul(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)
+ return GPRS_RLCMAC_CS_UNKNOWN;
+ return GPRS_RLCMAC_CS_1 + (num - 1);
+}
+
+enum gprs_rlcmac_coding_scheme gprs_rlcmac_mcs_get_egprs_by_num(unsigned num)
+{
+ if (num < 1 || num > 9)
+ return GPRS_RLCMAC_CS_UNKNOWN;
+ return GPRS_RLCMAC_MCS_1 + (num - 1);
+}
+
+bool gprs_rlcmac_mcs_is_valid(enum gprs_rlcmac_coding_scheme cs)
+{
+ return GPRS_RLCMAC_CS_UNKNOWN < cs && cs <= GPRS_RLCMAC_MCS_9;
+}
+
+bool gprs_rlcmac_mcs_is_compat_kind(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme_kind mode)
+{
+ switch (mode) {
+ case GPRS_RLCMAC_SCHEME_GPRS: return gprs_rlcmac_mcs_is_gprs(cs);
+ case GPRS_RLCMAC_SCHEME_EGPRS_GMSK: return gprs_rlcmac_mcs_is_edge_gmsk(cs);
+ case GPRS_RLCMAC_SCHEME_EGPRS: return gprs_rlcmac_mcs_is_edge(cs);
+ }
+
+ return false;
+}
+
+bool gprs_rlcmac_mcs_is_compat(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme o)
+{
+ return (gprs_rlcmac_mcs_is_gprs(cs) && gprs_rlcmac_mcs_is_gprs(o)) ||
+ (gprs_rlcmac_mcs_is_edge(cs) && gprs_rlcmac_mcs_is_edge(o));
+}
+
+uint8_t gprs_rlcmac_mcs_size_ul(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].uplink.bytes + (gprs_rlcmac_mcs_spare_bits_ul(cs) ? 1 : 0);
+}
+
+uint8_t gprs_rlcmac_mcs_size_dl(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].downlink.bytes + (gprs_rlcmac_mcs_spare_bits_dl(cs) ? 1 : 0);
+}
+
+uint8_t gprs_rlcmac_mcs_used_size_ul(enum gprs_rlcmac_coding_scheme cs)
+{
+ if (mcs_info[cs].data_hdr == GPRS_RLCMAC_HEADER_GPRS_DATA)
+ return mcs_info[cs].uplink.bytes;
+ else
+ return gprs_rlcmac_mcs_size_ul(cs);
+}
+
+uint8_t gprs_rlcmac_mcs_used_size_dl(enum gprs_rlcmac_coding_scheme cs)
+{
+ if (mcs_info[cs].data_hdr == GPRS_RLCMAC_HEADER_GPRS_DATA)
+ return mcs_info[cs].downlink.bytes;
+ else
+ return gprs_rlcmac_mcs_size_dl(cs);
+}
+
+uint8_t gprs_rlcmac_mcs_max_bytes_ul(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].uplink.bytes;
+}
+
+uint8_t gprs_rlcmac_mcs_max_bytes_dl(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].downlink.bytes;
+}
+
+uint8_t gprs_rlcmac_mcs_spare_bits_ul(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].uplink.ext_bits;
+}
+
+uint8_t gprs_rlcmac_mcs_spare_bits_dl(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].downlink.ext_bits;
+}
+
+uint8_t gprs_rlcmac_mcs_max_data_block_bytes(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].data_bytes;
+}
+
+uint8_t gprs_rlcmac_mcs_opt_padding_bits(enum gprs_rlcmac_coding_scheme cs)
+{
+ return mcs_info[cs].optional_padding_bits;
+}
+
+void gprs_rlcmac_mcs_inc_kind(enum gprs_rlcmac_coding_scheme *cs, enum
gprs_rlcmac_coding_scheme_kind mode)
+{
+ if (!gprs_rlcmac_mcs_is_compat_kind(*cs, mode))
+ /* This should not happen. TODO: Use assert? */
+ return;
+
+ enum gprs_rlcmac_coding_scheme new_cs = *cs + 1;
+ if (!gprs_rlcmac_mcs_is_compat_kind(new_cs, mode))
+ /* Clipping, do not change the value */
+ return;
+
+ *cs = new_cs;
+}
+
+void gprs_rlcmac_mcs_dec_kind(enum gprs_rlcmac_coding_scheme *cs, enum
gprs_rlcmac_coding_scheme_kind mode)
+{
+ if (!gprs_rlcmac_mcs_is_compat_kind(*cs, mode))
+ /* This should not happen. TODO: Use assert? */
+ return;
+
+ enum gprs_rlcmac_coding_scheme new_cs = *cs - 1;
+ if (!gprs_rlcmac_mcs_is_compat_kind(new_cs, mode))
+ /* Clipping, do not change the value */
+ return;
+
+ *cs = new_cs;
+}
+
+void gprs_rlcmac_mcs_inc(enum gprs_rlcmac_coding_scheme *cs)
+{
+ if (gprs_rlcmac_mcs_is_gprs(*cs) && *cs == GPRS_RLCMAC_CS_4)
+ return;
+
+ if (gprs_rlcmac_mcs_is_edge(*cs) && *cs == GPRS_RLCMAC_MCS_9)
+ return;
+
+ if (!gprs_rlcmac_mcs_is_valid(*cs))
+ return;
+
+ *cs = *cs + 1;
+}
+
+void gprs_rlcmac_mcs_dec(enum gprs_rlcmac_coding_scheme *cs)
+{
+ if (gprs_rlcmac_mcs_is_gprs(*cs) && *cs == GPRS_RLCMAC_CS_1)
+ return;
+
+ if (gprs_rlcmac_mcs_is_edge(*cs) && *cs == GPRS_RLCMAC_MCS_1)
+ return;
+
+ if (!gprs_rlcmac_mcs_is_valid(*cs))
+ return;
+
+ *cs = *cs - 1;
+}
+
+bool gprs_rlcmac_mcs_is_family_compat(enum gprs_rlcmac_coding_scheme cs, enum
gprs_rlcmac_coding_scheme o)
+{
+ if (cs == o)
+ return true;
+
+ if (mcs_info[cs].family == FAMILY_INVALID)
+ return false;
+
+ return mcs_info[cs].family == mcs_info[o].family;
+}
+
+void gprs_rlcmac_mcs_dec_to_single_block(enum gprs_rlcmac_coding_scheme *cs, bool
*need_stuffing)
+{
+ switch (*cs) {
+ case GPRS_RLCMAC_MCS_7: *need_stuffing = false; *cs = GPRS_RLCMAC_MCS_5; break;
+ case GPRS_RLCMAC_MCS_8: *need_stuffing = true; *cs = GPRS_RLCMAC_MCS_6; break;
+ case GPRS_RLCMAC_MCS_9: *need_stuffing = false; *cs = GPRS_RLCMAC_MCS_6; break;
+ default: *need_stuffing = false; break;
+ }
+}
+
+static struct {
+ struct {
+ uint8_t data_header_bits;
+ } uplink, downlink;
+ uint8_t data_block_header_bits;
+ uint8_t num_blocks;
+ const char *name;
+} hdr_type_info[GPRS_RLCMAC_NUM_HEADER_TYPES] = {
+ { { 0 }, { 0 }, 0, 0, "INVALID" },
+ { { 1 * 8 + 0 }, { 1 * 8 + 0 }, 0, 0, "CONTROL" },
+ { { 3 * 8 + 0 }, { 3 * 8 + 0 }, 0, 1, "GPRS_DATA" },
+ { { 5 * 8 + 6 }, { 5 * 8 + 0 }, 2, 2, "EGPRS_DATA_TYPE1" },
+ { { 4 * 8 + 5 }, { 3 * 8 + 4 }, 2, 1, "EGPRS_DATA_TYPE2" },
+ { { 3 * 8 + 7 }, { 3 * 8 + 7 }, 2, 1, "EGPRS_DATA_TYPE3" },
+};
+
+enum gprs_rlcmac_header_type gprs_rlcmac_mcs_header_type(enum gprs_rlcmac_coding_scheme
mcs)
+{
+ return mcs_info[mcs].data_hdr;
+}
+
+uint8_t gprs_rlcmac_num_data_blocks(enum gprs_rlcmac_header_type ht)
+{
+ OSMO_ASSERT(ht < GPRS_RLCMAC_NUM_HEADER_TYPES);
+ return hdr_type_info[ht].num_blocks;
+}
+
+uint8_t gprs_rlcmac_num_data_header_bits_UL(enum gprs_rlcmac_header_type ht)
+{
+ OSMO_ASSERT(ht < GPRS_RLCMAC_NUM_HEADER_TYPES);
+ return hdr_type_info[ht].uplink.data_header_bits;
+}
+
+uint8_t gprs_rlcmac_num_data_header_bits_DL(enum gprs_rlcmac_header_type ht)
+{
+ OSMO_ASSERT(ht < GPRS_RLCMAC_NUM_HEADER_TYPES);
+ return hdr_type_info[ht].downlink.data_header_bits;
+}
+
+uint8_t gprs_rlcmac_num_data_block_header_bits(enum gprs_rlcmac_header_type ht)
+{
+ OSMO_ASSERT(ht < GPRS_RLCMAC_NUM_HEADER_TYPES);
+ return hdr_type_info[ht].data_block_header_bits;
+}
+
+static const struct value_string gprs_rlcmac_mcs_kind_names[] = {
+ { GPRS_RLCMAC_SCHEME_GPRS, "GPRS" },
+ { GPRS_RLCMAC_SCHEME_EGPRS_GMSK, "EGPRS_GMSK-only"},
+ { GPRS_RLCMAC_SCHEME_EGPRS, "EGPRS"},
+ { 0, NULL }
+};
+
+const char *gprs_rlcmac_msc_kind_name(enum gprs_rlcmac_coding_scheme_kind val)
+{
+ return get_value_string(gprs_rlcmac_mcs_kind_names, val);
+}
+
+/* FIXME: take into account padding and special cases of commanded MCS (MCS-6-9 and
MCS-5-7) */
+enum gprs_rlcmac_coding_scheme gprs_rlcmac_get_retx_mcs(enum gprs_rlcmac_coding_scheme
initial_mcs, enum gprs_rlcmac_coding_scheme commanded_mcs, bool resegment_bit)
+{
+ OSMO_ASSERT(gprs_rlcmac_mcs_is_edge(initial_mcs));
+ OSMO_ASSERT(gprs_rlcmac_mcs_is_edge(commanded_mcs));
+ OSMO_ASSERT(GPRS_RLCMAC_NUM_SCHEMES - GPRS_RLCMAC_MCS_1 == 9);
+
+ if (resegment_bit) { /* 3GPP TS 44.060 Table 8.1.1.1, reflected over antidiagonal */
+ enum gprs_rlcmac_coding_scheme egprs_reseg[GPRS_RLCMAC_NUM_SCHEMES -
GPRS_RLCMAC_MCS_1][GPRS_RLCMAC_NUM_SCHEMES - GPRS_RLCMAC_MCS_1] = {
+ { GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1,
GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1,
GPRS_RLCMAC_MCS_1 },
+ { GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_2 },
+ { GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3 },
+ { GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_4,
GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4,
GPRS_RLCMAC_MCS_4 },
+ { GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_7, GPRS_RLCMAC_MCS_7,
GPRS_RLCMAC_MCS_7 },
+ { GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_9 },
+ { GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_7, GPRS_RLCMAC_MCS_7,
GPRS_RLCMAC_MCS_7 },
+ { GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_8,
GPRS_RLCMAC_MCS_8 },
+ { GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_9 },
+ };
+ return
egprs_reseg[gprs_rlcmac_mcs_chan_code(initial_mcs)][gprs_rlcmac_mcs_chan_code(commanded_mcs)];
+ }
+ /* else: 3GPP TS 44.060 Table 8.1.1.2, reflected over antidiagonal */
+ enum gprs_rlcmac_coding_scheme egprs_no_reseg[GPRS_RLCMAC_NUM_SCHEMES -
GPRS_RLCMAC_MCS_1][GPRS_RLCMAC_NUM_SCHEMES - GPRS_RLCMAC_MCS_1] = {
+ { GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1,
GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1, GPRS_RLCMAC_MCS_1,
GPRS_RLCMAC_MCS_1 },
+ { GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2, GPRS_RLCMAC_MCS_2,
GPRS_RLCMAC_MCS_2 },
+ { GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3, GPRS_RLCMAC_MCS_3,
GPRS_RLCMAC_MCS_3 },
+ { GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4,
GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4, GPRS_RLCMAC_MCS_4,
GPRS_RLCMAC_MCS_4 },
+ { GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5,
GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_7, GPRS_RLCMAC_MCS_7,
GPRS_RLCMAC_MCS_7 },
+ { GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_9 },
+ { GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5,
GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_5, GPRS_RLCMAC_MCS_7, GPRS_RLCMAC_MCS_7,
GPRS_RLCMAC_MCS_7 },
+ { GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_8,
GPRS_RLCMAC_MCS_8 },
+ { GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6, GPRS_RLCMAC_MCS_6,
GPRS_RLCMAC_MCS_9 },
+ };
+ return
egprs_no_reseg[gprs_rlcmac_mcs_chan_code(initial_mcs)][gprs_rlcmac_mcs_chan_code(commanded_mcs)];
+}
diff --git a/src/rlcmac/rlc.c b/src/rlcmac/rlc.c
new file mode 100644
index 0000000..5905c7b
--- /dev/null
+++ b/src/rlcmac/rlc.c
@@ -0,0 +1,272 @@
+/* RLC block management 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 <osmocom/core/utils.h>
+#include <osmocom/core/logging.h>
+
+#include <osmocom/gprs/rlcmac/rlc.h>
+#include <osmocom/gprs/rlcmac/rlcmac_private.h>
+
+void gprs_rlcmac_rlc_block_info_init(struct gprs_rlcmac_rlc_block_info *rdbi,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding, unsigned int spb)
+{
+ unsigned int data_len = gprs_rlcmac_mcs_max_data_block_bytes(cs);
+ if (with_padding)
+ data_len -= gprs_rlcmac_mcs_opt_padding_bits(cs) / 8;
+
+ rdbi->data_len = data_len;
+ rdbi->bsn = 0;
+ rdbi->ti = 0;
+ rdbi->e = 1;
+ rdbi->cv = 15;
+ rdbi->pi = 0;
+ rdbi->spb = spb;
+}
+
+static void gprs_rlcmac_rlc_data_header_init(struct gprs_rlcmac_rlc_data_info *rlc,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding, unsigned int header_bits,
+ unsigned int spb)
+{
+ unsigned int i;
+ unsigned int padding_bits = with_padding ? gprs_rlcmac_mcs_opt_padding_bits(cs) : 0;
+
+ rlc->cs = cs;
+ rlc->r = 0;
+ rlc->si = 0;
+ rlc->tfi = 0;
+ rlc->cps = 0;
+ rlc->rsb = 0;
+ rlc->usf = 0;
+ rlc->es_p = 0;
+ rlc->rrbp = 0;
+ rlc->pr = 0;
+ rlc->num_data_blocks = gprs_rlcmac_num_data_blocks(gprs_rlcmac_mcs_header_type(cs));
+ rlc->with_padding = with_padding;
+
+ OSMO_ASSERT(rlc->num_data_blocks <= ARRAY_SIZE(rlc->block_info));
+
+ for (i = 0; i < rlc->num_data_blocks; i++) {
+ gprs_rlcmac_rlc_block_info_init(&rlc->block_info[i], cs, with_padding, spb);
+
+ rlc->data_offs_bits[i] =
+ header_bits + padding_bits +
+ (i+1) * gprs_rlcmac_num_data_block_header_bits(gprs_rlcmac_mcs_header_type(cs)) +
+ i * 8 * rlc->block_info[0].data_len;
+ }
+}
+
+void gprs_rlcmac_rlc_data_info_init_ul(struct gprs_rlcmac_rlc_data_info *rlc,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding, unsigned int spb)
+{
+ OSMO_ASSERT(gprs_rlcmac_mcs_is_valid(cs));
+ return gprs_rlcmac_rlc_data_header_init(
+ rlc, cs, with_padding,
+ gprs_rlcmac_num_data_header_bits_UL(gprs_rlcmac_mcs_header_type(cs)), spb);
+}
+
+void gprs_rlcmac_rlc_data_info_init_dl(struct gprs_rlcmac_rlc_data_info *rlc,
+ enum gprs_rlcmac_coding_scheme cs,
+ bool with_padding)
+{
+ OSMO_ASSERT(gprs_rlcmac_mcs_is_valid(cs));
+ /*
+ * last parameter is sent as 0 since common function used
+ * for both DL and UL
+ */
+ return gprs_rlcmac_rlc_data_header_init(
+ rlc, cs, with_padding,
+ gprs_rlcmac_num_data_header_bits_DL(gprs_rlcmac_mcs_header_type(cs)), 0);
+}
+
+
+uint8_t *gprs_rlcmac_rlc_block_prepare(struct gprs_rlcmac_rlc_block *blk, size_t
block_data_len)
+{
+ /* todo.. only set it once if it turns out to be a bottleneck */
+ memset(blk->buf, 0x0, sizeof(blk->buf));
+ memset(blk->buf, 0x2b, block_data_len);
+
+ /* Initial value of puncturing scheme */
+ blk->next_ps = GPRS_RLCMAC_EGPRS_PS_1;
+
+ return blk->buf;
+}
+
+struct gprs_rlcmac_rlc_block_store *gprs_rlcmac_rlc_block_store_alloc(void *ctx)
+{
+ struct gprs_rlcmac_rlc_block_store *blkst;
+
+ blkst = talloc_zero(ctx, struct gprs_rlcmac_rlc_block_store);
+ if (!blkst)
+ return NULL;
+
+ return blkst;
+}
+
+void gprs_rlcmac_rlc_block_store_free(struct gprs_rlcmac_rlc_block_store *blkst)
+{
+ talloc_free(blkst);
+}
+
+struct gprs_rlcmac_rlc_block *gprs_rlcmac_rlc_block_store_get_block(struct
gprs_rlcmac_rlc_block_store *blkst, int bsn)
+{
+ return &blkst->blocks[bsn & mod_sns_half()];
+}
+
+unsigned int gprs_rlcmac_rlc_mcs_cps(enum gprs_rlcmac_coding_scheme cs,
+ enum gprs_rlcmac_egprs_puncturing_values punct,
+ enum gprs_rlcmac_egprs_puncturing_values punct2,
+ bool with_padding)
+{
+ /* validate that punct and punct2 are as expected */
+ switch (cs) {
+ case GPRS_RLCMAC_MCS_9:
+ case GPRS_RLCMAC_MCS_8:
+ case GPRS_RLCMAC_MCS_7:
+ if (punct2 == GPRS_RLCMAC_EGPRS_PS_INVALID) {
+ LOGRLCMAC(LOGL_ERROR, "Invalid punct2 value for coding scheme %d: %d\n",
+ cs, punct2);
+ return -1;
+ }
+ /* fall through */
+ case GPRS_RLCMAC_MCS_6:
+ case GPRS_RLCMAC_MCS_5:
+ case GPRS_RLCMAC_MCS_4:
+ case GPRS_RLCMAC_MCS_3:
+ case GPRS_RLCMAC_MCS_2:
+ case GPRS_RLCMAC_MCS_1:
+ if (punct == GPRS_RLCMAC_EGPRS_PS_INVALID) {
+ LOGRLCMAC(LOGL_ERROR, "Invalid punct value for coding scheme %d: %d\n",
+ cs, punct);
+ return -1;
+ }
+ break;
+ default:
+ return -1;
+ }
+
+ /* See 3GPP TS 44.060 10.4.8a.3.1, 10.4.8a.2.1, 10.4.8a.1.1 */
+ switch (cs) {
+ case GPRS_RLCMAC_MCS_1:
+ return 0b1011 +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2;
+ case GPRS_RLCMAC_MCS_2:
+ return 0b1001 +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2;
+ case GPRS_RLCMAC_MCS_3:
+ return (with_padding ? 0b0110 : 0b0011) +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3;
+ case GPRS_RLCMAC_MCS_4:
+ return 0b0000 +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3;
+ case GPRS_RLCMAC_MCS_5:
+ return 0b100 +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2;
+ case GPRS_RLCMAC_MCS_6:
+ return (with_padding ? 0b010 : 0b000) +
+ punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2;
+ case GPRS_RLCMAC_MCS_7:
+ return 0b10100 +
+ 3 * (punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3) +
+ punct2 % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3;
+ case GPRS_RLCMAC_MCS_8:
+ return 0b01011 +
+ 3 * (punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3) +
+ punct2 % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3;
+ case GPRS_RLCMAC_MCS_9:
+ return 0b00000 +
+ 4 * (punct % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3) +
+ punct2 % GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3;
+ default:
+ break;
+ }
+
+ return -1;
+}
+
+enum gprs_rlcmac_egprs_puncturing_values
+gprs_rlcmac_get_punct_scheme(enum gprs_rlcmac_egprs_puncturing_values punct,
+ enum gprs_rlcmac_coding_scheme cs,
+ enum gprs_rlcmac_coding_scheme cs_current,
+ enum gprs_rlcmac_rlc_egprs_ul_spb spb)
+{
+
+ /*
+ * 10.4.8b of TS 44.060
+ * If it is second segment of the block
+ * dont change the puncturing scheme
+ */
+ if (spb == GPRS_RLCMAC_EGPRS_UL_SPB_SEC_SEG)
+ return punct;
+
+ /* TS 44.060 9.3.2.1.1 */
+ if ((cs == GPRS_RLCMAC_MCS_9) &&
+ (cs_current == GPRS_RLCMAC_MCS_6)) {
+ if ((punct == GPRS_RLCMAC_EGPRS_PS_1) || (punct == GPRS_RLCMAC_EGPRS_PS_3))
+ return GPRS_RLCMAC_EGPRS_PS_1;
+ else if (punct == GPRS_RLCMAC_EGPRS_PS_2)
+ return GPRS_RLCMAC_EGPRS_PS_2;
+ } else if ((cs == GPRS_RLCMAC_MCS_6) &&
+ (cs_current == GPRS_RLCMAC_MCS_9)) {
+ if (punct == GPRS_RLCMAC_EGPRS_PS_1)
+ return GPRS_RLCMAC_EGPRS_PS_3;
+ else if (punct == GPRS_RLCMAC_EGPRS_PS_2)
+ return GPRS_RLCMAC_EGPRS_PS_2;
+ } else if ((cs == GPRS_RLCMAC_MCS_7) &&
+ (cs_current == GPRS_RLCMAC_MCS_5))
+ return GPRS_RLCMAC_EGPRS_PS_1;
+ else if ((cs == GPRS_RLCMAC_MCS_5) &&
+ (cs_current == GPRS_RLCMAC_MCS_7))
+ return GPRS_RLCMAC_EGPRS_PS_2;
+ else if (cs != cs_current)
+ return GPRS_RLCMAC_EGPRS_PS_1;
+ /* TS 44.060 9.3.2.1.1 ends here */
+ /*
+ * Below else will handle fresh transmission, retransmission with no
+ * MCS change case
+ */
+ else
+ return punct;
+ return GPRS_RLCMAC_EGPRS_PS_INVALID;
+}
+
+void gprs_rlcmac_update_punct_scheme(enum gprs_rlcmac_egprs_puncturing_values *punct,
enum gprs_rlcmac_coding_scheme cs)
+{
+ switch (cs) {
+ case GPRS_RLCMAC_MCS_1:
+ case GPRS_RLCMAC_MCS_2:
+ case GPRS_RLCMAC_MCS_5:
+ case GPRS_RLCMAC_MCS_6:
+ *punct = ((enum gprs_rlcmac_egprs_puncturing_values)((*punct + 1) %
+ GPRS_RLCMAC_EGPRS_MAX_PS_NUM_2));
+ break;
+ case GPRS_RLCMAC_MCS_3:
+ case GPRS_RLCMAC_MCS_4:
+ case GPRS_RLCMAC_MCS_7:
+ case GPRS_RLCMAC_MCS_8:
+ case GPRS_RLCMAC_MCS_9:
+ *punct = ((enum gprs_rlcmac_egprs_puncturing_values)((*punct + 1) %
+ GPRS_RLCMAC_EGPRS_MAX_PS_NUM_3));
+ break;
+ default:
+ break;
+ }
+}
diff --git a/src/rlcmac/rlc_window.c b/src/rlcmac/rlc_window.c
new file mode 100644
index 0000000..d17307b
--- /dev/null
+++ b/src/rlcmac/rlc_window.c
@@ -0,0 +1,76 @@
+/* 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 <osmocom/core/utils.h>
+
+#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;
+}
+
+void gprs_rlcmac_rlc_window_destructor(struct gprs_rlcmac_rlc_window *w)
+{
+ /* Nothing to be done here yet */
+}
+
+uint16_t gprs_rlcmac_rlc_window_mod_sns(const struct gprs_rlcmac_rlc_window *w)
+{
+ return gprs_rlcmac_rlc_window_sns(w) - 1;
+}
+
+uint16_t gprs_rlcmac_rlc_window_mod_sns_bsn(const struct gprs_rlcmac_rlc_window *w,
uint16_t bsn)
+{
+ return bsn & gprs_rlcmac_rlc_window_mod_sns(w);
+}
+
+uint16_t gprs_rlcmac_rlc_window_sns(const struct gprs_rlcmac_rlc_window *w)
+{
+ return w->sns;
+}
+
+uint16_t gprs_rlcmac_rlc_window_ws(const struct gprs_rlcmac_rlc_window *w)
+{
+ return w->ws;
+}
+
+void gprs_rlcmac_rlc_window_set_sns(struct gprs_rlcmac_rlc_window *w, uint16_t sns)
+{
+ OSMO_ASSERT(sns >= RLC_GPRS_SNS);
+ OSMO_ASSERT(sns <= RLC_MAX_SNS);
+ /* check for 2^n */
+ OSMO_ASSERT((sns & (-sns)) == sns);
+ w->sns = sns;
+}
+
+void gprs_rlcmac_rlc_window_set_ws(struct gprs_rlcmac_rlc_window *w, uint16_t ws)
+{
+ OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
+ OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
+ w->ws = ws;
+}
diff --git a/src/rlcmac/rlc_window_ul.c b/src/rlcmac/rlc_window_ul.c
new file mode 100644
index 0000000..ec19f82
--- /dev/null
+++ b/src/rlcmac/rlc_window_ul.c
@@ -0,0 +1,356 @@
+/* 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/gprs/rlcmac/rlc.h>
+#include <osmocom/gprs/rlcmac/rlc_window_ul.h>
+#include <osmocom/gprs/rlcmac/tbf_ul.h>
+
+
+static inline bool gprs_rlcmac_rlc_v_b_is_state(const struct gprs_rlcmac_rlc_v_b *v_b,
+ int bsn, enum gprs_rlcmac_rlc_ul_bsn_state type)
+{
+ return v_b->v_b[bsn & mod_sns_half()] == type;
+}
+
+static inline void gprs_rlcmac_rlc_v_b_mark(struct gprs_rlcmac_rlc_v_b *v_b,
+ int bsn, enum gprs_rlcmac_rlc_ul_bsn_state type)
+{
+ v_b->v_b[bsn & mod_sns_half()] = type;
+}
+
+void gprs_rlcmac_rlc_v_b_reset(struct gprs_rlcmac_rlc_v_b *v_b)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(v_b->v_b); i++)
+ gprs_rlcmac_rlc_v_b_mark_invalid(v_b, i);
+}
+
+/* Check for an individual frame */
+bool gprs_rlcmac_rlc_v_b_is_unacked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_is_state(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_UNACKED);
+}
+
+bool gprs_rlcmac_rlc_v_b_is_nacked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_is_state(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_NACKED);
+}
+
+bool gprs_rlcmac_rlc_v_b_is_acked(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_is_state(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_ACKED);
+}
+
+bool gprs_rlcmac_rlc_v_b_is_resend(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_is_state(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_RESEND);
+}
+
+bool gprs_rlcmac_rlc_v_b_is_invalid(const struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_is_state(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_INVALID);
+}
+
+enum gprs_rlcmac_rlc_ul_bsn_state gprs_rlcmac_rlc_v_b_get_state(const struct
gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return v_b->v_b[bsn & mod_sns_half()];
+}
+
+/* Mark a RLC frame for something */
+void gprs_rlcmac_rlc_v_b_mark_unacked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_mark(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_UNACKED);
+}
+
+void gprs_rlcmac_rlc_v_b_mark_nacked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_mark(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_NACKED);
+}
+
+void gprs_rlcmac_rlc_v_b_mark_acked(struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_mark(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_ACKED);
+}
+
+void gprs_rlcmac_rlc_v_b_mark_resend(struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_mark(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_RESEND);
+}
+
+void gprs_rlcmac_rlc_v_b_mark_invalid(struct gprs_rlcmac_rlc_v_b *v_b, int bsn)
+{
+ return gprs_rlcmac_rlc_v_b_mark(v_b, bsn, GPRS_RLCMAC_RLC_UL_BSN_INVALID);
+}
+
+
+/*************
+ * UL WINDOW
+*************/
+struct gprs_rlcmac_rlc_ul_window *gprs_rlcmac_rlc_ul_window_alloc(struct
gprs_rlcmac_ul_tbf *ul_tbf)
+{
+ struct gprs_rlcmac_rlc_ul_window *ulw;
+
+ ulw = talloc_zero(ul_tbf, struct gprs_rlcmac_rlc_ul_window);
+ if (!ulw)
+ return NULL;
+
+ gprs_rlcmac_rlc_window_constructor(rlc_ulw_as_w(ulw));
+
+ ulw->ul_tbf = ul_tbf;
+ gprs_rlcmac_rlc_ul_window_reset(ulw);
+
+ return ulw;
+}
+
+void gprs_rlcmac_rlc_ul_window_free(struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ if (!ulw)
+ return;
+
+ gprs_rlcmac_rlc_window_destructor(rlc_ulw_as_w(ulw));
+ talloc_free(ulw);
+}
+
+void gprs_rlcmac_rlc_ul_window_reset(struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ ulw->v_s = 0;
+ ulw->v_a = 0;
+ gprs_rlcmac_rlc_v_b_reset(&ulw->v_b);
+}
+
+bool gprs_rlcmac_rlc_ul_window_window_stalled(const struct gprs_rlcmac_rlc_ul_window
*ulw)
+{
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+
+ return (gprs_rlcmac_rlc_window_mod_sns_bsn(w, ulw->v_s - ulw->v_a) ==
+ gprs_rlcmac_rlc_window_ws(w));
+}
+
+bool gprs_rlcmac_rlc_ul_window_window_empty(const struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ return ulw->v_s == ulw->v_a;
+}
+
+void gprs_rlcmac_rlc_ul_window_increment_send(struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+
+ ulw->v_s = (ulw->v_s + 1) & gprs_rlcmac_rlc_window_mod_sns(w);
+}
+
+void gprs_rlcmac_rlc_ul_window_raise(struct gprs_rlcmac_rlc_ul_window *ulw, int moves)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+
+ ulw->v_a = (ulw->v_a + moves) & gprs_rlcmac_rlc_window_mod_sns(w);
+}
+
+uint16_t gprs_rlcmac_rlc_ul_window_v_s(const struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ return ulw->v_s;
+}
+
+uint16_t gprs_rlcmac_rlc_ul_window_v_s_mod(const struct gprs_rlcmac_rlc_ul_window *ulw,
int offset)
+{
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+
+ return gprs_rlcmac_rlc_window_mod_sns_bsn(w, ulw->v_s + offset);
+}
+
+uint16_t gprs_rlcmac_rlc_ul_window_v_a(const struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ return ulw->v_a;
+}
+
+uint16_t gprs_rlcmac_rlc_ul_window_distance(const struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+
+ return (ulw->v_s - ulw->v_a) & gprs_rlcmac_rlc_window_mod_sns(w);
+}
+
+/* Methods to manage reception */
+int gprs_rlcmac_rlc_ul_window_resend_needed(const struct gprs_rlcmac_rlc_ul_window *ulw)
+{
+ uint16_t bsn;
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+
+ for (bsn = gprs_rlcmac_rlc_ul_window_v_a(ulw);
+ bsn != gprs_rlcmac_rlc_ul_window_v_s(ulw);
+ bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) {
+ if (gprs_rlcmac_rlc_v_b_is_nacked(&ulw->v_b, bsn) ||
+ gprs_rlcmac_rlc_v_b_is_resend(&ulw->v_b, bsn))
+ return bsn;
+ }
+
+ return -1;
+}
+
+unsigned int gprs_rlcmac_rlc_ul_window_mark_for_resend(struct gprs_rlcmac_rlc_ul_window
*ulw)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+ unsigned int resend = 0;
+ uint16_t bsn;
+
+ for (bsn = gprs_rlcmac_rlc_ul_window_v_a(ulw);
+ bsn != gprs_rlcmac_rlc_ul_window_v_s(ulw);
+ bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) {
+ if (gprs_rlcmac_rlc_v_b_is_unacked(&ulw->v_b, bsn)) {
+ /* mark to be re-send */
+ gprs_rlcmac_rlc_v_b_mark_resend(&ulw->v_b, bsn);
+ resend += 1;
+ }
+ }
+
+ return resend;
+}
+
+static inline uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
+{
+ return (ssn - 1 - bitnum);
+}
+
+void gprs_rlcmac_rlc_ul_window_update_ssn(struct gprs_rlcmac_rlc_ul_window *ulw, char
*show_rbb,
+ uint16_t ssn, uint16_t *lost, uint16_t *received)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+ unsigned int bitpos;
+
+ /* SSN - 1 is in range V(A)..V(S)-1 */
+ for (bitpos = 0; bitpos < gprs_rlcmac_rlc_window_ws(w); bitpos++) {
+ uint16_t bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bitnum_to_bsn(bitpos, ssn));
+
+ if (bsn == gprs_rlcmac_rlc_window_mod_sns_bsn(w, gprs_rlcmac_rlc_ul_window_v_a(ulw) -
1))
+ break;
+
+ if (show_rbb[gprs_rlcmac_rlc_window_ws(w) - 1 - bitpos] == 'R') {
+ LOGRLCMAC(LOGL_DEBUG, "- got ack for BSN=%u\n", bsn);
+ if (!gprs_rlcmac_rlc_v_b_is_acked(&ulw->v_b, bsn))
+ *received += 1;
+ gprs_rlcmac_rlc_v_b_mark_acked(&ulw->v_b, bsn);
+ } else {
+ LOGRLCMAC(LOGL_DEBUG, "- got NACK for BSN=%u\n", bsn);
+ gprs_rlcmac_rlc_v_b_mark_nacked(&ulw->v_b, bsn);
+ *lost += 1;
+ }
+ }
+}
+
+void gprs_rlcmac_rlc_ul_window_update(struct gprs_rlcmac_rlc_ul_window *ulw, const struct
bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost, uint16_t *received)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+ unsigned int dist = gprs_rlcmac_rlc_ul_window_distance(ulw);
+ unsigned int num_blocks = rbb->cur_bit > dist
+ ? dist : rbb->cur_bit;
+ unsigned int bsn;
+ unsigned int bitpos;
+
+ /* first_bsn is in range V(A)..V(S) */
+
+ for (bitpos = 0; bitpos < num_blocks; bitpos++) {
+ bool is_ack;
+ bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, first_bsn + bitpos);
+ if (bsn == gprs_rlcmac_rlc_window_mod_sns_bsn(w, gprs_rlcmac_rlc_ul_window_v_a(ulw) -
1))
+ break;
+
+ is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
+
+ if (is_ack) {
+ LOGRLCMAC(LOGL_DEBUG, "- got ack for BSN=%u\n", bsn);
+ if (!gprs_rlcmac_rlc_v_b_is_acked(&ulw->v_b, bsn))
+ *received += 1;
+ gprs_rlcmac_rlc_v_b_mark_acked(&ulw->v_b, bsn);
+ } else {
+ LOGRLCMAC(LOGL_DEBUG, "- got NACK for BSN=%u\n", bsn);
+ gprs_rlcmac_rlc_v_b_mark_nacked(&ulw->v_b, bsn);
+ *lost += 1;
+ }
+ }
+}
+
+unsigned int gprs_rlcmac_rlc_ul_window_move_window(struct gprs_rlcmac_rlc_ul_window
*ulw)
+{
+ struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w(ulw);
+ unsigned int i;
+ uint16_t bsn;
+ unsigned int moved = 0;
+
+ for (i = 0, bsn = gprs_rlcmac_rlc_ul_window_v_a(ulw);
+ bsn != gprs_rlcmac_rlc_ul_window_v_s(ulw);
+ i++, bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) {
+ if (gprs_rlcmac_rlc_v_b_is_acked(&ulw->v_b, bsn)) {
+ gprs_rlcmac_rlc_v_b_mark_invalid(&ulw->v_b, bsn);
+ moved += 1;
+ } else
+ break;
+ }
+
+ return moved;
+}
+
+void gprs_rlcmac_rlc_ul_window_show_state(const struct gprs_rlcmac_rlc_ul_window *ulw,
char *show_v_b)
+{
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+ unsigned int i;
+ uint16_t bsn;
+
+ for (i = 0, bsn = gprs_rlcmac_rlc_ul_window_v_a(ulw);
+ bsn != gprs_rlcmac_rlc_ul_window_v_s(ulw);
+ i++, bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) {
+ uint16_t index = bsn & mod_sns_half();
+ switch (gprs_rlcmac_rlc_v_b_get_state(&ulw->v_b, index)) {
+ case GPRS_RLCMAC_RLC_UL_BSN_INVALID:
+ show_v_b[i] = 'I';
+ break;
+ case GPRS_RLCMAC_RLC_UL_BSN_ACKED:
+ show_v_b[i] = 'A';
+ break;
+ case GPRS_RLCMAC_RLC_UL_BSN_RESEND:
+ show_v_b[i] = 'X';
+ break;
+ case GPRS_RLCMAC_RLC_UL_BSN_NACKED:
+ show_v_b[i] = 'N';
+ break;
+ default:
+ show_v_b[i] = '?';
+ }
+ }
+ show_v_b[i] = '\0';
+}
+
+unsigned int gprs_rlcmac_rlc_ul_window_count_unacked(const struct
gprs_rlcmac_rlc_ul_window *ulw)
+{
+ const struct gprs_rlcmac_rlc_window *w = rlc_ulw_as_w_const(ulw);
+ unsigned int unacked = 0;
+ uint16_t bsn;
+
+ for (bsn = gprs_rlcmac_rlc_ul_window_v_a(ulw);
+ bsn != gprs_rlcmac_rlc_ul_window_v_s(ulw);
+ bsn = gprs_rlcmac_rlc_window_mod_sns_bsn(w, bsn + 1)) {
+ if (!gprs_rlcmac_rlc_v_b_is_acked(&ulw->v_b, bsn))
+ unacked += 1;
+ }
+
+ return unacked;
+}
diff --git a/src/rlcmac/rlcmac.c b/src/rlcmac/rlcmac.c
index af6188d..9ce5bcf 100644
--- a/src/rlcmac/rlcmac.c
+++ b/src/rlcmac/rlcmac.c
@@ -52,6 +52,8 @@
g_ctx->cfg.location = location;
g_ctx->cfg.codel.use = true;
g_ctx->cfg.codel.interval_msec = GPRS_CODEL_SLOW_INTERVAL_MS;
+ g_ctx->cfg.egprs_arq_type = GPRS_RLCMAC_EGPRS_ARQ1;
+ g_ctx->cfg.ul_tbf_preemptive_retransmission = true;
g_ctx->T_defs = T_defs_rlcmac;
INIT_LLIST_HEAD(&g_ctx->gre_list);
diff --git a/src/rlcmac/rlcmac_enc.c b/src/rlcmac/rlcmac_enc.c
index ef22f05..f03afd1 100644
--- a/src/rlcmac/rlcmac_enc.c
+++ b/src/rlcmac/rlcmac_enc.c
@@ -18,11 +18,312 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
+
+#include <osmocom/core/endian.h>
+
#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_ul.h>
+/* TS 44.060 10.2.2 */
+struct rlc_ul_header {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t r:1,
+ si:1,
+ cv:4,
+ pt:2;
+ uint8_t ti:1,
+ tfi:5,
+ pi:1,
+ spare:1;
+ uint8_t e:1,
+ bsn:7;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above
(libosmocore/contrib/struct_endianess.py) */
+ uint8_t pt:2, cv:4, si:1, r:1;
+ uint8_t spare:1, pi:1, tfi:5, ti:1;
+ uint8_t bsn:7, e:1;
+#endif
+} __attribute__ ((packed));
+
+struct rlc_li_field {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t e:1,
+ m:1,
+ li:6;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above
(libosmocore/contrib/struct_endianess.py) */
+ uint8_t li:6, m:1, e:1;
+#endif
+} __attribute__ ((packed));
+
+struct rlc_li_field_egprs {
+#if OSMO_IS_LITTLE_ENDIAN
+ uint8_t e:1,
+ li:7;
+#elif OSMO_IS_BIG_ENDIAN
+/* auto-generated from the little endian part above
(libosmocore/contrib/struct_endianess.py) */
+ uint8_t li:7, e:1;
+#endif
+} __attribute__ ((packed));
+
+int gprs_rlcmac_rlc_write_ul_data_header(const struct gprs_rlcmac_rlc_data_info *rlc,
uint8_t *data)
+{
+ struct rlc_ul_header *gprs;
+
+ switch (gprs_rlcmac_mcs_header_type(rlc->cs)) {
+ case GPRS_RLCMAC_HEADER_GPRS_DATA:
+ gprs = (struct rlc_ul_header *)data;
+
+ gprs->r = rlc->r;
+ gprs->si = rlc->si;
+ gprs->cv = rlc->block_info[0].cv;
+ gprs->pt = 0;
+
+ gprs->ti = 0; /* TODO */
+ gprs->tfi = rlc->tfi;
+ gprs->pi = 0; /* TODO */
+ gprs->spare = 0;
+
+ gprs->e = rlc->block_info[0].e;
+ gprs->bsn = rlc->block_info[0].bsn;
+ break;
+
+ case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1:
+ case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2:
+ case GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3:
+ /* TODO: EGPRS. See osmo-pcu.git Encoding::rlc_write_dl_data_header() */
+ default:
+ LOGRLCMAC(LOGL_ERROR, "Encoding of uplink %s data blocks not yet
supported.\n",
+ gprs_rlcmac_mcs_name(rlc->cs));
+ return -ENOTSUP;
+ };
+
+ return 0;
+}
+
+enum gpr_rlcmac_append_result gprs_rlcmac_enc_append_ul_data(
+ struct gprs_rlcmac_rlc_block_info *rdbi,
+ enum gprs_rlcmac_coding_scheme cs,
+ struct msgb *llc_msg, int *offset, int *num_chunks,
+ uint8_t *data_block, bool is_final, int *count_payload)
+{
+ int chunk;
+ int space;
+ struct rlc_li_field *li;
+ uint8_t *delimiter, *data, *e_pointer;
+
+ data = data_block + *offset;
+ delimiter = data_block + *num_chunks;
+ e_pointer = (*num_chunks ? delimiter - 1 : NULL);
+
+ chunk = msgb_length(llc_msg);
+ space = rdbi->data_len - *offset;
+
+ /* if chunk will exceed block limit */
+ if (chunk > space) {
+ LOGRLCMAC(LOGL_DEBUG, "-- Chunk with length %d "
+ "larger than space (%d) left in block: copy "
+ "only remaining space, and we are done\n",
+ chunk, space);
+ if (e_pointer) {
+ /* LLC frame not finished, so there is no extension octet */
+ *e_pointer |= 0x02; /* set previous M bit = 1 */
+ }
+ /* fill only space */
+ memcpy(data, msgb_data(llc_msg), space);
+ msgb_pull(llc_msg, space);
+ if (count_payload)
+ *count_payload = space;
+ /* return data block as message */
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ return GPRS_RLCMAC_AR_NEED_MORE_BLOCKS;
+ }
+ /* if FINAL chunk would fit precisely in space left */
+ if (chunk == space && is_final) {
+ LOGRLCMAC(LOGL_DEBUG, "-- Chunk with length %d "
+ "would exactly fit into space (%d): because "
+ "this is a final block, we don't add length "
+ "header, and we are done\n", chunk, space);
+ /* block is filled, so there is no extension */
+ if (e_pointer)
+ *e_pointer |= 0x01;
+ /* fill space */
+ memcpy(data, msgb_data(llc_msg), space);
+ msgb_pull(llc_msg, space);
+ if (count_payload)
+ *count_payload = space;
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ rdbi->cv = 0;
+ return GPRS_RLCMAC_AR_COMPLETED_BLOCK_FILLED;
+ }
+ /* if chunk would fit exactly in space left */
+ if (chunk == space) {
+ LOGRLCMAC(LOGL_DEBUG, "-- Chunk with length %d "
+ "would exactly fit into space (%d): add length "
+ "header with LI=0, to make frame extend to "
+ "next block, and we are done\n", chunk, space);
+ /* make space for delimiter */
+ if (delimiter != data)
+ memmove(delimiter + 1, delimiter,
+ data - delimiter);
+ if (e_pointer) {
+ *e_pointer &= 0xfe; /* set previous E bit = 0 */
+ *e_pointer |= 0x02; /* set previous M bit = 1 */
+ }
+ data++;
+ (*offset)++;
+ space--;
+ /* add LI with 0 length */
+ li = (struct rlc_li_field *)delimiter;
+ li->e = 1; /* not more extension */
+ li->m = 0; /* shall be set to 0, in case of li = 0 */
+ li->li = 0; /* chunk fills the complete space */
+ rdbi->e = 0; /* 0: extensions present */
+ // no need to set e_pointer nor increase delimiter
+ /* fill only space, which is 1 octet less than chunk */
+ memcpy(data, msgb_data(llc_msg), space);
+ msgb_pull(llc_msg, space);
+ if (count_payload)
+ *count_payload = space;
+ /* return data block as message */
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ return GPRS_RLCMAC_AR_NEED_MORE_BLOCKS;
+ }
+
+ LOGRLCMAC(LOGL_DEBUG, "-- Chunk with length %d is less "
+ "than remaining space (%d): add length header to "
+ "delimit LLC frame\n", chunk, space);
+ /* the LLC frame chunk ends in this block */
+ /* make space for delimiter */
+ if (delimiter != data)
+ memmove(delimiter + 1, delimiter, data - delimiter);
+ if (e_pointer) {
+ *e_pointer &= 0xfe; /* set previous E bit = 0 */
+ *e_pointer |= 0x02; /* set previous M bit = 1 */
+ }
+ data++;
+ (*offset)++;
+ space--;
+ /* add LI to delimit frame */
+ li = (struct rlc_li_field *)delimiter;
+ li->e = 1; /* not more extension, maybe set later */
+ li->m = 0; /* will be set later, if there is more LLC data */
+ li->li = chunk; /* length of chunk */
+ rdbi->e = 0; /* 0: extensions present */
+ (*num_chunks)++;
+ /* copy (rest of) LLC frame to space and reset later */
+ memcpy(data, msgb_data(llc_msg), chunk);
+ msgb_pull(llc_msg, chunk);
+ if (count_payload)
+ *count_payload = chunk;
+ data += chunk;
+ space -= chunk;
+ (*offset) += chunk;
+ /* if we have more data and we have space left */
+ if (space > 0 && !is_final)
+ return GPRS_RLCMAC_AR_COMPLETED_SPACE_LEFT;
+
+ /* if we don't have more LLC frames */
+ if (is_final) {
+ LOGRLCMAC(LOGL_DEBUG, "-- Final block, so we done.\n");
+ rdbi->cv = 0;
+ return GPRS_RLCMAC_AR_COMPLETED_BLOCK_FILLED;
+ }
+ /* we have no space left */
+ LOGRLCMAC(LOGL_DEBUG, "-- No space left, so we are done.\n");
+ return GPRS_RLCMAC_AR_COMPLETED_BLOCK_FILLED;
+}
+
+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 rlc_li_field_egprs *li;
+ struct 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 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 */
+
+ if (delimiter != data)
+ memmove(delimiter + 1, delimiter, data - delimiter);
+
+ /* set filling bytes extension */
+ li = (struct rlc_li_field_egprs *)delimiter;
+ li->e = 1;
+ li->li = 127;
+
+ /* tell previous extension header about the new one */
+ if (prev_li)
+ prev_li->e = 0;
+
+ (*num_chunks)++;
+ *offset = rdbi->data_len;
+}
+
+/**
+ * \brief Copy LSB bitstream RLC data block from 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_from_aligned_buffer(const struct
gprs_rlcmac_rlc_data_info *rlc,
+ unsigned int data_block_idx,
+ uint8_t *dst, const uint8_t *buffer)
+{
+ unsigned int hdr_bytes;
+ unsigned int extra_bits;
+ unsigned int i;
+
+ uint8_t c, last_c;
+ const uint8_t *src;
+ 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] / 8;
+ extra_bits = (rlc->data_offs_bits[data_block_idx] % 8);
+
+ if (extra_bits == 0) {
+ /* It is aligned already */
+ memmove(dst + hdr_bytes, buffer, rdbi->data_len);
+ return rdbi->data_len;
+ }
+
+ src = buffer;
+ dst = dst + hdr_bytes;
+ last_c = *dst << (8 - extra_bits);
+
+ for (i = 0; i < rdbi->data_len; i++) {
+ c = src[i];
+ *(dst++) = (last_c >> (8 - extra_bits)) | (c << extra_bits);
+ last_c = c;
+ }
+
+ /* overwrite the lower extra_bits */
+ *dst = (*dst & (0xff << extra_bits)) | (last_c >> (8 - extra_bits));
+
+ return rdbi->data_len;
+}
+
void gprs_rlcmac_enc_prepare_pkt_ul_dummy_block(RlcMacUplink_t *block, uint32_t tlli)
{
Packet_Uplink_Dummy_Control_Block_t *dummy;
diff --git a/src/rlcmac/tbf_ul.c b/src/rlcmac/tbf_ul.c
index ddb29a8..dd58b0c 100644
--- a/src/rlcmac/tbf_ul.c
+++ b/src/rlcmac/tbf_ul.c
@@ -24,6 +24,9 @@
#include <osmocom/gprs/rlcmac/tbf_ul.h>
#include <osmocom/gprs/rlcmac/rlcmac_enc.h>
#include <osmocom/gprs/rlcmac/gre.h>
+#include <osmocom/gprs/rlcmac/coding_scheme.h>
+#include <osmocom/gprs/rlcmac/rlc_window_ul.h>
+#include <osmocom/gprs/rlcmac/rlc.h>
struct gprs_rlcmac_ul_tbf *gprs_rlcmac_ul_tbf_alloc(struct gprs_rlcmac_entity *gre)
{
@@ -44,6 +47,14 @@
if (rc < 0)
goto err_state_fsm_destruct;
+ ul_tbf->ulw = gprs_rlcmac_rlc_ul_window_alloc(ul_tbf);
+ OSMO_ASSERT(ul_tbf->ulw);
+
+ ul_tbf->blkst = gprs_rlcmac_rlc_block_store_alloc(ul_tbf);
+ OSMO_ASSERT(ul_tbf->blkst);
+
+ ul_tbf->tx_cs = GPRS_RLCMAC_CS_1;
+
return ul_tbf;
err_state_fsm_destruct:
@@ -59,6 +70,12 @@
if (!ul_tbf)
return;
+ gprs_rlcmac_rlc_block_store_free(ul_tbf->blkst);
+ ul_tbf->blkst = NULL;
+
+ gprs_rlcmac_rlc_ul_window_free(ul_tbf->ulw);
+ ul_tbf->ulw = NULL;
+
gprs_rlcmac_tbf_ul_ass_fsm_destructor(ul_tbf);
gprs_rlcmac_tbf_ul_fsm_destructor(ul_tbf);
@@ -126,9 +143,569 @@
return NULL;
}
-struct msgb *gprs_rlcmac_ul_tbf_data_create(const struct gprs_rlcmac_ul_tbf *ul_tbf,
const struct gprs_rlcmac_rts_block_ind *bi)
+bool gprs_rlcmac_ul_tbf_have_data(const struct gprs_rlcmac_ul_tbf *ul_tbf)
{
+ return (msgb_length(ul_tbf->llc_tx_msg) > 0) ||
+ (gprs_rlcmac_llc_queue_size(ul_tbf->tbf.gre->llc_queue) > 0);
+}
+
+bool gprs_rlcmac_ul_tbf_shall_keep_open(const struct gprs_rlcmac_ul_tbf *ul_tbf, const
struct gprs_rlcmac_rts_block_ind *bi)
+{
+ /* TODO: In here a VTY timer can be defined which specifies an amount of
+ * time during which the MS stays in CV=15 while waiting for more data from
+ * upper layers. This way we avoid entering last CV modes and keep the TBF
+ * open (sending Uplink Dummy Ctrl Block if necessary).
+ * Amount of time elapsed in this condition can be valculated based on
+ * bi->fn - ul_tbf->last_ul_drained_fn;
+ */
+ return false;
+}
+
+void gprs_rlcmac_ul_tbf_schedule_next_llc_frame(struct gprs_rlcmac_ul_tbf *ul_tbf)
+{
+ if (msgb_length(ul_tbf->llc_tx_msg))
+ return;
+
+ msgb_free(ul_tbf->llc_tx_msg);
+ /* dequeue next LLC frame, if any */
+ ul_tbf->llc_tx_msg =
gprs_rlcmac_llc_queue_dequeue(ul_tbf->tbf.gre->llc_queue);
+ if (!ul_tbf->llc_tx_msg)
+ return;
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Dequeue next LLC (len=%d)\n",
msgb_length(ul_tbf->llc_tx_msg));
+
+ ul_tbf->last_ul_drained_fn = -1;
+}
+
+static int create_new_bsn(struct gprs_rlcmac_ul_tbf *ul_tbf, const struct
gprs_rlcmac_rts_block_ind *bi, enum gprs_rlcmac_coding_scheme cs)
+{
+ struct gprs_rlcmac_llc_queue *llc_queue = ul_tbf->tbf.gre->llc_queue;
+ const uint16_t bsn = gprs_rlcmac_rlc_ul_window_v_s(ul_tbf->ulw);
+ struct gprs_rlcmac_rlc_block *blk;
+ struct gprs_rlcmac_rlc_block_info *rdbi;
+ uint8_t *data;
+ int num_chunks = 0;
+ int write_offset = 0;
+ enum gpr_rlcmac_append_result ar;
+
+ if (!ul_tbf->llc_tx_msg || msgb_length(ul_tbf->llc_tx_msg) == 0)
+ gprs_rlcmac_ul_tbf_schedule_next_llc_frame(ul_tbf);
+ OSMO_ASSERT(ul_tbf->llc_tx_msg);
+
+ OSMO_ASSERT(gprs_rlcmac_mcs_is_valid(cs));
+
+ /* length of usable data block (single data unit w/o header) */
+ const uint8_t block_data_len = gprs_rlcmac_mcs_max_data_block_bytes(cs);
+
+ /* now we still have untransmitted LLC data, so we fill mac block */
+ blk = gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+ data = gprs_rlcmac_rlc_block_prepare(blk, block_data_len);
+ blk->cs_last = cs;
+ blk->cs_current_trans = cs;
+
+ /* Initialise the variable related to UL SPB */
+ blk->spb_status.block_status_ul = GPRS_RLCMAC_EGPRS_RESEG_UL_DEFAULT;
+ blk->cs_init = cs;
+
+ blk->len = block_data_len;
+
+ rdbi = &(blk->block_info);
+ memset(rdbi, 0, sizeof(*rdbi));
+ rdbi->data_len = block_data_len;
+
+ rdbi->cv = 15; /* Final Block Indicator, set late, if true */
+ rdbi->bsn = bsn; /* Block Sequence Number */
+ rdbi->e = 1; /* Extension bit, maybe set later (1: no extension) */
+
+ do {
+ bool is_final;
+ int payload_written = 0;
+
+ if (msgb_length(ul_tbf->llc_tx_msg) == 0) {
+ /* The data just drained, store the current fn */
+ if (ul_tbf->last_ul_drained_fn < 0)
+ ul_tbf->last_ul_drained_fn = bi->fn;
+
+ int space = block_data_len - write_offset;
+
+ if (num_chunks != 0) {
+ /* Nothing to send, and we already put some data in
+ * rlcmac data block, we are done */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG,
+ "LLC queue completely drained and there's "
+ "still %d free bytes in rlcmac data block\n", space);
+
+ /* We may need to update fbi in header here
+ * since ul_tbf->last_ul_drained_fn was updated above
+ * Specially important when X2031 is 0. */
+ is_final = gprs_rlcmac_llc_queue_size(llc_queue) == 0 &&
+ !gprs_rlcmac_ul_tbf_shall_keep_open(ul_tbf, bi);
+ if (is_final) {
+ rdbi->cv = 0;
+ osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi,
GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT, NULL);
+ }
+
+ if (gprs_rlcmac_mcs_is_edge(cs)) {
+ /* in EGPRS there's no M bit, so we need
+ * to flag padding with LI=127 */
+ gprs_rlcmac_rlc_data_to_ul_append_egprs_li_padding(
+ rdbi, &write_offset, &num_chunks, data);
+ }
+ break;
+ }
+ }
+
+ is_final = gprs_rlcmac_llc_queue_size(llc_queue) == 0 &&
+ !gprs_rlcmac_ul_tbf_shall_keep_open(ul_tbf, bi);
+
+ ar = gprs_rlcmac_enc_append_ul_data(rdbi, cs, ul_tbf->llc_tx_msg,
+ &write_offset, &num_chunks, data,
+ is_final, &payload_written);
+
+ if (ar == GPRS_RLCMAC_AR_NEED_MORE_BLOCKS)
+ break;
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Complete UL frame, len=%d\n",
msgb_length(ul_tbf->llc_tx_msg));
+ msgb_free(ul_tbf->llc_tx_msg);
+ ul_tbf->llc_tx_msg = NULL;
+
+ if (is_final)
+ osmo_fsm_inst_dispatch(ul_tbf->state_fsm.fi,
GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT, NULL);
+
+ /* dequeue next LLC frame, if any */
+ gprs_rlcmac_ul_tbf_schedule_next_llc_frame(ul_tbf);
+ } while (ar == GPRS_RLCMAC_AR_COMPLETED_SPACE_LEFT);
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "data block (BSN %d, %s): %s\n",
+ bsn, gprs_rlcmac_mcs_name(blk->cs_last),
+ osmo_hexdump(blk->buf, block_data_len));
+ /* raise send state and set ack state array */
+ gprs_rlcmac_rlc_v_b_mark_unacked(&ul_tbf->ulw->v_b, bsn);
+ gprs_rlcmac_rlc_ul_window_increment_send(ul_tbf->ulw);
+
+ return bsn;
+}
+
+static bool restart_bsn_cycle(const struct gprs_rlcmac_ul_tbf *ul_tbf)
+{
+ /* If V(S) == V(A) and finished state, we would have received
+ * acknowledgement of all transmitted block. In this case we would
+ * have transmitted the final block, and received ack from MS. But in
+ * this case we did not receive the final ack indication from MS. This
+ * should never happen if MS works correctly.
+ */
+ if (gprs_rlcmac_rlc_ul_window_window_empty(ul_tbf->ulw)) {
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "MS acked all blocks\n");
+ return false;
+ }
+
+ /* cycle through all unacked blocks */
+ int resend = gprs_rlcmac_rlc_ul_window_mark_for_resend(ul_tbf->ulw);
+
+ /* At this point there should be at least one unacked block
+ * to be resent. If not, this is an software error. */
+ if (resend == 0) {
+ LOGPTBFUL(ul_tbf, LOGL_ERROR,
+ "FIXME: Software error: There are no unacknowledged blocks, but V(A) != V(S).
PLEASE FIX!\n");
+ return false;
+ }
+
+ return true;
+}
+
+static int take_next_bsn(struct gprs_rlcmac_ul_tbf *ul_tbf, const struct
gprs_rlcmac_rts_block_ind *bi,
+ int previous_bsn, bool *may_combine)
+{
+ int bsn;
+ int data_len2;
+ int force_data_len = -1;
+ enum gprs_rlcmac_coding_scheme tx_cs;
+ struct gprs_rlcmac_rlc_block *blk;
+
+ /* search for a nacked or resend marked bsn */
+ bsn = gprs_rlcmac_rlc_ul_window_resend_needed(ul_tbf->ulw);
+
+ if (previous_bsn >= 0) {
+ struct gprs_rlcmac_rlc_block *pevious_blk =
gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, previous_bsn);
+ tx_cs = pevious_blk->cs_current_trans;
+ if (!gprs_rlcmac_mcs_is_edge(tx_cs))
+ return -1;
+ force_data_len = pevious_blk->len;
+ } else {
+ tx_cs = ul_tbf->tx_cs;
+ }
+
+ if (bsn >= 0) {
+ /* resend an unacked bsn or resend bsn. */
+ if (previous_bsn == bsn)
+ return -1;
+
+ if (previous_bsn >= 0 &&
+ gprs_rlcmac_rlc_window_mod_sns_bsn(ul_tbf->w, bsn - previous_bsn) >
RLC_EGPRS_MAX_BSN_DELTA)
+ return -1;
+
+ blk = gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+ if (ul_tbf->is_egprs) {
+ /* Table 8.1.1.2 and Table 8.1.1.1 of 44.060 */
+ blk->cs_current_trans = gprs_rlcmac_get_retx_mcs(blk->cs_init, tx_cs,
+ g_ctx->cfg.egprs_arq_type == GPRS_RLCMAC_EGPRS_ARQ1);
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG,
+ "initial_cs_dl(%s) last_mcs(%s) demanded_mcs(%s) cs_trans(%s) arq_type(%d)
bsn(%d)\n",
+ gprs_rlcmac_mcs_name(blk->cs_init),
+ gprs_rlcmac_mcs_name(blk->cs_last),
+ gprs_rlcmac_mcs_name(tx_cs),
+ gprs_rlcmac_mcs_name(blk->cs_current_trans),
+ g_ctx->cfg.egprs_arq_type, bsn);
+
+ /* TODO: Need to remove this check when MCS-8 -> MCS-6
+ * transistion is handled.
+ * Refer commit be881c028fc4da00c4046ecd9296727975c206a3
+ */
+ if (blk->cs_init == GPRS_RLCMAC_MCS_8)
+ blk->cs_current_trans = GPRS_RLCMAC_MCS_8;
+ } else {
+ /* gprs */
+ blk->cs_current_trans = blk->cs_last;
+ }
+
+ data_len2 = blk->len;
+ if (force_data_len > 0 && force_data_len != data_len2)
+ return -1;
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Resending BSN %d\n", bsn);
+ /* re-send block with negative acknowlegement */
+ gprs_rlcmac_rlc_v_b_mark_unacked(&ul_tbf->ulw->v_b, bsn);
+ } else if (gprs_rlcmac_tbf_ul_state(ul_tbf) == GPRS_RLCMAC_TBF_UL_ST_FINISHED) {
+ /* If the TBF is in finished, we already sent all packages at least once.
+ * If any packages could have been sent (because of unacked) it should have
+ * been catched up by the upper if(bsn >= 0) */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Restarting at BSN %d, because all blocks have been
transmitted.\n",
+ gprs_rlcmac_rlc_ul_window_v_a(ul_tbf->ulw));
+ if (restart_bsn_cycle(ul_tbf))
+ return take_next_bsn(ul_tbf, bi, previous_bsn, may_combine);
+ } else if (gprs_rlcmac_rlc_ul_window_window_stalled(ul_tbf->ulw)) {
+ /* There are no more packages to send, but the window is stalled.
+ * Restart the bsn_cycle to resend all unacked messages */
+ LOGPTBFUL(ul_tbf, LOGL_NOTICE, "Restarting at BSN %d, because the window is
stalled.\n",
+ gprs_rlcmac_rlc_ul_window_v_a(ul_tbf->ulw));
+ if (restart_bsn_cycle(ul_tbf))
+ return take_next_bsn(ul_tbf, bi, previous_bsn, may_combine);
+ } else if (gprs_rlcmac_ul_tbf_have_data(ul_tbf)) {
+ /* The window has space left, generate new bsn */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Sending new block at BSN %d, CS=%s%s\n",
+ gprs_rlcmac_rlc_ul_window_v_s(ul_tbf->ulw), gprs_rlcmac_mcs_name(tx_cs),
+ force_data_len != -1 ? " (forced)" : "");
+
+ bsn = create_new_bsn(ul_tbf, bi, tx_cs);
+ } else if (g_ctx->cfg.ul_tbf_preemptive_retransmission &&
+ !gprs_rlcmac_rlc_ul_window_window_empty(ul_tbf->ulw)) {
+ /* The window contains unacked packages, but not acked.
+ * Mark unacked bsns as RESEND */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Restarting at BSN %d, because all blocks have been
transmitted (FLOW).\n",
+ gprs_rlcmac_rlc_ul_window_v_a(ul_tbf->ulw));
+ if (restart_bsn_cycle(ul_tbf))
+ return take_next_bsn(ul_tbf, bi, previous_bsn, may_combine);
+ } else {
+ /* Nothing left to send, create dummy LLC commands */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Sending new dummy block at BSN %d, CS=%s\n",
+ gprs_rlcmac_rlc_ul_window_v_s(ul_tbf->ulw),
+ gprs_rlcmac_mcs_name(tx_cs));
+ bsn = create_new_bsn(ul_tbf, bi, tx_cs);
+ /* Don't send a second block, so don't set cs_current_trans */
+ }
+
+ if (bsn < 0) {
+ /* we just send final block again */
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Nothing else to send, Re-transmit final
block!\n");
+ bsn = gprs_rlcmac_rlc_ul_window_v_s_mod(ul_tbf->ulw, -1);
+ }
+
+ blk = gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+ *may_combine =
gprs_rlcmac_num_data_blocks(gprs_rlcmac_mcs_header_type(blk->cs_current_trans)) >
1;
+
+ return bsn;
+}
+
+/*
+ * This function returns the pointer to data which needs
+ * to be copied. Also updates the status of the block related to
+ * Split block handling in the RLC/MAC block.
+ */
+static enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state egprs_ul_get_data(const struct
gprs_rlcmac_ul_tbf *ul_tbf, int bsn, uint8_t **block_data)
+{
+ struct gprs_rlcmac_rlc_block *blk =
gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+ enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state *block_status_ul =
&blk->spb_status.block_status_ul;
+
+ enum gprs_rlcmac_coding_scheme cs_init = blk->cs_init;
+ enum gprs_rlcmac_coding_scheme cs_current_trans = blk->cs_current_trans;
+
+ enum gprs_rlcmac_header_type ht_cs_init = gprs_rlcmac_mcs_header_type(blk->cs_init);
+ enum gprs_rlcmac_header_type ht_cs_current_trans =
gprs_rlcmac_mcs_header_type(blk->cs_current_trans);
+
+ *block_data = &blk->buf[0];
+
+ /*
+ * Table 10.3a.0.1 of 44.060
+ * MCS6,9: second segment starts at 74/2 = 37
+ * MCS5,7: second segment starts at 56/2 = 28
+ * MCS8: second segment starts at 31
+ * MCS4: second segment starts at 44/2 = 22
+ */
+ if (ht_cs_current_trans == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3) {
+ if (*block_status_ul == GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_SENT) {
+ switch (cs_init) {
+ case GPRS_RLCMAC_MCS_6:
+ case GPRS_RLCMAC_MCS_9:
+ *block_data = &blk->buf[37];
+ break;
+ case GPRS_RLCMAC_MCS_7:
+ case GPRS_RLCMAC_MCS_5:
+ *block_data = &blk->buf[28];
+ break;
+ case GPRS_RLCMAC_MCS_8:
+ *block_data = &blk->buf[31];
+ break;
+ case GPRS_RLCMAC_MCS_4:
+ *block_data = &blk->buf[22];
+ break;
+ default:
+ LOGPTBFUL(ul_tbf, LOGL_ERROR,
+ "FIXME: Software error: hit invalid condition. "
+ "headerType(%d) blockstatus(%d) cs(%s) PLEASE FIX!\n",
+ ht_cs_current_trans,
+ *block_status_ul, gprs_rlcmac_mcs_name(cs_init));
+ break;
+
+ }
+ return GPRS_RLCMAC_EGPRS_RESEG_SECOND_SEG_SENT;
+ } else if ((ht_cs_init == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1) ||
+ (ht_cs_init == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2)) {
+ return GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_SENT;
+ } else if ((cs_init == GPRS_RLCMAC_MCS_4) &&
+ (cs_current_trans == GPRS_RLCMAC_MCS_1)) {
+ return GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_SENT;
+ }
+ }
+ return GPRS_RLCMAC_EGPRS_RESEG_UL_DEFAULT;
+}
+
+/*
+ * This function returns the spb value to be sent OTA
+ * for RLC/MAC block.
+ */
+static enum gprs_rlcmac_rlc_egprs_ul_spb get_egprs_ul_spb(const struct gprs_rlcmac_ul_tbf
*ul_tbf, int bsn)
+{
+ struct gprs_rlcmac_rlc_block *blk =
gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+ enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state block_status_ul =
blk->spb_status.block_status_ul;
+
+ enum gprs_rlcmac_coding_scheme cs_init = blk->cs_init;
+ enum gprs_rlcmac_coding_scheme cs_current_trans = blk->cs_current_trans;
+
+ enum gprs_rlcmac_header_type ht_cs_init = gprs_rlcmac_mcs_header_type(blk->cs_init);
+ enum gprs_rlcmac_header_type ht_cs_current_trans =
gprs_rlcmac_mcs_header_type(blk->cs_current_trans);
+
+ /* Table 10.4.8b.1 of 44.060 */
+ if (ht_cs_current_trans == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_3) {
+ /*
+ * if we are sending the second segment the spb should be 3
+ * otherwise it should be 2
+ */
+ if (block_status_ul == GPRS_RLCMAC_EGPRS_RESEG_FIRST_SEG_SENT) {
+ return GPRS_RLCMAC_EGPRS_UL_SPB_SEC_SEG;
+ } else if ((ht_cs_init == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_1) ||
+ (ht_cs_init == GPRS_RLCMAC_HEADER_EGPRS_DATA_TYPE_2)) {
+ return GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_6NOPAD;
+ } else if ((cs_init == GPRS_RLCMAC_MCS_4) &&
+ (cs_current_trans == GPRS_RLCMAC_MCS_1)) {
+ return GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_10PAD;
+ }
+ }
+ /* Non SPB cases 0 is reurned */
+ return GPRS_RLCMAC_EGPRS_UL_SPB_NO_RETX;
+}
+
+static struct msgb *create_ul_acked_block(struct gprs_rlcmac_ul_tbf *ul_tbf,
+ const struct gprs_rlcmac_rts_block_ind *bi,
+ int index, int index2)
+{
+ uint8_t *msg_data;
+ struct msgb *msg;
+ unsigned msg_len;
+ /* TODO: support MCS-7 - MCS-9, where data_block_idx can be 1 */
+ uint8_t data_block_idx = 0;
+ bool is_final = false;
+ enum gprs_rlcmac_coding_scheme cs_init, cs;
+ struct gprs_rlcmac_rlc_data_info rlc;
+ int bsns[ARRAY_SIZE(rlc.block_info)];
+ unsigned num_bsns;
+ bool need_padding = false;
+ enum gprs_rlcmac_rlc_egprs_ul_spb spb = GPRS_RLCMAC_EGPRS_UL_SPB_NO_RETX;
+ unsigned int spb_status;
+ struct gprs_rlcmac_rlc_block *blk;
+
+ blk = gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, index);
+ spb_status = blk->spb_status.block_status_ul;
+
+ enum gprs_rlcmac_egprs_puncturing_values punct[2] = {
+ GPRS_RLCMAC_EGPRS_PS_INVALID, GPRS_RLCMAC_EGPRS_PS_INVALID
+ };
+ osmo_static_assert(ARRAY_SIZE(rlc.block_info) == 2, rlc_block_info_size_is_two);
+
+ /*
+ * TODO: This is an experimental work-around to put 2 BSN into
+ * MCS-7 to MCS-9 encoded messages. It just sends the same BSN
+ * twice in the block. The cs should be derived from the TBF's
+ * current CS such that both BSNs (that must be compatible) can
+ * be put into the data area, even if the resulting CS is higher than
+ * the current limit.
+ */
+ cs = blk->cs_current_trans;
+ cs_init = blk->cs_init;
+ bsns[0] = index;
+ num_bsns = 1;
+
+ if (index2 >= 0) {
+ bsns[num_bsns] = index2;
+ num_bsns += 1;
+ }
+
+ /*
+ * if the initial mcs is 8 and retransmission mcs is either 6 or 3
+ * we have to include the padding of 6 octets in first segment
+ */
+ if ((cs_init == GPRS_RLCMAC_MCS_8) &&
+ (cs == GPRS_RLCMAC_MCS_6 || cs == GPRS_RLCMAC_MCS_3)) {
+ if (spb_status == GPRS_RLCMAC_EGPRS_RESEG_UL_DEFAULT ||
+ spb_status == GPRS_RLCMAC_EGPRS_RESEG_SECOND_SEG_SENT)
+ need_padding = true;
+ } else if (num_bsns == 1) {
+ /* TODO: remove the conditional when MCS-6 padding isn't
+ * failing to be decoded by MEs anymore */
+ /* TODO: support of MCS-8 -> MCS-6 transition should be
+ * handled
+ * Refer commit be881c028fc4da00c4046ecd9296727975c206a3
+ * dated 2016-02-07 23:45:40 (UTC)
+ */
+ if (cs != GPRS_RLCMAC_MCS_8)
+ gprs_rlcmac_mcs_dec_to_single_block(&cs, &need_padding);
+ }
+
+ spb = get_egprs_ul_spb(ul_tbf, index);
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "need_padding %d spb_status %d spb %d (BSN1 %d BSN2
%d)\n",
+ need_padding, spb_status, spb, index, index2);
+
+ gprs_rlcmac_rlc_data_info_init_ul(&rlc, cs, need_padding, spb);
+
+ rlc.usf = 7; /* will be set at scheduler */
+ rlc.pr = 0; /* FIXME: power reduction */
+ rlc.tfi = ul_tbf->cur_alloc.tfi; /* TFI */
+
+ /* return data block(s) as message */
+ msg_len = gprs_rlcmac_mcs_size_dl(cs);
+ msg = msgb_alloc(msg_len, "rlcmac_ul_data");
+ if (!msg)
+ return NULL;
+
+ msg_data = msgb_put(msg, msg_len);
+
+ OSMO_ASSERT(rlc.num_data_blocks <= ARRAY_SIZE(rlc.block_info));
+ OSMO_ASSERT(rlc.num_data_blocks > 0);
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Copying %u RLC blocks, %u BSNs\n",
rlc.num_data_blocks, num_bsns);
+
+ /* Copy block(s) to RLC message: the num_data_blocks cannot be more than 2 - see assert
above */
+ for (data_block_idx = 0; data_block_idx < OSMO_MIN(rlc.num_data_blocks, 2);
data_block_idx++) {
+ int bsn;
+ uint8_t *block_data;
+ struct gprs_rlcmac_rlc_block_info *rdbi, *block_info;
+ enum gprs_rlcmac_rlc_egprs_ul_reseg_bsn_state reseg_status;
+
+ /* Check if there are more blocks than BSNs */
+ if (data_block_idx < num_bsns)
+ bsn = bsns[data_block_idx];
+ else
+ bsn = bsns[0];
+
+ /* Get current puncturing scheme from block */
+ blk = gprs_rlcmac_rlc_block_store_get_block(ul_tbf->blkst, bsn);
+
+ blk->next_ps = gprs_rlcmac_get_punct_scheme(blk->next_ps, blk->cs_last, cs,
spb);
+
+ if (gprs_rlcmac_mcs_is_edge(cs)) {
+ OSMO_ASSERT(blk->next_ps >= GPRS_RLCMAC_EGPRS_PS_1);
+ OSMO_ASSERT(blk->next_ps <= GPRS_RLCMAC_EGPRS_PS_3);
+ }
+
+ punct[data_block_idx] = blk->next_ps;
+
+ rdbi = &rlc.block_info[data_block_idx];
+ block_info = &blk->block_info;
+
+ /*
+ * get data and header from current block
+ * function returns the reseg status
+ */
+ reseg_status = egprs_ul_get_data(ul_tbf, bsn, &block_data);
+ blk->spb_status.block_status_dl = reseg_status;
+
+ /*
+ * If it is first segment of the split block set the state of
+ * bsn to nacked. If it is the first segment dont update the
+ * next ps value of bsn. since next segment also needs same cps
+ */
+ if (spb == GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_10PAD ||
+ spb == GPRS_RLCMAC_EGPRS_UL_SPB_FIRST_SEG_6NOPAD)
+ gprs_rlcmac_rlc_v_b_mark_nacked(&ul_tbf->ulw->v_b, bsn);
+ else {
+ /*
+ * TODO: Need to handle 2 same bsns
+ * in header type 1
+ */
+ gprs_rlcmac_update_punct_scheme(&blk->next_ps, cs);
+ }
+
+ blk->cs_last = cs;
+ rdbi->e = block_info->e;
+ rdbi->cv = block_info->cv;
+ rdbi->bsn = bsn;
+ is_final = is_final || rdbi->cv == 0;
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "Copying data unit %d (BSN %d)\n",
+ data_block_idx, bsn);
+
+ gprs_rlcmac_rlc_copy_from_aligned_buffer(&rlc, data_block_idx, msg_data,
block_data);
+ }
+
+ /* Calculate CPS only for EGPRS case */
+ if (gprs_rlcmac_mcs_is_edge(cs))
+ rlc.cps = gprs_rlcmac_rlc_mcs_cps(cs, punct[0], punct[1], need_padding);
+
+ gprs_rlcmac_rlc_write_ul_data_header(&rlc, msg_data);
+
+ LOGPTBFUL(ul_tbf, LOGL_DEBUG, "msg block (BSN %d, %s%s): %s\n",
+ index, gprs_rlcmac_mcs_name(cs),
+ need_padding ? ", padded" : "",
+ msgb_hexdump(msg));
+
+ return msg;
+}
+
+struct msgb *gprs_rlcmac_ul_tbf_data_create(struct gprs_rlcmac_ul_tbf *ul_tbf, const
struct gprs_rlcmac_rts_block_ind *bi)
+{
+ int bsn;
+ int bsn2 = -1;
+ bool may_combine;
+
+ bsn = take_next_bsn(ul_tbf, bi, -1, &may_combine);
+ if (bsn < 0)
+ return NULL;
+
+ if (may_combine)
+ bsn2 = take_next_bsn(ul_tbf, bi, bsn, &may_combine);
+
LOGPTBFUL(ul_tbf, LOGL_ERROR, "(trx=%u,ts=%u,fn=%u,usf=%u) TODO: implement dequeue
from LLC\n",
bi->trx, bi->ts, bi->fn, bi->usf);
- return NULL;
+
+ return create_ul_acked_block(ul_tbf, bi, bsn, bsn2);
}
diff --git a/src/rlcmac/tbf_ul_fsm.c b/src/rlcmac/tbf_ul_fsm.c
index a5d4a29..c2ff45c 100644
--- a/src/rlcmac/tbf_ul_fsm.c
+++ b/src/rlcmac/tbf_ul_fsm.c
@@ -30,8 +30,9 @@
#define X(s) (1 << (s))
static const struct value_string tbf_ul_fsm_event_names[] = {
- { GPRS_RLCMAC_TBF_UL_EV_UL_ASS_COMPL, "UL_ASS_COMPL" },
- { GPRS_RLCMAC_TBF_UL_EV_FOOBAR, "FOOBAR" },
+ { 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" },
{ 0, NULL }
};
@@ -39,6 +40,7 @@
[GPRS_RLCMAC_TBF_UL_ST_NEW] = { },
[GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN] = { },
[GPRS_RLCMAC_TBF_UL_ST_FLOW] = { },
+ [GPRS_RLCMAC_TBF_UL_ST_FINISHED] = { },
};
/* Transition to a state, using the T timer defined in tbf_fsm_timeouts.
@@ -82,6 +84,18 @@
{
//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_LAST_UL_DATA_SENT:
+ tbf_ul_fsm_state_chg(fi, GPRS_RLCMAC_TBF_UL_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_ul_fsm_ctx *ctx = (struct gprs_rlcmac_tbf_ul_fsm_ctx
*)fi->priv;
+ switch (event) {
default:
OSMO_ASSERT(0);
}
@@ -108,11 +122,19 @@
},
[GPRS_RLCMAC_TBF_UL_ST_FLOW] = {
.in_event_mask =
+ X(GPRS_RLCMAC_TBF_UL_EV_LAST_UL_DATA_SENT),
+ .out_state_mask =
+ X(GPRS_RLCMAC_TBF_UL_ST_FINISHED),
+ .name = "FLOW",
+ .action = st_flow,
+ },
+ [GPRS_RLCMAC_TBF_UL_ST_FINISHED] = {
+ .in_event_mask =
X(GPRS_RLCMAC_TBF_UL_EV_FOOBAR),
.out_state_mask =
X(GPRS_RLCMAC_TBF_UL_ST_WAIT_ASSIGN),
- .name = "FLOW",
- .action = st_flow,
+ .name = "FINISHED",
+ .action = st_finished,
},
};
--
To view, visit
https://gerrit.osmocom.org/c/libosmo-gprs/+/31170
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: libosmo-gprs
Gerrit-Branch: master
Gerrit-Change-Id: I0a01d79d16bbfc63aa88e6bb0f432f3772645730
Gerrit-Change-Number: 31170
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin(a)sysmocom.de>
Gerrit-MessageType: newchange