/*******************************************************************************
OpenAirInterface
Copyright(c) 1999 - 2014 Eurecom
OpenAirInterface 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 3 of the License, or
(at your option) any later version.
OpenAirInterface 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.
You should have received a copy of the GNU General Public License
along with OpenAirInterface.The full GNU General Public License is
included in this distribution in the file called "COPYING". If not,
see .
Contact Information
OpenAirInterface Admin: openair_admin@eurecom.fr
OpenAirInterface Tech : openair_tech@eurecom.fr
OpenAirInterface Dev : openair4g-devel@eurecom.fr
Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE
*******************************************************************************/
/*! \file pdcp.c
* \brief pdcp interface with RLC
* \author Lionel GAUTHIER and Navid Nikaein
* \date 2009-2012
* \version 1.0
*/
#define PDCP_C
#ifndef USER_MODE
#include
#endif
#include "assertions.h"
#include "pdcp.h"
#include "pdcp_util.h"
#include "pdcp_sequence_manager.h"
#include "LAYER2/RLC/rlc.h"
#include "LAYER2/MAC/extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "pdcp_primitives.h"
#include "OCG.h"
#include "OCG_extern.h"
#include "UTIL/LOG/log.h"
#include
#include "platform_constants.h"
#include "UTIL/LOG/vcd_signal_dumper.h"
#if defined(ENABLE_SECURITY)
# include "UTIL/OSA/osa_defs.h"
#endif
#if defined(ENABLE_ITTI)
# include "intertask_interface.h"
#endif
#if defined(LINK_PDCP_TO_GTPV1U)
# include "gtpv1u_eNB_task.h"
#endif
#ifndef OAI_EMU
extern int otg_enabled;
#endif
//extern char *packet_gen(int src, int dst, int ctime, int *pkt_size);
extern int otg_rx_pkt( int src, int dst, int ctime, char *buffer_tx, unsigned int size);
//-----------------------------------------------------------------------------
/*
* If PDCP_UNIT_TEST is set here then data flow between PDCP and RLC is broken
* and PDCP has no longer anything to do with RLC. In this case, after it's handed
* an SDU it appends PDCP header and returns (by filling in incoming pointer parameters)
* this mem_block_t to be dissected for testing purposes. For further details see test
* code at targets/TEST/PDCP/test_pdcp.c:test_pdcp_data_req()
*/
boolean_t pdcp_data_req(
const module_id_t enb_mod_idP,
const module_id_t ue_mod_idP,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const srb_flag_t srb_flagP,
const rb_id_t rb_idP,
const mui_t muiP,
const confirm_t confirmP,
const sdu_size_t sdu_buffer_sizeP,
unsigned char *const sdu_buffer_pP,
const pdcp_transmission_mode_t modeP)
{
//-----------------------------------------------------------------------------
pdcp_t *pdcp_p = NULL;
uint8_t i = 0;
uint8_t pdcp_header_len = 0;
uint8_t pdcp_tailer_len = 0;
uint16_t pdcp_pdu_size = 0;
uint16_t current_sn = 0;
mem_block_t *pdcp_pdu_p = NULL;
rlc_op_status_t rlc_status;
boolean_t ret = TRUE;
AssertError (enb_mod_idP < NUMBER_OF_eNB_MAX, return FALSE, "eNB id is too high (%u/%d) %u %u!\n", enb_mod_idP, NUMBER_OF_eNB_MAX, ue_mod_idP, rb_idP);
AssertError (ue_mod_idP < NUMBER_OF_UE_MAX, return FALSE, "UE id is too high (%u/%d) %u %u!\n", ue_mod_idP, NUMBER_OF_UE_MAX, enb_mod_idP, rb_idP);
if (modeP == PDCP_TRANSMISSION_MODE_TRANSPARENT) {
AssertError (rb_idP < NB_RB_MBMS_MAX, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_idP, NB_RB_MBMS_MAX, ue_mod_idP, enb_mod_idP);
} else {
if (srb_flagP) {
AssertError (rb_idP < 2, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_idP, 2, ue_mod_idP, enb_mod_idP);
} else {
AssertError (rb_idP < maxDRB, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_idP, maxDRB, ue_mod_idP, enb_mod_idP);
}
}
if (enb_flagP == ENB_FLAG_NO) {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_ue[ue_mod_idP][rb_idP-1];
} else {
pdcp_p = &pdcp_array_drb_ue[ue_mod_idP][rb_idP-1];
}
} else {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_eNB[enb_mod_idP][ue_mod_idP][rb_idP-1];
} else {
pdcp_p = &pdcp_array_drb_eNB[enb_mod_idP][ue_mod_idP][rb_idP-1];
}
}
if ((pdcp_p->instanciated_instance == FALSE) && (modeP != PDCP_TRANSMISSION_MODE_TRANSPARENT)) {
if (enb_flagP == ENB_FLAG_NO) {
LOG_W(PDCP, "[UE %d] Instance is not configured for eNB %d, rb_id %d Ignoring SDU...\n",
ue_mod_idP, enb_mod_idP, rb_idP);
} else {
LOG_W(PDCP, "[eNB %d] Instance is not configured for UE %d, rb_id %d Ignoring SDU...\n",
enb_mod_idP, ue_mod_idP, rb_idP);
}
return FALSE;
}
if (sdu_buffer_sizeP == 0) {
LOG_W(PDCP, "Handed SDU is of size 0! Ignoring...\n");
return FALSE;
}
/*
* XXX MAX_IP_PACKET_SIZE is 4096, shouldn't this be MAX SDU size, which is 8188 bytes?
*/
if (sdu_buffer_sizeP > MAX_IP_PACKET_SIZE) {
LOG_E(PDCP, "Requested SDU size (%d) is bigger than that can be handled by PDCP (%u)!\n",
sdu_buffer_sizeP, MAX_IP_PACKET_SIZE);
// XXX What does following call do?
mac_xface->macphy_exit("PDCP sdu buffer size > MAX_IP_PACKET_SIZE");
}
if (enb_flagP == ENB_FLAG_NO)
start_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_IN);
// PDCP transparent mode for MBMS traffic
if (modeP == PDCP_TRANSMISSION_MODE_TRANSPARENT) {
LOG_D(PDCP, " [TM] Asking for a new mem_block of size %d\n",sdu_buffer_sizeP);
pdcp_pdu_p = get_free_mem_block(sdu_buffer_sizeP);
if (pdcp_pdu_p != NULL) {
memcpy(&pdcp_pdu_p->data[0], sdu_buffer_pP, sdu_buffer_sizeP);
rlc_util_print_hex_octets(PDCP,
(unsigned char*)&pdcp_pdu_p->data[0],
sdu_buffer_sizeP);
rlc_status = rlc_data_req(enb_mod_idP, ue_mod_idP, frameP, enb_flagP, srb_flagP, MBMS_FLAG_YES, rb_idP, muiP, confirmP, sdu_buffer_sizeP, pdcp_pdu_p);
} else {
rlc_status = RLC_OP_STATUS_OUT_OF_RESSOURCES;
LOG_W(PDCP,"[FRAME %5u][%s][PDCP][MOD %u/%u][RB %u] PDCP_DATA_REQ SDU DROPPED, OUT OF MEMORY \n",
frameP,
(enb_flagP) ? "eNB" : "UE",
enb_mod_idP,
ue_mod_idP,
rb_idP);
#if defined(STOP_ON_IP_TRAFFIC_OVERLOAD)
AssertFatal(0, "[FRAME %5u][%s][PDCP][MOD %u/%u][RB %u] PDCP_DATA_REQ SDU DROPPED, OUT OF MEMORY \n",
frameP,
(enb_flagP) ? "eNB" : "UE",
enb_mod_idP,
ue_mod_idP,
rb_idP);
#endif
}
} else {
// calculate the pdcp header and trailer size
if (srb_flagP) {
pdcp_header_len = PDCP_CONTROL_PLANE_DATA_PDU_SN_SIZE;
pdcp_tailer_len = PDCP_CONTROL_PLANE_DATA_PDU_MAC_I_SIZE;
} else {
pdcp_header_len = PDCP_USER_PLANE_DATA_PDU_LONG_SN_HEADER_SIZE;
pdcp_tailer_len = 0;
}
pdcp_pdu_size = sdu_buffer_sizeP + pdcp_header_len + pdcp_tailer_len;
LOG_D(PDCP, "Data request notification for PDCP entity %s enb id %u ue_id %u and radio bearer ID %d pdu size %d (header%d, trailer%d)\n",
(enb_flagP) ? "eNB" : "UE",
enb_mod_idP,
ue_mod_idP,
rb_idP,
pdcp_pdu_size,
pdcp_header_len,
pdcp_tailer_len);
/*
* Allocate a new block for the new PDU (i.e. PDU header and SDU payload)
*/
LOG_D(PDCP, "Asking for a new mem_block of size %d\n", pdcp_pdu_size);
pdcp_pdu_p = get_free_mem_block(pdcp_pdu_size);
if (pdcp_pdu_p != NULL) {
/*
* Create a Data PDU with header and append data
*
* Place User Plane PDCP Data PDU header first
*/
if (srb_flagP) { // this Control plane PDCP Data PDU
pdcp_control_plane_data_pdu_header pdu_header;
pdu_header.sn = pdcp_get_next_tx_seq_number(pdcp_p);
current_sn = pdu_header.sn;
memset(&pdu_header.mac_i[0],0,PDCP_CONTROL_PLANE_DATA_PDU_MAC_I_SIZE);
memset(&pdcp_pdu_p->data[sdu_buffer_sizeP + pdcp_header_len],0,PDCP_CONTROL_PLANE_DATA_PDU_MAC_I_SIZE);
if (pdcp_serialize_control_plane_data_pdu_with_SRB_sn_buffer((unsigned char*)pdcp_pdu_p->data, &pdu_header) == FALSE) {
LOG_E(PDCP, "Cannot fill PDU buffer with relevant header fields!\n");
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_OUT);
return FALSE;
}
} else {
pdcp_user_plane_data_pdu_header_with_long_sn pdu_header;
pdu_header.dc = (modeP == PDCP_TRANSMISSION_MODE_DATA) ? PDCP_DATA_PDU_BIT_SET : PDCP_CONTROL_PDU_BIT_SET;
pdu_header.sn = pdcp_get_next_tx_seq_number(pdcp_p);
current_sn = pdu_header.sn ;
if (pdcp_serialize_user_plane_data_pdu_with_long_sn_buffer((unsigned char*)pdcp_pdu_p->data, &pdu_header) == FALSE) {
LOG_E(PDCP, "Cannot fill PDU buffer with relevant header fields!\n");
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_OUT);
return FALSE;
}
}
/*
* Validate incoming sequence number, there might be a problem with PDCP initialization
*/
if (current_sn > pdcp_calculate_max_seq_num_for_given_size(pdcp_p->seq_num_size)) {
LOG_E(PDCP, "Generated sequence number (%lu) is greater than a sequence number could ever be!\n", current_sn);
LOG_E(PDCP, "There must be a problem with PDCP initialization, ignoring this PDU...\n");
free_mem_block(pdcp_pdu_p);
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_OUT);
return FALSE;
}
LOG_D(PDCP, "Sequence number %d is assigned to current PDU\n", current_sn);
/* Then append data... */
memcpy(&pdcp_pdu_p->data[pdcp_header_len], sdu_buffer_pP, sdu_buffer_sizeP);
//For control plane data that are not integrity protected,
// the MAC-I field is still present and should be padded with padding bits set to 0.
// NOTE: user-plane data are never integrity protected
for (i=0;idata[pdcp_header_len + sdu_buffer_sizeP + i] = 0x00;// pdu_header.mac_i[i];
#if defined(ENABLE_SECURITY)
if ((pdcp_p->security_activated != 0) &&
(((pdcp_p->cipheringAlgorithm) != 0) ||
((pdcp_p->integrityProtAlgorithm) != 0))) {
if (enb_flagP == ENB_FLAG_NO)
start_meas(&eNB_pdcp_stats[enb_mod_idP].apply_security);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].apply_security);
pdcp_apply_security(pdcp_p,
srb_flagP,
rb_idP % maxDRB,
pdcp_header_len,
current_sn,
pdcp_pdu_p->data,
sdu_buffer_sizeP);
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].apply_security);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].apply_security);
}
#endif
/* Print octets of outgoing data in hexadecimal form */
LOG_D(PDCP, "Following content with size %d will be sent over RLC (PDCP PDU header is the first two bytes)\n",
pdcp_pdu_size);
//util_print_hex_octets(PDCP, (unsigned char*)pdcp_pdu_p->data, pdcp_pdu_size);
//util_flush_hex_octets(PDCP, (unsigned char*)pdcp_pdu->data, pdcp_pdu_size);
} else {
LOG_E(PDCP, "Cannot create a mem_block for a PDU!\n");
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
#if defined(STOP_ON_IP_TRAFFIC_OVERLOAD)
AssertFatal(0, "[FRAME %5u][%s][PDCP][MOD %u/%u][RB %u] PDCP_DATA_REQ SDU DROPPED, OUT OF MEMORY \n",
frameP,
(enb_flagP) ? "eNB" : "UE",
enb_mod_idP,
ue_mod_idP,
rb_idP);
#endif
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_OUT);
return FALSE;
}
/*
* Ask sublayer to transmit data and check return value
* to see if RLC succeeded
*/
#ifdef PDCP_MSG_PRINT
int i=0;
LOG_F(PDCP,"[MSG] PDCP DL %s PDU on rb_id %d\n", (srb_flagP)? "CONTROL" : "DATA", rb_idP);
for (i = 0; i < pdcp_pdu_size; i++)
LOG_F(PDCP,"%02x ", ((uint8_t*)pdcp_pdu_p->data)[i]);
LOG_F(PDCP,"\n");
#endif
rlc_status = rlc_data_req(enb_mod_idP, ue_mod_idP, frameP, enb_flagP, srb_flagP, MBMS_FLAG_NO, rb_idP, muiP, confirmP, pdcp_pdu_size, pdcp_pdu_p);
}
switch (rlc_status) {
case RLC_OP_STATUS_OK:
LOG_D(PDCP, "Data sending request over RLC succeeded!\n");
ret=TRUE;
break;
case RLC_OP_STATUS_BAD_PARAMETER:
LOG_W(PDCP, "Data sending request over RLC failed with 'Bad Parameter' reason!\n");
ret= FALSE;
break;
case RLC_OP_STATUS_INTERNAL_ERROR:
LOG_W(PDCP, "Data sending request over RLC failed with 'Internal Error' reason!\n");
ret= FALSE;
break;
case RLC_OP_STATUS_OUT_OF_RESSOURCES:
LOG_W(PDCP, "Data sending request over RLC failed with 'Out of Resources' reason!\n");
ret= FALSE;
break;
default:
LOG_W(PDCP, "RLC returned an unknown status code after PDCP placed the order to send some data (Status Code:%d)\n", rlc_status);
ret= FALSE;
break;
}
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_req);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_req);
/*
* Control arrives here only if rlc_data_req() returns RLC_OP_STATUS_OK
* so we return TRUE afterwards
*/
/*
if (rb_id>=DTCH) {
if (enb_flagP == 1) {
Pdcp_stats_tx[module_id][(rb_id & RAB_OFFSET2 )>> RAB_SHIFT2][(rb_id & RAB_OFFSET)-DTCH]++;
Pdcp_stats_tx_bytes[module_id][(rb_id & RAB_OFFSET2 )>> RAB_SHIFT2][(rb_id & RAB_OFFSET)-DTCH] += sdu_buffer_size;
} else {
Pdcp_stats_tx[module_id][(rb_id & RAB_OFFSET2 )>> RAB_SHIFT2][(rb_id & RAB_OFFSET)-DTCH]++;
Pdcp_stats_tx_bytes[module_id][(rb_id & RAB_OFFSET2 )>> RAB_SHIFT2][(rb_id & RAB_OFFSET)-DTCH] += sdu_buffer_size;
}
}*/
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_REQ,VCD_FUNCTION_OUT);
return ret;
}
boolean_t pdcp_data_ind(
const module_id_t enb_mod_idP,
const module_id_t ue_mod_idP,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const srb_flag_t srb_flagP,
const MBMS_flag_t MBMS_flagP,
const rb_id_t rb_idP,
const sdu_size_t sdu_buffer_sizeP,
mem_block_t* const sdu_buffer_pP)
{
//-----------------------------------------------------------------------------
pdcp_t *pdcp_p = NULL;
list_t *sdu_list_p = NULL;
mem_block_t *new_sdu_p = NULL;
uint8_t pdcp_header_len = 0;
uint8_t pdcp_tailer_len = 0;
pdcp_sn_t sequence_number = 0;
volatile sdu_size_t payload_offset = 0;
rb_id_t rb_id = rb_idP;
boolean_t packet_forwarded = FALSE;
#ifdef OAI_EMU
if (enb_flagP) {
AssertFatal ((enb_mod_idP >= oai_emulation.info.first_enb_local) && (oai_emulation.info.nb_enb_local > 0),
"eNB module id is too low (%u/%d)!\n",
enb_mod_idP,
oai_emulation.info.first_enb_local);
AssertFatal ((enb_mod_idP < (oai_emulation.info.first_enb_local + oai_emulation.info.nb_enb_local)) && (oai_emulation.info.nb_enb_local > 0),
"eNB module id is too high (%u/%d)!\n",
enb_mod_idP,
oai_emulation.info.first_enb_local + oai_emulation.info.nb_enb_local);
AssertFatal (ue_mod_idP < NB_UE_INST,
"UE module id is too high (%u/%d)!\n",
ue_mod_idP,
oai_emulation.info.first_ue_local + oai_emulation.info.nb_ue_local);
} else {
AssertFatal (ue_mod_idP < (oai_emulation.info.first_ue_local + oai_emulation.info.nb_ue_local),
"UE module id is too high (%u/%d)!\n",
ue_mod_idP,
oai_emulation.info.first_ue_local + oai_emulation.info.nb_ue_local);
AssertFatal (ue_mod_idP >= oai_emulation.info.first_ue_local,
"UE module id is too low (%u/%d)!\n",
ue_mod_idP,
oai_emulation.info.first_ue_local);
}
#endif
#ifdef PDCP_MSG_PRINT
int i=0;
LOG_F(PDCP,"[MSG] PDCP UL %s PDU on rb_id %d\n", (srb_flagP)? "CONTROL" : "DATA", rb_idP);
for (i = 0; i < sdu_buffer_sizeP; i++)
LOG_F(PDCP,"%02x ", ((uint8_t*)sdu_buffer_pP->data)[i]);
LOG_F(PDCP,"\n");
#endif
if (MBMS_flagP) {
AssertError (rb_idP < NB_RB_MBMS_MAX, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_idP, NB_RB_MBMS_MAX, ue_mod_idP, enb_mod_idP);
if (enb_flagP == ENB_FLAG_NO) {
LOG_I(PDCP, "e-MBMS Data indication notification for PDCP entity from eNB %u to UE %u "
"and radio bearer ID %d rlc sdu size %d enb_flagP %d\n",
enb_mod_idP, ue_mod_idP, rb_idP, sdu_buffer_sizeP, enb_flagP);
} else {
LOG_I(PDCP, "Data indication notification for PDCP entity from UE %u to eNB %u "
"and radio bearer ID %d rlc sdu size %d enb_flagP %d eNB_id %d\n",
ue_mod_idP, enb_mod_idP , rb_idP, sdu_buffer_sizeP, enb_flagP, enb_mod_idP);
}
} else {
rb_id = rb_idP % maxDRB;
AssertError (rb_id < maxDRB, return FALSE, "RB id is too high (%u/%d) %u %u!\n", rb_id, maxDRB, ue_mod_idP, enb_mod_idP);
AssertError (rb_id > 0, return FALSE, "RB id is too low (%u/%d) %u %u!\n", rb_id, maxDRB, ue_mod_idP, enb_mod_idP);
if (enb_flagP == ENB_FLAG_NO) {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_ue[ue_mod_idP][rb_id-1];
LOG_D(PDCP, "Data indication notification for PDCP entity from eNB %u to UE %u "
"and signalling radio bearer ID %d rlc sdu size %d enb_flagP %d\n",
enb_mod_idP, ue_mod_idP, rb_id, sdu_buffer_sizeP, enb_flagP);
} else {
pdcp_p = &pdcp_array_drb_ue[ue_mod_idP][rb_id-1];
LOG_D(PDCP, "Data indication notification for PDCP entity from eNB %u to UE %u "
"and data radio bearer ID %d rlc sdu size %d enb_flagP %d\n",
enb_mod_idP, ue_mod_idP, rb_id, sdu_buffer_sizeP, enb_flagP);
}
} else {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_eNB[enb_mod_idP][ue_mod_idP][rb_id-1];
LOG_D(PDCP, "Data indication notification for PDCP entity from UE %u to eNB %u "
"and signalling radio bearer ID %d rlc sdu size %d enb_flagP %d eNB_id %d\n",
ue_mod_idP, enb_mod_idP , rb_id, sdu_buffer_sizeP, enb_flagP, enb_mod_idP);
} else {
pdcp_p = &pdcp_array_drb_eNB[enb_mod_idP][ue_mod_idP][rb_id-1];
LOG_D(PDCP, "Data indication notification for PDCP entity from UE %u to eNB %u "
"and data radio bearer ID %d rlc sdu size %d enb_flagP %d eNB_id %d\n",
ue_mod_idP, enb_mod_idP , rb_id, sdu_buffer_sizeP, enb_flagP, enb_mod_idP);
}
}
}
sdu_list_p = &pdcp_sdu_list;
if (sdu_buffer_sizeP == 0) {
LOG_W(PDCP, "SDU buffer size is zero! Ignoring this chunk!\n");
return FALSE;
}
if (enb_flagP)
start_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_IN);
/*
* Parse the PDU placed at the beginning of SDU to check
* if incoming SN is in line with RX window
*/
if (MBMS_flagP == 0 ) {
if (srb_flagP) { //SRB1/2
pdcp_header_len = PDCP_CONTROL_PLANE_DATA_PDU_SN_SIZE;
pdcp_tailer_len = PDCP_CONTROL_PLANE_DATA_PDU_MAC_I_SIZE;
sequence_number = pdcp_get_sequence_number_of_pdu_with_SRB_sn((unsigned char*)sdu_buffer_pP->data);
} else { // DRB
pdcp_tailer_len = 0;
if (pdcp_p->seq_num_size == PDCP_SN_7BIT){
pdcp_header_len = PDCP_USER_PLANE_DATA_PDU_SHORT_SN_HEADER_SIZE;
sequence_number = pdcp_get_sequence_number_of_pdu_with_short_sn((unsigned char*)sdu_buffer_pP->data);
}else if (pdcp_p->seq_num_size == PDCP_SN_12BIT){
pdcp_header_len = PDCP_USER_PLANE_DATA_PDU_LONG_SN_HEADER_SIZE;
sequence_number = pdcp_get_sequence_number_of_pdu_with_long_sn((unsigned char*)sdu_buffer_pP->data);
} else {
//sequence_number = 4095;
LOG_E(PDCP,"wrong sequence number (%d) for this pdcp entity \n", pdcp_p->seq_num_size);
}
//uint8_t dc = pdcp_get_dc_filed((unsigned char*)sdu_buffer_pP->data);
}
/*
* Check if incoming SDU is long enough to carry a PDU header
*/
if (sdu_buffer_sizeP < pdcp_header_len + pdcp_tailer_len ) {
LOG_W(PDCP, "Incoming (from RLC) SDU is short of size (size:%d)! Ignoring...\n", sdu_buffer_sizeP);
free_mem_block(sdu_buffer_pP);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_OUT);
return FALSE;
}
if (pdcp_is_rx_seq_number_valid(sequence_number, pdcp_p, srb_flagP) == TRUE) {
LOG_D(PDCP, "Incoming PDU has a sequence number (%d) in accordance with RX window\n", sequence_number);
/* if (dc == PDCP_DATA_PDU )
LOG_D(PDCP, "Passing piggybacked SDU to NAS driver...\n");
else
LOG_D(PDCP, "Passing piggybacked SDU to RRC ...\n");*/
} else {
LOG_W(PDCP, "Incoming PDU has an unexpected sequence number (%d), RX window snychronisation have probably been lost!\n", sequence_number);
/*
* XXX Till we implement in-sequence delivery and duplicate discarding
* mechanism all out-of-order packets will be delivered to RRC/IP
*/
#if 0
LOG_D(PDCP, "Ignoring PDU...\n");
free_mem_block(sdu_buffer);
return FALSE;
#else
LOG_W(PDCP, "Delivering out-of-order SDU to upper layer...\n");
#endif
}
// SRB1/2: control-plane data
if (srb_flagP){
#if defined(ENABLE_SECURITY)
if (pdcp_p->security_activated == 1) {
if (enb_flagP == ENB_FLAG_NO)
start_meas(&eNB_pdcp_stats[enb_mod_idP].validate_security);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].validate_security);
pdcp_validate_security(pdcp_p,
srb_flagP,
rb_idP,
pdcp_header_len,
sequence_number,
sdu_buffer_pP->data,
sdu_buffer_sizeP - pdcp_tailer_len);
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].validate_security);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].validate_security);
}
#endif
//rrc_lite_data_ind(module_id, //Modified MW - L2 Interface
pdcp_rrc_data_ind(enb_mod_idP,
ue_mod_idP,
frameP,
enb_flagP,
rb_id,
sdu_buffer_sizeP - pdcp_header_len - pdcp_tailer_len,
(uint8_t*)&sdu_buffer_pP->data[pdcp_header_len]);
free_mem_block(sdu_buffer_pP);
// free_mem_block(new_sdu);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_OUT);
return TRUE;
}
/*
* DRBs
*/
payload_offset=pdcp_header_len;// PDCP_USER_PLANE_DATA_PDU_LONG_SN_HEADER_SIZE;
#if defined(ENABLE_SECURITY)
if (pdcp_p->security_activated == 1) {
if (enb_flagP == ENB_FLAG_NO)
start_meas(&eNB_pdcp_stats[enb_mod_idP].validate_security);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].validate_security);
pdcp_validate_security(pdcp_p,
srb_flagP,
rb_idP,
pdcp_header_len,
sequence_number,
sdu_buffer_pP->data,
sdu_buffer_sizeP - pdcp_tailer_len);
if (enb_flagP == ENB_FLAG_NO)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].validate_security);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].validate_security);
}
#endif
} else {
payload_offset=0;
}
#if defined(USER_MODE) && defined(OAI_EMU)
if (oai_emulation.info.otg_enabled == 1) {
module_id_t src_id, dst_id;
int ctime;
if (pdcp_p->rlc_mode == RLC_MODE_AM ) {
pdcp_p->last_submitted_pdcp_rx_sn = sequence_number;
}
rlc_util_print_hex_octets(PDCP,
(unsigned char*)&sdu_buffer_pP->data[payload_offset],
sdu_buffer_sizeP - payload_offset);
src_id = (enb_flagP == ENB_FLAG_NO) ? enb_mod_idP : ue_mod_idP + NB_eNB_INST;
dst_id = (enb_flagP == ENB_FLAG_NO) ? ue_mod_idP + NB_eNB_INST: enb_mod_idP;
ctime = oai_emulation.info.time_ms; // avg current simulation time in ms : we may get the exact time through OCG?
LOG_D(PDCP, "Check received buffer : enb_flag %d rab id %d (src %d, dst %d)\n",
enb_flagP, rb_id, src_id, dst_id);
if (otg_rx_pkt(src_id, dst_id,ctime,&sdu_buffer_pP->data[payload_offset],
sdu_buffer_sizeP - payload_offset ) == 0 ) {
free_mem_block(sdu_buffer_pP);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_OUT);
return TRUE;
}
}
#else
if (otg_enabled==1) {
LOG_D(OTG,"Discarding received packed\n");
free_mem_block(sdu_buffer_pP);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_OUT);
return TRUE;
}
#endif
// XXX Decompression would be done at this point
/*
* After checking incoming sequence number PDCP header
* has to be stripped off so here we copy SDU buffer starting
* from its second byte (skipping 0th and 1st octets, i.e.
* PDCP header)
*/
#if defined(LINK_PDCP_TO_GTPV1U)
if ((TRUE == enb_flagP) && (FALSE == srb_flagP)) {
LOG_I(PDCP,"Sending to GTPV1U %d bytes\n", sdu_buffer_sizeP - payload_offset);
gtpv1u_new_data_req(
enb_mod_idP, //gtpv1u_data_t *gtpv1u_data_p,
ue_mod_idP,//rb_id/maxDRB, TO DO UE ID
rb_id + 4,
&sdu_buffer_pP->data[payload_offset],
sdu_buffer_sizeP - payload_offset);
packet_forwarded = TRUE;
}
#else
packet_forwarded = FALSE;
#endif
if (FALSE == packet_forwarded) {
new_sdu_p = get_free_mem_block(sdu_buffer_sizeP - payload_offset + sizeof (pdcp_data_ind_header_t));
if (new_sdu_p) {
if (pdcp_p->rlc_mode == RLC_MODE_AM ) {
pdcp_p->last_submitted_pdcp_rx_sn = sequence_number;
}
/*
* Prepend PDCP indication header which is going to be removed at pdcp_fifo_flush_sdus()
*/
memset(new_sdu_p->data, 0, sizeof (pdcp_data_ind_header_t));
((pdcp_data_ind_header_t *) new_sdu_p->data)->data_size = sdu_buffer_sizeP - payload_offset;
// Here there is no virtualization possible
// set ((pdcp_data_ind_header_t *) new_sdu_p->data)->inst for IP layer here
if (enb_flagP == ENB_FLAG_NO) {
((pdcp_data_ind_header_t *) new_sdu_p->data)->rb_id = rb_id;
#if defined(OAI_EMU)
((pdcp_data_ind_header_t *) new_sdu_p->data)->inst = ue_mod_idP + oai_emulation.info.nb_enb_local - oai_emulation.info.first_ue_local;
#endif
} else {
((pdcp_data_ind_header_t *) new_sdu_p->data)->rb_id = rb_id + (ue_mod_idP * maxDRB);
#if defined(OAI_EMU)
((pdcp_data_ind_header_t *) new_sdu_p->data)->inst = enb_mod_idP - oai_emulation.info.first_enb_local;
#endif
}
memcpy(&new_sdu_p->data[sizeof (pdcp_data_ind_header_t)], \
&sdu_buffer_pP->data[payload_offset], \
sdu_buffer_sizeP - payload_offset);
list_add_tail_eurecom (new_sdu_p, sdu_list_p);
/* Print octets of incoming data in hexadecimal form */
LOG_D(PDCP, "Following content has been received from RLC (%d,%d)(PDCP header has already been removed):\n",
sdu_buffer_sizeP - payload_offset + sizeof(pdcp_data_ind_header_t),
sdu_buffer_sizeP - payload_offset);
//util_print_hex_octets(PDCP, &new_sdu_p->data[sizeof (pdcp_data_ind_header_t)], sdu_buffer_sizeP - payload_offset);
//util_flush_hex_octets(PDCP, &new_sdu_p->data[sizeof (pdcp_data_ind_header_t)], sdu_buffer_sizeP - payload_offset);
/*
* Update PDCP statistics
* XXX Following two actions are identical, is there a merge error?
*/
/*if (enb_flagP == 1) {
Pdcp_stats_rx[module_id][(rb_idP & RAB_OFFSET2) >> RAB_SHIFT2][(rb_idP & RAB_OFFSET) - DTCH]++;
Pdcp_stats_rx_bytes[module_id][(rb_idP & RAB_OFFSET2) >> RAB_SHIFT2][(rb_idP & RAB_OFFSET) - DTCH] += sdu_buffer_sizeP;
} else {
Pdcp_stats_rx[module_id][(rb_idP & RAB_OFFSET2) >> RAB_SHIFT2][(rb_idP & RAB_OFFSET) - DTCH]++;
Pdcp_stats_rx_bytes[module_id][(rb_idP & RAB_OFFSET2) >> RAB_SHIFT2][(rb_idP & RAB_OFFSET) - DTCH] += sdu_buffer_sizeP;
}*/
}
}
#if defined(STOP_ON_IP_TRAFFIC_OVERLOAD)
else {
AssertFatal(0, "[FRAME %5u][%s][PDCP][MOD %u/%u][RB %u] PDCP_DATA_IND SDU DROPPED, OUT OF MEMORY \n",
frameP,
(enb_flagP) ? "eNB" : "UE",
enb_mod_idP,
ue_mod_idP,
rb_id);
}
#endif
free_mem_block(sdu_buffer_pP);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].data_ind);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].data_ind);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_DATA_IND,VCD_FUNCTION_OUT);
return TRUE;
}
//-----------------------------------------------------------------------------
void pdcp_run (
const frame_t frameP,
const eNB_flag_t enb_flagP,
const module_id_t ue_mod_idP,
const module_id_t enb_mod_idP) {
//-----------------------------------------------------------------------------
#if defined(ENABLE_ITTI)
MessageDef *msg_p;
const char *msg_name;
instance_t instance;
int result;
#endif
if (enb_flagP)
start_meas(&eNB_pdcp_stats[enb_mod_idP].pdcp_run);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].pdcp_run);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_RUN, VCD_FUNCTION_IN);
#if defined(ENABLE_ITTI)
do {
// Checks if a message has been sent to PDCP sub-task
itti_poll_msg (enb_flagP ? TASK_PDCP_ENB : TASK_PDCP_UE, &msg_p);
if (msg_p != NULL) {
msg_name = ITTI_MSG_NAME (msg_p);
instance = ITTI_MSG_INSTANCE (msg_p);
switch (ITTI_MSG_ID(msg_p)) {
case RRC_DCCH_DATA_REQ:
LOG_D(PDCP, "Received %s from %s: instance %d, frame %d, enb_flagP %d, rb_id %d, muiP %d, confirmP %d, mode %d\n",
msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance,
RRC_DCCH_DATA_REQ (msg_p).frame, RRC_DCCH_DATA_REQ (msg_p).enb_flag, RRC_DCCH_DATA_REQ (msg_p).rb_id,
RRC_DCCH_DATA_REQ (msg_p).muip, RRC_DCCH_DATA_REQ (msg_p).confirmp, RRC_DCCH_DATA_REQ (msg_p).mode);
result = pdcp_data_req (RRC_DCCH_DATA_REQ (msg_p).eNB_index,
RRC_DCCH_DATA_REQ (msg_p).ue_index,
RRC_DCCH_DATA_REQ (msg_p).frame,
RRC_DCCH_DATA_REQ (msg_p).enb_flag,
SRB_FLAG_YES,
RRC_DCCH_DATA_REQ (msg_p).rb_id,
RRC_DCCH_DATA_REQ (msg_p).muip,
RRC_DCCH_DATA_REQ (msg_p).confirmp,
RRC_DCCH_DATA_REQ (msg_p).sdu_size,
RRC_DCCH_DATA_REQ (msg_p).sdu_p,
RRC_DCCH_DATA_REQ (msg_p).mode);
AssertFatal (result == TRUE, "PDCP data request failed!\n");
// Message buffer has been processed, free it now.
result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), RRC_DCCH_DATA_REQ (msg_p).sdu_p);
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
break;
default:
LOG_E(PDCP, "Received unexpected message %s\n", msg_name);
break;
}
result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
}
} while(msg_p != NULL);
# if 0
{
MessageDef *msg_resp_p;
msg_resp_p = itti_alloc_new_message(TASK_PDCP_ENB, MESSAGE_TEST);
itti_send_msg_to_task(TASK_RRC_ENB, 1, msg_resp_p);
}
{
MessageDef *msg_resp_p;
msg_resp_p = itti_alloc_new_message(TASK_PDCP_ENB, MESSAGE_TEST);
itti_send_msg_to_task(TASK_ENB_APP, 2, msg_resp_p);
}
{
MessageDef *msg_resp_p;
msg_resp_p = itti_alloc_new_message(TASK_PDCP_ENB, MESSAGE_TEST);
itti_send_msg_to_task(TASK_MAC_ENB, 3, msg_resp_p);
}
# endif
#endif
#if defined(USER_MODE) && defined(OAI_EMU)
pdcp_t *pdcp_p = NULL;
int drb_id=1 ;
int ue_id=0;
int read_otg=1;
// add check for other rb_ids later
if (enb_flagP == ENB_FLAG_NO) {
if (pdcp_array_drb_ue[ue_mod_idP][drb_id-1].instanciated_instance != TRUE )
read_otg=0;
} else {
for (ue_id=0; ue_id < NB_UE_INST; ue_id++)
if (pdcp_array_drb_eNB[enb_mod_idP][ue_id][drb_id-1].instanciated_instance != TRUE ){
read_otg =0;
break;
}
}
if (read_otg == 1 )
pdcp_fifo_read_input_sdus_from_otg(frameP, enb_flagP, ue_mod_idP, enb_mod_idP);
#endif
// IP/NAS -> PDCP traffic : TX, read the pkt from the upper layer buffer
#if defined(LINK_PDCP_TO_GTPV1U)
if (enb_flagP == ENB_FLAG_NO)
#endif
{
pdcp_fifo_read_input_sdus(frameP, enb_flagP, ue_mod_idP, enb_mod_idP);
}
// PDCP -> NAS/IP traffic: RX
if (enb_flagP)
start_meas(&eNB_pdcp_stats[enb_mod_idP].pdcp_ip);
else
start_meas(&UE_pdcp_stats[ue_mod_idP].pdcp_ip);
pdcp_fifo_flush_sdus(frameP, enb_flagP, enb_mod_idP, ue_mod_idP);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].pdcp_ip);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].pdcp_ip);
if (enb_flagP)
stop_meas(&eNB_pdcp_stats[enb_mod_idP].pdcp_run);
else
stop_meas(&UE_pdcp_stats[ue_mod_idP].pdcp_run);
vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_PDCP_RUN, VCD_FUNCTION_OUT);
}
boolean_t rrc_pdcp_config_asn1_req (
const module_id_t enb_mod_idP,
const module_id_t ue_mod_idP,
const frame_t frameP,
const eNB_flag_t enb_flagP,
SRB_ToAddModList_t *const srb2add_list_pP,
DRB_ToAddModList_t *const drb2add_list_pP,
DRB_ToReleaseList_t *const drb2release_list_pP,
const uint8_t security_modeP,
uint8_t *const kRRCenc_pP,
uint8_t *const kRRCint_pP,
uint8_t *const kUPenc_pP
#ifdef Rel10
,PMCH_InfoList_r9_t* const pmch_InfoList_r9_pP
#endif
)
{
long int lc_id = 0;
DRB_Identity_t srb_id = 0;
long int mch_id = 0;
rlc_mode_t rlc_type = RLC_MODE_NONE;
DRB_Identity_t drb_id = 0;
DRB_Identity_t *pdrb_id_p = NULL;
uint8_t drb_sn = 12;
uint8_t srb_sn = 5; // fixed sn for SRBs
uint8_t drb_report = 0;
long int cnt = 0;
uint16_t header_compression_profile = 0;
config_action_t action = CONFIG_ACTION_ADD;
SRB_ToAddMod_t *srb_toaddmod_p = NULL;
DRB_ToAddMod_t *drb_toaddmod_p = NULL;
pdcp_t *pdcp_p = NULL;
#ifdef Rel10
int i,j;
MBMS_SessionInfoList_r9_t *mbms_SessionInfoList_r9_p = NULL;
MBMS_SessionInfo_r9_t *MBMS_SessionInfo_p = NULL;
#endif
if (enb_flagP == ENB_FLAG_NO) {
LOG_D(PDCP, "[UE %u] CONFIG REQ ASN1 for eNB %u\n", ue_mod_idP, enb_mod_idP);
} else {
LOG_D(PDCP, "[eNB %u] CONFIG REQ ASN1 for UE %u\n", enb_mod_idP, ue_mod_idP);
}
// srb2add_list does not define pdcp config, we use rlc info to setup the pdcp dcch0 and dcch1 channels
if (srb2add_list_pP != NULL) {
for (cnt=0;cntlist.count;cnt++) {
srb_id = srb2add_list_pP->list.array[cnt]->srb_Identity;
srb_toaddmod_p = srb2add_list_pP->list.array[cnt];
rlc_type = RLC_MODE_AM;
lc_id = srb_id + 2;
if (enb_flagP == ENB_FLAG_NO) {
pdcp_p = &pdcp_array_srb_ue[ue_mod_idP][srb_id-1];
} else {
pdcp_p = &pdcp_array_srb_eNB[enb_mod_idP][ue_mod_idP][srb_id-1];
}
if (pdcp_p->instanciated_instance == TRUE) {
action = CONFIG_ACTION_MODIFY;
} else {
action = CONFIG_ACTION_ADD;
}
if (srb_toaddmod_p->rlc_Config) {
switch (srb_toaddmod_p->rlc_Config->present) {
case SRB_ToAddMod__rlc_Config_PR_NOTHING:
break;
case SRB_ToAddMod__rlc_Config_PR_explicitValue:
switch (srb_toaddmod_p->rlc_Config->choice.explicitValue.present) {
case RLC_Config_PR_NOTHING:
break;
default:
pdcp_config_req_asn1 (pdcp_p,
enb_mod_idP,
ue_mod_idP,
frameP,
enb_flagP, // not really required
SRB_FLAG_YES,
rlc_type,
action,
lc_id,
mch_id,
srb_id,
srb_sn,
0, // drb_report
0, // header compression
security_modeP,
kRRCenc_pP,
kRRCint_pP,
kUPenc_pP);
break;
}
break;
case SRB_ToAddMod__rlc_Config_PR_defaultValue:
// already the default values
break;
default:
DevParam(srb_toaddmod_p->rlc_Config->present, ue_mod_idP, enb_mod_idP);
break;
}
}
}
}
// reset the action
if (drb2add_list_pP != NULL) {
for (cnt=0;cntlist.count;cnt++) {
drb_toaddmod_p = drb2add_list_pP->list.array[cnt];
drb_id = drb_toaddmod_p->drb_Identity;// + drb_id_offset;
lc_id = drb_id + 2;
DevCheck4(drb_id < maxDRB, drb_id, maxDRB, ue_mod_idP, enb_mod_idP);
if (enb_flagP == ENB_FLAG_NO) {
pdcp_p = &pdcp_array_drb_ue[ue_mod_idP][drb_id-1];
} else {
pdcp_p = &pdcp_array_drb_eNB[enb_mod_idP][ue_mod_idP][drb_id-1];
}
if (pdcp_p->instanciated_instance == TRUE)
action = CONFIG_ACTION_MODIFY;
else
action = CONFIG_ACTION_ADD;
if (drb_toaddmod_p->pdcp_Config){
if (drb_toaddmod_p->pdcp_Config->discardTimer) {
// set the value of the timer
}
if (drb_toaddmod_p->pdcp_Config->rlc_AM) {
drb_report = drb_toaddmod_p->pdcp_Config->rlc_AM->statusReportRequired;
drb_sn = PDCP_Config__rlc_UM__pdcp_SN_Size_len12bits; // default SN size
rlc_type = RLC_MODE_AM;
}
if (drb_toaddmod_p->pdcp_Config->rlc_UM){
drb_sn = drb_toaddmod_p->pdcp_Config->rlc_UM->pdcp_SN_Size;
rlc_type =RLC_MODE_UM;
}
switch (drb_toaddmod_p->pdcp_Config->headerCompression.present) {
case PDCP_Config__headerCompression_PR_NOTHING:
case PDCP_Config__headerCompression_PR_notUsed:
header_compression_profile=0x0;
break;
case PDCP_Config__headerCompression_PR_rohc:
// parse the struc and get the rohc profile
if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0001)
header_compression_profile=0x0001;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0002)
header_compression_profile=0x0002;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0003)
header_compression_profile=0x0003;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0004)
header_compression_profile=0x0004;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0006)
header_compression_profile=0x0006;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0101)
header_compression_profile=0x0101;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0102)
header_compression_profile=0x0102;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0103)
header_compression_profile=0x0103;
else if(drb_toaddmod_p->pdcp_Config->headerCompression.choice.rohc.profiles.profile0x0104)
header_compression_profile=0x0104;
else {
header_compression_profile=0x0;
LOG_W(PDCP,"unknown header compresion profile\n");
}
// set the applicable profile
break;
default:
LOG_W(PDCP,"[MOD_id %u/%u][RB %u] unknown drb_toaddmod->PDCP_Config->headerCompression->present \n",
enb_mod_idP, ue_mod_idP, drb_id);
break;
}
pdcp_config_req_asn1 (pdcp_p,
enb_mod_idP,
ue_mod_idP,
frameP,
enb_flagP, // not really required
SRB_FLAG_NO,
rlc_type,
action,
lc_id,
mch_id,
drb_id,
drb_sn,
drb_report,
header_compression_profile,
security_modeP,
kRRCenc_pP,
kRRCint_pP,
kUPenc_pP);
}
}
}
if (drb2release_list_pP != NULL) {
for (cnt=0;cntlist.count;cnt++) {
pdrb_id_p = drb2release_list_pP->list.array[cnt];
drb_id = *pdrb_id_p;
lc_id = drb_id + 2;
if (enb_flagP == ENB_FLAG_NO) {
pdcp_p = &pdcp_array_drb_ue[ue_mod_idP][drb_id-1];
} else {
pdcp_p = &pdcp_array_drb_eNB[enb_mod_idP][ue_mod_idP][drb_id-1];
}
action = CONFIG_ACTION_REMOVE;
pdcp_config_req_asn1 (pdcp_p,
enb_mod_idP,
ue_mod_idP,
frameP,
enb_flagP, // not really required
SRB_FLAG_NO,
rlc_type,
action,
lc_id,
mch_id,
drb_id,
0,
0,
0,
security_modeP,
kRRCenc_pP,
kRRCint_pP,
kUPenc_pP);
}
}
#ifdef Rel10
if (pmch_InfoList_r9_pP != NULL) {
for (i=0;ilist.count;i++) {
mbms_SessionInfoList_r9_p = &(pmch_InfoList_r9_pP->list.array[i]->mbms_SessionInfoList_r9);
for (j=0;jlist.count;j++) {
MBMS_SessionInfo_p = mbms_SessionInfoList_r9_p->list.array[j];
lc_id = MBMS_SessionInfo_p->sessionId_r9->buf[0];
mch_id = MBMS_SessionInfo_p->tmgi_r9.serviceId_r9.buf[2]; //serviceId is 3-octet string
// can set the mch_id = i
if (enb_flagP) {
drb_id = (mch_id * maxSessionPerPMCH ) + lc_id ;//+ (maxDRB + 3)*MAX_MOBILES_PER_ENB; // 1
if (pdcp_mbms_array_eNB[enb_mod_idP][mch_id][lc_id].instanciated_instance == TRUE) {
action = CONFIG_ACTION_MBMS_MODIFY;
}else {
action = CONFIG_ACTION_MBMS_ADD;
}
} else {
drb_id = (mch_id * maxSessionPerPMCH ) + lc_id; // + (maxDRB + 3); // 15
if (pdcp_mbms_array_ue[ue_mod_idP][mch_id][lc_id].instanciated_instance == TRUE) {
action = CONFIG_ACTION_MBMS_MODIFY;
} else {
action = CONFIG_ACTION_MBMS_ADD;
}
}
pdcp_config_req_asn1 (
NULL, // unused for MBMS
enb_mod_idP,
ue_mod_idP,
frameP,
enb_flagP,
SRB_FLAG_NO,
RLC_MODE_NONE,
action,
lc_id,
mch_id,
drb_id,
0, // unused for MBMS
0, // unused for MBMS
0, // unused for MBMS
0, // unused for MBMS
NULL, // unused for MBMS
NULL, // unused for MBMS
NULL); // unused for MBMS
}
}
}
#endif
return 1;
}
boolean_t pdcp_config_req_asn1 (pdcp_t *pdcp_pP,
module_id_t enb_mod_idP,
module_id_t ue_mod_idP,
frame_t frameP,
eNB_flag_t enb_flagP,
srb_flag_t srb_flagP,
rlc_mode_t rlc_modeP,
config_action_t actionP,
uint16_t lc_idP,
uint16_t mch_idP,
rb_id_t rb_idP,
uint8_t rb_snP,
uint8_t rb_reportP,
uint16_t header_compression_profileP,
uint8_t security_modeP,
uint8_t *kRRCenc_pP,
uint8_t *kRRCint_pP,
uint8_t *kUPenc_pP)
{
switch (actionP) {
case CONFIG_ACTION_ADD:
DevAssert(pdcp_pP != NULL);
pdcp_pP->instanciated_instance = TRUE;
pdcp_pP->is_ue = (enb_flagP == ENB_FLAG_NO) ? TRUE : FALSE;
pdcp_pP->is_srb = (srb_flagP == SRB_FLAG_YES) ? TRUE : FALSE;
pdcp_pP->lcid = lc_idP;
pdcp_pP->rb_id = rb_idP;
pdcp_pP->header_compression_profile = header_compression_profileP;
pdcp_pP->status_report = rb_reportP;
if (rb_snP == PDCP_Config__rlc_UM__pdcp_SN_Size_len12bits) {
pdcp_pP->seq_num_size = PDCP_SN_12BIT;
} else if (rb_snP == PDCP_Config__rlc_UM__pdcp_SN_Size_len7bits) {
pdcp_pP->seq_num_size = PDCP_SN_7BIT;
} else {
pdcp_pP->seq_num_size = PDCP_SN_5BIT;
}
pdcp_pP->rlc_mode = rlc_modeP;
pdcp_pP->next_pdcp_tx_sn = 0;
pdcp_pP->next_pdcp_rx_sn = 0;
pdcp_pP->next_pdcp_rx_sn_before_integrity = 0;
pdcp_pP->tx_hfn = 0;
pdcp_pP->rx_hfn = 0;
pdcp_pP->last_submitted_pdcp_rx_sn = 4095;
pdcp_pP->first_missing_pdu = -1;
pdcp_pP->rx_hfn_offset = 0;
if (enb_flagP == ENB_FLAG_NO) {
LOG_I(PDCP, "[UE %d] Config request : Action ADD for eNB %d: Frame %d LCID %d (rb id %d) "
"configured with SN size %d bits and RLC %s\n",
ue_mod_idP, enb_mod_idP, frameP, lc_idP, rb_idP, pdcp_pP->seq_num_size,
(rlc_modeP == RLC_MODE_AM ) ? "AM" : (rlc_modeP == RLC_MODE_TM) ? "TM" : "UM");
} else {
LOG_D(PDCP, "[eNB %d] Config request : Action ADD for UE %d: Frame %d LCID %d (rb id %d) "
"configured with SN size %d bits and RLC %s\n",
enb_mod_idP, ue_mod_idP, frameP, lc_idP, rb_idP, pdcp_pP->seq_num_size,
(rlc_modeP == RLC_MODE_AM) ? "AM" : (rlc_modeP == RLC_MODE_TM) ? "TM" : "UM");
}
/* Setup security */
if (security_modeP != 0xff) {
pdcp_config_set_security(pdcp_pP, enb_mod_idP, ue_mod_idP, frameP, enb_flagP, rb_idP, lc_idP, security_modeP, kRRCenc_pP, kRRCint_pP, kUPenc_pP);
}
LOG_D(PDCP, "[FRAME %5u][%s][PDCP][MOD %u/%u][RB %u]\n", frameP, (enb_flagP == ENB_FLAG_NO) ? "UE" : "eNB", enb_mod_idP, ue_mod_idP, rb_idP);
break;
case CONFIG_ACTION_MODIFY:
DevAssert(pdcp_pP != NULL);
pdcp_pP->header_compression_profile=header_compression_profileP;
pdcp_pP->status_report = rb_reportP;
pdcp_pP->rlc_mode = rlc_modeP;
/* Setup security */
if (security_modeP != 0xff) {
pdcp_config_set_security(pdcp_pP, enb_mod_idP, ue_mod_idP, frameP, enb_flagP, rb_idP, lc_idP, security_modeP, kRRCenc_pP, kRRCint_pP, kUPenc_pP);
}
if (rb_snP == PDCP_Config__rlc_UM__pdcp_SN_Size_len7bits) {
pdcp_pP->seq_num_size = 7;
} else if (rb_snP == PDCP_Config__rlc_UM__pdcp_SN_Size_len12bits) {
pdcp_pP->seq_num_size = 12;
} else {
pdcp_pP->seq_num_size=5;
}
if (enb_flagP == ENB_FLAG_NO) {
LOG_I(PDCP,"[UE %d] Config request : Action MODIFY for eNB %d: Frame %d LCID %d "
"RB id %d configured with SN size %d and RLC %s \n",
ue_mod_idP, enb_mod_idP, frameP, lc_idP, rb_idP, rb_snP,
(rlc_modeP == RLC_MODE_AM) ? "AM" : (rlc_modeP == RLC_MODE_TM) ? "TM" : "UM");
} else {
LOG_D(PDCP,"[eNB %d] Config request : Action MODIFY for UE %d: Frame %d LCID %d "
"RB id %d configured with SN size %d and RLC %s \n",
enb_mod_idP, ue_mod_idP, frameP, lc_idP, rb_idP, rb_snP,
(rlc_modeP == RLC_MODE_AM) ? "AM" : (rlc_modeP == RLC_MODE_TM) ? "TM" : "UM");
}
break;
case CONFIG_ACTION_REMOVE:
DevAssert(pdcp_pP != NULL);
pdcp_pP->instanciated_instance = FALSE;
pdcp_pP->lcid = 0;
pdcp_pP->header_compression_profile = 0x0;
pdcp_pP->cipheringAlgorithm = 0xff;
pdcp_pP->integrityProtAlgorithm = 0xff;
pdcp_pP->status_report = 0;
pdcp_pP->rlc_mode = RLC_MODE_NONE;
pdcp_pP->next_pdcp_tx_sn = 0;
pdcp_pP->next_pdcp_rx_sn = 0;
pdcp_pP->tx_hfn = 0;
pdcp_pP->rx_hfn = 0;
pdcp_pP->last_submitted_pdcp_rx_sn = 4095;
pdcp_pP->seq_num_size = 0;
pdcp_pP->first_missing_pdu = -1;
pdcp_pP->security_activated = 0;
if (enb_flagP == ENB_FLAG_NO) {
LOG_I(PDCP, "[UE %d] Config request : CONFIG_ACTION_REMOVE for eNB %d: Frame %d LCID %d RBID %d configured\n",
ue_mod_idP, enb_mod_idP, frameP, lc_idP, rb_idP);
} else {
LOG_D(PDCP, "[eNB %d] Config request : CONFIG_ACTION_REMOVE for UE %d: Frame %d LCID %d RBID %d configured\n",
enb_mod_idP, ue_mod_idP, frameP, lc_idP, rb_idP);
}
/* Security keys */
if (pdcp_pP->kUPenc != NULL) {
free(pdcp_pP->kUPenc);
}
if (pdcp_pP->kRRCint != NULL) {
free(pdcp_pP->kRRCint);
}
if (pdcp_pP->kRRCenc != NULL) {
free(pdcp_pP->kRRCenc);
}
break;
#if defined(Rel10)
case CONFIG_ACTION_MBMS_ADD:
case CONFIG_ACTION_MBMS_MODIFY:
if (enb_flagP == ENB_FLAG_NO) {
LOG_I(PDCP,"[UE %d] Config request for eNB %d: %s: Frame %d service_id/mch index %d, session_id/lcid %d, rbid %d configured\n",
ue_mod_idP, enb_mod_idP, actionP == CONFIG_ACTION_MBMS_ADD ? "CONFIG_ACTION_MBMS_ADD" : "CONFIG_ACTION_MBMS_MODIFY", frameP, mch_idP, lc_idP, rb_idP);
} else {
LOG_D(PDCP,"[eNB %d] Config request for UE %d: %s: Frame %d service_id/mch index %d, session_id/lcid %d, rbid %d configured\n",
enb_mod_idP, ue_mod_idP, actionP == CONFIG_ACTION_MBMS_ADD ? "CONFIG_ACTION_MBMS_ADD" : "CONFIG_ACTION_MBMS_MODIFY", frameP, mch_idP, lc_idP, rb_idP);
}
if (enb_flagP == 1) {
pdcp_mbms_array_eNB[enb_mod_idP][mch_idP][lc_idP].instanciated_instance = TRUE ;
pdcp_mbms_array_eNB[enb_mod_idP][mch_idP][lc_idP].rb_id = rb_idP;
} else {
pdcp_mbms_array_ue[ue_mod_idP][mch_idP][lc_idP].instanciated_instance = TRUE ;
pdcp_mbms_array_ue[ue_mod_idP][mch_idP][lc_idP].rb_id = rb_idP;
}
break;
#endif
case CONFIG_ACTION_SET_SECURITY_MODE:
pdcp_config_set_security(pdcp_pP, enb_mod_idP, ue_mod_idP, frameP, enb_flagP, rb_idP, lc_idP, security_modeP, kRRCenc_pP, kRRCint_pP, kUPenc_pP);
break;
default:
DevParam(actionP, enb_mod_idP, ue_mod_idP);
break;
}
return 0;
}
void pdcp_config_set_security(pdcp_t *pdcp_pP,
module_id_t enb_mod_idP,
module_id_t ue_mod_idP,
frame_t frameP,
eNB_flag_t enb_flagP,
rb_id_t rb_idP,
uint16_t lc_idP,
uint8_t security_modeP,
uint8_t *kRRCenc,
uint8_t *kRRCint,
uint8_t *kUPenc)
{
DevAssert(pdcp_pP != NULL);
if ((security_modeP >= 0) && (security_modeP <= 0x77)) {
pdcp_pP->cipheringAlgorithm = security_modeP & 0x0f;
pdcp_pP->integrityProtAlgorithm = (security_modeP>>4) & 0xf;
if (enb_flagP == ENB_FLAG_NO) {
LOG_D(PDCP,"[UE %d][RB %02d] Set security mode : CONFIG_ACTION_SET_SECURITY_MODE: "
"Frame %d cipheringAlgorithm %d integrityProtAlgorithm %d\n",
ue_mod_idP,
rb_idP,
frameP,
pdcp_pP->cipheringAlgorithm,
pdcp_pP->integrityProtAlgorithm);
} else {
LOG_D(PDCP,"[eNB %d][UE %d][RB %02d] Set security mode : CONFIG_ACTION_SET_SECURITY_MODE: "
"Frame %d cipheringAlgorithm %d integrityProtAlgorithm %d\n",
enb_mod_idP,
ue_mod_idP,
rb_idP,
frameP,
pdcp_pP->cipheringAlgorithm,
pdcp_pP->integrityProtAlgorithm);
}
pdcp_pP->kRRCenc = kRRCenc;
pdcp_pP->kRRCint = kRRCint;
pdcp_pP->kUPenc = kUPenc;
/* Activate security */
pdcp_pP->security_activated = 1;
} else {
LOG_E(PDCP,"[%s %d] bad security mode %d", security_modeP);
}
}
void rrc_pdcp_config_req (module_id_t enb_mod_idP, module_id_t ue_mod_idP, frame_t frameP, eNB_flag_t enb_flagP, srb_flag_t srb_flagP, uint32_t actionP, rb_id_t rb_idP, uint8_t security_modeP)
{
pdcp_t *pdcp_p = NULL;
if (enb_flagP == ENB_FLAG_NO) {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_ue[ue_mod_idP][rb_idP-1];
} else {
pdcp_p = &pdcp_array_drb_ue[ue_mod_idP][rb_idP-1];
}
} else {
if (srb_flagP) {
pdcp_p = &pdcp_array_srb_eNB[enb_mod_idP][ue_mod_idP][rb_idP-1];
} else {
pdcp_p = &pdcp_array_drb_eNB[enb_mod_idP][ue_mod_idP][rb_idP-1];
}
}
/*
* Initialize sequence number state variables of relevant PDCP entity
*/
switch (actionP) {
case CONFIG_ACTION_ADD:
pdcp_p->instanciated_instance = TRUE;
pdcp_p->is_srb = srb_flagP;
pdcp_p->rb_id = rb_idP;
if (enb_flagP == ENB_FLAG_NO) {
pdcp_p->is_ue = TRUE;
} else {
pdcp_p->is_ue = FALSE;
}
pdcp_p->next_pdcp_tx_sn = 0;
pdcp_p->next_pdcp_rx_sn = 0;
pdcp_p->tx_hfn = 0;
pdcp_p->rx_hfn = 0;
/* SN of the last PDCP SDU delivered to upper layers */
pdcp_p->last_submitted_pdcp_rx_sn = 4095;
if (rb_idP < DTCH) { // SRB
pdcp_p->seq_num_size = 5;
} else { // DRB
pdcp_p->seq_num_size = 12;
}
pdcp_p->first_missing_pdu = -1;
LOG_D(PDCP,"[%s %d] Config request : Action ADD: Frame %d radio bearer id %d configured\n",
(enb_flagP) ? "eNB" : "UE", (enb_flagP) ? enb_mod_idP : ue_mod_idP, frameP, rb_idP);
break;
case CONFIG_ACTION_MODIFY:
break;
case CONFIG_ACTION_REMOVE:
pdcp_p->instanciated_instance = FALSE;
pdcp_p->next_pdcp_tx_sn = 0;
pdcp_p->next_pdcp_rx_sn = 0;
pdcp_p->tx_hfn = 0;
pdcp_p->rx_hfn = 0;
pdcp_p->last_submitted_pdcp_rx_sn = 4095;
pdcp_p->seq_num_size = 0;
pdcp_p->first_missing_pdu = -1;
pdcp_p->security_activated = 0;
LOG_D(PDCP,"[%s %d] Config request : CONFIG_ACTION_REMOVE: Frame %d radio bearer id %d configured\n",
(enb_flagP) ? "eNB" : "UE", (enb_flagP) ? enb_mod_idP : ue_mod_idP, frameP, rb_idP);
break;
case CONFIG_ACTION_SET_SECURITY_MODE:
if ((security_modeP >= 0) && (security_modeP <= 0x77)) {
pdcp_p->cipheringAlgorithm= security_modeP & 0x0f;
pdcp_p->integrityProtAlgorithm = (security_modeP>>4) & 0xf;
LOG_D(PDCP,"[%s %d]Set security mode : CONFIG_ACTION_SET_SECURITY_MODE: Frame %d cipheringAlgorithm %d integrityProtAlgorithm %d\n",
(enb_flagP) ? "eNB" : "UE", (enb_flagP) ? enb_mod_idP : ue_mod_idP, frameP,
pdcp_p->cipheringAlgorithm,
pdcp_p->integrityProtAlgorithm );
} else {
LOG_D(PDCP,"[%s %d] bad security mode %d", security_modeP);
}
break;
default:
DevParam(actionP, ue_mod_idP, enb_mod_idP);
break;
}
}
// TODO PDCP module initialization code might be removed
int pdcp_module_init (void) {
//-----------------------------------------------------------------------------
#ifdef NAS_FIFO
int ret;
ret=rtf_create(PDCP2NAS_FIFO,32768);
if (ret < 0) {
LOG_E(PDCP, "Cannot create PDCP2NAS fifo %d (ERROR %d)\n", PDCP2NAS_FIFO, ret);
return -1;
} else {
LOG_D(PDCP, "Created PDCP2NAS fifo %d\n", PDCP2NAS_FIFO);
rtf_reset(PDCP2NAS_FIFO);
}
ret=rtf_create(NAS2PDCP_FIFO,32768);
if (ret < 0) {
LOG_E(PDCP, "Cannot create NAS2PDCP fifo %d (ERROR %d)\n", NAS2PDCP_FIFO, ret);
return -1;
} else {
LOG_D(PDCP, "Created NAS2PDCP fifo %d\n", NAS2PDCP_FIFO);
rtf_reset(NAS2PDCP_FIFO);
}
pdcp_2_nas_irq = 0;
pdcp_input_sdu_remaining_size_to_read=0;
pdcp_input_sdu_size_read=0;
#endif
return 0;
}
//-----------------------------------------------------------------------------
void pdcp_module_cleanup (void)
//-----------------------------------------------------------------------------
{
#ifdef NAS_FIFO
rtf_destroy(NAS2PDCP_FIFO);
rtf_destroy(PDCP2NAS_FIFO);
#endif
}
//-----------------------------------------------------------------------------
void pdcp_layer_init(void)
{
//-----------------------------------------------------------------------------
module_id_t instance;
module_id_t instance2;
rb_id_t rb_id;
#if defined(Rel10)
mbms_session_id_t session_id;
mbms_service_id_t service_id;
#endif
/*
* Initialize SDU list
*/
list_init(&pdcp_sdu_list, NULL);
for (instance = 0; instance < NUMBER_OF_UE_MAX; instance++) {
for (rb_id = 0; rb_id < maxDRB; rb_id++) {
memset(&pdcp_array_drb_ue[instance][rb_id], 0, sizeof(pdcp_t));
}
for (rb_id = 0; rb_id < 2; rb_id++) {
memset(&pdcp_array_srb_ue[instance][rb_id], 0, sizeof(pdcp_t));
}
#if defined(Rel10)
for (service_id = 0; service_id < maxServiceCount; service_id++) {
for (session_id = 0; session_id < maxSessionPerPMCH; session_id++) {
memset(&pdcp_mbms_array_ue[instance][service_id][session_id], 0, sizeof(pdcp_mbms_t));
}
}
#endif
}
for (instance = 0; instance < NUMBER_OF_eNB_MAX; instance++) {
for (instance2 = 0; instance2 < NUMBER_OF_UE_MAX; instance2++) {
for (rb_id = 0; rb_id < maxDRB; rb_id++) {
memset(&pdcp_array_drb_eNB[instance][instance2][rb_id], 0, sizeof(pdcp_t));
}
for (rb_id = 0; rb_id < 2; rb_id++) {
memset(&pdcp_array_srb_eNB[instance][instance2][rb_id], 0, sizeof(pdcp_t));
}
}
#if defined(Rel10)
for (service_id = 0; service_id < maxServiceCount; service_id++) {
for (session_id = 0; session_id < maxSessionPerPMCH; session_id++) {
memset(&pdcp_mbms_array_eNB[instance][service_id][session_id], 0, sizeof(pdcp_mbms_t));
}
}
#endif
}
LOG_I(PDCP, "PDCP layer has been initialized\n");
pdcp_output_sdu_bytes_to_write=0;
pdcp_output_header_bytes_to_write=0;
pdcp_input_sdu_remaining_size_to_read=0;
memset(Pdcp_stats_tx, 0, sizeof(Pdcp_stats_tx));
memset(Pdcp_stats_tx_bytes, 0, sizeof(Pdcp_stats_tx_bytes));
memset(Pdcp_stats_tx_bytes_last, 0, sizeof(Pdcp_stats_tx_bytes_last));
memset(Pdcp_stats_tx_rate, 0, sizeof(Pdcp_stats_tx_rate));
memset(Pdcp_stats_rx, 0, sizeof(Pdcp_stats_rx));
memset(Pdcp_stats_rx_bytes, 0, sizeof(Pdcp_stats_rx_bytes));
memset(Pdcp_stats_rx_bytes_last, 0, sizeof(Pdcp_stats_rx_bytes_last));
memset(Pdcp_stats_rx_rate, 0, sizeof(Pdcp_stats_rx_rate));
}
//-----------------------------------------------------------------------------
void pdcp_layer_cleanup (void)
//-----------------------------------------------------------------------------
{
list_free (&pdcp_sdu_list);
}
#ifdef NAS_FIFO
EXPORT_SYMBOL(pdcp_2_nas_irq);
#endif //NAS_FIFO