aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2023-07-26 16:14:52 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2023-07-26 16:55:22 +0200
commitff33597e4bec2ab97440d3ff860013412e5d62a5 (patch)
tree784ea1172c86d58026286290036cff1bb1581a94
parent61f64c39a0689e131d4286990d36876b2685dfc5 (diff)
Split rlc_{dl_,ul_,}window out of rlc.{h,cpp}
This new structure was already used when porting existing osmo-pcu code to libosmo-gprs-rlcmac, and proved to be much more clear. It makes no sense to keep both ul and dl window code mixed, since the code base is mostly different between them, and the developer usually is only interested in reading/debugging one side at a time. Change-Id: If21b6f48ef190a718502389cbfde8cbdfc8d7f7a
-rw-r--r--src/Makefile.am6
-rw-r--r--src/decoding.cpp1
-rw-r--r--src/decoding.h2
-rw-r--r--src/rlc.cpp269
-rw-r--r--src/rlc.h391
-rw-r--r--src/rlc_window.cpp42
-rw-r--r--src/rlc_window.h71
-rw-r--r--src/rlc_window_dl.cpp182
-rw-r--r--src/rlc_window_dl.h205
-rw-r--r--src/rlc_window_ul.cpp119
-rw-r--r--src/rlc_window_ul.h174
-rw-r--r--src/tbf.h1
-rw-r--r--src/tbf_dl.h1
-rw-r--r--src/tbf_ul.h1
14 files changed, 805 insertions, 660 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6153aa02..ded6632c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -79,6 +79,9 @@ libgprs_la_SOURCES = \
decoding.cpp \
llc.c \
rlc.cpp \
+ rlc_window.cpp \
+ rlc_window_dl.cpp \
+ rlc_window_ul.cpp \
osmobts_sock.c \
gprs_codel.c \
coding_scheme.c \
@@ -121,6 +124,9 @@ noinst_HEADERS = \
encoding.h \
sba.h \
rlc.h \
+ rlc_window.h \
+ rlc_window_dl.h \
+ rlc_window_ul.h \
decoding.h \
llc.h \
pcu_utils.h \
diff --git a/src/decoding.cpp b/src/decoding.cpp
index db6b0720..9f93f015 100644
--- a/src/decoding.cpp
+++ b/src/decoding.cpp
@@ -15,6 +15,7 @@
*/
#include <decoding.h>
#include <rlc.h>
+#include <rlc_window.h>
#include <gprs_debug.h>
#include <egprs_rlc_compression.h>
diff --git a/src/decoding.h b/src/decoding.h
index b0a5aed9..c89b5b1a 100644
--- a/src/decoding.h
+++ b/src/decoding.h
@@ -32,6 +32,8 @@ struct bitvec;
#ifdef __cplusplus
+#include "rlc_window_dl.h"
+
class Decoding {
public:
/* represents (parts) LLC PDUs within one RLC Data block */
diff --git a/src/rlc.cpp b/src/rlc.cpp
index a0825976..2166e4d9 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -41,275 +41,6 @@ uint8_t *prepare(struct gprs_rlc_data *rlc, size_t block_data_len)
return rlc->block;
}
-void gprs_rlc_v_b::reset()
-{
- for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
- mark_invalid(i);
-}
-
-void gprs_rlc_dl_window::reset()
-{
- m_v_s = 0;
- m_v_a = 0;
- m_v_b.reset();
-}
-
-int gprs_rlc_dl_window::resend_needed() const
-{
- for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
- return bsn;
- }
-
- return -1;
-}
-
-int gprs_rlc_dl_window::mark_for_resend()
-{
- int resend = 0;
-
- for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_unacked(bsn)) {
- /* mark to be re-send */
- m_v_b.mark_resend(bsn);
- resend += 1;
- }
- }
-
- return resend;
-}
-
-/* Update the receive block bitmap */
-uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
-{
- uint16_t i;
- uint16_t bsn;
- uint16_t bitmask = 0x80;
- int8_t pos = 0;
- int8_t bit_pos = 0;
- for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
- bsn = this->mod_sns(bsn + 1)) {
- if (m_v_n.is_received(bsn)) {
- rbb[pos] = rbb[pos] | bitmask;
- } else {
- rbb[pos] = rbb[pos] & (~bitmask);
- }
- bitmask = bitmask >> 1;
- bit_pos++;
- bit_pos = bit_pos % 8;
- if (bit_pos == 0) {
- pos++;
- bitmask = 0x80;
- }
- }
- return i;
-}
-
-int gprs_rlc_dl_window::count_unacked()
-{
- uint16_t unacked = 0;
- uint16_t bsn;
-
- for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
- if (!m_v_b.is_acked(bsn))
- unacked += 1;
- }
-
- return unacked;
-}
-
-static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
-{
- return (ssn - 1 - bitnum);
-}
-
-void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
- uint16_t first_bsn, uint16_t *lost,
- uint16_t *received)
-{
- unsigned dist = distance();
- unsigned num_blocks = rbb->cur_bit > dist
- ? dist : rbb->cur_bit;
- unsigned bsn;
-
- /* first_bsn is in range V(A)..V(S) */
-
- for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
- bool is_ack;
- bsn = mod_sns(first_bsn + bitpos);
- if (bsn == mod_sns(v_a() - 1))
- break;
-
- is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
-
- if (is_ack) {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
- if (!m_v_b.is_acked(bsn))
- *received += 1;
- m_v_b.mark_acked(bsn);
- } else {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
- m_v_b.mark_nacked(bsn);
- bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
- *lost += 1;
- }
- }
-}
-
-void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
- uint16_t *lost, uint16_t *received)
-{
- /* SSN - 1 is in range V(A)..V(S)-1 */
- for (int bitpos = 0; bitpos < ws(); bitpos++) {
- uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
-
- if (bsn == mod_sns(v_a() - 1))
- break;
-
- if (show_rbb[ws() - 1 - bitpos] == 'R') {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
- if (!m_v_b.is_acked(bsn))
- *received += 1;
- m_v_b.mark_acked(bsn);
- } else {
- LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
- m_v_b.mark_nacked(bsn);
- bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
- *lost += 1;
- }
- }
-}
-
-int gprs_rlc_dl_window::move_window()
-{
- int i;
- uint16_t bsn;
- int moved = 0;
-
- for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
- if (m_v_b.is_acked(bsn)) {
- m_v_b.mark_invalid(bsn);
- moved += 1;
- } else
- break;
- }
-
- return moved;
-}
-
-void gprs_rlc_dl_window::show_state(char *show_v_b)
-{
- int i;
- uint16_t bsn;
-
- for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
- uint16_t index = bsn & mod_sns_half();
- switch(m_v_b.get_state(index)) {
- case GPRS_RLC_DL_BSN_INVALID:
- show_v_b[i] = 'I';
- break;
- case GPRS_RLC_DL_BSN_ACKED:
- show_v_b[i] = 'A';
- break;
- case GPRS_RLC_DL_BSN_RESEND:
- show_v_b[i] = 'X';
- break;
- case GPRS_RLC_DL_BSN_NACKED:
- show_v_b[i] = 'N';
- break;
- default:
- show_v_b[i] = '?';
- }
- }
- show_v_b[i] = '\0';
-}
-
-void gprs_rlc_v_n::reset()
-{
- for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
- m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
-}
-
-void gprs_rlc_window::set_sns(uint16_t sns)
-{
- OSMO_ASSERT(sns >= RLC_GPRS_SNS);
- OSMO_ASSERT(sns <= RLC_MAX_SNS);
- /* check for 2^n */
- OSMO_ASSERT((sns & (-sns)) == sns);
- m_sns = sns;
-}
-
-void gprs_rlc_window::set_ws(uint16_t ws)
-{
- LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
- ws);
- OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
- OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
- m_ws = ws;
-}
-
-/* Update the receive block bitmap */
-void gprs_rlc_ul_window::update_rbb(char *rbb)
-{
- int i;
- for (i=0; i < ws(); i++) {
- if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
- rbb[ws()-1-i] = 'R';
- else
- rbb[ws()-1-i] = 'I';
- }
-}
-
-/* Raise V(R) to highest received sequence number not received. */
-void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
-{
- uint16_t offset_v_r;
- offset_v_r = mod_sns(bsn + 1 - v_r());
- /* Positive offset, so raise. */
- if (offset_v_r < (sns() >> 1)) {
- while (offset_v_r--) {
- if (offset_v_r) /* all except the received block */
- m_v_n.mark_missing(v_r());
- raise_v_r_to(1);
- }
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
- }
-}
-
-/*
- * Raise V(Q) if possible. This is looped until there is a gap
- * (non received block) or the window is empty.
- */
-uint16_t gprs_rlc_ul_window::raise_v_q()
-{
- uint16_t count = 0;
-
- while (v_q() != v_r()) {
- if (!m_v_n.is_received(v_q()))
- break;
- LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
- "V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
- raise_v_q(1);
- count += 1;
- }
-
- return count;
-}
-
-void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
-{
- m_v_n.mark_received(bsn);
- raise_v_r(bsn);
-}
-
-bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
-{
- bool was_valid = m_v_n.is_received(bsn);
- m_v_n.mark_missing(bsn);
-
- return was_valid;
-}
-
static void gprs_rlc_data_header_init(struct gprs_rlc_data_info *rlc,
enum CodingScheme cs, bool with_padding, unsigned int header_bits,
const unsigned int spb)
diff --git a/src/rlc.h b/src/rlc.h
index d4a7a5e1..dd2f0c7c 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -29,34 +29,10 @@ extern "C" {
#include <string.h>
#define RLC_GPRS_SNS 128 /* GPRS, must be power of 2 */
-#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_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_WS RLC_EGPRS_MAX_WS
#define RLC_MAX_LEN 74 /* MCS-9 data unit */
-struct gprs_rlcmac_bts;
-
-/* The state of a BSN in the send/receive window */
-enum gprs_rlc_ul_bsn_state {
- GPRS_RLC_UL_BSN_INVALID,
- GPRS_RLC_UL_BSN_RECEIVED,
- GPRS_RLC_UL_BSN_MISSING,
- GPRS_RLC_UL_BSN_MAX,
-};
-
-enum gprs_rlc_dl_bsn_state {
- GPRS_RLC_DL_BSN_INVALID,
- GPRS_RLC_DL_BSN_NACKED,
- GPRS_RLC_DL_BSN_ACKED,
- GPRS_RLC_DL_BSN_UNACKED,
- GPRS_RLC_DL_BSN_RESEND,
- GPRS_RLC_DL_BSN_MAX,
-};
-
/*
* EGPRS resegment status information for UL
* When only first split block is received bsn state
@@ -239,138 +215,6 @@ struct gprs_rlc {
gprs_rlc_data m_blocks[RLC_MAX_SNS/2];
};
-/**
- * TODO: for GPRS/EDGE maybe make sns a template parameter
- * so we create specialized versions...
- */
-struct gprs_rlc_v_b {
- /* Check for an individual frame */
- bool is_unacked(int bsn) const;
- bool is_nacked(int bsn) const;
- bool is_acked(int bsn) const;
- bool is_resend(int bsn) const;
- bool is_invalid(int bsn) const;
- gprs_rlc_dl_bsn_state get_state(int bsn) const;
-
- /* Mark a RLC frame for something */
- void mark_unacked(int bsn);
- void mark_nacked(int bsn);
- void mark_acked(int bsn);
- void mark_resend(int bsn);
- void mark_invalid(int bsn);
-
- void reset();
-
-
-private:
- bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
- void mark(int bsn, const gprs_rlc_dl_bsn_state state);
-
- gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
-};
-
-
-/**
- * TODO: The UL/DL code could/should share a base class.
- */
-class gprs_rlc_window {
-public:
- gprs_rlc_window();
-
- const uint16_t mod_sns() const;
- const uint16_t mod_sns(uint16_t bsn) const;
- const uint16_t sns() const;
- const uint16_t ws() const;
-
- void set_sns(uint16_t sns);
- void set_ws(uint16_t ws);
-
-protected:
- uint16_t m_sns;
- uint16_t m_ws;
-};
-
-struct gprs_rlc_dl_window: public gprs_rlc_window {
- void reset();
-
- bool window_stalled() const;
- bool window_empty() const;
-
- void increment_send();
- void raise(int moves);
-
- const uint16_t v_s() const;
- const uint16_t v_s_mod(int offset) const;
- const uint16_t v_a() const;
- const uint16_t distance() const;
-
- /* Methods to manage reception */
- int resend_needed() const;
- int mark_for_resend();
- void update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
- uint16_t *lost, uint16_t *received);
- void update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
- uint16_t first_bsn, uint16_t *lost,
- uint16_t *received);
- int move_window();
- void show_state(char *show_v_b);
- int count_unacked();
-
- uint16_t m_v_s; /* send state */
- uint16_t m_v_a; /* ack state */
-
- gprs_rlc_v_b m_v_b;
-
- gprs_rlc_dl_window();
-};
-
-struct gprs_rlc_v_n {
- void reset();
-
- void mark_received(int bsn);
- void mark_missing(int bsn);
-
- bool is_received(int bsn) const;
-
- gprs_rlc_ul_bsn_state state(int bsn) const;
-private:
- bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
- void mark(int bsn, const gprs_rlc_ul_bsn_state state);
- gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
-};
-
-struct gprs_rlc_ul_window: public gprs_rlc_window {
- const uint16_t v_r() const;
- const uint16_t v_q() const;
-
- const void set_v_r(int);
- const void set_v_q(int);
- void reset_state();
-
- const uint16_t ssn() const;
-
- bool is_in_window(uint16_t bsn) const;
- bool is_received(uint16_t bsn) const;
-
- void update_rbb(char *rbb);
- uint16_t update_egprs_rbb(uint8_t *rbb);
- void raise_v_r_to(int moves);
- void raise_v_r(const uint16_t bsn);
- uint16_t raise_v_q();
-
- void raise_v_q(int);
-
- void receive_bsn(const uint16_t bsn);
- bool invalidate_bsn(const uint16_t bsn);
-
- uint16_t m_v_r; /* receive state */
- uint16_t m_v_q; /* receive window state */
-
- gprs_rlc_v_n m_v_n;
-
- gprs_rlc_ul_window();
-};
-
extern "C" {
/* TS 44.060 10.2.2 */
struct rlc_ul_header {
@@ -434,241 +278,6 @@ struct rlc_li_field_egprs {
} __attribute__ ((packed));
}
-inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
-{
- return m_v_b[bsn & mod_sns_half()] == type;
-}
-
-inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
-{
- m_v_b[bsn & mod_sns_half()] = type;
-}
-
-inline bool gprs_rlc_v_b::is_nacked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
-}
-
-inline bool gprs_rlc_v_b::is_acked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
-}
-
-inline bool gprs_rlc_v_b::is_unacked(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
-}
-
-inline bool gprs_rlc_v_b::is_resend(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
-}
-
-inline bool gprs_rlc_v_b::is_invalid(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
-}
-
-inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
-{
- return m_v_b[bsn & mod_sns_half()];
-}
-
-inline void gprs_rlc_v_b::mark_resend(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
-}
-
-inline void gprs_rlc_v_b::mark_unacked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
-}
-
-inline void gprs_rlc_v_b::mark_acked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
-}
-
-inline void gprs_rlc_v_b::mark_nacked(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
-}
-
-inline void gprs_rlc_v_b::mark_invalid(int bsn)
-{
- return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
-}
-
-inline gprs_rlc_window::gprs_rlc_window()
- : m_sns(RLC_GPRS_SNS)
- , m_ws(RLC_GPRS_WS)
-{
-}
-
-inline const uint16_t gprs_rlc_window::sns() const
-{
- return m_sns;
-}
-
-inline const uint16_t gprs_rlc_window::ws() const
-{
- return m_ws;
-}
-
-inline const uint16_t gprs_rlc_window::mod_sns() const
-{
- return sns() - 1;
-}
-
-inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
-{
- return bsn & mod_sns();
-}
-
-inline gprs_rlc_dl_window::gprs_rlc_dl_window()
- : m_v_s(0)
- , m_v_a(0)
-{
- reset();
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_s() const
-{
- return m_v_s;
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
-{
- return mod_sns(m_v_s + offset);
-}
-
-inline const uint16_t gprs_rlc_dl_window::v_a() const
-{
- return m_v_a;
-}
-
-inline bool gprs_rlc_dl_window::window_stalled() const
-{
- return (mod_sns(m_v_s - m_v_a)) == ws();
-}
-
-inline bool gprs_rlc_dl_window::window_empty() const
-{
- return m_v_s == m_v_a;
-}
-
-inline void gprs_rlc_dl_window::increment_send()
-{
- m_v_s = (m_v_s + 1) & mod_sns();
-}
-
-inline void gprs_rlc_dl_window::raise(int moves)
-{
- m_v_a = (m_v_a + moves) & mod_sns();
-}
-
-inline const uint16_t gprs_rlc_dl_window::distance() const
-{
- return (m_v_s - m_v_a) & mod_sns();
-}
-
-inline gprs_rlc_ul_window::gprs_rlc_ul_window()
-{
- reset_state();
-}
-
-inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
-{
- uint16_t offset_v_q;
-
- /* current block relative to lowest unreceived block */
- offset_v_q = (bsn - m_v_q) & mod_sns();
- /* If out of window (may happen if blocks below V(Q) are received
- * again. */
- return offset_v_q < ws();
-}
-
-inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
-{
- uint16_t offset_v_r;
-
- /* Offset to the end of the received window */
- offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
- return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
-}
-
-inline void gprs_rlc_ul_window::reset_state()
-{
- m_v_r = 0;
- m_v_q = 0;
- m_v_n.reset();
-}
-
-inline const void gprs_rlc_ul_window::set_v_r(int v_r)
-{
- m_v_r = v_r;
-}
-
-inline const void gprs_rlc_ul_window::set_v_q(int v_q)
-{
- m_v_q = v_q;
-}
-
-inline const uint16_t gprs_rlc_ul_window::v_r() const
-{
- return m_v_r;
-}
-
-inline const uint16_t gprs_rlc_ul_window::v_q() const
-{
- return m_v_q;
-}
-
-inline const uint16_t gprs_rlc_ul_window::ssn() const
-{
- return m_v_r;
-}
-
-inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
-{
- m_v_r = mod_sns(m_v_r + moves);
-}
-
-inline void gprs_rlc_ul_window::raise_v_q(int incr)
-{
- m_v_q = mod_sns(m_v_q + incr);
-}
-
-inline void gprs_rlc_v_n::mark_received(int bsn)
-{
- return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
-}
-
-inline void gprs_rlc_v_n::mark_missing(int bsn)
-{
- return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
-}
-
-inline bool gprs_rlc_v_n::is_received(int bsn) const
-{
- return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
-}
-
-inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
-{
- return m_v_n[bsn & mod_sns_half()] == type;
-}
-
-inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
-{
- m_v_n[bsn & mod_sns_half()] = type;
-}
-
-inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
-{
- return m_v_n[bsn & mod_sns_half()];
-}
-
inline void gprs_rlc::init()
{
memset(m_blocks, 0, sizeof(m_blocks));
diff --git a/src/rlc_window.cpp b/src/rlc_window.cpp
new file mode 100644
index 00000000..c056170f
--- /dev/null
+++ b/src/rlc_window.cpp
@@ -0,0 +1,42 @@
+/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "gprs_debug.h"
+#include "rlc_window.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+void gprs_rlc_window::set_sns(uint16_t sns)
+{
+ OSMO_ASSERT(sns >= RLC_GPRS_SNS);
+ OSMO_ASSERT(sns <= RLC_MAX_SNS);
+ /* check for 2^n */
+ OSMO_ASSERT((sns & (-sns)) == sns);
+ m_sns = sns;
+}
+
+void gprs_rlc_window::set_ws(uint16_t ws)
+{
+ LOGP(DRLCMAC, LOGL_INFO, "ws(%d)\n",
+ ws);
+ OSMO_ASSERT(ws >= RLC_GPRS_SNS/2);
+ OSMO_ASSERT(ws <= RLC_MAX_SNS/2);
+ m_ws = ws;
+} \ No newline at end of file
diff --git a/src/rlc_window.h b/src/rlc_window.h
new file mode 100644
index 00000000..1b0227f4
--- /dev/null
+++ b/src/rlc_window.h
@@ -0,0 +1,71 @@
+/* RLC Window (common for both UL/DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#pragma once
+
+#include <stdint.h>
+
+#include "rlc.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
+
+class gprs_rlc_window {
+public:
+ gprs_rlc_window();
+
+ const uint16_t mod_sns(void) const;
+ const uint16_t mod_sns(uint16_t bsn) const;
+ const uint16_t sns(void) const;
+ const uint16_t ws(void) const;
+
+ void set_sns(uint16_t sns);
+ void set_ws(uint16_t ws);
+
+protected:
+ uint16_t m_sns;
+ uint16_t m_ws;
+};
+
+
+inline gprs_rlc_window::gprs_rlc_window(void)
+ : m_sns(RLC_GPRS_SNS)
+ , m_ws(RLC_GPRS_WS)
+{
+}
+
+inline const uint16_t gprs_rlc_window::sns(void) const
+{
+ return m_sns;
+}
+
+inline const uint16_t gprs_rlc_window::ws(void) const
+{
+ return m_ws;
+}
+
+inline const uint16_t gprs_rlc_window::mod_sns(void) const
+{
+ return sns() - 1;
+}
+
+inline const uint16_t gprs_rlc_window::mod_sns(uint16_t bsn) const
+{
+ return bsn & mod_sns();
+}
diff --git a/src/rlc_window_dl.cpp b/src/rlc_window_dl.cpp
new file mode 100644
index 00000000..41f9887d
--- /dev/null
+++ b/src/rlc_window_dl.cpp
@@ -0,0 +1,182 @@
+/* RLC Window (DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "gprs_debug.h"
+#include "bts.h"
+#include "rlc_window_dl.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+static uint16_t bitnum_to_bsn(int bitnum, uint16_t ssn)
+{
+ return (ssn - 1 - bitnum);
+}
+
+void gprs_rlc_v_b::reset()
+{
+ for (size_t i = 0; i < ARRAY_SIZE(m_v_b); ++i)
+ mark_invalid(i);
+}
+
+void gprs_rlc_dl_window::reset()
+{
+ m_v_s = 0;
+ m_v_a = 0;
+ m_v_b.reset();
+}
+
+int gprs_rlc_dl_window::resend_needed() const
+{
+ for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_nacked(bsn) || m_v_b.is_resend(bsn))
+ return bsn;
+ }
+
+ return -1;
+}
+
+int gprs_rlc_dl_window::mark_for_resend()
+{
+ int resend = 0;
+
+ for (uint16_t bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_unacked(bsn)) {
+ /* mark to be re-send */
+ m_v_b.mark_resend(bsn);
+ resend += 1;
+ }
+ }
+
+ return resend;
+}
+
+int gprs_rlc_dl_window::count_unacked()
+{
+ uint16_t unacked = 0;
+ uint16_t bsn;
+
+ for (bsn = v_a(); bsn != v_s(); bsn = mod_sns(bsn + 1)) {
+ if (!m_v_b.is_acked(bsn))
+ unacked += 1;
+ }
+
+ return unacked;
+}
+
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost,
+ uint16_t *received)
+{
+ unsigned dist = distance();
+ unsigned num_blocks = rbb->cur_bit > dist
+ ? dist : rbb->cur_bit;
+ unsigned bsn;
+
+ /* first_bsn is in range V(A)..V(S) */
+
+ for (unsigned int bitpos = 0; bitpos < num_blocks; bitpos++) {
+ bool is_ack;
+ bsn = mod_sns(first_bsn + bitpos);
+ if (bsn == mod_sns(v_a() - 1))
+ break;
+
+ is_ack = bitvec_get_bit_pos(rbb, bitpos) == 1;
+
+ if (is_ack) {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
+ if (!m_v_b.is_acked(bsn))
+ *received += 1;
+ m_v_b.mark_acked(bsn);
+ } else {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
+ m_v_b.mark_nacked(bsn);
+ bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
+ *lost += 1;
+ }
+ }
+}
+
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
+ uint16_t *lost, uint16_t *received)
+{
+ /* SSN - 1 is in range V(A)..V(S)-1 */
+ for (int bitpos = 0; bitpos < ws(); bitpos++) {
+ uint16_t bsn = mod_sns(bitnum_to_bsn(bitpos, ssn));
+
+ if (bsn == mod_sns(v_a() - 1))
+ break;
+
+ if (show_rbb[ws() - 1 - bitpos] == 'R') {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got ack for BSN=%d\n", bsn);
+ if (!m_v_b.is_acked(bsn))
+ *received += 1;
+ m_v_b.mark_acked(bsn);
+ } else {
+ LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
+ m_v_b.mark_nacked(bsn);
+ bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
+ *lost += 1;
+ }
+ }
+}
+
+int gprs_rlc_dl_window::move_window()
+{
+ int i;
+ uint16_t bsn;
+ int moved = 0;
+
+ for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
+ if (m_v_b.is_acked(bsn)) {
+ m_v_b.mark_invalid(bsn);
+ moved += 1;
+ } else
+ break;
+ }
+
+ return moved;
+}
+
+void gprs_rlc_dl_window::show_state(char *show_v_b)
+{
+ int i;
+ uint16_t bsn;
+
+ for (i = 0, bsn = v_a(); bsn != v_s(); i++, bsn = mod_sns(bsn + 1)) {
+ uint16_t index = bsn & mod_sns_half();
+ switch(m_v_b.get_state(index)) {
+ case GPRS_RLC_DL_BSN_INVALID:
+ show_v_b[i] = 'I';
+ break;
+ case GPRS_RLC_DL_BSN_ACKED:
+ show_v_b[i] = 'A';
+ break;
+ case GPRS_RLC_DL_BSN_RESEND:
+ show_v_b[i] = 'X';
+ break;
+ case GPRS_RLC_DL_BSN_NACKED:
+ show_v_b[i] = 'N';
+ break;
+ default:
+ show_v_b[i] = '?';
+ }
+ }
+ show_v_b[i] = '\0';
+} \ No newline at end of file
diff --git a/src/rlc_window_dl.h b/src/rlc_window_dl.h
new file mode 100644
index 00000000..e2da6145
--- /dev/null
+++ b/src/rlc_window_dl.h
@@ -0,0 +1,205 @@
+/* RLC Window (DL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#pragma once
+
+#include "rlc.h"
+#include "rlc_window.h"
+
+enum gprs_rlc_dl_bsn_state {
+ GPRS_RLC_DL_BSN_INVALID,
+ GPRS_RLC_DL_BSN_NACKED,
+ GPRS_RLC_DL_BSN_ACKED,
+ GPRS_RLC_DL_BSN_UNACKED,
+ GPRS_RLC_DL_BSN_RESEND,
+ GPRS_RLC_DL_BSN_MAX,
+};
+
+/**
+ * TODO: for GPRS/EDGE maybe make sns a template parameter
+ * so we create specialized versions...
+ */
+struct gprs_rlc_v_b {
+ /* Check for an individual frame */
+ bool is_unacked(int bsn) const;
+ bool is_nacked(int bsn) const;
+ bool is_acked(int bsn) const;
+ bool is_resend(int bsn) const;
+ bool is_invalid(int bsn) const;
+ gprs_rlc_dl_bsn_state get_state(int bsn) const;
+
+ /* Mark a RLC frame for something */
+ void mark_unacked(int bsn);
+ void mark_nacked(int bsn);
+ void mark_acked(int bsn);
+ void mark_resend(int bsn);
+ void mark_invalid(int bsn);
+
+ void reset(void);
+
+
+private:
+ bool is_state(int bsn, const gprs_rlc_dl_bsn_state state) const;
+ void mark(int bsn, const gprs_rlc_dl_bsn_state state);
+
+ gprs_rlc_dl_bsn_state m_v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
+};
+
+inline bool gprs_rlc_v_b::is_state(int bsn, const gprs_rlc_dl_bsn_state type) const
+{
+ return m_v_b[bsn & mod_sns_half()] == type;
+}
+
+inline void gprs_rlc_v_b::mark(int bsn, const gprs_rlc_dl_bsn_state type)
+{
+ m_v_b[bsn & mod_sns_half()] = type;
+}
+
+inline bool gprs_rlc_v_b::is_nacked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_NACKED);
+}
+
+inline bool gprs_rlc_v_b::is_acked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_ACKED);
+}
+
+inline bool gprs_rlc_v_b::is_unacked(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_UNACKED);
+}
+
+inline bool gprs_rlc_v_b::is_resend(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_RESEND);
+}
+
+inline bool gprs_rlc_v_b::is_invalid(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_DL_BSN_INVALID);
+}
+
+inline gprs_rlc_dl_bsn_state gprs_rlc_v_b::get_state(int bsn) const
+{
+ return m_v_b[bsn & mod_sns_half()];
+}
+
+inline void gprs_rlc_v_b::mark_resend(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_RESEND);
+}
+
+inline void gprs_rlc_v_b::mark_unacked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_UNACKED);
+}
+
+inline void gprs_rlc_v_b::mark_acked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_ACKED);
+}
+
+inline void gprs_rlc_v_b::mark_nacked(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_NACKED);
+}
+
+inline void gprs_rlc_v_b::mark_invalid(int bsn)
+{
+ return mark(bsn, GPRS_RLC_DL_BSN_INVALID);
+}
+
+struct gprs_rlc_dl_window : public gprs_rlc_window {
+ void reset(void);
+
+ bool window_stalled(void) const;
+ bool window_empty(void) const;
+
+ void increment_send(void);
+ void raise(int moves);
+
+ const uint16_t v_s(void) const;
+ const uint16_t v_s_mod(int offset) const;
+ const uint16_t v_a(void) const;
+ const uint16_t distance(void) const;
+
+ /* Methods to manage reception */
+ int resend_needed(void) const;
+ int mark_for_resend(void);
+ void update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
+ uint16_t *lost, uint16_t *received);
+ void update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
+ uint16_t first_bsn, uint16_t *lost,
+ uint16_t *received);
+ int move_window(void);
+ void show_state(char *show_v_b);
+ int count_unacked(void);
+
+ uint16_t m_v_s; /* send state */
+ uint16_t m_v_a; /* ack state */
+
+ gprs_rlc_v_b m_v_b;
+
+ gprs_rlc_dl_window(void);
+};
+
+inline gprs_rlc_dl_window::gprs_rlc_dl_window(void)
+ : m_v_s(0)
+ , m_v_a(0)
+{
+ reset();
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_s(void) const
+{
+ return m_v_s;
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_s_mod(int offset) const
+{
+ return mod_sns(m_v_s + offset);
+}
+
+inline const uint16_t gprs_rlc_dl_window::v_a(void) const
+{
+ return m_v_a;
+}
+
+inline bool gprs_rlc_dl_window::window_stalled(void) const
+{
+ return (mod_sns(m_v_s - m_v_a)) == ws();
+}
+
+inline bool gprs_rlc_dl_window::window_empty(void) const
+{
+ return m_v_s == m_v_a;
+}
+
+inline void gprs_rlc_dl_window::increment_send(void)
+{
+ m_v_s = (m_v_s + 1) & mod_sns();
+}
+
+inline void gprs_rlc_dl_window::raise(int moves)
+{
+ m_v_a = (m_v_a + moves) & mod_sns();
+}
+
+inline const uint16_t gprs_rlc_dl_window::distance(void) const
+{
+ return (m_v_s - m_v_a) & mod_sns();
+}
diff --git a/src/rlc_window_ul.cpp b/src/rlc_window_ul.cpp
new file mode 100644
index 00000000..568e126e
--- /dev/null
+++ b/src/rlc_window_ul.cpp
@@ -0,0 +1,119 @@
+/* RLC Window (UL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include "gprs_debug.h"
+#include "rlc_window_ul.h"
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bitvec.h>
+#include <osmocom/core/logging.h>
+}
+
+void gprs_rlc_v_n::reset()
+{
+ for (size_t i = 0; i < ARRAY_SIZE(m_v_n); ++i)
+ m_v_n[i] = GPRS_RLC_UL_BSN_INVALID;
+}
+
+/* Raise V(R) to highest received sequence number not received. */
+void gprs_rlc_ul_window::raise_v_r(const uint16_t bsn)
+{
+ uint16_t offset_v_r;
+ offset_v_r = mod_sns(bsn + 1 - v_r());
+ /* Positive offset, so raise. */
+ if (offset_v_r < (sns() >> 1)) {
+ while (offset_v_r--) {
+ if (offset_v_r) /* all except the received block */
+ m_v_n.mark_missing(v_r());
+ raise_v_r_to(1);
+ }
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n", v_r());
+ }
+}
+
+/*
+ * Raise V(Q) if possible. This is looped until there is a gap
+ * (non received block) or the window is empty.
+ */
+uint16_t gprs_rlc_ul_window::raise_v_q()
+{
+ uint16_t count = 0;
+
+ while (v_q() != v_r()) {
+ if (!m_v_n.is_received(v_q()))
+ break;
+ LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
+ "V(Q) to %d\n", v_q(), mod_sns(v_q() + 1));
+ raise_v_q(1);
+ count += 1;
+ }
+
+ return count;
+}
+
+void gprs_rlc_ul_window::receive_bsn(const uint16_t bsn)
+{
+ m_v_n.mark_received(bsn);
+ raise_v_r(bsn);
+}
+
+bool gprs_rlc_ul_window::invalidate_bsn(const uint16_t bsn)
+{
+ bool was_valid = m_v_n.is_received(bsn);
+ m_v_n.mark_missing(bsn);
+
+ return was_valid;
+}
+
+
+/* Update the receive block bitmap */
+uint16_t gprs_rlc_ul_window::update_egprs_rbb(uint8_t *rbb)
+{
+ uint16_t i;
+ uint16_t bsn;
+ uint16_t bitmask = 0x80;
+ int8_t pos = 0;
+ int8_t bit_pos = 0;
+ for (i = 0, bsn = (v_q()+1); ((bsn < (v_r())) && (i < ws())); i++,
+ bsn = this->mod_sns(bsn + 1)) {
+ if (m_v_n.is_received(bsn)) {
+ rbb[pos] = rbb[pos] | bitmask;
+ } else {
+ rbb[pos] = rbb[pos] & (~bitmask);
+ }
+ bitmask = bitmask >> 1;
+ bit_pos++;
+ bit_pos = bit_pos % 8;
+ if (bit_pos == 0) {
+ pos++;
+ bitmask = 0x80;
+ }
+ }
+ return i;
+}
+
+/* Update the receive block bitmap */
+void gprs_rlc_ul_window::update_rbb(char *rbb)
+{
+ int i;
+ for (i=0; i < ws(); i++) {
+ if (m_v_n.is_received((ssn()-1-i) & mod_sns()))
+ rbb[ws()-1-i] = 'R';
+ else
+ rbb[ws()-1-i] = 'I';
+ }
+} \ No newline at end of file
diff --git a/src/rlc_window_ul.h b/src/rlc_window_ul.h
new file mode 100644
index 00000000..a2e60c0d
--- /dev/null
+++ b/src/rlc_window_ul.h
@@ -0,0 +1,174 @@
+/* RLC Window (UL TBF), 3GPP TS 44.060
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2023 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#pragma once
+
+#include "rlc.h"
+#include "rlc_window.h"
+
+/* The state of a BSN in the send/receive window */
+enum gprs_rlc_ul_bsn_state {
+ GPRS_RLC_UL_BSN_INVALID,
+ GPRS_RLC_UL_BSN_RECEIVED,
+ GPRS_RLC_UL_BSN_MISSING,
+ GPRS_RLC_UL_BSN_MAX,
+};
+
+struct gprs_rlc_v_n {
+ void reset(void);
+
+ void mark_received(int bsn);
+ void mark_missing(int bsn);
+
+ bool is_received(int bsn) const;
+
+ gprs_rlc_ul_bsn_state state(int bsn) const;
+private:
+ bool is_state(int bsn, const gprs_rlc_ul_bsn_state state) const;
+ void mark(int bsn, const gprs_rlc_ul_bsn_state state);
+ gprs_rlc_ul_bsn_state m_v_n[RLC_MAX_SNS/2]; /* receive state array */
+};
+
+
+inline void gprs_rlc_v_n::mark_received(int bsn)
+{
+ return mark(bsn, GPRS_RLC_UL_BSN_RECEIVED);
+}
+
+inline void gprs_rlc_v_n::mark_missing(int bsn)
+{
+ return mark(bsn, GPRS_RLC_UL_BSN_MISSING);
+}
+
+inline bool gprs_rlc_v_n::is_received(int bsn) const
+{
+ return is_state(bsn, GPRS_RLC_UL_BSN_RECEIVED);
+}
+
+inline bool gprs_rlc_v_n::is_state(int bsn, gprs_rlc_ul_bsn_state type) const
+{
+ return m_v_n[bsn & mod_sns_half()] == type;
+}
+
+inline void gprs_rlc_v_n::mark(int bsn, gprs_rlc_ul_bsn_state type)
+{
+ m_v_n[bsn & mod_sns_half()] = type;
+}
+
+inline gprs_rlc_ul_bsn_state gprs_rlc_v_n::state(int bsn) const
+{
+ return m_v_n[bsn & mod_sns_half()];
+}
+
+struct gprs_rlc_ul_window : public gprs_rlc_window {
+ const uint16_t v_r(void) const;
+ const uint16_t v_q(void) const;
+
+ const void set_v_r(int v_r);
+ const void set_v_q(int v_q);
+ void reset_state(void);
+
+ const uint16_t ssn(void) const;
+
+ bool is_in_window(uint16_t bsn) const;
+ bool is_received(uint16_t bsn) const;
+
+ void update_rbb(char *rbb);
+ uint16_t update_egprs_rbb(uint8_t *rbb);
+ void raise_v_r_to(int moves);
+ void raise_v_r(const uint16_t bsn);
+ uint16_t raise_v_q(void);
+
+ void raise_v_q(int incr);
+
+ void receive_bsn(const uint16_t bsn);
+ bool invalidate_bsn(const uint16_t bsn);
+
+ uint16_t m_v_r; /* receive state */
+ uint16_t m_v_q; /* receive window state */
+
+ gprs_rlc_v_n m_v_n;
+
+ gprs_rlc_ul_window(void);
+};
+
+
+inline gprs_rlc_ul_window::gprs_rlc_ul_window(void)
+{
+ reset_state();
+}
+
+inline bool gprs_rlc_ul_window::is_in_window(uint16_t bsn) const
+{
+ uint16_t offset_v_q;
+
+ /* current block relative to lowest unreceived block */
+ offset_v_q = (bsn - m_v_q) & mod_sns();
+ /* If out of window (may happen if blocks below V(Q) are received
+ * again. */
+ return offset_v_q < ws();
+}
+
+inline bool gprs_rlc_ul_window::is_received(uint16_t bsn) const
+{
+ uint16_t offset_v_r;
+
+ /* Offset to the end of the received window */
+ offset_v_r = (m_v_r - 1 - bsn) & mod_sns();
+ return is_in_window(bsn) && m_v_n.is_received(bsn) && offset_v_r < ws();
+}
+
+inline void gprs_rlc_ul_window::reset_state(void)
+{
+ m_v_r = 0;
+ m_v_q = 0;
+ m_v_n.reset();
+}
+
+inline const void gprs_rlc_ul_window::set_v_r(int v_r)
+{
+ m_v_r = v_r;
+}
+
+inline const void gprs_rlc_ul_window::set_v_q(int v_q)
+{
+ m_v_q = v_q;
+}
+
+inline const uint16_t gprs_rlc_ul_window::v_r(void) const
+{
+ return m_v_r;
+}
+
+inline const uint16_t gprs_rlc_ul_window::v_q(void) const
+{
+ return m_v_q;
+}
+
+inline const uint16_t gprs_rlc_ul_window::ssn(void) const
+{
+ return m_v_r;
+}
+
+inline void gprs_rlc_ul_window::raise_v_r_to(int moves)
+{
+ m_v_r = mod_sns(m_v_r + moves);
+}
+
+inline void gprs_rlc_ul_window::raise_v_q(int incr)
+{
+ m_v_q = mod_sns(m_v_q + incr);
+}
diff --git a/src/tbf.h b/src/tbf.h
index 124a2e6d..97f55624 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -20,6 +20,7 @@
#include "llc.h"
#include "rlc.h"
+#include "rlc_window.h"
#include "cxx_linuxlist.h"
#include "pcu_utils.h"
#include <gprs_debug.h>
diff --git a/src/tbf_dl.h b/src/tbf_dl.h
index 7223fa03..59a03565 100644
--- a/src/tbf_dl.h
+++ b/src/tbf_dl.h
@@ -18,6 +18,7 @@
#ifdef __cplusplus
#include "tbf.h"
+#include "rlc_window_dl.h"
#include <stdint.h>
diff --git a/src/tbf_ul.h b/src/tbf_ul.h
index 14884ab3..4ec0f6f2 100644
--- a/src/tbf_ul.h
+++ b/src/tbf_ul.h
@@ -20,6 +20,7 @@
#include <stdbool.h>
#include "tbf.h"
+#include "rlc_window_ul.h"
#ifdef __cplusplus
extern "C" {