356 lines
10 KiB
C
356 lines
10 KiB
C
/* Uplink RLC Window as per 3GPP TS 44.060 */
|
|
/*
|
|
* (C) 2012 Ivan Klyuchnikov
|
|
* (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
|
|
* (C) 2023 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
|
*
|
|
* All Rights Reserved
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <osmocom/core/bitvec.h>
|
|
|
|
#include <osmocom/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;
|
|
}
|