Commit 4b4e419a authored by Cedric Roux's avatar Cedric Roux

nr rlc: fix NR RLC AM status generation

Several problems were found and fixed.
parent b8ac9d5d
...@@ -908,8 +908,6 @@ typedef struct { ...@@ -908,8 +908,6 @@ typedef struct {
int so_start; int so_start;
int sn_end; int sn_end;
int so_end; int so_end;
/* data for maximum ack */
int ack_sn; /* -1 if not to be used */
/* pdu to use for next call to 'next_missing' */ /* pdu to use for next call to 'next_missing' */
nr_rlc_pdu_t *next; nr_rlc_pdu_t *next;
} missing_data_t; } missing_data_t;
...@@ -924,13 +922,12 @@ static missing_data_t next_missing(nr_rlc_entity_am_t *entity, ...@@ -924,13 +922,12 @@ static missing_data_t next_missing(nr_rlc_entity_am_t *entity,
int max_so = 0; int max_so = 0;
int last_reached = 0; int last_reached = 0;
ret.ack_sn = -1;
/* special case: missing part before the head of RX list */ /* special case: missing part before the head of RX list */
if (check_head) { if (check_head) {
if (cur->sn != entity->rx_next || !cur->is_first) { if (cur->sn != entity->rx_next || !cur->is_first) {
/* don't report if out of reporting window */ /* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status, cur->sn) <= 0) { if (sn_compare_rx(entity, entity->rx_highest_status,
entity->rx_next) <= 0) {
ret.sn_start = -1; ret.sn_start = -1;
return ret; return ret;
} }
...@@ -954,10 +951,6 @@ next_pdu: ...@@ -954,10 +951,6 @@ next_pdu:
max_so = cur_max_so; max_so = cur_max_so;
last_reached = last_reached | cur->is_last; last_reached = last_reached | cur->is_last;
/* if cur already processed, it can be the acked SDU */
if (cur->data == NULL)
ret.ack_sn = (cur->sn + 1) % entity->sn_modulus;
/* no next? */ /* no next? */
if (cur->next == NULL) { if (cur->next == NULL) {
/* inform the caller that work is over */ /* inform the caller that work is over */
...@@ -1033,32 +1026,36 @@ next_pdu: ...@@ -1033,32 +1026,36 @@ next_pdu:
} }
/* discontinuity between different SDUs */ /* discontinuity between different SDUs */
ret.sn_start = sn; if (last_reached) {
ret.sn_start = (sn + 1) % entity->sn_modulus;
ret.so_start = 0;
} else {
ret.sn_start = sn;
ret.so_start = max_so + 1;
}
/* don't report if out of reporting window */ /* don't report if out of reporting window */
if (sn_compare_rx(entity, entity->rx_highest_status, ret.sn_start) <= 0) { if (sn_compare_rx(entity, entity->rx_highest_status, ret.sn_start) <= 0) {
ret.sn_start = -1; ret.sn_start = -1;
return ret; return ret;
} }
ret.so_start = max_so + 1;
set_end_different_sdu: set_end_different_sdu:
/* don't go more than rx_highest_status - 1 */
if (sn_compare_rx(entity, entity->rx_highest_status, cur->sn) <= 0) {
ret.sn_end = (entity->rx_highest_status - 1 + entity->sn_modulus) %
entity->sn_modulus;
ret.so_end = 0xffff;
return ret;
}
/* if cur is the head of a SDU, then use cur-1 */ /* if cur is the head of a SDU, then use cur-1 */
if (cur->is_first) { if (cur->is_first) {
ret.sn_end = (cur->sn - 1 + entity->sn_modulus) % entity->sn_modulus; ret.sn_end = (cur->sn - 1 + entity->sn_modulus) % entity->sn_modulus;
ret.so_end = 0xffff; ret.so_end = 0xffff;
return ret; } else {
ret.sn_end = cur->sn;
ret.so_end = cur->so - 1;
}
/* don't go more than rx_highest_status - 1 */
if (sn_compare_rx(entity, entity->rx_highest_status, ret.sn_end) <= 0) {
ret.sn_end = (entity->rx_highest_status - 1 + entity->sn_modulus) %
entity->sn_modulus;
ret.so_end = 0xffff;
ret.next = NULL;
} }
ret.sn_end = cur->sn;
ret.so_end = cur->so - 1;
return ret; return ret;
} }
...@@ -1098,21 +1095,20 @@ static void get_e1_position(nr_rlc_entity_am_t *entity, ...@@ -1098,21 +1095,20 @@ static void get_e1_position(nr_rlc_entity_am_t *entity,
} }
} }
/* returns the number of nacks serialized. /* returns the last nack SN generated, -1 if nothing generated.
* In most cases it is 1, it can be more if the
* missing data consists of a range that is more
* than 255 SNs in which case it has to be cut in
* smaller ranges.
* If there is no more room in the status buffer, * If there is no more room in the status buffer,
* will set m->next = NULL (and may serialize * will set m->next = NULL (and may serialize
* less nacks than required by 'm'). * less nacks than required by 'm'), also
* sets *generation_truncated to 1.
*/ */
static int generate_missing(nr_rlc_entity_am_t *entity, static int generate_missing(nr_rlc_entity_am_t *entity,
nr_rlc_pdu_encoder_t *encoder, nr_rlc_pdu_encoder_t *encoder,
missing_data_t *m, int *e1_byte, int *e1_bit) missing_data_t *m, int *e1_byte, int *e1_bit,
int *generation_truncated,
unsigned char **so_end_address)
{ {
int r_bits = entity->sn_field_length == 18 ? 3 : 1; int r_bits = entity->sn_field_length == 18 ? 3 : 1;
int range_count = 0; int last_nack_generated = -1;
int sn_start; int sn_start;
int so_start; int so_start;
int sn_end; int sn_end;
...@@ -1164,6 +1160,7 @@ static int generate_missing(nr_rlc_entity_am_t *entity, ...@@ -1164,6 +1160,7 @@ static int generate_missing(nr_rlc_entity_am_t *entity,
m_nack.so_end = so_end; m_nack.so_end = so_end;
if (encoder->byte + nack_size(entity, &m_nack) > encoder->size) { if (encoder->byte + nack_size(entity, &m_nack) > encoder->size) {
m->next = NULL; m->next = NULL;
*generation_truncated = 1;
break; break;
} }
...@@ -1197,6 +1194,7 @@ static int generate_missing(nr_rlc_entity_am_t *entity, ...@@ -1197,6 +1194,7 @@ static int generate_missing(nr_rlc_entity_am_t *entity,
/* nack_sn */ /* nack_sn */
nr_rlc_pdu_encoder_put_bits(encoder, sn_start, nr_rlc_pdu_encoder_put_bits(encoder, sn_start,
entity->sn_field_length); entity->sn_field_length);
last_nack_generated = sn_start;
/* e1 = 0 (set later if needed) */ /* e1 = 0 (set later if needed) */
nr_rlc_pdu_encoder_put_bits(encoder, 0, 1); nr_rlc_pdu_encoder_put_bits(encoder, 0, 1);
/* e2 */ /* e2 */
...@@ -1208,34 +1206,44 @@ static int generate_missing(nr_rlc_entity_am_t *entity, ...@@ -1208,34 +1206,44 @@ static int generate_missing(nr_rlc_entity_am_t *entity,
/* so_start/so_end */ /* so_start/so_end */
if (e2) { if (e2) {
nr_rlc_pdu_encoder_put_bits(encoder, so_start, 16); nr_rlc_pdu_encoder_put_bits(encoder, so_start, 16);
*so_end_address = (unsigned char *)encoder->buffer + encoder->byte;
nr_rlc_pdu_encoder_put_bits(encoder, so_end, 16); nr_rlc_pdu_encoder_put_bits(encoder, so_end, 16);
} } else
*so_end_address = NULL;
/* nack range */ /* nack range */
if (e3) if (e3) {
nr_rlc_pdu_encoder_put_bits(encoder, cur_sn_count, 8); nr_rlc_pdu_encoder_put_bits(encoder, cur_sn_count, 8);
last_nack_generated += cur_sn_count - 1;
}
sn_count -= cur_sn_count; sn_count -= cur_sn_count;
sn_start = (sn_start + cur_sn_count) % entity->sn_modulus; sn_start = (sn_start + cur_sn_count) % entity->sn_modulus;
range_count++;
} }
return range_count; return last_nack_generated;
} }
static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size) static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size)
{ {
int ack_sn = entity->rx_next; int last_nack;
missing_data_t m; int ack_sn;
missing_data_t m;
nr_rlc_pdu_t *cur; nr_rlc_pdu_t *cur;
int nack_count = 0; int check_head = 1;
nr_rlc_pdu_encoder_t encoder; nr_rlc_pdu_encoder_t encoder;
int e1_byte; int e1_byte;
int e1_bit; int e1_bit;
int generation_truncated;
int ln;
unsigned char *so_end_address = NULL;
/* if not enough room, do nothing */ /* if not enough room, do nothing */
if (size < 3) if (size < 3)
return 0; return 0;
/* initial last_nack is rx_next - 1 */
last_nack = (entity->rx_next - 1 + entity->sn_modulus) % entity->sn_modulus;
nr_rlc_pdu_encoder_init(&encoder, buffer, size); nr_rlc_pdu_encoder_init(&encoder, buffer, size);
/* first 3 bytes, ack_sn and e1 will be set later */ /* first 3 bytes, ack_sn and e1 will be set later */
...@@ -1250,22 +1258,33 @@ static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size) ...@@ -1250,22 +1258,33 @@ static int generate_status(nr_rlc_entity_am_t *entity, char *buffer, int size)
e1_bit = entity->sn_field_length == 18 ? 1 : 7; e1_bit = entity->sn_field_length == 18 ? 1 : 7;
while (cur != NULL) { while (cur != NULL) {
m = next_missing(entity, cur, nack_count == 0); m = next_missing(entity, cur, check_head);
check_head = 0;
/* update ack_sn if the returned value is valid */
if (m.ack_sn != -1)
ack_sn = m.ack_sn;
/* stop here if no more nack to report */ /* stop here if no more nack to report */
if (m.sn_start == -1) if (m.sn_start == -1)
break; break;
nack_count += generate_missing(entity, &encoder, &m, &e1_byte, &e1_bit); generation_truncated = 0;
ln = generate_missing(entity, &encoder, &m, &e1_byte, &e1_bit,
&generation_truncated, &so_end_address);
/* remember the last nack put, if any */
if (ln != -1)
last_nack = ln;
/* if generation was truncated and so_end was put, we force its value to
* 0xffff (end of SDU) because we don't know what missing nack information
* was supposed to be put, so we nack until the end of the PDU to be sure
*/
if (generation_truncated && so_end_address != NULL) {
so_end_address[0] = 0xff;
so_end_address[1] = 0xff;
}
cur = m.next; cur = m.next;
} }
/* put ack_sn */ /* put ack_sn, which is last_nack + 1 */
ack_sn = (last_nack + 1) % entity->sn_modulus;
if (entity->sn_field_length == 12) { if (entity->sn_field_length == 12) {
buffer[0] = ack_sn >> 8; buffer[0] = ack_sn >> 8;
buffer[1] = ack_sn & 255; buffer[1] = ack_sn & 255;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment