Files
openairinterface5g/openair2/LAYER2/RLC/AM/rlc_am_status.c
T
Lionel Gauthier 862dbaa2fb Licence
git-svn-id: http://svn.eurecom.fr/openair4G/trunk@5156 818b1a75-f10b-46b9-bf7c-635c3b92a50f
2014-03-14 09:42:31 +00:00

618 lines
22 KiB
C
Executable File

/*******************************************************************************
Eurecom OpenAirInterface 2
Copyright(c) 1999 - 2014 Eurecom
This program is free software; you can redistribute it and/or modify it
under the terms and conditions of the GNU General Public License,
version 2, as published by the Free Software Foundation.
This program is distributed in the hope 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.
You should have received a copy of the GNU General Public License along with
this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
The full GNU General Public License is included in this distribution in
the file called "COPYING".
Contact Information
Openair Admin: openair_admin@eurecom.fr
Openair Tech : openair_tech@eurecom.fr
Forums : http://forums.eurecom.fsr/openairinterface
Address : EURECOM,
Campus SophiaTech,
450 Route des Chappes,
CS 50193
06904 Biot Sophia Antipolis cedex,
FRANCE
*******************************************************************************/
/***************************************************************************
rlc_am_status.c -
-------------------
AUTHOR : Lionel GAUTHIER
COMPANY : EURECOM
EMAIL : Lionel.Gauthier@eurecom.fr
***************************************************************************/
#include "rtos_header.h"
#include "platform_types.h"
//-----------------------------------------------------------------------------
#include "rlc_am_entity.h"
#include "rlc_am_retrans_proto_extern.h"
#include "rlc_am_status_proto_extern.h"
#include "rlc_am_discard_rx_proto_extern.h"
#include "rlc_am_proto_extern.h"
#include "rlc_am_util_proto_extern.h"
#include "LAYER2/MAC/extern.h"
//#define DEBUG_CREATE_STATUS
//#define DEBUG_CREATE_STATUS_SUFI
//#define PROCESS_STATUS
void rlc_am_write_sufi_no_more_in_control_pdu (uint8_t * dataP, uint8_t byte_alignedP);
void rlc_am_write_sufi_ack_in_control_pdu (uint8_t * dataP, uint16_t snP, uint8_t byte_alignedP);
int rlc_am_send_status (struct rlc_am_entity *rlcP);
int rlc_am_create_status_pdu (struct rlc_am_entity *rlcP, list_t* listP);
void rlc_am_find_holes (struct rlc_am_entity *rlcP);
//-----------------------------------------------------------------------------
void
rlc_am_write_sufi_no_more_in_control_pdu (uint8_t * dataP, uint8_t byte_alignedP)
{
//-----------------------------------------------------------------------------
#ifdef DEBUG_CREATE_STATUS_SUFI
msg ("[RLC_AM][STATUS] GENERATE SUFI NO_MORE\n");
#endif
if (!byte_alignedP) {
*dataP = (*dataP & 0xF0) | RLC_AM_SUFI_NO_MORE;
} else {
*dataP = RLC_AM_SUFI_NO_MORE << 4;
}
}
//-----------------------------------------------------------------------------
void
rlc_am_write_sufi_ack_in_control_pdu (uint8_t * dataP, uint16_t snP, uint8_t byte_alignedP)
{
//-----------------------------------------------------------------------------
#ifdef DEBUG_CREATE_STATUS_SUFI
msg ("[RLC_AM][STATUS] GENERATE SUFI ACK 0x%04X\n", snP);
#endif
if (!byte_alignedP) {
*dataP = (*dataP & 0xF0) | RLC_AM_SUFI_ACK;
dataP++;
*dataP++ = (uint8_t) (snP >> 4);
*dataP = (uint8_t) (snP << 4);
} else {
*dataP = RLC_AM_SUFI_ACK << 4;
*dataP = *dataP | (uint8_t) (snP >> 8);
dataP++;
*dataP = (uint8_t) (snP);
}
}
//-----------------------------------------------------------------------------
int
rlc_am_send_status (struct rlc_am_entity *rlcP)
{
//-----------------------------------------------------------------------------
#ifdef DEBUG_CREATE_STATUS
display_receiver_buffer (rlcP);
#endif
if (rlcP->timer_status_prohibit > 0) {
if (rlcP->running_timer_status_prohibit > Mac_rlc_xface->frame) {
//msg ("[RLC_AM][RB %d][STATUS] PREVENTED CREATE_STATUS_PDU %d %d\n", rlcP->rb_id, rlcP->running_timer_status_prohibit, Mac_rlc_xface->frame);
return 0;
} else {
//msg ("[RLC_AM][RB %d][STATUS] AUTHORIZE CREATE_STATUS_PDU frame %d timer %d timeout %d\n", rlcP->rb_id, Mac_rlc_xface->frame, rlcP->running_timer_status_prohibit, rlcP->timer_status_prohibit);
rlcP->running_timer_status_prohibit = rlcP->timer_status_prohibit/10 + Mac_rlc_xface->frame;
}
}
// sufis are created in a array of sufi in rlc struct;
rlc_am_find_holes (rlcP);
// create pdu with array of sufi previously generated;
return rlc_am_create_status_pdu (rlcP, &rlcP->control);
}
//-----------------------------------------------------------------------------
int
rlc_am_create_status_pdu (struct rlc_am_entity *rlcP, list_t * listP)
{
//-----------------------------------------------------------------------------
mem_block_t *mb = NULL;
struct rlc_am_status_header *pdu;
uint8_t *p8;
uint8_t *tmp;
int hole_index = 0;
signed int pdu_remaining_size; // remaining size for SUFIs BITMAP, LIST, RLIST, WINDOW
int current_sn;
signed int remaining_sn;
int pdu_status_count = 0;
int length_sufi;
int16_t last_hole_sn = -1;
uint8_t byte_aligned;
remaining_sn = rlcP->vr_h - rlcP->vr_r;
if (remaining_sn < 0) {
remaining_sn = remaining_sn + SN_12BITS_MASK + 1;
}
while ((rlcP->holes[hole_index].valid) && (remaining_sn)) {
if (!(mb)) {
mb = get_free_mem_block (rlcP->pdu_size + sizeof (struct rlc_am_tx_control_pdu_allocation) + GUARD_CRC_LIH_SIZE);
if (mb == NULL) {
msg ("[MEM_MNGT] ERROR create_status_pdu() no free blocks\n");
return 0;
} else {
pdu_status_count += 1;
#ifdef DEBUG_CREATE_STATUS
msg ("[RLC_AM][RB %d][STATUS] CREATE_STATUS_PDU %d\n", rlcP->rb_id, pdu_status_count);
#endif
memset (mb->data, 0, rlcP->pdu_size + sizeof (struct rlc_am_tx_control_pdu_allocation));
if (pdu_status_count == 1) {
((struct rlc_am_tx_control_pdu_management *) (mb->data))->rlc_tb_type = RLC_AM_FIRST_STATUS_PDU_TYPE;
} else {
((struct rlc_am_tx_control_pdu_management *) (mb->data))->rlc_tb_type = RLC_AM_STATUS_PDU_TYPE;
}
pdu = (struct rlc_am_status_header *) (&mb->data[sizeof (struct rlc_am_tx_control_pdu_allocation)]);
pdu->byte1 = RLC_PDU_TYPE_STATUS;
p8 = &(pdu->byte1);
// if first pdu of status include ack field
if (pdu_status_count == 1) {
pdu_remaining_size = (rlcP->pdu_size << 3) - 16; //-4(PDU_TYPE) -12(SUFI ACK)
current_sn = rlcP->ack_sn;
remaining_sn = rlcP->vr_h - rlcP->vr_r;
if (remaining_sn < 0) {
remaining_sn = remaining_sn + SN_12BITS_MASK + 1;
}
} else {
pdu_remaining_size = (rlcP->pdu_size << 3) - 8; //-4(PDU_TYPE) - 4(SUFI NO_MORE)
}
byte_aligned = 0;
}
}
// From 3GPP TS 25.322 V4.2.0 :
// Which SUFI fields to use is implementation dependent,...
// IMPLEMENTATION : KEEP GENERATION OF SUFI SIMPLE : GENERATE ONLY LIST
if (pdu_remaining_size >= RLC_AM_SUFI_LIST_SIZE_MIN) {
//----------------------------------
// generate LIST
//----------------------------------
if (!byte_aligned) {
*p8 = *p8 | RLC_AM_SUFI_LIST;
p8 = p8 + 1;
tmp = p8; //(*tmp = length_sufi << 4) when we will know length
length_sufi = 0;
pdu_remaining_size -= 8;
while ((length_sufi < 15) && (pdu_remaining_size >= 16) && (rlcP->holes[hole_index].valid) && (rlcP->nb_missing_pdus)) {
#ifdef DEBUG_CREATE_STATUS
msg ("[RLC_AM][RB %d][STATUS] GENERATE SUFI LIST HOLE START 0x%04X ", rlcP->rb_id, rlcP->holes[hole_index].fsn);
#endif
*p8 = *p8 | (rlcP->holes[hole_index].fsn >> 8);
p8 += 1;
*p8 = rlcP->holes[hole_index].fsn;
p8 += 1;
if (rlcP->holes[hole_index].length <= 15) {
*p8 = rlcP->holes[hole_index].length << 4;
#ifdef DEBUG_CREATE_STATUS
msg ("LENGTH %d\n", rlcP->holes[hole_index].length);
#endif
rlcP->nb_missing_pdus -= rlcP->holes[hole_index].length;
current_sn = (rlcP->holes[hole_index].fsn + rlcP->holes[hole_index].length) & SN_12BITS_MASK;
last_hole_sn = (rlcP->holes[hole_index].fsn + rlcP->holes[hole_index].length) & SN_12BITS_MASK;
hole_index += 1;
} else {
#ifdef DEBUG_CREATE_STATUS
msg ("LENGTH 15\n");
#endif
*p8 = 0xF0;
rlcP->nb_missing_pdus -= 15;
rlcP->holes[hole_index].length -= 15;
rlcP->holes[hole_index].fsn = (rlcP->holes[hole_index].fsn + 15) & SN_12BITS_MASK;
current_sn = rlcP->holes[hole_index].fsn;
}
length_sufi += 1;
pdu_remaining_size -= 16;
}
*tmp = *tmp | (length_sufi << 4);
} else {
*p8 = RLC_AM_SUFI_LIST << 4;
tmp = p8; //(*tmp = length_sufi) when we will know length
p8 = p8 + 1;
length_sufi = 0;
pdu_remaining_size -= 8;
while ((length_sufi < 15) && (pdu_remaining_size >= 16) && (rlcP->holes[hole_index].valid) && (rlcP->nb_missing_pdus)) {
#ifdef DEBUG_CREATE_STATUS
msg ("[RLC_AM][RB %d][STATUS] GENERATE SUFI LIST HOLE START 0x%04X ", rlcP->rb_id, rlcP->holes[hole_index].fsn);
#endif
*p8 = rlcP->holes[hole_index].fsn >> 4;
p8 += 1;
*p8 = rlcP->holes[hole_index].fsn << 4;
if (rlcP->holes[hole_index].length <= 15) {
#ifdef DEBUG_CREATE_STATUS
msg ("LENGTH %d\n", rlcP->holes[hole_index].length);
#endif
*p8 = *p8 | rlcP->holes[hole_index].length;
p8 += 1;
rlcP->nb_missing_pdus -= rlcP->holes[hole_index].length;
current_sn = (rlcP->holes[hole_index].fsn + rlcP->holes[hole_index].length) & SN_12BITS_MASK;
last_hole_sn = (rlcP->holes[hole_index].fsn + rlcP->holes[hole_index].length) & SN_12BITS_MASK;
hole_index += 1;
} else {
#ifdef DEBUG_CREATE_STATUS
msg ("LENGTH 15\n");
#endif
*p8 = *p8 | 15;
p8 += 1;
rlcP->nb_missing_pdus -= 15;
rlcP->holes[hole_index].length -= 15;
rlcP->holes[hole_index].fsn = (rlcP->holes[hole_index].fsn + 15) & SN_12BITS_MASK;
}
length_sufi += 1;
pdu_remaining_size -= 16;
}
*tmp = *tmp | length_sufi;
}
} else {
// it is not possible to write a sufi LIST in this control pdu, so close this pdu (write ACK or NO_MORE)
if (pdu_status_count == 1) {
//rlc_am_write_sufi_ack_in_control_pdu(p8, current_sn, byte_aligned);
if (last_hole_sn > 0) {
rlc_am_write_sufi_ack_in_control_pdu (p8, last_hole_sn, byte_aligned);
} else {
rlc_am_write_sufi_ack_in_control_pdu (p8, current_sn, byte_aligned);
}
} else {
rlc_am_write_sufi_no_more_in_control_pdu (p8, byte_aligned);
}
//hole_index += 1;
list_add_tail_eurecom (mb, listP);
mb = NULL;
}
remaining_sn = rlcP->vr_h - current_sn;
if (remaining_sn < 0) {
remaining_sn = remaining_sn + SN_12BITS_MASK + 1;
}
}
if ((mb == NULL) && !(pdu_status_count)) {
mb = get_free_mem_block (rlcP->pdu_size + sizeof (struct rlc_am_tx_control_pdu_allocation) + GUARD_CRC_LIH_SIZE);
if (mb == NULL) {
msg ("[MEM_MNGT] ERROR create_status_pdu() no free blocks\n");
return 0;
} else {
#ifdef DEBUG_CREATE_STATUS
msg ("[RLC_AM][RB %d][STATUS] CREATE_STATUS_PDU %d\n", rlcP->rb_id, pdu_status_count + 1);
#endif
memset (mb->data, 0, rlcP->pdu_size + sizeof (struct rlc_am_tx_control_pdu_allocation));
((struct rlc_am_tx_control_pdu_management *) (mb->data))->rlc_tb_type = RLC_AM_FIRST_STATUS_PDU_TYPE | RLC_AM_LAST_STATUS_PDU_TYPE;
pdu = (struct rlc_am_status_header *) (&mb->data[sizeof (struct rlc_am_tx_control_pdu_allocation)]);
pdu->byte1 = RLC_PDU_TYPE_STATUS;
p8 = &(pdu->byte1);
byte_aligned = 0;
rlc_am_write_sufi_ack_in_control_pdu (p8, rlcP->ack_sn, byte_aligned);
list_add_tail_eurecom (mb, listP);
}
} else if ((mb)) {
// if first pdu insert ack field updated to last sn referenced by a sufi in this pdu
// else insert no more field
((struct rlc_am_tx_control_pdu_management *) (mb->data))->rlc_tb_type |= RLC_AM_LAST_STATUS_PDU_TYPE;
if (pdu_status_count == 1) {
if (last_hole_sn > 0) {
rlc_am_write_sufi_ack_in_control_pdu (p8, last_hole_sn, byte_aligned);
} else {
rlc_am_write_sufi_ack_in_control_pdu (p8, current_sn, byte_aligned);
}
} else {
rlc_am_write_sufi_no_more_in_control_pdu (p8, byte_aligned);
}
list_add_tail_eurecom (mb, listP);
}
return 1;
}
//-----------------------------------------------------------------------------
void
rlc_am_find_holes (struct rlc_am_entity *rlcP)
{
//-----------------------------------------------------------------------------
uint16_t working_sn, working_sn_index;
uint16_t end_sn;
uint16_t distance;
int hole_index;
//--------------------------------------------------
// FIND HOLES
//--------------------------------------------------
hole_index = 0;
// ACK
rlcP->ack_sn = rlcP->vr_r;
// HOLES
rlcP->holes[0].valid = 0;
rlcP->nb_missing_pdus = 0;
working_sn = rlcP->vr_r;
working_sn_index = working_sn % rlcP->recomputed_configured_rx_window_size;
end_sn = rlcP->vr_h;
while (working_sn != end_sn) {
if ((rlcP->receiver_buffer[working_sn_index])) {
// UPDATE ACK FIELD :
if (rlcP->ack_sn == working_sn) {
rlcP->ack_sn = (working_sn + 1) & SN_12BITS_MASK;
}
// STOP PREVIOUS HOLE IF ANY
if (rlcP->holes[hole_index].valid) {
hole_index += 1;
rlcP->holes[hole_index].valid = 0;
}
} else {
// CONTINUE PREVIOUS HOLE IF ANY
if (rlcP->holes[hole_index].valid) {
rlcP->holes[hole_index].length += 1;
rlcP->nb_missing_pdus += 1;
// START HOLE IF ANY
} else {
#ifdef DEBUG_RLC_AM_FIND_HOLE
msg ("[RLC_AM %p] FOUND HOLE %d START 0x%04x(hex)\n", rlcP, hole_index, working_sn);
#endif
if (hole_index > 0) {
distance = working_sn - rlcP->holes[hole_index - 1].fsn;
if (distance & 0x8000) { // < 0
rlcP->holes[hole_index - 1].dist_to_next = distance + SN_12BITS_MASK + 1;
} else {
rlcP->holes[hole_index - 1].dist_to_next = distance;
}
}
rlcP->holes[hole_index].valid = 1;
rlcP->holes[hole_index + 1].valid = 0;
rlcP->holes[hole_index].fsn = working_sn;
rlcP->holes[hole_index].length = 1;
rlcP->nb_missing_pdus += 1;
}
}
working_sn = (working_sn + 1) & SN_12BITS_MASK;
working_sn_index = working_sn % rlcP->recomputed_configured_rx_window_size;
}
// compute the number of holes
if (rlcP->holes[hole_index].valid) {
rlcP->nb_holes = hole_index;
} else {
if (hole_index > 0) {
rlcP->nb_holes = hole_index - 1;
} else {
rlcP->nb_holes = 0;
}
}
}
//-----------------------------------------------------------------------------
void
rlc_am_process_status_info (struct rlc_am_entity *rlcP, uint8_t * statusP)
{
//-----------------------------------------------------------------------------
uint8_t *byte1;
uint8_t byte_aligned; // 1: quartet starts on bit 7, 0: quartet starts on bit 3 (of a byte)
uint8_t sufi_type;
uint8_t end_process = 0;
int16_t ack_sn = -1;
int16_t first_error_indicated_sn = -1;
int16_t sn_index;
int16_t current_sn;
int16_t current_index;
byte1 = statusP;
#ifdef DEBUG_PROCESS_STATUS
display_retransmission_buffer (rlcP);
#endif
if ((*byte1 & RLC_PIGGY_PDU_TYPE_MASK) == RLC_PIGGY_PDU_TYPE_STATUS) {
sufi_type = *byte1 & 0x0F;
byte_aligned = 0;
// process all bytes of the PDU
while (!end_process) {
// reset may have been triggered during processing of status, so exit
//if (rlcP->protocol_state == RLC_DATA_TRANSFER_READY_STATE){
switch (sufi_type) {
case RLC_AM_SUFI_NO_MORE:
// end of pdu
end_process = 1;
break;
case RLC_AM_SUFI_WINDOW:
msg ("[RLC_AM %p]PROCESS_STATUS ERROR process_status_info() RLC_AM_SUFI_WINDOW not implemented\n", rlcP);
end_process = 1;
break;
case RLC_AM_SUFI_ACK:
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS ACK\n", rlcP);
#endif
ack_sn = retransmission_buffer_management_ack (rlcP, byte1, byte_aligned, &first_error_indicated_sn);
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS ACK WAS LSN %d(b10) %4X(b16)\n", rlcP, ack_sn, ack_sn);
#endif
// DISCARD : A discard procedure is terminated in the sender on the reception of a status pdu which contains
// an ACK SUFI indicating VR(R) > SN_MRWlength
rlc_am_received_sufi_ack_check_discard_procedures (rlcP);
// end of pdu, no NO_MORE sufi;
end_process = 1;
#ifdef DEBUG_PROCESS_STATUS
display_protocol_vars_rlc_am (rlcP);
#endif
break;
case RLC_AM_SUFI_LIST:
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS LIST\n", rlcP);
#endif
byte1 = retransmission_buffer_management_list (rlcP, byte1, byte_aligned, &first_error_indicated_sn);
if (byte1 == NULL) {
// error in processing;
end_process = 1;
break;
}
// byte aligned is not changed for this sufi;
if (!byte_aligned) {
sufi_type = *byte1 & 0X0F;
} else {
sufi_type = (*byte1 & 0xF0) >> 4;
}
break;
case RLC_AM_SUFI_BITMAP:
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS BITMAP %p 0x%02X\n", rlcP, byte1, *byte1);
#endif
byte1 = retransmission_buffer_management_bitmap (rlcP, byte1, byte_aligned, &first_error_indicated_sn);
if (byte1 == NULL) {
// error in processing;
end_process = 1;
break;
}
// the length of this sufi is always n bytes + 4;
// so invert byte_aligned;
if (byte_aligned) {
byte_aligned = 0;
sufi_type = *byte1 & 0X0F;
} else {
byte_aligned = 1;
sufi_type = (*byte1 & 0xF0) >> 4;
}
break;
case RLC_AM_SUFI_RLIST:
msg ("[RLC_AM %p]PROCESS_STATUS ERROR SUFI RLIST NOT IMPLEMENTED\n", rlcP);
end_process = 1;
break;
case RLC_AM_SUFI_MRW:
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS MRW %p 0x%02X\n", rlcP, byte1, *byte1);
#endif
byte1 = retransmission_buffer_management_mrw (rlcP, byte1, &byte_aligned);
if (byte1 == NULL) {
// error in processing;
end_process = 1;
break;
}
// the length of this sufi is always n bytes + 4;
// so invert byte_aligned;
if (byte_aligned) {
sufi_type = (*byte1 & 0xF0) >> 4;
} else {
sufi_type = *byte1 & 0X0F;
}
break;
case RLC_AM_SUFI_MRW_ACK:
#ifdef DEBUG_PROCESS_STATUS
msg ("[RLC_AM %p]PROCESS_STATUS MRW_ACK %p 0x%02X\n", rlcP, byte1, *byte1);
#endif
byte1 = retransmission_buffer_management_mrw_ack (rlcP, byte1, &byte_aligned);
if (byte1 == NULL) {
// error in processing;
end_process = 1;
break;
}
// the length of this sufi is always n bytes + 4;
// so invert byte_aligned;
if (byte_aligned) {
sufi_type = (*byte1 & 0xF0) >> 4;
} else {
sufi_type = *byte1 & 0X0F;
}
break;
default:
msg ("[RLC_AM %p]PROCESS_STATUS ERROR SUFI UNKNOWN 0x%02X\n", rlcP, *byte1);
end_process = 1;
}
/*} else {
return;
} */
}
//-------------------------
// CLEAR ALL EVENTS RECEIVED;
//-------------------------
current_sn = rlcP->vt_a;
current_index = rlcP->vt_a % rlcP->recomputed_configured_tx_window_size;
sn_index = rlcP->vt_s % rlcP->recomputed_configured_tx_window_size;
while (sn_index != current_index) {
if (rlcP->retransmission_buffer[current_index] != NULL) {
((struct rlc_am_tx_data_pdu_management *) (rlcP->retransmission_buffer[current_index]->data))->ack = RLC_AM_PDU_ACK_NO_EVENT;
}
current_sn = (current_sn + 1) & SN_12BITS_MASK;
current_index = current_sn % rlcP->recomputed_configured_tx_window_size;
}
} else {
msg ("[RLC_AM %p][PROCESS_STATUS] ERROR process_piggybacked_status_info() PDU type field is not STATUS\n", rlcP);
}
#ifdef DEBUG_PROCESS_STATUS
display_retransmission_buffer (rlcP);
#endif
}
//-----------------------------------------------------------------------------
mem_block_t *
rlc_am_create_status_pdu_mrw_ack (struct rlc_am_entity *rlcP, uint8_t nP, uint16_t sn_ackP)
{
//-----------------------------------------------------------------------------
mem_block_t *le;
struct rlc_am_status_header *pdu;
uint8_t *p8;
#ifdef DEBUG_STATUS
msg ("[RLC_AM %p][STATUS] rlc_am_mrw_send_ack(N=%d, sn_ack=0x%04X)\n", rlcP, nP, sn_ackP);
#endif
le = get_free_mem_block (rlcP->pdu_size + sizeof (struct rlc_am_tx_control_pdu_allocation) + GUARD_CRC_LIH_SIZE);
if (le == NULL) {
msg ("[MEM_MNGT][ERROR] rlc_am_mrw_send_ack() no free blocks\n");
return NULL;
} else {
((struct rlc_am_tx_control_pdu_management *) (le->data))->rlc_tb_type = RLC_AM_STATUS_PDU_TYPE;
pdu = (struct rlc_am_status_header *) (&le->data[sizeof (struct rlc_am_tx_control_pdu_allocation)]);
pdu->byte1 = RLC_PDU_TYPE_STATUS;
p8 = &(pdu->byte1);
*p8 = *p8 | RLC_AM_SUFI_MRW_ACK;
p8 = p8 + 1;
*p8 = (nP << 4) | (sn_ackP >> 8);
p8 = p8 + 1;
*p8 = sn_ackP;
p8 = p8 + 1;
*p8 = RLC_AM_SUFI_NO_MORE << 4;
return le;
}
}