Commit f6383343 authored by Raphael Defosseux's avatar Raphael Defosseux

Merge remote-tracking branch 'origin/new_rlc_2020' into develop_integration_2020_w17

parents ddf94710 591b45d2
......@@ -288,7 +288,7 @@ MCEs = (
mnc_length = 2;
}
service_id=0;
lcid=8; #this must be properly defined lcid:8+service:0 -> rab_id:8
lcid=5; #this must be properly defined lcid:8+service:0 -> rab_id:5
}
);
}
......
......@@ -76,6 +76,11 @@ nullPointer:common/utils/T/local_tracer.c:243
// first iteration of the loop
nullPointer:common/utils/T/tracer/multi.c:264
nullPointer:common/utils/T/tracer/multi.c:265
//-----------------------------------------------------------------------------
// this file is used for testing the RLC V2 implementation, this error is
// not a problem, the programmer has to know what she does when writing
// the tests
arrayIndexOutOfBounds:openair2/LAYER2/rlc_v2/tests/test.c:401
//
//*****************************************************************************
//
......
......@@ -727,7 +727,6 @@ Message("CPU_Affinity flag is ${CPU_AFFINITY}")
##############################################################
add_boolean_option(NO_RRM True "DO WE HAVE A RADIO RESSOURCE MANAGER: NO")
add_boolean_option(RRC_DEFAULT_RAB_IS_AM False "set the RLC mode to AM for the default bearer")
add_boolean_option(OAI_NW_DRIVER_TYPE_ETHERNET False "????")
add_boolean_option(DEADLINE_SCHEDULER True "Use the Linux scheduler SCHED_DEADLINE: kernel >= 3.14")
......@@ -828,7 +827,7 @@ add_boolean_option(TRACE_RLC_UM_TX_STATUS False "TRACE for RLC UM, TO BE CHANGE
##########################
# RRC LAYER OPTIONS
##########################
add_boolean_option(RRC_DEFAULT_RAB_IS_AM False "Otherwise it is UM, configure params are actually set in rrc_eNB.c:rrc_eNB_generate_defaultRRCConnectionReconfiguration(...)")
add_boolean_option(RRC_DEFAULT_RAB_IS_AM True "set the RLC mode to AM for the default bearer, otherwise it is UM.")
##########################
......@@ -1641,7 +1640,7 @@ set(NR_RRC_DIR ${OPENAIR2_DIR}/RRC/NR)
set(NR_UE_RRC_DIR ${OPENAIR2_DIR}/RRC/NR_UE)
set(PDCP_DIR ${OPENAIR2_DIR}/LAYER2/PDCP_v10.1.0)
set(LTE_RLC_SRC
set(RLC_V1
${RLC_AM_DIR}/rlc_am.c
${RLC_AM_DIR}/rlc_am_init.c
${RLC_AM_DIR}/rlc_am_timer_poll_retransmit.c
......@@ -1671,6 +1670,17 @@ set(LTE_RLC_SRC
${RLC_DIR}/rlc_mpls.c
)
set(RLC_V2
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_oai_api.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/asn1_utils.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_ue_manager.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_entity.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_entity_am.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_entity_um.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_pdu.c
${OPENAIR2_DIR}/LAYER2/rlc_v2/rlc_sdu.c
)
set(NR_RLC_SRC
${OPENAIR2_DIR}/LAYER2/nr_rlc/asn1_utils.c
${OPENAIR2_DIR}/LAYER2/nr_rlc/nr_rlc_entity.c
......@@ -1692,6 +1702,7 @@ set(L2_SRC
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
${RLC_V2}
# ${RRC_DIR}/rrc_UE.c
${RRC_DIR}/rrc_eNB.c
${RRC_DIR}/rrc_eNB_endc.c
......@@ -1705,7 +1716,7 @@ set(L2_SRC
)
set(L2_LTE_SRC
${LTE_RLC_SRC}
${RLC_V1}
)
set(L2_NR_SRC
......@@ -1728,6 +1739,7 @@ set(L2_SRC_UE
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
${RLC_V2}
${RRC_DIR}/rrc_UE.c
${RRC_DIR}/rrc_common.c
${RRC_DIR}/L2_interface_common.c
......@@ -1742,7 +1754,7 @@ set(LTE_NR_L2_SRC_UE
${PDCP_DIR}/pdcp_util.c
${PDCP_DIR}/pdcp_security.c
${PDCP_DIR}/pdcp_netlink.c
${LTE_RLC_SRC}
${RLC_V1}
)
set(NR_L2_SRC_UE
......
#!/bin/bash
# use UP and DOWN arrow keys to scroll the view displayed by timeplot
while read -n 1 key
do
case "$key" in
'B' )
kill -SIGUSR1 `ps aux|grep timeplot|grep -v grep|grep -v sh|tr -s ' ' :|cut -f 2 -d :`
;;
'A' )
kill -SIGUSR2 `ps aux|grep timeplot|grep -v grep|grep -v sh|tr -s ' ' :|cut -f 2 -d :`
;;
esac
done
......@@ -13,7 +13,8 @@ void usage(void)
"options:\n"
" -d <database file> this option is mandatory\n"
" -ip <host> connect to given IP address (default %s)\n"
" -p <port> connect to given port (default %d)\n",
" -p <port> connect to given port (default %d)\n"
" -e <event> event to trace (default VCD_FUNCTION_ENB_DLSCH_ULSCH_SCHEDULER)\n",
DEFAULT_REMOTE_IP,
DEFAULT_REMOTE_PORT
);
......@@ -47,6 +48,7 @@ int main(int n, char **v)
int ev_fun;
int start_valid = 0;
struct timespec start_time, stop_time, delta_time;
char *name = "VCD_FUNCTION_ENB_DLSCH_ULSCH_SCHEDULER";
for (i = 1; i < n; i++) {
if (!strcmp(v[i], "-h") || !strcmp(v[i], "--help")) usage();
......@@ -55,6 +57,7 @@ int main(int n, char **v)
if (!strcmp(v[i], "-ip")) { if (i > n-2) usage(); ip = v[++i]; continue; }
if (!strcmp(v[i], "-p"))
{ if (i > n-2) usage(); port = atoi(v[++i]); continue; }
if (!strcmp(v[i], "-e")) { if (i > n-2) usage(); name = v[++i]; continue; }
usage();
}
......@@ -71,10 +74,9 @@ int main(int n, char **v)
is_on = calloc(number_of_events, sizeof(int));
if (is_on == NULL) abort();
on_off(database, "VCD_FUNCTION_ENB_DLSCH_ULSCH_SCHEDULER", is_on, 1);
on_off(database, name, is_on, 1);
ev_fun = event_id_from_name(database,
"VCD_FUNCTION_ENB_DLSCH_ULSCH_SCHEDULER");
ev_fun = event_id_from_name(database, name);
socket = connect_to(ip, port);
......
......@@ -28,6 +28,7 @@ int bins[50];
#define N 1000
int data[N];
long start = 100;
void plot(void)
{
......@@ -45,7 +46,8 @@ void plot(void)
if (data[i] < vmin) vmin = data[i];
if (data[i] > vmax) vmax = data[i];
vavg += data[i];
int ms2 = data[i]/binsize_ns;
int ms2 = (data[i] - start * 1000)/binsize_ns;
if (ms2 < 0) ms2 = 0;
if (ms2 > 49) ms2 = 49;
bins[ms2]++;
if (bins[ms2] > max) max = bins[ms2];
......@@ -55,7 +57,7 @@ void plot(void)
GOTO(1,1);
for (i = 0; i < 50; i++) {
double binend = (i+1) * binsize_ns / 1000.;
double binend = (i+1) * binsize_ns / 1000. + start;
int k;
int width = bins[i] * 70 / max;
/* force at least width of 1 if some point is there */
......@@ -68,11 +70,23 @@ void plot(void)
printf("min %d ns max %d ns avg %ld ns\n", vmin, vmax, vavg);
}
void up(int x)
{
start += 5;
}
void down(int x)
{
start -= 5;
}
int main(void)
{
int i;
int pos = 0;
signal(SIGINT, sig);
signal(SIGUSR1, up);
signal(SIGUSR2, down);
RESET();
HIDE_CURSOR();
while (!feof(stdin)) {
......
......@@ -76,3 +76,6 @@ MESSAGE_DEF(NAS_DOWNLINK_DATA_IND, MESSAGE_PRIORITY_MED, NasDlDataInd
// eNB: realtime -> RRC messages
MESSAGE_DEF(RRC_SUBFRAME_PROCESS, MESSAGE_PRIORITY_MED, RrcSubframeProcess, rrc_subframe_process)
// eNB: RLC -> RRC messages
MESSAGE_DEF(RLC_SDU_INDICATION, MESSAGE_PRIORITY_MED, RlcSduIndication, rlc_sdu_indication)
......@@ -87,6 +87,8 @@
#define RRC_SUBFRAME_PROCESS(mSGpTR) (mSGpTR)->ittiMsg.rrc_subframe_process
#define RLC_SDU_INDICATION(mSGpTR) (mSGpTR)->ittiMsg.rlc_sdu_indication
//-------------------------------------------------------------------------------------------//
typedef struct RrcStateInd_s {
Rrc_State_t state;
......@@ -429,4 +431,12 @@ typedef struct rrc_subframe_process_s {
int CC_id;
} RrcSubframeProcess;
// eNB: RLC -> RRC messages
typedef struct rlc_sdu_indication_s {
int rnti;
int is_successful;
int srb_id;
int message_id;
} RlcSduIndication;
#endif /* RRC_MESSAGES_TYPES_H_ */
......@@ -714,7 +714,7 @@ schedule_MBMS_NFAPI(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
mbms_rab_id = cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9;
rlc_status =
mac_rlc_status_ind(module_idP, 0/*0xfffd*/, frameP, subframeP,
mac_rlc_status_ind(module_idP, 0xfffd, frameP, subframeP,
module_idP, ENB_FLAG_YES, MBMS_FLAG_YES,
cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
//MTCH,
......@@ -771,7 +771,7 @@ schedule_MBMS_NFAPI(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
TBS - header_len_mcch - header_len_msi -
sdu_length_total - header_len_mtch, header_len_mtch, rlc_status.bytes_in_buffer);
sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES,cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0xfffd, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES,cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
TBS - header_len_mcch - header_len_msi - sdu_length_total - header_len_mtch,
(char *)
&mch_buffer[sdu_length_total]
......@@ -1553,7 +1553,7 @@ schedule_MBMS(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
mbms_rab_id = cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9;
rlc_status =
mac_rlc_status_ind(module_idP, 0/*0xfffd*/, frameP, subframeP,
mac_rlc_status_ind(module_idP, 0xfffd, frameP, subframeP,
module_idP, ENB_FLAG_YES, MBMS_FLAG_YES,
cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
//MTCH,
......@@ -1576,7 +1576,7 @@ schedule_MBMS(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
TBS - header_len_mcch - header_len_msi -
sdu_length_total - header_len_mtch, header_len_mtch, rlc_status.bytes_in_buffer);
sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES,cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0xfffd, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES,cc->mbms_SessionList[0]->list.array[0]->logicalChannelIdentity_r9,
TBS - header_len_mcch - header_len_msi - sdu_length_total - header_len_mtch,
(char *)
&mch_buffer[sdu_length_total]
......
......@@ -1972,6 +1972,10 @@ ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
subframe, ENB_FLAG_NO,
lcid);
lcid_buffer_occupancy_new = lcid_buffer_occupancy_old;
#if 0
/* TODO: those assert crash the L2 simulator with the new RLC.
* Are they necessary?
*/
AssertFatal(lcid_buffer_occupancy_new ==
UE_mac_inst[module_idP].
scheduling_info.LCID_buffer_remain[lcid],
......@@ -1990,6 +1994,7 @@ ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
scheduling_info.BSR_bytes[UE_mac_inst[module_idP].
scheduling_info.LCGID
[lcid]]);
#endif
//Multiplex all available DCCH RLC PDUs considering to multiplex the last PDU each time for maximize the data
//Adjust at the end of the loop
......@@ -2085,18 +2090,10 @@ ue_get_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
}
//Update Buffer remain and BSR bytes after transmission
AssertFatal(lcid_buffer_occupancy_new <=
lcid_buffer_occupancy_old,
"MAC UE Tx error : Buffer Occupancy After Tx=%d greater than before=%d BO! for LCID=%d RLC PDU nb=%d Frame %d Subrame %d\n",
lcid_buffer_occupancy_new,
lcid_buffer_occupancy_old, lcid,
lcid_rlc_pdu_count, frameP, subframe);
UE_mac_inst[module_idP].scheduling_info.
LCID_buffer_remain[lcid] = lcid_buffer_occupancy_new;
UE_mac_inst[module_idP].
scheduling_info.BSR_bytes[UE_mac_inst[module_idP].
scheduling_info.LCGID[lcid]] +=
(lcid_buffer_occupancy_new - lcid_buffer_occupancy_old);
UE_mac_inst[module_idP].scheduling_info.LCID_buffer_remain[lcid] = lcid_buffer_occupancy_new;
UE_mac_inst[module_idP].scheduling_info.BSR_bytes[UE_mac_inst[module_idP].scheduling_info.LCGID[lcid]] += (lcid_buffer_occupancy_new - lcid_buffer_occupancy_old);
if (UE_mac_inst[module_idP].scheduling_info.BSR_bytes[UE_mac_inst[module_idP].scheduling_info.LCGID[lcid]] < 0)
UE_mac_inst[module_idP].scheduling_info.BSR_bytes[UE_mac_inst[module_idP].scheduling_info.LCGID[lcid]] = 0;
//Update the number of LCGID with data as BSR shall reflect status after BSR transmission
if ((num_lcg_id_with_data > 1)
......
......@@ -107,7 +107,6 @@ tbs_size_t mac_rlc_data_req(
int ret;
nr_rlc_ue_t *ue;
nr_rlc_entity_t *rb;
int is_enb;
int maxsize;
nr_rlc_manager_lock(nr_rlc_ue_manager);
......@@ -121,8 +120,6 @@ tbs_size_t mac_rlc_data_req(
if (rb != NULL) {
rb->set_time(rb, nr_rlc_current_time);
/* UE does not seem to use saved_status_ind_tb_size */
is_enb = nr_rlc_manager_get_enb_flag(nr_rlc_ue_manager);
maxsize = tb_sizeP;
ret = rb->generate_pdu(rb, buffer_pP, maxsize);
} else {
......
......@@ -28,10 +28,6 @@ typedef void nr_rlc_ue_manager_t;
typedef struct nr_rlc_ue_t {
int rnti;
/* due to openair calling status_ind/data_req, we need to keep this.
* To be considered 'hackish'.
*/
int saved_status_ind_tb_size[2+5];
nr_rlc_entity_t *srb[2];
nr_rlc_entity_t *drb[5];
} nr_rlc_ue_t;
......
RLC AM
======
- 36.322 5.4 Re-establishment procedure
when possible, reassemble RLC SDUs from any byte segments of AMD PDUs
with SN < VR(MR) in the receiving side, remove RLC headers when doing
so and deliver all reassembled RLC SDUs to upper layer in ascending order
of the RLC SN, if not delivered before;
- 36.322 5.2.3 Status reporting
delay triggering the STATUS report until x < VR(MS) or x >= VR(MR)
- 36.322 5.1.3.2.3 Actions when a RLC data PDU is placed in the reception
buffer
[...] and in-sequence byte segments of the AMD PDU with SN = VR(R) [...]
- use SOstart/SOend in NACK reporting, do not NACK full PDU if
parts of it have been received
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc.h"
int decode_t_reordering(int v)
{
static int tab[32] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85,
90, 95, 100, 110, 120, 130, 140, 150, 160, 170, 180, 190, 200, 1600
};
if (v < 0 || v > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_t_status_prohibit(int v)
{
static int tab[62] = {
0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90,
95, 100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165,
170, 175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240,
245, 250, 300, 350, 400, 450, 500, 800, 1000, 1200, 1600, 2000, 2400
};
if (v < 0 || v > 61) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_t_poll_retransmit(int v)
{
static int tab[59] = {
5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95,
100, 105, 110, 115, 120, 125, 130, 135, 140, 145, 150, 155, 160, 165, 170,
175, 180, 185, 190, 195, 200, 205, 210, 215, 220, 225, 230, 235, 240, 245,
250, 300, 350, 400, 450, 500, 800, 1000, 2000, 4000
};
if (v < 0 || v > 58) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_poll_pdu(int v)
{
static int tab[8] = {
4, 8, 16, 32, 64, 128, 256, -1 /* -1 means infinity */
};
if (v < 0 || v > 7) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_poll_byte(int v)
{
static int tab[15] = {
25, 50, 75, 100, 125, 250, 375, 500, 750, 1000, 1250, 1500, 2000, 3000,
-1 /* -1 means infinity */
};
if (v < 0 || v > 14) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (tab[v] == -1) return -1;
return tab[v] * 1024;
}
int decode_max_retx_threshold(int v)
{
static int tab[8] = {
1, 2, 3, 4, 6, 8, 16, 32
};
if (v < 0 || v > 7) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
int decode_sn_field_length(int v)
{
static int tab[2] = {
5, 10
};
if (v < 0 || v > 1) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
return tab[v];
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _ASN1_UTILS_H_
#define _ASN1_UTILS_H_
int decode_t_reordering(int v);
int decode_t_status_prohibit(int v);
int decode_t_poll_retransmit(int v);
int decode_poll_pdu(int v);
int decode_poll_byte(int v);
int decode_max_retx_threshold(int v);
int decode_sn_field_length(int v);
#endif /* _ASN1_UTILS_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_entity.h"
#include <stdlib.h>
#include "rlc_entity_am.h"
#include "rlc_entity_um.h"
#include "LOG/log.h"
rlc_entity_t *new_rlc_entity_am(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct rlc_entity_t *entity,
int sdu_id),
void *sdu_successful_delivery_data,
void (*max_retx_reached)(void *max_retx_reached_data,
struct rlc_entity_t *entity),
void *max_retx_reached_data,
int t_reordering,
int t_status_prohibit,
int t_poll_retransmit,
int poll_pdu,
int poll_byte,
int max_retx_threshold)
{
rlc_entity_am_t *ret;
ret = calloc(1, sizeof(rlc_entity_am_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->common.recv_pdu = rlc_entity_am_recv_pdu;
ret->common.buffer_status = rlc_entity_am_buffer_status;
ret->common.generate_pdu = rlc_entity_am_generate_pdu;
ret->common.recv_sdu = rlc_entity_am_recv_sdu;
ret->common.set_time = rlc_entity_am_set_time;
ret->common.discard_sdu = rlc_entity_am_discard_sdu;
ret->common.reestablishment = rlc_entity_am_reestablishment;
ret->common.delete = rlc_entity_am_delete;
ret->common.deliver_sdu = deliver_sdu;
ret->common.deliver_sdu_data = deliver_sdu_data;
ret->common.sdu_successful_delivery = sdu_successful_delivery;
ret->common.sdu_successful_delivery_data = sdu_successful_delivery_data;
ret->common.max_retx_reached = max_retx_reached;
ret->common.max_retx_reached_data = max_retx_reached_data;
ret->rx_maxsize = rx_maxsize;
ret->tx_maxsize = tx_maxsize;
ret->t_reordering = t_reordering;
ret->t_status_prohibit = t_status_prohibit;
ret->t_poll_retransmit = t_poll_retransmit;
ret->poll_pdu = poll_pdu;
ret->poll_byte = poll_byte;
ret->max_retx_threshold = max_retx_threshold;
return (rlc_entity_t *)ret;
}
rlc_entity_t *new_rlc_entity_um(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
int t_reordering,
int sn_field_length)
{
rlc_entity_um_t *ret;
ret = calloc(1, sizeof(rlc_entity_um_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->common.recv_pdu = rlc_entity_um_recv_pdu;
ret->common.buffer_status = rlc_entity_um_buffer_status;
ret->common.generate_pdu = rlc_entity_um_generate_pdu;
ret->common.recv_sdu = rlc_entity_um_recv_sdu;
ret->common.set_time = rlc_entity_um_set_time;
ret->common.discard_sdu = rlc_entity_um_discard_sdu;
ret->common.reestablishment = rlc_entity_um_reestablishment;
ret->common.delete = rlc_entity_um_delete;
ret->common.deliver_sdu = deliver_sdu;
ret->common.deliver_sdu_data = deliver_sdu_data;
ret->sn_field_length = sn_field_length;
ret->rx_maxsize = rx_maxsize;
ret->tx_maxsize = tx_maxsize;
ret->t_reordering = t_reordering;
if (sn_field_length == 5)
ret->sn_modulus = 32;
else if (sn_field_length == 10)
ret->sn_modulus = 1024;
else {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->window_size = ret->sn_modulus / 2;
return (rlc_entity_t *)ret;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_ENTITY_H_
#define _RLC_ENTITY_H_
#include <stdint.h>
#define SDU_MAX 16000 /* maximum PDCP SDU size is 8188, let's take more */
typedef struct {
int status_size;
int tx_size;
int retx_size;
} rlc_entity_buffer_status_t;
typedef struct rlc_entity_t {
/* functions provided by the RLC module */
void (*recv_pdu)(struct rlc_entity_t *entity, char *buffer, int size);
rlc_entity_buffer_status_t (*buffer_status)(
struct rlc_entity_t *entity, int maxsize);
int (*generate_pdu)(struct rlc_entity_t *entity, char *buffer, int size);
void (*recv_sdu)(struct rlc_entity_t *entity, char *buffer, int size,
int sdu_id);
void (*set_time)(struct rlc_entity_t *entity, uint64_t now);
void (*discard_sdu)(struct rlc_entity_t *entity, int sdu_id);
void (*reestablishment)(struct rlc_entity_t *entity);
void (*delete)(struct rlc_entity_t *entity);
/* callbacks provided to the RLC module */
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
char *buf, int size);
void *deliver_sdu_data;
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct rlc_entity_t *entity,
int sdu_id);
void *sdu_successful_delivery_data;
void (*max_retx_reached)(void *max_retx_reached_data,
struct rlc_entity_t *entity);
void *max_retx_reached_data;
} rlc_entity_t;
rlc_entity_t *new_rlc_entity_am(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
void (*sdu_successful_delivery)(void *sdu_successful_delivery_data,
struct rlc_entity_t *entity,
int sdu_id),
void *sdu_successful_delivery_data,
void (*max_retx_reached)(void *max_retx_reached_data,
struct rlc_entity_t *entity),
void *max_retx_reached_data,
int t_reordering,
int t_status_prohibit,
int t_poll_retransmit,
int poll_pdu,
int poll_byte,
int max_retx_threshold);
rlc_entity_t *new_rlc_entity_um(
int rx_maxsize,
int tx_maxsize,
void (*deliver_sdu)(void *deliver_sdu_data, struct rlc_entity_t *entity,
char *buf, int size),
void *deliver_sdu_data,
int t_reordering,
int sn_field_length);
#endif /* _RLC_ENTITY_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_entity_am.h"
#include "rlc_pdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
static int modulus_rx(rlc_entity_am_t *entity, int a)
{
/* as per 36.322 7.1, modulus base is vr(r) and modulus is 1024 for rx */
int r = a - entity->vr_r;
if (r < 0) r += 1024;
return r;
}
/* used in both RX and TX processing */
static int modulus_tx(rlc_entity_am_t *entity, int a)
{
/* as per 36.322 7.1, modulus base is vt(a) and modulus is 1024 for tx */
int r = a - entity->vt_a;
if (r < 0) r += 1024;
return r;
}
static int sn_in_recv_window(void *_entity, int sn)
{
rlc_entity_am_t *entity = _entity;
int mod_sn = modulus_rx(entity, sn);
/* we simplify vr(r)<=sn<vr(mr). base is vr(r) and vr(mr) = vr(r) + 512 */
return mod_sn < 512;
}
static int sn_compare_rx(void *_entity, int a, int b)
{
rlc_entity_am_t *entity = _entity;
return modulus_rx(entity, a) - modulus_rx(entity, b);
}
/* used in both RX and TX processing */
static int sn_compare_tx(void *_entity, int a, int b)
{
rlc_entity_am_t *entity = _entity;
return modulus_tx(entity, a) - modulus_tx(entity, b);
}
static int segment_already_received(rlc_entity_am_t *entity,
int sn, int so, int data_size)
{
/* TODO: optimize */
rlc_rx_pdu_segment_t *l = entity->rx_list;
while (l != NULL) {
if (l->sn == sn && l->so <= so &&
l->so + l->size - l->data_offset >= so + data_size)
return 1;
l = l->next;
}
return 0;
}
static int rlc_am_segment_full(rlc_entity_am_t *entity, int sn)
{
rlc_rx_pdu_segment_t *l = entity->rx_list;
int last_byte;
int new_last_byte;
last_byte = -1;
while (l != NULL) {
if (l->sn == sn)
break;
l = l->next;
}
while (l != NULL && l->sn == sn) {
if (l->so > last_byte + 1)
return 0;
if (l->is_last)
return 1;
new_last_byte = l->so + l->size - l->data_offset - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
l = l->next;
}
return 0;
}
/* return 1 if the new segment has some data to consume, 0 if not */
static int rlc_am_reassemble_next_segment(rlc_am_reassemble_t *r)
{
int rf;
int sn;
r->sdu_offset = r->start->data_offset;
rlc_pdu_decoder_init(&r->dec, r->start->data, r->start->size);
rlc_pdu_decoder_get_bits(&r->dec, 1); /* dc */
rf = rlc_pdu_decoder_get_bits(&r->dec, 1);
rlc_pdu_decoder_get_bits(&r->dec, 1); /* p */
r->fi = rlc_pdu_decoder_get_bits(&r->dec, 2);
r->e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sn = rlc_pdu_decoder_get_bits(&r->dec, 10);
if (rf) {
rlc_pdu_decoder_get_bits(&r->dec, 1); /* lsf */
r->so = rlc_pdu_decoder_get_bits(&r->dec, 15);
} else {
r->so = 0;
}
if (r->e) {
r->e = rlc_pdu_decoder_get_bits(&r->dec, 1);
r->sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
r->sdu_len = r->start->size - r->sdu_offset;
/* new sn: read starts from PDU byte 0 */
if (sn != r->sn) {
r->pdu_byte = 0;
r->sn = sn;
}
r->data_pos = r->start->data_offset + r->pdu_byte - r->so;
/* TODO: remove this check, it is useless, data has been validated before */
if (r->pdu_byte < r->so) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
/* if pdu_byte is not in [so .. so+len-1] then all bytes from this segment
* have already been consumed
*/
if (r->pdu_byte >= r->so + r->start->size - r->start->data_offset)
return 0;
/* go to correct SDU */
while (r->pdu_byte >= r->so + (r->sdu_offset - r->start->data_offset) + r->sdu_len) {
r->sdu_offset += r->sdu_len;
if (r->e) {
r->e = rlc_pdu_decoder_get_bits(&r->dec, 1);
r->sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else {
r->sdu_len = r->start->size - r->sdu_offset;
}
}
return 1;
}
static void rlc_am_reassemble(rlc_entity_am_t *entity)
{
rlc_am_reassemble_t *r = &entity->reassemble;
while (r->start != NULL) {
if (r->sdu_pos >= SDU_MAX) {
/* TODO: proper error handling (discard PDUs with current sn from
* reassembly queue? something else?)
*/
LOG_E(RLC, "%s:%d:%s: bad RLC PDU\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
r->sdu[r->sdu_pos] = r->start->data[r->data_pos];
r->sdu_pos++;
r->data_pos++;
r->pdu_byte++;
if (r->data_pos == r->sdu_offset + r->sdu_len) {
/* all bytes of SDU are consumed, check if SDU is fully there.
* It is if the data pointer is not at the end of the PDU segment
* or if 'fi' & 1 == 0
*/
if (r->data_pos != r->start->size ||
(r->fi & 1) == 0) {
/* SDU is full - deliver to higher layer */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(rlc_entity_t *)entity,
r->sdu, r->sdu_pos);
r->sdu_pos = 0;
}
if (r->data_pos != r->start->size) {
/* not at the end, process next SDU */
r->sdu_offset += r->sdu_len;
if (r->e) {
r->e = rlc_pdu_decoder_get_bits(&r->dec, 1);
r->sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
r->sdu_len = r->start->size - r->sdu_offset;
} else {
/* all bytes are consumend, go to next segment not already fully
* processed, if any
*/
do {
rlc_rx_pdu_segment_t *e = r->start;
entity->rx_size -= e->size;
r->start = r->start->next;
rlc_rx_free_pdu_segment(e);
} while (r->start != NULL && !rlc_am_reassemble_next_segment(r));
}
}
}
}
static void rlc_am_reception_actions(rlc_entity_am_t *entity,
rlc_rx_pdu_segment_t *pdu_segment)
{
int x = pdu_segment->sn;
int vr_ms;
int vr_r;
if (modulus_rx(entity, x) >= modulus_rx(entity, entity->vr_h))
entity->vr_h = (x + 1) % 1024;
vr_ms = entity->vr_ms;
while (rlc_am_segment_full(entity, vr_ms))
vr_ms = (vr_ms + 1) % 1024;
entity->vr_ms = vr_ms;
if (x == entity->vr_r) {
vr_r = entity->vr_r;
while (rlc_am_segment_full(entity, vr_r)) {
/* move segments with sn=vr(r) from rx list to end of reassembly list */
while (entity->rx_list != NULL && entity->rx_list->sn == vr_r) {
rlc_rx_pdu_segment_t *e = entity->rx_list;
entity->rx_list = e->next;
e->next = NULL;
if (entity->reassemble.start == NULL) {
entity->reassemble.start = e;
/* the list was empty, we need to init decoder */
entity->reassemble.sn = -1;
if (!rlc_am_reassemble_next_segment(&entity->reassemble)) {
/* TODO: proper error recovery (or remove the test, it should not happen) */
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
} else {
entity->reassemble.end->next = e;
}
entity->reassemble.end = e;
}
/* update vr_r */
vr_r = (vr_r + 1) % 1024;
}
entity->vr_r = vr_r;
}
rlc_am_reassemble(entity);
if (entity->t_reordering_start) {
int vr_x = entity->vr_x;
if (vr_x < entity->vr_r) vr_x += 1024;
if (vr_x == entity->vr_r || vr_x > entity->vr_r + 512)
entity->t_reordering_start = 0;
}
if (entity->t_reordering_start == 0) {
if (sn_compare_rx(entity, entity->vr_h, entity->vr_r) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_x = entity->vr_h;
}
}
}
static void process_received_ack(rlc_entity_am_t *entity, int sn)
{
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
rlc_tx_pdu_segment_t *prev;
/* put all PDUs from wait and retransmit lists with SN < 'sn' to ack_list */
/* process wait list */
head.next = entity->wait_list;
prev = &head;
cur = entity->wait_list;
while (cur != NULL) {
if (sn_compare_tx(entity, cur->sn, sn) < 0) {
/* remove from wait list */
prev->next = cur->next;
/* put the PDU in the ack list */
entity->ack_list = rlc_tx_pdu_list_add(sn_compare_tx, entity,
entity->ack_list, cur);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
entity->wait_list = head.next;
/* process retransmit list */
head.next = entity->retransmit_list;
prev = &head;
cur = entity->retransmit_list;
while (cur != NULL) {
if (sn_compare_tx(entity, cur->sn, sn) < 0) {
/* dec. retx_count in case we put this segment back in retransmit list
* in 'process_received_nack'
*/
cur->retx_count--;
/* remove from retransmit list */
prev->next = cur->next;
/* put the PDU in the ack list */
entity->ack_list = rlc_tx_pdu_list_add(sn_compare_tx, entity,
entity->ack_list, cur);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
entity->retransmit_list = head.next;
}
static void consider_retransmission(rlc_entity_am_t *entity,
rlc_tx_pdu_segment_t *cur)
{
cur->retx_count++;
/* let's report max RETX reached for all retx_count >= max_retx_threshold
* (specs say to report if retx_count == max_retx_threshold).
* Upper layers should react (radio link failure), so no big deal actually.
*/
if (cur->retx_count >= entity->max_retx_threshold) {
entity->common.max_retx_reached(entity->common.max_retx_reached_data,
(rlc_entity_t *)entity);
}
/* let's put in retransmit list even if we are over max_retx_threshold.
* upper layers should deal with this condition, internally it's better
* for the RLC code to keep going with this segment (we only remove
* a segment that was ACKed)
*/
entity->retransmit_list = rlc_tx_pdu_list_add(sn_compare_tx, entity,
entity->retransmit_list, cur);
}
static int so_overlap(int s1, int e1, int s2, int e2)
{
if (s1 < s2) {
if (e1 == -1 || e1 >= s2)
return 1;
return 0;
}
if (e2 == -1 || s1 <= e2)
return 1;
return 0;
}
static void process_received_nack(rlc_entity_am_t *entity, int sn,
int so_start, int so_end)
{
/* put all PDU segments with SN == 'sn' and with an overlapping so start/end
* to the retransmit list
* source lists are ack list and wait list.
* Not sure if we should consider wait list, isn't the other end supposed
* to only NACK SNs lower than the ACK SN sent in the status PDU, in which
* case all potential PDU segments should all be in ack list when calling
* the current function? in doubt let's accept anything and thus process
* also wait list.
*/
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
rlc_tx_pdu_segment_t *prev;
/* check that VT(A) <= sn < VT(S) */
if (!(sn_compare_tx(entity, entity->vt_a, sn) <= 0 &&
sn_compare_tx(entity, sn, entity->vt_s) < 0))
return;
/* process wait list */
head.next = entity->wait_list;
prev = &head;
cur = entity->wait_list;
while (cur != NULL) {
if (cur->sn == sn &&
so_overlap(so_start, so_end, cur->so, cur->so + cur->data_size - 1)) {
/* remove from wait list */
prev->next = cur->next;
/* consider the PDU segment for retransmission */
consider_retransmission(entity, cur);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
entity->wait_list = head.next;
/* process ack list */
head.next = entity->ack_list;
prev = &head;
cur = entity->ack_list;
while (cur != NULL) {
if (cur->sn == sn &&
so_overlap(so_start, so_end, cur->so, cur->so + cur->data_size - 1)) {
/* remove from ack list */
prev->next = cur->next;
/* consider the PDU segment for retransmission */
consider_retransmission(entity, cur);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
entity->ack_list = head.next;
}
int tx_pdu_in_ack_list_full(rlc_tx_pdu_segment_t *pdu)
{
int sn = pdu->sn;
int last_byte = -1;
int new_last_byte;
int is_last_seen = 0;
while (pdu != NULL && pdu->sn == sn) {
if (pdu->so > last_byte + 1) return 0;
if (pdu->is_last)
is_last_seen = 1;
new_last_byte = pdu->so + pdu->data_size - 1;
if (new_last_byte > last_byte)
last_byte = new_last_byte;
pdu = pdu->next;
}
return is_last_seen == 1;
}
int tx_pdu_in_ack_list_size(rlc_tx_pdu_segment_t *pdu)
{
int sn = pdu->sn;
int ret = 0;
while (pdu != NULL && pdu->sn == sn) {
ret += pdu->data_size;
pdu = pdu->next;
}
return ret;
}
void ack_sdu_bytes(rlc_sdu_t *start, int start_byte, int sdu_size)
{
rlc_sdu_t *cur = start;
int remaining_size = sdu_size;
while (remaining_size) {
int cursize = cur->size - start_byte;
if (cursize > remaining_size)
cursize = remaining_size;
cur->acked_bytes += cursize;
remaining_size -= cursize;
/* start_byte is only meaningful for the 1st SDU, then it is 0 */
start_byte = 0;
cur = cur->next;
}
}
rlc_tx_pdu_segment_t *tx_list_remove_sn(rlc_tx_pdu_segment_t *list, int sn)
{
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
rlc_tx_pdu_segment_t *prev;
head.next = list;
cur = list;
prev = &head;
while (cur != NULL) {
if (cur->sn == sn) {
prev->next = cur->next;
rlc_tx_free_pdu(cur);
cur = prev->next;
} else {
prev = cur;
cur = cur->next;
}
}
return head.next;
}
void cleanup_sdu_list(rlc_entity_am_t *entity)
{
rlc_sdu_t head;
rlc_sdu_t *cur;
rlc_sdu_t *prev;
/* remove fully acked SDUs, indicate successful delivery to upper layer */
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL) {
if (cur->acked_bytes == cur->size) {
prev->next = cur->next;
entity->tx_size -= cur->size;
entity->common.sdu_successful_delivery(
entity->common.sdu_successful_delivery_data,
(rlc_entity_t *)entity, cur->upper_layer_id);
rlc_free_sdu(cur);
entity->tx_end = prev;
cur = prev->next;
} else {
entity->tx_end = cur;
prev = cur;
cur = cur->next;
}
}
entity->tx_list = head.next;
/* if tx_end == head then it means that the list is now empty */
if (entity->tx_end == &head)
entity->tx_end = NULL;
}
static void finalize_ack_nack_processing(rlc_entity_am_t *entity)
{
int sn;
rlc_tx_pdu_segment_t *cur = entity->ack_list;
int pdu_size;
if (cur == NULL)
return;
/* Remove full PDUs and ack the SDU bytes they cover. Start from SN == VT(A)
* and process increasing SNs until end of list or missing ACK or PDU not
* fully ACKed.
*/
while (cur != NULL && cur->sn == entity->vt_a &&
tx_pdu_in_ack_list_full(cur)) {
sn = cur->sn;
entity->vt_a = (entity->vt_a + 1) % 1024;
pdu_size = tx_pdu_in_ack_list_size(cur);
ack_sdu_bytes(cur->start_sdu, cur->sdu_start_byte, pdu_size);
while (cur != NULL && cur->sn == sn)
cur = cur->next;
entity->ack_list = tx_list_remove_sn(entity->ack_list, sn);
}
cleanup_sdu_list(entity);
}
void rlc_entity_am_recv_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
#define R(d) do { if (rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
rlc_pdu_decoder_t decoder;
rlc_pdu_decoder_t data_decoder;
rlc_pdu_decoder_t control_decoder;
int dc;
int rf;
int p = 0;
int fi;
int e;
int sn;
int lsf;
int so;
int cpt;
int e1;
int e2;
int ack_sn;
int nack_sn;
int so_start;
int so_end;
int control_e1;
int control_e2;
int data_e;
int data_li;
int packet_count;
int data_size;
int data_start;
int indicated_data_size;
rlc_rx_pdu_segment_t *pdu_segment;
rlc_pdu_decoder_init(&decoder, buffer, size);
dc = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
if (dc == 0) goto control;
/* data PDU */
rf = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
p = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
fi = rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder);
e = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
sn = rlc_pdu_decoder_get_bits(&decoder, 10); R(decoder);
/* dicard PDU if rx buffer is full */
if (entity->rx_size + size > entity->rx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
if (!sn_in_recv_window(entity, sn)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, sn out of window (sn %d vr_r %d)\n",
__FILE__, __LINE__, __FUNCTION__,
sn, entity->vr_r);
goto discard;
}
if (rf) {
lsf = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
so = rlc_pdu_decoder_get_bits(&decoder, 15); R(decoder);
} else {
lsf = 1;
so = 0;
}
packet_count = 1;
/* go to start of data */
indicated_data_size = 0;
data_decoder = decoder;
data_e = e;
while (data_e) {
data_e = rlc_pdu_decoder_get_bits(&data_decoder, 1); R(data_decoder);
data_li = rlc_pdu_decoder_get_bits(&data_decoder, 11); R(data_decoder);
if (data_li == 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, li == 0\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
indicated_data_size += data_li;
packet_count++;
}
rlc_pdu_decoder_align(&data_decoder);
data_start = data_decoder.byte;
data_size = size - data_start;
if (data_size <= 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, wrong data size (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
goto discard;
}
if (indicated_data_size >= data_size) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, bad LIs (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
goto discard;
}
/* discard segment if all the bytes of the segment are already there */
if (segment_already_received(entity, sn, so, data_size)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, already received\n",
__FILE__, __LINE__, __FUNCTION__);
goto discard;
}
char *fi_str[] = {
"first byte: YES last byte: YES",
"first byte: YES last byte: NO",
"first byte: NO last byte: YES",
"first byte: NO last byte: NO",
};
LOG_D(RLC, "found %d packets, data size %d data start %d [fi %d %s] (sn %d) (p %d)\n",
packet_count, data_size, data_decoder.byte, fi, fi_str[fi], sn, p);
/* put in pdu reception list */
entity->rx_size += size;
pdu_segment = rlc_rx_new_pdu_segment(sn, so, size, lsf, buffer, data_start);
entity->rx_list = rlc_rx_pdu_segment_list_add(sn_compare_rx, entity,
entity->rx_list, pdu_segment);
/* do reception actions (36.322 5.1.3.2.3) */
rlc_am_reception_actions(entity, pdu_segment);
if (p) {
/* 36.322 5.2.3 says status triggering should be delayed
* until x < VR(MS) or x >= VR(MR). This is not clear (what
* is x then? we keep the same?). So let's trigger no matter what.
*/
int vr_mr = (entity->vr_r + 512) % 1024;
entity->status_triggered = 1;
if (!(sn_compare_rx(entity, sn, entity->vr_ms) < 0 ||
sn_compare_rx(entity, sn, vr_mr) >= 0)) {
LOG_D(RLC, "%s:%d:%s: warning: STATUS trigger should be delayed, according to specs\n",
__FILE__, __LINE__, __FUNCTION__);
}
}
return;
control:
cpt = rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder);
if (cpt != 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, CPT not 0 (%d)\n",
__FILE__, __LINE__, __FUNCTION__, cpt);
goto discard;
}
ack_sn = rlc_pdu_decoder_get_bits(&decoder, 10); R(decoder);
e1 = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
/* let's try to parse the control PDU once to check consistency */
control_decoder = decoder;
control_e1 = e1;
while (control_e1) {
rlc_pdu_decoder_get_bits(&control_decoder, 10); R(control_decoder); /* NACK_SN */
control_e1 = rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
control_e2 = rlc_pdu_decoder_get_bits(&control_decoder, 1); R(control_decoder);
if (control_e2) {
rlc_pdu_decoder_get_bits(&control_decoder, 15); R(control_decoder); /* SOstart */
rlc_pdu_decoder_get_bits(&control_decoder, 15); R(control_decoder); /* SOend */
}
}
/* 36.322 5.2.2.2 says to stop t_poll_retransmit if a ACK or NACK is
* received for the SN 'poll_sn'
*/
if (sn_compare_tx(entity, entity->poll_sn, ack_sn) < 0)
entity->t_poll_retransmit_start = 0;
/* at this point, accept the PDU even if the actual values
* may be incorrect (eg. if so_start > so_end)
*/
process_received_ack(entity, ack_sn);
while (e1) {
nack_sn = rlc_pdu_decoder_get_bits(&decoder, 10); R(decoder);
e1 = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
e2 = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
if (e2) {
so_start = rlc_pdu_decoder_get_bits(&decoder, 15); R(decoder);
so_end = rlc_pdu_decoder_get_bits(&decoder, 15); R(decoder);
if (so_end < so_start) {
LOG_W(RLC, "%s:%d:%s: warning, bad so start/end, NACK the whole PDU (sn %d)\n",
__FILE__, __LINE__, __FUNCTION__, nack_sn);
so_start = 0;
so_end = -1;
}
/* special value 0x7fff indicates 'all bytes to the end' */
if (so_end == 0x7fff)
so_end = -1;
} else {
so_start = 0;
so_end = -1;
}
process_received_nack(entity, nack_sn, so_start, so_end);
/* 36.322 5.2.2.2 says to stop t_poll_retransmit if a ACK or NACK is
* received for the SN 'poll_sn'
*/
if (entity->poll_sn == nack_sn)
entity->t_poll_retransmit_start = 0;
}
finalize_ack_nack_processing(entity);
return;
err:
LOG_W(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
goto discard;
discard:
if (p)
entity->status_triggered = 1;
#undef R
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
static int pdu_size(rlc_entity_am_t *entity, rlc_tx_pdu_segment_t *pdu)
{
int header_size;
int sdu_count;
int data_size;
int li_bits;
rlc_sdu_t *sdu;
header_size = 2;
if (pdu->is_segment)
header_size += 2;
data_size = pdu->data_size;
sdu = pdu->start_sdu;
sdu_count = 1;
data_size -= sdu->size - pdu->sdu_start_byte;
sdu = sdu->next;
while (data_size > 0) {
sdu_count++;
data_size -= sdu->size;
sdu = sdu->next;
}
li_bits = 12 * (sdu_count - 1);
header_size += (li_bits + 7) / 8;
return header_size + pdu->data_size;
}
static int header_size(int sdu_count)
{
int bits = 16 + 12 * (sdu_count - 1);
/* padding if we have to */
return (bits + 7) / 8;
}
typedef struct {
int sdu_count;
int data_size;
int header_size;
} tx_pdu_size_t;
static tx_pdu_size_t compute_new_pdu_size(rlc_entity_am_t *entity, int maxsize)
{
tx_pdu_size_t ret;
int sdu_count;
int sdu_size;
int pdu_data_size;
rlc_sdu_t *sdu;
int vt_ms = (entity->vt_a + 512) % 1024;
ret.sdu_count = 0;
ret.data_size = 0;
ret.header_size = 0;
/* sn out of window? nothing to do */
if (!(sn_compare_tx(entity, entity->vt_s, entity->vt_a) >= 0 &&
sn_compare_tx(entity, entity->vt_s, vt_ms) < 0))
return ret;
/* TX PDU - let's make the biggest PDU we can with the SDUs we have */
sdu_count = 0;
pdu_data_size = 0;
sdu = entity->tx_list;
while (sdu != NULL) {
/* include SDU only if it has not been fully included in PDUs already */
if (sdu->next_byte != sdu->size) {
int new_header_size = header_size(sdu_count + 1);
/* if we cannot put new header + at least 1 byte of data then over */
if (new_header_size + pdu_data_size + 1 > maxsize)
break;
sdu_count++;
/* only include the bytes of this SDU not included in PDUs already */
sdu_size = sdu->size - sdu->next_byte;
/* don't feed more than 'maxsize' bytes */
if (new_header_size + pdu_data_size + sdu_size > maxsize)
sdu_size = maxsize - new_header_size - pdu_data_size;
pdu_data_size += sdu_size;
/* if we put more than 2^11-1 bytes then the LI field cannot be used,
* so this is the last SDU we can put
*/
if (sdu_size > 2047)
break;
}
sdu = sdu->next;
}
if (sdu_count) {
ret.sdu_count = sdu_count;
ret.data_size = pdu_data_size;
ret.header_size = header_size(sdu_count);
}
return ret;
}
static int status_size(rlc_entity_am_t *entity, int maxsize)
{
/* let's count bits */
int bits = 15; /* minimum size is 15 (header+ack_sn+e1) */
int sn;
maxsize *= 8;
if (bits > maxsize) {
LOG_W(RLC, "%s:%d:%s: warning: cannot generate status PDU, not enough room\n",
__FILE__, __LINE__, __FUNCTION__);
return 0;
}
/* each NACK adds 12 bits */
sn = entity->vr_r;
while (bits + 12 <= maxsize && sn_compare_rx(entity, sn, entity->vr_ms) < 0) {
if (!(rlc_am_segment_full(entity, sn)))
bits += 12;
sn = (sn + 1) % 1024;
}
return (bits + 7) / 8;
}
static int generate_status(rlc_entity_am_t *entity, char *buffer, int size)
{
/* let's count bits */
int bits = 15; /* minimum size is 15 (header+ack_sn+e1) */
int sn;
rlc_pdu_encoder_t encoder;
int has_nack = 0;
int ack;
rlc_pdu_encoder_init(&encoder, buffer, size);
size *= 8;
if (bits > size) {
LOG_W(RLC, "%s:%d:%s: warning: cannot generate status PDU, not enough room\n",
__FILE__, __LINE__, __FUNCTION__);
return 0;
}
/* header */
rlc_pdu_encoder_put_bits(&encoder, 0, 1); /* D/C */
rlc_pdu_encoder_put_bits(&encoder, 0, 3); /* CPT */
/* reserve room for ACK (it will be set after putting the NACKs) */
rlc_pdu_encoder_put_bits(&encoder, 0, 10);
/* at this point, ACK is VR(R) */
ack = entity->vr_r;
/* each NACK adds 12 bits */
sn = entity->vr_r;
while (bits + 12 <= size && sn_compare_rx(entity, sn, entity->vr_ms) < 0) {
if (!(rlc_am_segment_full(entity, sn))) {
/* put previous e1 (is 1) */
rlc_pdu_encoder_put_bits(&encoder, 1, 1);
/* if previous was NACK, put previous e2 (0, we don't do 'so' thing) */
if (has_nack)
rlc_pdu_encoder_put_bits(&encoder, 0, 1);
/* put NACKed sn */
rlc_pdu_encoder_put_bits(&encoder, sn, 10);
has_nack = 1;
bits += 12;
} else {
/* this sn is full and we put all NACKs before it, use it for ACK */
ack = (sn + 1) % 1024;
}
sn = (sn + 1) % 1024;
}
/* go to highest full sn+1 for ACK, VR(MS) is the limit */
while (sn_compare_rx(entity, sn, entity->vr_ms) < 0 &&
rlc_am_segment_full(entity, sn)) {
ack = (sn + 1) % 1024;
sn = (sn + 1) % 1024;
}
/* at this point, if last put was NACK then put 2 bits else put 1 bit */
if (has_nack)
rlc_pdu_encoder_put_bits(&encoder, 0, 2);
else
rlc_pdu_encoder_put_bits(&encoder, 0, 1);
rlc_pdu_encoder_align(&encoder);
/* let's put the ACK */
buffer[0] |= ack >> 6;
buffer[1] |= (ack & 0x3f) << 2;
/* reset the trigger */
entity->status_triggered = 0;
/* start t_status_prohibit */
entity->t_status_prohibit_start = entity->t_current;
return encoder.byte;
}
int transmission_buffer_empty(rlc_entity_am_t *entity)
{
rlc_sdu_t *sdu;
/* is transmission buffer empty? */
sdu = entity->tx_list;
while (sdu != NULL) {
if (sdu->next_byte != sdu->size)
return 0;
sdu = sdu->next;
}
return 1;
}
int check_poll_after_pdu_assembly(rlc_entity_am_t *entity)
{
int retransmission_buffer_empty;
int window_stalling;
int vt_ms;
/* is retransmission buffer empty? */
if (entity->retransmit_list == NULL)
retransmission_buffer_empty = 1;
else
retransmission_buffer_empty = 0;
/* is window stalling? */
vt_ms = (entity->vt_a + 512) % 1024;
if (!(sn_compare_tx(entity, entity->vt_s, entity->vt_a) >= 0 &&
sn_compare_tx(entity, entity->vt_s, vt_ms) < 0))
window_stalling = 1;
else
window_stalling = 0;
return (transmission_buffer_empty(entity) && retransmission_buffer_empty) ||
window_stalling;
}
void include_poll(rlc_entity_am_t *entity, char *buffer)
{
/* set the P bit to 1 */
buffer[0] |= 0x20;
entity->pdu_without_poll = 0;
entity->byte_without_poll = 0;
/* set POLL_SN to VT(S) - 1 */
entity->poll_sn = (entity->vt_s + 1023) % 1024;
/* start t_poll_retransmit */
entity->t_poll_retransmit_start = entity->t_current;
}
static int serialize_pdu(rlc_entity_am_t *entity, char *buffer, int bufsize,
rlc_tx_pdu_segment_t *pdu, int p)
{
int first_sdu_full;
int last_sdu_full;
int sdu_next_byte;
rlc_sdu_t *sdu;
int i;
int cursize;
rlc_pdu_encoder_t encoder;
int fi;
int e;
int li;
char *out;
int outpos;
int sdu_count;
int header_size;
int sdu_start_byte;
first_sdu_full = pdu->sdu_start_byte == 0;
/* is last SDU full? (and also compute sdu_count) */
last_sdu_full = 1;
sdu = pdu->start_sdu;
sdu_next_byte = pdu->sdu_start_byte;
cursize = 0;
sdu_count = 0;
while (cursize != pdu->data_size) {
int sdu_size = sdu->size - sdu_next_byte;
sdu_count++;
if (cursize + sdu_size > pdu->data_size) {
last_sdu_full = 0;
break;
}
cursize += sdu_size;
sdu = sdu->next;
sdu_next_byte = 0;
}
/* generate header */
rlc_pdu_encoder_init(&encoder, buffer, bufsize);
rlc_pdu_encoder_put_bits(&encoder, 1, 1); /* D/C: 1 = data */
rlc_pdu_encoder_put_bits(&encoder, pdu->is_segment, 1); /* RF */
rlc_pdu_encoder_put_bits(&encoder, 0, 1); /* P: reserve, set later */
fi = 0;
if (!first_sdu_full)
fi |= 0x02;
if (!last_sdu_full)
fi |= 0x01;
rlc_pdu_encoder_put_bits(&encoder, fi, 2); /* FI */
/* to understand the logic for Es and LIs:
* If we have:
* 1 SDU: E=0
*
* 2 SDUs: E=1
* then: E=0 LI(sdu[0])
*
* 3 SDUs: E=1
* then: E=1 LI(sdu[0])
* then: E=0 LI(sdu[1])
*
* 4 SDUs: E=1
* then: E=1 LI(sdu[0])
* then: E=1 LI(sdu[1])
* then: E=0 LI(sdu[2])
*/
if (sdu_count >= 2)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, pdu->sn, 10); /* SN */
if (pdu->is_segment) {
rlc_pdu_encoder_put_bits(&encoder, pdu->is_last, 1); /* LSF */
rlc_pdu_encoder_put_bits(&encoder, pdu->so, 15); /* SO */
}
/* put LIs */
sdu = pdu->start_sdu;
/* first SDU */
li = sdu->size - pdu->sdu_start_byte;
/* put E+LI only if at least 2 SDUs */
if (sdu_count >= 2) {
/* E is 1 if at least 3 SDUs */
if (sdu_count >= 3)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
/* next SDUs, but not the last (no LI for the last) */
sdu = sdu->next;
for (i = 2; i < sdu_count; i++, sdu = sdu->next) {
if (i != sdu_count - 1)
e = 1;
else
e = 0;
li = sdu->size;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
rlc_pdu_encoder_align(&encoder);
header_size = encoder.byte;
/* generate data */
out = buffer + header_size;
sdu = pdu->start_sdu;
sdu_start_byte = pdu->sdu_start_byte;
outpos = 0;
for (i = 0; i < sdu_count; i++, sdu = sdu->next) {
li = sdu->size - sdu_start_byte;
if (outpos + li >= pdu->data_size)
li = pdu->data_size - outpos;
memcpy(out+outpos, sdu->data + sdu_start_byte, li);
outpos += li;
sdu_start_byte = 0;
}
if (p)
include_poll(entity, buffer);
return header_size + pdu->data_size;
}
static int generate_tx_pdu(rlc_entity_am_t *entity, char *buffer, int bufsize)
{
int vt_ms;
tx_pdu_size_t pdu_size;
rlc_sdu_t *sdu;
int i;
int cursize;
int p;
rlc_tx_pdu_segment_t *pdu;
/* sn out of window? do nothing */
vt_ms = (entity->vt_a + 512) % 1024;
if (!(sn_compare_tx(entity, entity->vt_s, entity->vt_a) >= 0 &&
sn_compare_tx(entity, entity->vt_s, vt_ms) < 0))
return 0;
pdu_size = compute_new_pdu_size(entity, bufsize);
if (pdu_size.sdu_count == 0)
return 0;
pdu = rlc_tx_new_pdu();
pdu->sn = entity->vt_s;
entity->vt_s = (entity->vt_s + 1) % 1024;
/* go to first SDU (skip those already fully processed) */
sdu = entity->tx_list;
while (sdu->next_byte == sdu->size)
sdu = sdu->next;
pdu->start_sdu = sdu;
pdu->sdu_start_byte = sdu->next_byte;
pdu->so = 0;
pdu->is_segment = 0;
pdu->is_last = 1;
/* to conform to specs' logic, put -1 (specs say "for 1st retransmission
* put 0 otherwise increase", let's put -1 and always increase when the
* segment goes to retransmit list)
*/
pdu->retx_count = -1;
/* reserve SDU bytes */
cursize = 0;
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
int sdu_size = sdu->size - sdu->next_byte;
if (cursize + sdu_size > pdu_size.data_size)
sdu_size = pdu_size.data_size - cursize;
sdu->next_byte += sdu_size;
cursize += sdu_size;
}
pdu->data_size = cursize;
/* put PDU at the end of the wait list */
entity->wait_list = rlc_tx_pdu_list_append(entity->wait_list, pdu);
/* polling actions for a new PDU */
entity->pdu_without_poll++;
entity->byte_without_poll += pdu_size.data_size;
if ((entity->poll_pdu != -1 &&
entity->pdu_without_poll >= entity->poll_pdu) ||
(entity->poll_byte != -1 &&
entity->byte_without_poll >= entity->poll_byte))
p = 1;
else
p = check_poll_after_pdu_assembly(entity);
if (entity->force_poll) {
p = 1;
entity->force_poll = 0;
}
return serialize_pdu(entity, buffer, bufsize, pdu, p);
}
static void resegment(rlc_tx_pdu_segment_t *pdu, int size)
{
rlc_tx_pdu_segment_t *new_pdu;
rlc_sdu_t *sdu;
int sdu_count;
int pdu_header_size;
int pdu_data_size;
int sdu_pos;
int sdu_bytes_to_take;
/* PDU segment too big, cut in two parts so that first part fits into
* size bytes (including header)
*/
sdu = pdu->start_sdu;
pdu_data_size = 0;
sdu_pos = pdu->sdu_start_byte;
sdu_count = 0;
while (1) {
/* can we put a new header and at least one byte of data? */
/* header has 2 more bytes for SO */
pdu_header_size = 2 + header_size(sdu_count + 1);
if (pdu_header_size + pdu_data_size + 1 > size) {
/* no we can't, stop here */
break;
}
/* yes we can, go ahead */
sdu_count++;
sdu_bytes_to_take = sdu->size - sdu_pos;
if (pdu_header_size + pdu_data_size + sdu_bytes_to_take > size) {
sdu_bytes_to_take = size - (pdu_header_size + pdu_data_size);
}
sdu_pos += sdu_bytes_to_take;
if (sdu_pos == sdu->size) {
sdu = sdu->next;
sdu_pos = 0;
}
pdu_data_size += sdu_bytes_to_take;
}
new_pdu = rlc_tx_new_pdu();
pdu->is_segment = 1;
*new_pdu = *pdu;
new_pdu->so = pdu->so + pdu_data_size;
new_pdu->data_size = pdu->data_size - pdu_data_size;
new_pdu->start_sdu = sdu;
new_pdu->sdu_start_byte = sdu_pos;
pdu->is_last = 0;
pdu->data_size = pdu_data_size;
pdu->next = new_pdu;
}
static int generate_retx_pdu(rlc_entity_am_t *entity, char *buffer, int size)
{
rlc_tx_pdu_segment_t *pdu;
int orig_size;
int p;
pdu = entity->retransmit_list;
orig_size = pdu_size(entity, pdu);
if (orig_size > size) {
/* we can't resegment if size is less than 5
* (4 bytes for header, 1 byte for data)
*/
if (size < 5)
return 0;
resegment(pdu, size);
}
/* remove from retransmit list and put in wait list */
entity->retransmit_list = pdu->next;
entity->wait_list = rlc_tx_pdu_list_add(sn_compare_tx, entity,
entity->wait_list, pdu);
p = check_poll_after_pdu_assembly(entity);
if (entity->force_poll) {
p = 1;
entity->force_poll = 0;
}
return serialize_pdu(entity, buffer, orig_size, pdu, p);
}
static int status_to_report(rlc_entity_am_t *entity)
{
return entity->status_triggered &&
(entity->t_status_prohibit_start == 0 ||
entity->t_current - entity->t_status_prohibit_start >
entity->t_status_prohibit);
}
static int retx_pdu_size(rlc_entity_am_t *entity, int maxsize)
{
int size;
if (entity->retransmit_list == NULL)
return 0;
size = pdu_size(entity, entity->retransmit_list);
if (size <= maxsize)
return size;
/* we can segment head of retransmist list if maxsize is large enough
* to hold a PDU segment with at least 1 data byte (so 5 bytes: 4 bytes
* header + 1 byte data)
*/
if (maxsize < 5)
return 0;
/* a later segmentation of the head of retransmit list will generate a pdu
* of maximum size 'maxsize' (can be less)
*/
return maxsize;
}
rlc_entity_buffer_status_t rlc_entity_am_buffer_status(
rlc_entity_t *_entity, int maxsize)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
rlc_entity_buffer_status_t ret;
tx_pdu_size_t tx_size;
/* status PDU, if we have to */
if (status_to_report(entity))
ret.status_size = status_size(entity, maxsize);
else
ret.status_size = 0;
/* TX PDU */
/* todo: if an SDU has size >2047 in the tx list then processing
* stops and computed size will not be accurate. Change the computation
* to be more accurate (if needed).
*/
tx_size = compute_new_pdu_size(entity, maxsize);
ret.tx_size = tx_size.data_size + tx_size.header_size;
/* reTX PDU */
/* todo: report size of all available data, not just first PDU */
ret.retx_size = retx_pdu_size(entity, maxsize);
return ret;
}
int rlc_entity_am_generate_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
int ret;
if (status_to_report(entity)) {
ret = generate_status(entity, buffer, size);
if (ret != 0)
return ret;
}
if (entity->retransmit_list != NULL) {
ret = generate_retx_pdu(entity, buffer, size);
if (ret != 0)
return ret;
}
return generate_tx_pdu(entity, buffer, size);
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void rlc_entity_am_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
int sdu_id)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
rlc_sdu_t *sdu;
if (size > SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = rlc_new_sdu(buffer, size, sdu_id);
rlc_sdu_list_add(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
static void check_t_poll_retransmit(rlc_entity_am_t *entity)
{
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
rlc_tx_pdu_segment_t *prev;
int sn;
/* 36.322 5.2.2.3 */
/* did t_poll_retransmit expire? */
if (entity->t_poll_retransmit_start == 0 ||
entity->t_current <= entity->t_poll_retransmit_start +
entity->t_poll_retransmit)
return;
/* stop timer */
entity->t_poll_retransmit_start = 0;
/* 36.322 5.2.2.3 says:
*
* - include a poll in a RLC data PDU as described in section 5.2.2.1
*
* That does not seem to be conditional. So we forcefully will send
* a poll as soon as we generate a PDU.
* Hopefully this interpretation is correct. In the worst case we generate
* more polling than necessary, but it's not a big deal. When
* 't_poll_retransmit' expires it means we didn't receive a status report,
* meaning a bad radio link, so things are quite bad at this point and
* asking again for a poll won't hurt much more.
*/
entity->force_poll = 1;
LOG_D(RLC, "%s:%d:%s: warning: t_poll_retransmit expired\n",
__FILE__, __LINE__, __FUNCTION__);
/* do we meet conditions of 36.322 5.2.2.3? */
if (!check_poll_after_pdu_assembly(entity))
return;
/* search wait list for PDU with SN = VT(S)-1 */
sn = (entity->vt_s + 1023) % 1024;
head.next = entity->wait_list;
cur = entity->wait_list;
prev = &head;
while (cur != NULL) {
if (cur->sn == sn)
break;
prev = cur;
cur = cur->next;
}
/* PDU with SN = VT(S)-1 not found?, take the head of wait list */
if (cur == NULL) {
cur = entity->wait_list;
prev = &head;
sn = cur->sn;
}
/* 36.322 says "PDU", not "PDU segment", so let's retransmit all
* PDU segments with this SN
*/
while (cur != NULL && cur->sn == sn) {
prev->next = cur->next;
entity->wait_list = head.next;
/* put in retransmit list */
consider_retransmission(entity, cur);
cur = prev->next;
}
}
static void check_t_reordering(rlc_entity_am_t *entity)
{
int sn;
/* is t_reordering running and if yes has it expired? */
if (entity->t_reordering_start == 0 ||
entity->t_current <= entity->t_reordering_start + entity->t_reordering)
return;
/* stop timer */
entity->t_reordering_start = 0;
LOG_D(RLC, "%s:%d:%s: t_reordering expired\n", __FILE__, __LINE__, __FUNCTION__);
/* update VR(MS) to first SN >= VR(X) for which not all PDU segments
* have been received
*/
sn = entity->vr_x;
while (rlc_am_segment_full(entity, sn))
sn = (sn + 1) % 1024;
entity->vr_ms = sn;
if (sn_compare_rx(entity, entity->vr_h, entity->vr_ms) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_x = entity->vr_h;
}
/* trigger STATUS report */
entity->status_triggered = 1;
}
void rlc_entity_am_set_time(rlc_entity_t *_entity, uint64_t now)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
entity->t_current = now;
check_t_poll_retransmit(entity);
check_t_reordering(entity);
/* t_status_prohibit is handled by generate_status */
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void rlc_entity_am_discard_sdu(rlc_entity_t *_entity, int sdu_id)
{
/* implements 36.322 5.3 */
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
rlc_sdu_t head;
rlc_sdu_t *cur;
rlc_sdu_t *prev;
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL && cur->upper_layer_id != sdu_id) {
prev = cur;
cur = cur->next;
}
/* if sdu_id not found or some bytes have already been 'PDU-ized'
* then do nothing
*/
if (cur == NULL || cur->next_byte != 0)
return;
/* remove SDU from tx_list */
prev->next = cur->next;
entity->tx_list = head.next;
if (entity->tx_end == cur) {
if (prev != &head)
entity->tx_end = prev;
else
entity->tx_end = NULL;
}
rlc_free_sdu(cur);
}
static void free_pdu_segment_list(rlc_tx_pdu_segment_t *l)
{
rlc_tx_pdu_segment_t *cur;
while (l != NULL) {
cur = l;
l = l->next;
rlc_tx_free_pdu(cur);
}
}
static void clear_entity(rlc_entity_am_t *entity)
{
rlc_rx_pdu_segment_t *cur_rx;
rlc_sdu_t *cur_tx;
entity->vr_r = 0;
entity->vr_x = 0;
entity->vr_ms = 0;
entity->vr_h = 0;
entity->status_triggered = 0;
entity->vt_a = 0;
entity->vt_s = 0;
entity->poll_sn = 0;
entity->pdu_without_poll = 0;
entity->byte_without_poll = 0;
entity->force_poll = 0;
entity->t_current = 0;
entity->t_reordering_start = 0;
entity->t_status_prohibit_start = 0;
entity->t_poll_retransmit_start = 0;
cur_rx = entity->rx_list;
while (cur_rx != NULL) {
rlc_rx_pdu_segment_t *p = cur_rx;
cur_rx = cur_rx->next;
rlc_rx_free_pdu_segment(p);
}
entity->rx_list = NULL;
entity->rx_size = 0;
memset(&entity->reassemble, 0, sizeof(rlc_am_reassemble_t));
cur_tx = entity->tx_list;
while (cur_tx != NULL) {
rlc_sdu_t *p = cur_tx;
cur_tx = cur_tx->next;
rlc_free_sdu(p);
}
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
free_pdu_segment_list(entity->wait_list);
free_pdu_segment_list(entity->retransmit_list);
free_pdu_segment_list(entity->ack_list);
entity->wait_list = NULL;
entity->retransmit_list = NULL;
entity->ack_list = NULL;
}
void rlc_entity_am_reestablishment(rlc_entity_t *_entity)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
/* 36.322 5.4 says to deliver SDUs if possible.
* Let's not do that, it makes the code simpler.
* TODO: change this behavior if wanted/needed.
*/
clear_entity(entity);
}
void rlc_entity_am_delete(rlc_entity_t *_entity)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
clear_entity(entity);
free(entity);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_ENTITY_AM_H_
#define _RLC_ENTITY_AM_H_
#include <stdint.h>
#include "rlc_entity.h"
#include "rlc_pdu.h"
#include "rlc_sdu.h"
/*
* Here comes some documentation to understand the reassembly
* logic in the code and the fields in the structure rlc_am_reassemble_t.
*
* Inside RLC, we deal with SDUs, PDUs and PDU segments.
* SDUs are packets coming from upper layer.
* A PDU is made of a header and a payload.
* In the payload there are SDUs.
* First SDU and last SDU in a PDU may be incomplete.
* PDU segments exist in case of retransmissions when the MAC
* layer asks for less data than previously, in which case
* only part of the previous PDU is sent.
*
* This is PDU data (just bytes):
* ---------------------------------------------------------
* | PDU data |
* ---------------------------------------------------------
* It contains SDUs, like:
* ---------------------------------------------------------
* | SDU 1 | SDU 2 | [...] | SDU n |
* ---------------------------------------------------------
* SDU 1 may be only the end of an SDU from which previous bytes were
* transmitted in previous PDUs.
* SDU n may be only the start of an SDU, that is more bytes from
* this SDU may be sent in successive PDUs.
*
* At front of the PDU data, we have a header:
* --------------- ---------------------------------------------------------
* | PDU header | | SDU 1 | SDU 2 | [...] | SDU n |
* --------------- ---------------------------------------------------------
* PDU header describes PDU data (most notably lengths).
*
* A PDU segment is a part of a PDU. For example, from this PDU data:
* ---------------------------------------------------------
* | SDU 1 | SDU 2 | [...] | SDU n |
* ---------------------------------------------------------
* We can extract the following PDU segment (data part only):
* ----------------------
* | PDU segment data |
* ----------------------
* This PDU segment would contain the end of SDU 2 above and some SDUs up to,
* let's say SDU x (x is 5 below).
*
* In front of a transmitted PDU segment, we have a header,
* containing the important variable 'so' (segment offset) that gives
* the index of the first byte of the segment in the original PDU.
* -------------- ----------------------
* | seg. header| | PDU segment data |
* -------------- ----------------------
*
* Let's now explain the data structure rlc_am_reassemble_t.
*
* In the structure rlc_am_reassemble_t, the fields fi, e, sn and so
* are coming from the PDU segment header and the semantics is the
* one of the RLC specs.
*
* The currently processed PDU segment is stored in 'start'.
* We have 'start->s->data_offset' and 'start->s->size'.
* start->s->data_offset is the index of the start of the data in the
* PDU segment. That is if the header is of length 3 bytes
* then start->s->data_offset is 3.
* start->s->size is the total length of the PDU segment,
* including header.
* The size of actual data bytes in the PDU segment is thus
* start->s->size - start->s->data_offset.
*
* The field sdu_len is the length of the current SDU being
* processed.
*
* The field sdu_offset is the starting point of the
* current SDU being processed (starting from beginning
* of PDU segment, including header).
*
* The field data_pos is the current read pointer. 0 points to
* the beginning of the PDU segment (including header).
*
* The field pdu_byte points to the current byte in the original
* PDU (not the PDU segment). It starts at 0 when we start
* processing a new PDU (when a new 'sn' is seen) and always
* increases after each byte processed. This is tha variable
* that is used to know if the next PDU segment will be used
* or not and if yes, starting from which data byte (see
* function rlc_am_reassemble_next_segment).
*
* 'so' is important and points to the byte in the original PDU
* that is the first byte of the PDU segment.
*
* For example, let's take this PDU segment data from above:
* ----------------------
* | PDU segment data |
* ----------------------
* Let's say it is decomposed as:
* ----------------------
* |222|33|4444|55555555|
* ----------------------
* It contains SDUs 2, 3, 4, and 5.
* SDU 2 is 3 bytes, SDU 3 is 2 bytes, SDU 4 is 4 bytes, SDU 5 is 8 bytes.
*
* Let's suppose that the original PDU starts with:
* ----------------
* |1111111|222222|
* ----------------
*
* (In this example, in the PDU segment, SDU 2 is not full,
* we only have its end.)
*
* Then 'so' is 13 (SDU 1 is 7 bytes, head of SDU 2 is 6 bytes).
*
* Let's continue with our PDU segment data.
* Let's say we are current processing SDU 4.
* Let's say the read pointer (variable 'data_pos') is there:
* ----------------------
* |222|33|4444|55555555|
* ----------------------
* ^
* read pointer (data_pos)
*
* Then:
* - sdu_len is 4
* - sdu_offset is 5 + [PDU segment header length]
* (it points to the beginning of SDU 4, starting
* from the head of the PDU segment, that is
* 3 bytes for SDU 2, 2 bytes for SDU 3, and the
* PDU segment header length)
* - start->s->data_offset is [PDU segment header length]
* - pdu_byte is 20
* (13 bytes from beginning of original PDU,
* 3 bytes for SDU 2, 2 bytes for SDU 3, then 2 bytes for SDU 4)
* - data_pos = read pointer = 7 + [PDU segment header length]
*
* To finish this description, in the code, a PDU is simply
* seen as a PDU segment with 'so' = 0 (and is_last == 1 (lsf in the specs),
* but this variable is not used by the reassembly logic).
*
* And for [PDU segment header length] we use start->s->data_offset.
*
* To recap, here is an illustration of the various variables
* and what starting point they use. In the figures, the start
* of the variable name is aligned to the byte it refers to.
* + is used to show the starting point.
*
* Let's put the PDU segment back into the original PDU.
* And let's show the values for when the read pointer
* is on the second byte of SDU 4 (as above).
*
* +++++++++++++++ so
* +++++++++++++++++++++++ pdu_byte
* ---------------------------------------------------------
* | SDU 1| SDU 2..222|33|4444|55555555| [...] | SDU n |
* ---------------------------------------------------------
*
* And now the PDU segment with header.
*
*
* ++++ sdu_len
* ++++++++++++++++++++++ sdu_offset
* +++++++++++++++++++++++ data_pos
* +++++++++++++++ start->s->data_offset
* +++++++++++++++++++++++++++++++++++++ start->s->size
* -------------- ----------------------
* | seg. header| |222|33|4444|55555555|
* -------------- ----------------------
*
* We see three case for the starting point:
* - start of original PDU (without any header)
* - start of header of current PDU segment
* - start of current SDU (for sdu_len)
*/
typedef struct {
rlc_rx_pdu_segment_t *start; /* start of list */
rlc_rx_pdu_segment_t *end; /* end of list (last element) */
int pos; /* byte to get from current buffer */
char sdu[SDU_MAX]; /* sdu is reassembled here */
int sdu_pos; /* next byte to put in sdu */
/* decoder of current PDU */
rlc_pdu_decoder_t dec;
int fi;
int e;
int sn;
int so;
int sdu_len;
int sdu_offset;
int data_pos;
int pdu_byte;
} rlc_am_reassemble_t;
typedef struct {
rlc_entity_t common;
/* configuration */
int t_reordering;
int t_status_prohibit;
int t_poll_retransmit;
int poll_pdu; /* -1 means infinity */
int poll_byte; /* -1 means infinity */
int max_retx_threshold;
/* runtime rx */
int vr_r;
int vr_x;
int vr_ms;
int vr_h;
int status_triggered;
/* runtime tx */
int vt_a;
int vt_s;
int poll_sn;
int pdu_without_poll;
int byte_without_poll;
int force_poll;
/* set to the latest know time by the user of the module. Unit: ms */
uint64_t t_current;
/* timers (stores the TTI of activation, 0 means not active) */
uint64_t t_reordering_start;
uint64_t t_status_prohibit_start;
uint64_t t_poll_retransmit_start;
/* rx management */
rlc_rx_pdu_segment_t *rx_list;
int rx_size;
int rx_maxsize;
/* reassembly management */
rlc_am_reassemble_t reassemble;
/* tx management */
rlc_sdu_t *tx_list;
rlc_sdu_t *tx_end;
int tx_size;
int tx_maxsize;
rlc_tx_pdu_segment_t *wait_list;
rlc_tx_pdu_segment_t *retransmit_list;
rlc_tx_pdu_segment_t *ack_list;
} rlc_entity_am_t;
void rlc_entity_am_recv_sdu(rlc_entity_t *entity, char *buffer, int size,
int sdu_id);
void rlc_entity_am_recv_pdu(rlc_entity_t *entity, char *buffer, int size);
rlc_entity_buffer_status_t rlc_entity_am_buffer_status(
rlc_entity_t *entity, int maxsize);
int rlc_entity_am_generate_pdu(rlc_entity_t *entity, char *buffer, int size);
void rlc_entity_am_set_time(rlc_entity_t *entity, uint64_t now);
void rlc_entity_am_discard_sdu(rlc_entity_t *entity, int sdu_id);
void rlc_entity_am_reestablishment(rlc_entity_t *entity);
void rlc_entity_am_delete(rlc_entity_t *entity);
#endif /* _RLC_ENTITY_AM_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_entity_um.h"
#include "rlc_pdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
/*************************************************************************/
/* PDU RX functions */
/*************************************************************************/
static int modulus_rx(rlc_entity_um_t *entity, int a)
{
/* as per 36.322 7.1, modulus base is vr(uh)-window_size and modulus is
* 2^sn_field_length (which is 'sn_modulus' in rlc_entity_um_t)
*/
int r = a - (entity->vr_uh - entity->window_size);
if (r < 0) r += entity->sn_modulus;
return r % entity->sn_modulus;
}
static int sn_compare_rx(void *_entity, int a, int b)
{
rlc_entity_um_t *entity = _entity;
return modulus_rx(entity, a) - modulus_rx(entity, b);
}
static int sn_in_recv_window(void *_entity, int sn)
{
rlc_entity_um_t *entity = _entity;
int mod_sn = modulus_rx(entity, sn);
/* we simplify (VR(UH) - UM_Window_Size) <= SN < VR(UH), base is
* (VR(UH) - UM_Window_Size) and VR(UH) = base + window_size
*/
return mod_sn < entity->window_size;
}
/* return 1 if a PDU with SN == 'sn' is in the rx list, 0 otherwise */
static int rlc_um_pdu_received(rlc_entity_um_t *entity, int sn)
{
rlc_rx_pdu_segment_t *cur = entity->rx_list;
while (cur != NULL) {
if (cur->sn == sn)
return 1;
cur = cur->next;
}
return 0;
}
static int less_than_vr_ur(rlc_entity_um_t *entity, int sn)
{
return sn_compare_rx(entity, sn, entity->vr_ur) < 0;
}
static int outside_of_reordering_window(rlc_entity_um_t *entity, int sn)
{
return !sn_in_recv_window(entity, sn);
}
static int less_than_vr_uh(rlc_entity_um_t *entity, int sn)
{
return sn_compare_rx(entity, sn, entity->vr_uh) < 0;
}
static void rlc_um_reassemble_pdu(rlc_entity_um_t *entity,
rlc_rx_pdu_segment_t *pdu)
{
rlc_um_reassemble_t *r = &entity->reassemble;
int fi;
int e;
int sn;
int data_pos;
int sdu_len;
int sdu_offset;
sdu_offset = pdu->data_offset;
rlc_pdu_decoder_init(&r->dec, pdu->data, pdu->size);
if (entity->sn_field_length == 10)
rlc_pdu_decoder_get_bits(&r->dec, 3);
fi = rlc_pdu_decoder_get_bits(&r->dec, 2);
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sn = rlc_pdu_decoder_get_bits(&r->dec, entity->sn_field_length);
if (e) {
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
sdu_len = pdu->size - sdu_offset;
/* discard current SDU being reassembled if bad SN or bad FI */
if (sn != (r->sn + 1) % entity->sn_modulus ||
!(fi & 0x02)) {
if (r->sdu_pos)
LOG_D(RLC, "%s:%d:%s: warning: discard partially reassembled SDU\n",
__FILE__, __LINE__, __FUNCTION__);
r->sdu_pos = 0;
}
/* if the head of the SDU is missing, still process the PDU
* but remember to discard the reassembled SDU later on (the
* head has not been received).
* The head is missing if sdu_pos == 0 and fi says the PDU does not
* start an SDU.
*/
if (r->sdu_pos == 0 && (fi & 0x02))
r->sdu_head_missing = 1;
r->sn = sn;
data_pos = pdu->data_offset;
while (1) {
if (r->sdu_pos >= SDU_MAX) {
/* TODO: proper error handling (discard PDUs with current sn from
* reassembly queue? something else?)
*/
LOG_E(RLC, "%s:%d:%s: bad RLC PDU\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
r->sdu[r->sdu_pos] = pdu->data[data_pos];
r->sdu_pos++;
data_pos++;
if (data_pos == sdu_offset + sdu_len) {
/* all bytes of SDU are consumed, check if SDU is fully there.
* It is if the data pointer is not at the end of the PDU segment
* or if 'fi' & 1 == 0
*/
if (data_pos != pdu->size || (fi & 1) == 0) {
/* time to discard the SDU if we didn't receive the head */
if (r->sdu_head_missing) {
LOG_D(RLC, "%s:%d:%s: warning: discard SDU, head not received\n",
__FILE__, __LINE__, __FUNCTION__);
r->sdu_head_missing = 0;
} else {
/* SDU is full - deliver to higher layer */
entity->common.deliver_sdu(entity->common.deliver_sdu_data,
(rlc_entity_t *)entity,
r->sdu, r->sdu_pos);
}
r->sdu_pos = 0;
}
/* done with PDU? */
if (data_pos == pdu->size)
break;
/* not at the end of PDU, process next SDU */
sdu_offset += sdu_len;
if (e) {
e = rlc_pdu_decoder_get_bits(&r->dec, 1);
sdu_len = rlc_pdu_decoder_get_bits(&r->dec, 11);
} else
sdu_len = pdu->size - sdu_offset;
}
}
}
static void rlc_um_reassemble(rlc_entity_um_t *entity,
int (*check_sn)(rlc_entity_um_t *entity, int sn))
{
rlc_rx_pdu_segment_t *cur;
/* process all PDUs from head of rx list until all is processed or
* the SN is not valid anymore with respect to 'check_sn'
*/
while (entity->rx_list != NULL && check_sn(entity, entity->rx_list->sn)) {
cur = entity->rx_list;
rlc_um_reassemble_pdu(entity, cur);
entity->rx_size -= cur->size;
entity->rx_list = cur->next;
rlc_rx_free_pdu_segment(cur);
}
}
static void rlc_um_reception_actions(rlc_entity_um_t *entity,
rlc_rx_pdu_segment_t *pdu_segment)
{
if (!sn_in_recv_window(entity, pdu_segment->sn)) {
entity->vr_uh = (pdu_segment->sn + 1) % entity->sn_modulus;
rlc_um_reassemble(entity, outside_of_reordering_window);
if (!sn_in_recv_window(entity, entity->vr_ur))
entity->vr_ur = (entity->vr_uh - entity->window_size
+ entity->sn_modulus) % entity->sn_modulus;
}
if (rlc_um_pdu_received(entity, entity->vr_ur)) {
do {
entity->vr_ur = (entity->vr_ur + 1) % entity->sn_modulus;
} while (rlc_um_pdu_received(entity, entity->vr_ur));
rlc_um_reassemble(entity, less_than_vr_ur);
}
if (entity->t_reordering_start) {
if (sn_compare_rx(entity, entity->vr_ux, entity->vr_ur) <= 0 ||
(!sn_in_recv_window(entity, entity->vr_ux) &&
entity->vr_ux != entity->vr_uh))
entity->t_reordering_start = 0;
}
if (entity->t_reordering_start == 0) {
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_ux = entity->vr_uh;
}
}
}
void rlc_entity_um_recv_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
#define R(d) do { if (rlc_pdu_decoder_in_error(&d)) goto err; } while (0)
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_pdu_decoder_t decoder;
rlc_pdu_decoder_t data_decoder;
int e;
int sn;
int data_e;
int data_li;
int packet_count;
int data_size;
int data_start;
int indicated_data_size;
rlc_rx_pdu_segment_t *pdu_segment;
rlc_pdu_decoder_init(&decoder, buffer, size);
if (entity->sn_field_length == 10) {
rlc_pdu_decoder_get_bits(&decoder, 3); R(decoder); /* R1 */
}
rlc_pdu_decoder_get_bits(&decoder, 2); R(decoder); /* FI */
e = rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
sn = rlc_pdu_decoder_get_bits(&decoder, entity->sn_field_length); R(decoder);
/* dicard PDU if rx buffer is full */
if (entity->rx_size + size > entity->rx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, RX buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
/* discard according to 36.322 5.1.2.2.2 */
if ((sn_compare_rx(entity, entity->vr_ur, sn) < 0 &&
sn_compare_rx(entity, sn, entity->vr_uh) < 0 &&
rlc_um_pdu_received(entity, sn)) ||
(sn_compare_rx(entity, entity->vr_uh - entity->window_size, sn) <= 0 &&
sn_compare_rx(entity, sn, entity->vr_ur) < 0)) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU (sn %d vr(ur) %d vr(uh) %d)\n",
__FILE__, __LINE__, __FUNCTION__,
sn, entity->vr_ur, entity->vr_uh);
return;
}
packet_count = 1;
/* go to start of data */
indicated_data_size = 0;
data_decoder = decoder;
data_e = e;
while (data_e) {
data_e = rlc_pdu_decoder_get_bits(&data_decoder, 1); R(data_decoder);
data_li = rlc_pdu_decoder_get_bits(&data_decoder, 11); R(data_decoder);
if (data_li == 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, li == 0\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
indicated_data_size += data_li;
packet_count++;
}
rlc_pdu_decoder_align(&data_decoder);
data_start = data_decoder.byte;
data_size = size - data_start;
if (data_size <= 0) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, wrong data size (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
return;
}
if (indicated_data_size >= data_size) {
LOG_D(RLC, "%s:%d:%s: warning: discard PDU, bad LIs (sum of LI %d data size %d)\n",
__FILE__, __LINE__, __FUNCTION__,
indicated_data_size, data_size);
return;
}
/* put in pdu reception list */
entity->rx_size += size;
pdu_segment = rlc_rx_new_pdu_segment(sn, 0, size, 1, buffer, data_start);
entity->rx_list = rlc_rx_pdu_segment_list_add(sn_compare_rx, entity,
entity->rx_list, pdu_segment);
/* do reception actions (36.322 5.1.2.2.3) */
rlc_um_reception_actions(entity, pdu_segment);
return;
err:
LOG_D(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
#undef R
}
/*************************************************************************/
/* TX functions */
/*************************************************************************/
typedef struct {
int sdu_count;
int data_size;
int header_size;
int last_sdu_is_full;
int first_sdu_length;
} tx_pdu_size_t;
static int header_size(int sn_field_length, int sdu_count)
{
int bits = 8 + 8 * (sn_field_length == 10) + 12 * (sdu_count - 1);
/* padding if we have to */
return (bits + 7) / 8;
}
static tx_pdu_size_t tx_pdu_size(rlc_entity_um_t *entity, int maxsize)
{
tx_pdu_size_t ret;
int sdu_count;
int sdu_size;
int pdu_data_size;
rlc_sdu_t *sdu;
ret.sdu_count = 0;
ret.data_size = 0;
ret.header_size = 0;
ret.last_sdu_is_full = 1;
/* TX PDU - let's make the biggest PDU we can with the SDUs we have */
sdu_count = 0;
pdu_data_size = 0;
sdu = entity->tx_list;
while (sdu != NULL) {
int new_header_size = header_size(entity->sn_field_length, sdu_count+1);
/* if we cannot put new header + at least 1 byte of data then over */
if (new_header_size + pdu_data_size >= maxsize)
break;
sdu_count++;
/* only include the bytes of this SDU not included in PDUs already */
sdu_size = sdu->size - sdu->next_byte;
/* don't feed more than 'maxsize' bytes */
if (new_header_size + pdu_data_size + sdu_size > maxsize) {
sdu_size = maxsize - new_header_size - pdu_data_size;
ret.last_sdu_is_full = 0;
}
if (sdu_count == 1)
ret.first_sdu_length = sdu_size;
pdu_data_size += sdu_size;
/* if we put more than 2^11-1 bytes then the LI field cannot be used,
* so this is the last SDU we can put
*/
if (sdu_size > 2047)
break;
sdu = sdu->next;
}
if (sdu_count) {
ret.sdu_count = sdu_count;
ret.data_size = pdu_data_size;
ret.header_size = header_size(entity->sn_field_length, sdu_count);
}
return ret;
}
rlc_entity_buffer_status_t rlc_entity_um_buffer_status(
rlc_entity_t *_entity, int maxsize)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_entity_buffer_status_t ret;
tx_pdu_size_t tx_size;
ret.status_size = 0;
/* todo: if an SDU has size >2047 in the tx list then processing
* stops and computed size will not be accurate. Change the computation
* to be more accurate (if needed).
*/
tx_size = tx_pdu_size(entity, maxsize);
ret.tx_size = tx_size.data_size + tx_size.header_size;
ret.retx_size = 0;
return ret;
}
int rlc_entity_um_generate_pdu(rlc_entity_t *_entity, char *buffer, int size)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
tx_pdu_size_t pdu_size;
rlc_sdu_t *sdu;
int i;
int cursize;
int first_sdu_full;
int last_sdu_full;
rlc_pdu_encoder_t encoder;
int fi;
int e;
int li;
char *out;
int outpos;
int first_sdu_start_byte;
int sdu_start_byte;
pdu_size = tx_pdu_size(entity, size);
if (pdu_size.sdu_count == 0)
return 0;
sdu = entity->tx_list;
first_sdu_start_byte = sdu->next_byte;
/* reserve SDU bytes */
cursize = 0;
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
int sdu_size = sdu->size - sdu->next_byte;
if (cursize + sdu_size > pdu_size.data_size)
sdu_size = pdu_size.data_size - cursize;
sdu->next_byte += sdu_size;
cursize += sdu_size;
}
first_sdu_full = first_sdu_start_byte == 0;
last_sdu_full = pdu_size.last_sdu_is_full;
/* generate header */
rlc_pdu_encoder_init(&encoder, buffer, size);
if (entity->sn_field_length == 10)
rlc_pdu_encoder_put_bits(&encoder, 0, 3); /* R1 */
fi = 0;
if (!first_sdu_full)
fi |= 0x02;
if (!last_sdu_full)
fi |= 0x01;
rlc_pdu_encoder_put_bits(&encoder, fi, 2); /* FI */
/* see the AM code to understand the logic for Es and LIs */
if (pdu_size.sdu_count >= 2)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
if (entity->sn_field_length == 10)
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 10); /* SN */
else
rlc_pdu_encoder_put_bits(&encoder, entity->vt_us, 5); /* SN */
/* put LIs */
sdu = entity->tx_list;
/* first SDU */
li = pdu_size.first_sdu_length;
/* put E+LI only if at least 2 SDUs */
if (pdu_size.sdu_count >= 2) {
/* E is 1 if at least 3 SDUs */
if (pdu_size.sdu_count >= 3)
e = 1;
else
e = 0;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
/* next SDUs, but not the last (no LI for the last) */
sdu = sdu->next;
for (i = 2; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
if (i != pdu_size.sdu_count - 1)
e = 1;
else
e = 0;
li = sdu->size;
rlc_pdu_encoder_put_bits(&encoder, e, 1); /* E */
rlc_pdu_encoder_put_bits(&encoder, li, 11); /* LI */
}
rlc_pdu_encoder_align(&encoder);
/* generate data */
out = buffer + pdu_size.header_size;
sdu = entity->tx_list;
sdu_start_byte = first_sdu_start_byte;
outpos = 0;
for (i = 0; i < pdu_size.sdu_count; i++, sdu = sdu->next) {
li = sdu->size - sdu_start_byte;
if (outpos + li >= pdu_size.data_size)
li = pdu_size.data_size - outpos;
memcpy(out+outpos, sdu->data + sdu_start_byte, li);
outpos += li;
sdu_start_byte = 0;
}
/* cleanup sdu list */
while (entity->tx_list != NULL &&
entity->tx_list->size == entity->tx_list->next_byte) {
rlc_sdu_t *c = entity->tx_list;
/* release SDU bytes */
entity->tx_size -= c->size;
entity->tx_list = c->next;
rlc_free_sdu(c);
}
if (entity->tx_list == NULL)
entity->tx_end = NULL;
/* update VT(US) */
entity->vt_us = (entity->vt_us + 1) % entity->sn_modulus;
return pdu_size.header_size + pdu_size.data_size;
}
/*************************************************************************/
/* SDU RX functions */
/*************************************************************************/
void rlc_entity_um_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
int sdu_id)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_sdu_t *sdu;
if (size > SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
if (entity->tx_size + size > entity->tx_maxsize) {
LOG_D(RLC, "%s:%d:%s: warning: SDU rejected, SDU buffer full\n",
__FILE__, __LINE__, __FUNCTION__);
return;
}
entity->tx_size += size;
sdu = rlc_new_sdu(buffer, size, sdu_id);
rlc_sdu_list_add(&entity->tx_list, &entity->tx_end, sdu);
}
/*************************************************************************/
/* time/timers */
/*************************************************************************/
static void check_t_reordering(rlc_entity_um_t *entity)
{
int sn;
/* is t_reordering running and if yes has it expired? */
if (entity->t_reordering_start == 0 ||
entity->t_current <= entity->t_reordering_start + entity->t_reordering)
return;
/* stop timer */
entity->t_reordering_start = 0;
LOG_D(RLC, "%s:%d:%s: t_reordering expired\n", __FILE__, __LINE__, __FUNCTION__);
/* update VR(UR) to first SN >= VR(UX) of PDU not received
*/
sn = entity->vr_ux;
while (rlc_um_pdu_received(entity, sn))
sn = (sn + 1) % entity->sn_modulus;
entity->vr_ur = sn;
rlc_um_reassemble(entity, less_than_vr_ur);
if (sn_compare_rx(entity, entity->vr_uh, entity->vr_ur) > 0) {
entity->t_reordering_start = entity->t_current;
entity->vr_ux = entity->vr_uh;
}
}
void rlc_entity_um_set_time(rlc_entity_t *_entity, uint64_t now)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
entity->t_current = now;
check_t_reordering(entity);
}
/*************************************************************************/
/* discard/re-establishment/delete */
/*************************************************************************/
void rlc_entity_um_discard_sdu(rlc_entity_t *_entity, int sdu_id)
{
/* implements 36.322 5.3 */
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_sdu_t head;
rlc_sdu_t *cur;
rlc_sdu_t *prev;
head.next = entity->tx_list;
cur = entity->tx_list;
prev = &head;
while (cur != NULL && cur->upper_layer_id != sdu_id) {
prev = cur;
cur = cur->next;
}
/* if sdu_id not found or some bytes have already been 'PDU-ized'
* then do nothing
*/
if (cur == NULL || cur->next_byte != 0)
return;
/* remove SDU from tx_list */
prev->next = cur->next;
entity->tx_list = head.next;
if (entity->tx_end == cur) {
if (prev != &head)
entity->tx_end = prev;
else
entity->tx_end = NULL;
}
rlc_free_sdu(cur);
}
static void clear_entity(rlc_entity_um_t *entity)
{
rlc_rx_pdu_segment_t *cur_rx;
rlc_sdu_t *cur_tx;
entity->vr_ur = 0;
entity->vr_ux = 0;
entity->vr_uh = 0;
entity->vt_us = 0;
entity->t_current = 0;
entity->t_reordering_start = 0;
cur_rx = entity->rx_list;
while (cur_rx != NULL) {
rlc_rx_pdu_segment_t *p = cur_rx;
cur_rx = cur_rx->next;
rlc_rx_free_pdu_segment(p);
}
entity->rx_list = NULL;
entity->rx_size = 0;
memset(&entity->reassemble, 0, sizeof(rlc_um_reassemble_t));
cur_tx = entity->tx_list;
while (cur_tx != NULL) {
rlc_sdu_t *p = cur_tx;
cur_tx = cur_tx->next;
rlc_free_sdu(p);
}
entity->tx_list = NULL;
entity->tx_end = NULL;
entity->tx_size = 0;
}
void rlc_entity_um_reestablishment(rlc_entity_t *_entity)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
rlc_um_reassemble(entity, less_than_vr_uh);
clear_entity(entity);
}
void rlc_entity_um_delete(rlc_entity_t *_entity)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
clear_entity(entity);
free(entity);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_ENTITY_UM_H_
#define _RLC_ENTITY_UM_H_
#include "rlc_entity.h"
#include "rlc_pdu.h"
#include "rlc_sdu.h"
typedef struct {
char sdu[SDU_MAX]; /* sdu is reassembled here */
int sdu_pos; /* next byte to put in sdu */
/* decoder of current PDU */
rlc_pdu_decoder_t dec;
int sn;
int sdu_head_missing;
} rlc_um_reassemble_t;
typedef struct {
rlc_entity_t common;
/* configuration */
int t_reordering;
int sn_field_length;
int sn_modulus; /* 1024 for sn_field_length == 10, 32 for 5 */
int window_size; /* 512 for sn_field_length == 10, 16 for 5 */
/* runtime rx */
int vr_ur;
int vr_ux;
int vr_uh;
/* runtime tx */
int vt_us;
/* set to the latest know time by the user of the module. Unit: ms */
uint64_t t_current;
/* timers (stores the TTI of activation, 0 means not active) */
uint64_t t_reordering_start;
/* rx management */
rlc_rx_pdu_segment_t *rx_list;
int rx_size;
int rx_maxsize;
/* reassembly management */
rlc_um_reassemble_t reassemble;
/* tx management */
rlc_sdu_t *tx_list;
rlc_sdu_t *tx_end;
int tx_size;
int tx_maxsize;
} rlc_entity_um_t;
void rlc_entity_um_recv_sdu(rlc_entity_t *_entity, char *buffer, int size,
int sdu_id);
void rlc_entity_um_recv_pdu(rlc_entity_t *entity, char *buffer, int size);
rlc_entity_buffer_status_t rlc_entity_um_buffer_status(
rlc_entity_t *entity, int maxsize);
int rlc_entity_um_generate_pdu(rlc_entity_t *_entity, char *buffer, int size);
void rlc_entity_um_set_time(rlc_entity_t *entity, uint64_t now);
void rlc_entity_um_discard_sdu(rlc_entity_t *entity, int sdu_id);
void rlc_entity_um_reestablishment(rlc_entity_t *entity);
void rlc_entity_um_delete(rlc_entity_t *entity);
#endif /* _RLC_ENTITY_UM_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
/* from openair */
#include "rlc.h"
#include "pdcp.h"
/* from new rlc module */
#include "asn1_utils.h"
#include "rlc_ue_manager.h"
#include "rlc_entity.h"
#include <stdint.h>
static rlc_ue_manager_t *rlc_ue_manager;
/* TODO: handle time a bit more properly */
static uint64_t rlc_current_time;
static int rlc_current_time_last_frame;
static int rlc_current_time_last_subframe;
void mac_rlc_data_ind (
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
char *buffer_pP,
const tb_size_t tb_sizeP,
num_tb_t num_tbP,
crc_t *crcs_pP)
{
rlc_ue_t *ue;
rlc_entity_t *rb;
int rnti;
int channel_id;
if (enb_flagP == 1 && module_idP != 0) {
LOG_E(RLC, "%s:%d:%s: fatal, module_id must be 0 for eNB\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (/*module_idP != 0 ||*/ eNB_index != 0 /*|| enb_flagP != 1 || MBMS_flagP != 0*/) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (enb_flagP)
T(T_ENB_RLC_MAC_UL, T_INT(module_idP), T_INT(rntiP),
T_INT(channel_idP), T_INT(tb_sizeP));
/* TODO: better handle mbms, maybe we should not change rnti here */
if (!enb_flagP && MBMS_flagP) {
rnti = 0xfffd;
/* TODO: handle channel_id properly */
if (channel_idP != 5) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
channel_id = 7;
} else {
rnti = rntiP;
channel_id = channel_idP;
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rnti);
switch (channel_id) {
case 1 ... 2: rb = ue->srb[channel_id - 1]; break;
case 3 ... 7: rb = ue->drb[channel_id - 3]; break;
default: rb = NULL; break;
}
if (rb != NULL) {
rb->set_time(rb, rlc_current_time);
rb->recv_pdu(rb, buffer_pP, tb_sizeP);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: no RB found (rnti %d channel ID %d)\n",
__FILE__, __LINE__, __FUNCTION__, rnti, channel_id);
exit(1);
}
rlc_manager_unlock(rlc_ue_manager);
}
tbs_size_t mac_rlc_data_req(
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
const tb_size_t tb_sizeP,
char *buffer_pP,
const uint32_t sourceL2Id,
const uint32_t destinationL2Id
)
{
int ret;
rlc_ue_t *ue;
rlc_entity_t *rb;
int maxsize;
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (MBMS_flagP == MBMS_FLAG_YES) {
rb = ue->drb[channel_idP - 1];
}
if (rb != NULL) {
rb->set_time(rb, rlc_current_time);
maxsize = tb_sizeP;
ret = rb->generate_pdu(rb, buffer_pP, maxsize);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: data req for unknown RB\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
ret = 0;
}
rlc_manager_unlock(rlc_ue_manager);
if (enb_flagP)
T(T_ENB_RLC_MAC_DL, T_INT(module_idP), T_INT(rntiP),
T_INT(channel_idP), T_INT(ret));
return ret;
}
mac_rlc_status_resp_t mac_rlc_status_ind(
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const sub_frame_t subframeP,
const eNB_flag_t enb_flagP,
const MBMS_flag_t MBMS_flagP,
const logical_chan_id_t channel_idP,
const uint32_t sourceL2Id,
const uint32_t destinationL2Id
)
{
rlc_ue_t *ue;
mac_rlc_status_resp_t ret;
rlc_entity_t *rb;
/* TODO: handle time a bit more properly */
if (rlc_current_time_last_frame != frameP ||
rlc_current_time_last_subframe != subframeP) {
rlc_current_time++;
rlc_current_time_last_frame = frameP;
rlc_current_time_last_subframe = subframeP;
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (MBMS_flagP == MBMS_FLAG_YES) {
rb = ue->drb[channel_idP - 1];
}
if (rb != NULL) {
rlc_entity_buffer_status_t buf_stat;
rb->set_time(rb, rlc_current_time);
/* 36.321 deals with BSR values up to 3000000 bytes, after what it
* reports '> 3000000' (table 6.1.3.1-2). Passing 4000000 is thus
* more than enough.
*/
buf_stat = rb->buffer_status(rb, 4000000);
ret.bytes_in_buffer = buf_stat.status_size
+ buf_stat.retx_size
+ buf_stat.tx_size;
} else {
ret.bytes_in_buffer = 0;
}
rlc_manager_unlock(rlc_ue_manager);
ret.pdus_in_buffer = 0;
/* TODO: creation time may be important (unit: frame, as it seems) */
ret.head_sdu_creation_time = 0;
ret.head_sdu_remaining_size_to_send = 0;
ret.head_sdu_is_segmented = 0;
return ret;
}
rlc_buffer_occupancy_t mac_rlc_get_buffer_occupancy_ind(
const module_id_t module_idP,
const rnti_t rntiP,
const eNB_index_t eNB_index,
const frame_t frameP,
const sub_frame_t subframeP,
const eNB_flag_t enb_flagP,
const logical_chan_id_t channel_idP)
{
rlc_ue_t *ue;
rlc_buffer_occupancy_t ret;
rlc_entity_t *rb;
if (enb_flagP) {
LOG_E(RLC, "Tx mac_rlc_get_buffer_occupancy_ind function is not implemented for eNB LcId=%u\n", channel_idP);
exit(1);
}
/* TODO: handle time a bit more properly */
if (rlc_current_time_last_frame != frameP ||
rlc_current_time_last_subframe != subframeP) {
rlc_current_time++;
rlc_current_time_last_frame = frameP;
rlc_current_time_last_subframe = subframeP;
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rntiP);
switch (channel_idP) {
case 1 ... 2: rb = ue->srb[channel_idP - 1]; break;
case 3 ... 7: rb = ue->drb[channel_idP - 3]; break;
default: rb = NULL; break;
}
if (rb != NULL) {
rlc_entity_buffer_status_t buf_stat;
rb->set_time(rb, rlc_current_time);
/* 36.321 deals with BSR values up to 3000000 bytes, after what it
* reports '> 3000000' (table 6.1.3.1-2). Passing 4000000 is thus
* more than enough.
*/
buf_stat = rb->buffer_status(rb, 4000000);
ret = buf_stat.status_size
+ buf_stat.retx_size
+ buf_stat.tx_size;
} else {
ret = 0;
}
rlc_manager_unlock(rlc_ue_manager);
return ret;
}
int oai_emulation;
rlc_op_status_t rlc_data_req (const protocol_ctxt_t *const ctxt_pP,
const srb_flag_t srb_flagP,
const MBMS_flag_t MBMS_flagP,
const rb_id_t rb_idP,
const mui_t muiP,
confirm_t confirmP,
sdu_size_t sdu_sizeP,
mem_block_t *sdu_pP,
const uint32_t *const sourceL2Id,
const uint32_t *const destinationL2Id
)
{
int rnti = ctxt_pP->rnti;
rlc_ue_t *ue;
rlc_entity_t *rb;
if (MBMS_flagP == MBMS_FLAG_YES)
rnti = 0xfffd;
LOG_D(RLC, "%s rnti %d srb_flag %d rb_id %d mui %d confirm %d sdu_size %d MBMS_flag %d\n",
__FUNCTION__, rnti, srb_flagP, (int)rb_idP, muiP, confirmP, sdu_sizeP,
MBMS_flagP);
if (ctxt_pP->enb_flag)
T(T_ENB_RLC_DL, T_INT(ctxt_pP->module_id),
T_INT(ctxt_pP->rnti), T_INT(rb_idP), T_INT(sdu_sizeP));
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rnti);
rb = NULL;
if (srb_flagP) {
if (rb_idP >= 1 && rb_idP <= 2)
rb = ue->srb[rb_idP - 1];
} else {
if (rb_idP >= 1 && rb_idP <= 5)
rb = ue->drb[rb_idP - 1];
}
if( MBMS_flagP == MBMS_FLAG_YES) {
rb = ue->drb[rb_idP - 1];
}
if (rb != NULL) {
rb->set_time(rb, rlc_current_time);
rb->recv_sdu(rb, (char *)sdu_pP->data, sdu_sizeP, muiP);
} else {
LOG_E(RLC, "%s:%d:%s: fatal: SDU sent to unknown RB\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
rlc_manager_unlock(rlc_ue_manager);
free_mem_block(sdu_pP, __func__);
return RLC_OP_STATUS_OK;
}
int rlc_module_init(int enb_flag)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int inited = 0;
if (pthread_mutex_lock(&lock)) abort();
if (inited) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
inited = 1;
rlc_ue_manager = new_rlc_ue_manager(enb_flag);
if (pthread_mutex_unlock(&lock)) abort();
return 0;
}
void rlc_util_print_hex_octets(comp_name_t componentP, unsigned char *dataP, const signed long sizeP)
{
}
#include "common/ran_context.h"
extern RAN_CONTEXT_t RC;
static void deliver_sdu(void *_ue, rlc_entity_t *entity, char *buf, int size)
{
rlc_ue_t *ue = _ue;
int is_srb;
int rb_id;
protocol_ctxt_t ctx;
mem_block_t *memblock;
int i;
int is_enb;
int is_mbms;
/* TODO: be sure it's fine to check rnti for MBMS */
is_mbms = ue->rnti == 0xfffd;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "%s:%d:%s: delivering SDU (rnti %d is_srb %d rb_id %d) size %d",
__FILE__, __LINE__, __FUNCTION__, ue->rnti, is_srb, rb_id, size);
memblock = get_free_mem_block(size, __func__);
if (memblock == NULL) {
LOG_E(RLC, "%s:%d:%s: ERROR: get_free_mem_block failed\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
memcpy(memblock->data, buf, size);
/* unused fields? */
ctx.instance = ue->module_id;
ctx.frame = 0;
ctx.subframe = 0;
ctx.eNB_index = 0;
ctx.configured = 1;
ctx.brOption = 0;
/* used fields? */
ctx.module_id = ue->module_id;
ctx.rnti = ue->rnti;
is_enb = rlc_manager_get_enb_flag(rlc_ue_manager);
ctx.enb_flag = is_enb;
if (is_enb) {
T(T_ENB_RLC_UL,
T_INT(0 /*ctxt_pP->module_id*/),
T_INT(ue->rnti), T_INT(rb_id), T_INT(size));
const ngran_node_t type = RC.rrc[0 /*ctxt_pP->module_id*/]->node_type;
AssertFatal(type != ngran_eNB_CU && type != ngran_ng_eNB_CU && type != ngran_gNB_CU,
"Can't be CU, bad node type %d\n", type);
if (NODE_IS_DU(type) && is_srb == 1) {
MessageDef *msg = itti_alloc_new_message(TASK_RLC_ENB, F1AP_UL_RRC_MESSAGE);
F1AP_UL_RRC_MESSAGE(msg).rnti = ue->rnti;
F1AP_UL_RRC_MESSAGE(msg).srb_id = rb_id;
F1AP_UL_RRC_MESSAGE(msg).rrc_container = (unsigned char *)buf;
F1AP_UL_RRC_MESSAGE(msg).rrc_container_length = size;
itti_send_msg_to_task(TASK_DU_F1, ENB_MODULE_ID_TO_INSTANCE(0 /*ctxt_pP->module_id*/), msg);
return;
}
}
if (!get_pdcp_data_ind_func()(&ctx, is_srb, is_mbms, rb_id, size, memblock, NULL, NULL)) {
LOG_E(RLC, "%s:%d:%s: ERROR: pdcp_data_ind failed (is_srb %d rb_id %d rnti %d)\n",
__FILE__, __LINE__, __FUNCTION__,
is_srb, rb_id, ue->rnti);
/* what to do in case of failure? for the moment: nothing */
}
}
static void successful_delivery(void *_ue, rlc_entity_t *entity, int sdu_id)
{
rlc_ue_t *ue = _ue;
int i;
int is_srb;
int rb_id;
MessageDef *msg;
int is_enb;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "sdu %d was successfully delivered on %s %d\n",
sdu_id,
is_srb ? "SRB" : "DRB",
rb_id);
/* TODO: do something for DRBs? */
if (is_srb == 0)
return;
is_enb = rlc_manager_get_enb_flag(rlc_ue_manager);
if (!is_enb)
return;
msg = itti_alloc_new_message(TASK_RLC_ENB, RLC_SDU_INDICATION);
RLC_SDU_INDICATION(msg).rnti = ue->rnti;
RLC_SDU_INDICATION(msg).is_successful = 1;
RLC_SDU_INDICATION(msg).srb_id = rb_id;
RLC_SDU_INDICATION(msg).message_id = sdu_id;
/* TODO: accept more than 1 instance? here we send to instance id 0 */
itti_send_msg_to_task(TASK_RRC_ENB, 0, msg);
}
static void max_retx_reached(void *_ue, rlc_entity_t *entity)
{
rlc_ue_t *ue = _ue;
int i;
int is_srb;
int rb_id;
MessageDef *msg;
int is_enb;
/* is it SRB? */
for (i = 0; i < 2; i++) {
if (entity == ue->srb[i]) {
is_srb = 1;
rb_id = i+1;
goto rb_found;
}
}
/* maybe DRB? */
for (i = 0; i < 5; i++) {
if (entity == ue->drb[i]) {
is_srb = 0;
rb_id = i+1;
goto rb_found;
}
}
LOG_E(RLC, "%s:%d:%s: fatal, no RB found for ue %d\n",
__FILE__, __LINE__, __FUNCTION__, ue->rnti);
exit(1);
rb_found:
LOG_D(RLC, "max RETX reached on %s %d\n",
is_srb ? "SRB" : "DRB",
rb_id);
/* TODO: do something for DRBs? */
if (is_srb == 0)
return;
is_enb = rlc_manager_get_enb_flag(rlc_ue_manager);
if (!is_enb)
return;
msg = itti_alloc_new_message(TASK_RLC_ENB, RLC_SDU_INDICATION);
RLC_SDU_INDICATION(msg).rnti = ue->rnti;
RLC_SDU_INDICATION(msg).is_successful = 0;
RLC_SDU_INDICATION(msg).srb_id = rb_id;
RLC_SDU_INDICATION(msg).message_id = -1;
/* TODO: accept more than 1 instance? here we send to instance id 0 */
itti_send_msg_to_task(TASK_RRC_ENB, 0, msg);
}
static void add_srb(int rnti, int module_id, struct LTE_SRB_ToAddMod *s)
{
rlc_entity_t *rlc_am;
rlc_ue_t *ue;
struct LTE_SRB_ToAddMod__rlc_Config *r = s->rlc_Config;
struct LTE_SRB_ToAddMod__logicalChannelConfig *l = s->logicalChannelConfig;
int srb_id = s->srb_Identity;
int logical_channel_group;
int t_reordering;
int t_status_prohibit;
int t_poll_retransmit;
int poll_pdu;
int poll_byte;
int max_retx_threshold;
if (srb_id != 1 && srb_id != 2) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, srb_id);
exit(1);
}
switch (l->present) {
case LTE_SRB_ToAddMod__logicalChannelConfig_PR_explicitValue:
logical_channel_group = *l->choice.explicitValue.ul_SpecificParameters->logicalChannelGroup;
break;
case LTE_SRB_ToAddMod__logicalChannelConfig_PR_defaultValue:
/* default value from 36.331 9.2.1 */
logical_channel_group = 0;
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
/* TODO: accept other values? */
if (logical_channel_group != 0) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_SRB_ToAddMod__rlc_Config_PR_explicitValue: {
struct LTE_RLC_Config__am *am;
if (r->choice.explicitValue.present != LTE_RLC_Config_PR_am) {
LOG_E(RLC, "%s:%d:%s: fatal error, must be RLC AM\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
am = &r->choice.explicitValue.choice.am;
t_reordering = decode_t_reordering(am->dl_AM_RLC.t_Reordering);
t_status_prohibit = decode_t_status_prohibit(am->dl_AM_RLC.t_StatusProhibit);
t_poll_retransmit = decode_t_poll_retransmit(am->ul_AM_RLC.t_PollRetransmit);
poll_pdu = decode_poll_pdu(am->ul_AM_RLC.pollPDU);
poll_byte = decode_poll_byte(am->ul_AM_RLC.pollByte);
max_retx_threshold = decode_max_retx_threshold(am->ul_AM_RLC.maxRetxThreshold);
break;
}
case LTE_SRB_ToAddMod__rlc_Config_PR_defaultValue:
/* default values from 36.331 9.2.1 */
t_reordering = 35;
t_status_prohibit = 0;
t_poll_retransmit = 45;
poll_pdu = -1;
poll_byte = -1;
max_retx_threshold = 4;
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rnti);
ue->module_id = module_id;
if (ue->srb[srb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning SRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, srb_id, rnti);
} else {
rlc_am = new_rlc_entity_am(100000,
100000,
deliver_sdu, ue,
successful_delivery, ue,
max_retx_reached, ue,
t_reordering, t_status_prohibit,
t_poll_retransmit,
poll_pdu, poll_byte, max_retx_threshold);
rlc_ue_add_srb_rlc_entity(ue, srb_id, rlc_am);
LOG_D(RLC, "%s:%d:%s: added srb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, srb_id, rnti);
}
rlc_manager_unlock(rlc_ue_manager);
}
static void add_drb_am(int rnti, int module_id, struct LTE_DRB_ToAddMod *s)
{
rlc_entity_t *rlc_am;
rlc_ue_t *ue;
struct LTE_RLC_Config *r = s->rlc_Config;
struct LTE_LogicalChannelConfig *l = s->logicalChannelConfig;
int drb_id = s->drb_Identity;
int channel_id = *s->logicalChannelIdentity;
int logical_channel_group;
int t_reordering;
int t_status_prohibit;
int t_poll_retransmit;
int poll_pdu;
int poll_byte;
int max_retx_threshold;
if (!(drb_id >= 1 && drb_id <= 5)) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id);
exit(1);
}
if (channel_id != drb_id + 2) {
LOG_E(RLC, "%s:%d:%s: todo, remove this limitation\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
logical_channel_group = *l->ul_SpecificParameters->logicalChannelGroup;
/* TODO: accept other values? */
if (logical_channel_group != 1) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_RLC_Config_PR_am: {
struct LTE_RLC_Config__am *am;
am = &r->choice.am;
t_reordering = decode_t_reordering(am->dl_AM_RLC.t_Reordering);
t_status_prohibit = decode_t_status_prohibit(am->dl_AM_RLC.t_StatusProhibit);
t_poll_retransmit = decode_t_poll_retransmit(am->ul_AM_RLC.t_PollRetransmit);
poll_pdu = decode_poll_pdu(am->ul_AM_RLC.pollPDU);
poll_byte = decode_poll_byte(am->ul_AM_RLC.pollByte);
max_retx_threshold = decode_max_retx_threshold(am->ul_AM_RLC.maxRetxThreshold);
break;
}
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rnti);
ue->module_id = module_id;
if (ue->drb[drb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
} else {
rlc_am = new_rlc_entity_am(1000000,
1000000,
deliver_sdu, ue,
successful_delivery, ue,
max_retx_reached, ue,
t_reordering, t_status_prohibit,
t_poll_retransmit,
poll_pdu, poll_byte, max_retx_threshold);
rlc_ue_add_drb_rlc_entity(ue, drb_id, rlc_am);
LOG_D(RLC, "%s:%d:%s: added drb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
}
rlc_manager_unlock(rlc_ue_manager);
}
static void add_drb_um(int rnti, int module_id, struct LTE_DRB_ToAddMod *s)
{
rlc_entity_t *rlc_um;
rlc_ue_t *ue;
struct LTE_RLC_Config *r = s->rlc_Config;
struct LTE_LogicalChannelConfig *l = s->logicalChannelConfig;
int drb_id = s->drb_Identity;
int channel_id = *s->logicalChannelIdentity;
int logical_channel_group;
int t_reordering;
int sn_field_length;
if (!(drb_id >= 1 && drb_id <= 5)) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id);
exit(1);
}
if (channel_id != drb_id + 2) {
LOG_E(RLC, "%s:%d:%s: todo, remove this limitation\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
logical_channel_group = *l->ul_SpecificParameters->logicalChannelGroup;
/* TODO: accept other values? */
if (logical_channel_group != 1) {
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
switch (r->present) {
case LTE_RLC_Config_PR_um_Bi_Directional: {
struct LTE_RLC_Config__um_Bi_Directional *um;
um = &r->choice.um_Bi_Directional;
t_reordering = decode_t_reordering(um->dl_UM_RLC.t_Reordering);
if (um->dl_UM_RLC.sn_FieldLength != um->ul_UM_RLC.sn_FieldLength) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
sn_field_length = decode_sn_field_length(um->dl_UM_RLC.sn_FieldLength);
break;
}
default:
LOG_E(RLC, "%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, rnti);
ue->module_id = module_id;
if (ue->drb[drb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
} else {
rlc_um = new_rlc_entity_um(1000000,
1000000,
deliver_sdu, ue,
t_reordering,
sn_field_length);
rlc_ue_add_drb_rlc_entity(ue, drb_id, rlc_um);
LOG_D(RLC, "%s:%d:%s: added drb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, drb_id, rnti);
}
rlc_manager_unlock(rlc_ue_manager);
}
static void add_drb(int rnti, int module_id, struct LTE_DRB_ToAddMod *s)
{
switch (s->rlc_Config->present) {
case LTE_RLC_Config_PR_am:
add_drb_am(rnti, module_id, s);
break;
case LTE_RLC_Config_PR_um_Bi_Directional:
add_drb_um(rnti, module_id, s);
break;
default:
LOG_E(RLC, "%s:%d:%s: fatal: unhandled DRB type\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
rlc_op_status_t rrc_rlc_config_asn1_req (const protocol_ctxt_t * const ctxt_pP,
const LTE_SRB_ToAddModList_t * const srb2add_listP,
const LTE_DRB_ToAddModList_t * const drb2add_listP,
const LTE_DRB_ToReleaseList_t * const drb2release_listP,
const LTE_PMCH_InfoList_r9_t * const pmch_InfoList_r9_pP,
const uint32_t sourceL2Id,
const uint32_t destinationL2Id
)
{
int rnti = ctxt_pP->rnti;
int module_id = ctxt_pP->module_id;
int i;
int j;
if (ctxt_pP->enb_flag == 1 &&
(ctxt_pP->module_id != 0 || ctxt_pP->instance != 0)) {
LOG_E(RLC, "%s: module_id != 0 or instance != 0 not handled for eNB\n",
__FUNCTION__);
exit(1);
}
if (0 /*||
ctxt_pP->instance != 0 || ctxt_pP->eNB_index != 0 ||
ctxt_pP->configured != 1 || ctxt_pP->brOption != 0 */) {
LOG_E(RLC, "%s: ctxt_pP not handled (%d %d %d %d %d %d)\n", __FUNCTION__,
ctxt_pP->enb_flag , ctxt_pP->module_id, ctxt_pP->instance,
ctxt_pP->eNB_index, ctxt_pP->configured, ctxt_pP->brOption);
exit(1);
}
if (pmch_InfoList_r9_pP != NULL) {
int mbms_rnti = 0xfffd;
LTE_MBMS_SessionInfoList_r9_t *mbms_SessionInfoList_r9_p = NULL;
LTE_MBMS_SessionInfo_r9_t *MBMS_SessionInfo_p = NULL;
mbms_session_id_t mbms_session_id;
mbms_service_id_t mbms_service_id;
rb_id_t drb_id = 0;
logical_chan_id_t lc_id = 0;
//LTE_DRB_Identity_t drb_id = 0;
//LTE_DRB_Identity_t* pdrb_id = NULL;
for (i=0; i<pmch_InfoList_r9_pP->list.count; i++) {
mbms_SessionInfoList_r9_p = &(pmch_InfoList_r9_pP->list.array[i]->mbms_SessionInfoList_r9);
for (j=0; j<mbms_SessionInfoList_r9_p->list.count; j++) {
MBMS_SessionInfo_p = mbms_SessionInfoList_r9_p->list.array[j];
if (0/*MBMS_SessionInfo_p->sessionId_r9*/)
mbms_session_id = MBMS_SessionInfo_p->sessionId_r9->buf[0];
else
mbms_session_id = MBMS_SessionInfo_p->logicalChannelIdentity_r9;
lc_id = mbms_session_id;
mbms_service_id = MBMS_SessionInfo_p->tmgi_r9.serviceId_r9.buf[2]; //serviceId is 3-octet string
// mbms_service_id = j;
// can set the mch_id = i
if (ctxt_pP->enb_flag) {
drb_id = (mbms_service_id * LTE_maxSessionPerPMCH ) + mbms_session_id;//+ (LTE_maxDRB + 3) * MAX_MOBILES_PER_ENB; // 1
} else {
drb_id = (mbms_service_id * LTE_maxSessionPerPMCH ) + mbms_session_id; // + (LTE_maxDRB + 3); // 15
}
LOG_I(RLC, PROTOCOL_CTXT_FMT" CONFIG REQ MBMS ASN1 LC ID %u RB ID %u SESSION ID %u SERVICE ID %u, mbms_rnti %x\n",
PROTOCOL_CTXT_ARGS(ctxt_pP),
lc_id,
(int)drb_id,
mbms_session_id,
mbms_service_id,
mbms_rnti
);
rlc_entity_t *rlc_um;
rlc_ue_t *ue;
//drb_id = rb_id;
rlc_manager_lock(rlc_ue_manager);
ue = rlc_manager_get_ue(rlc_ue_manager, mbms_rnti);
if (ue->drb[drb_id-1] != NULL) {
LOG_D(RLC, "%s:%d:%s: warning DRB %d already exist for ue %d, do nothing\n",
__FILE__, __LINE__, __FUNCTION__, (int)drb_id, mbms_rnti);
} else {
rlc_um = new_rlc_entity_um(1000000,
1000000,
deliver_sdu, ue,
0,//LTE_T_Reordering_ms0,//t_reordering,
5//LTE_SN_FieldLength_size5//sn_field_length
);
rlc_ue_add_drb_rlc_entity(ue, drb_id, rlc_um);
LOG_D(RLC, "%s:%d:%s: added drb %d to ue %d\n",
__FILE__, __LINE__, __FUNCTION__, (int)drb_id, mbms_rnti);
}
rlc_manager_unlock(rlc_ue_manager);
}
}
}
if (drb2release_listP != NULL) {
LOG_E(RLC, "%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (srb2add_listP != NULL) {
for (i = 0; i < srb2add_listP->list.count; i++) {
add_srb(rnti, module_id, srb2add_listP->list.array[i]);
}
}
if (drb2add_listP != NULL) {
for (i = 0; i < drb2add_listP->list.count; i++) {
add_drb(rnti, module_id, drb2add_listP->list.array[i]);
}
}
return RLC_OP_STATUS_OK;
}
rlc_op_status_t rrc_rlc_config_req (
const protocol_ctxt_t* const ctxt_pP,
const srb_flag_t srb_flagP,
const MBMS_flag_t mbms_flagP,
const config_action_t actionP,
const rb_id_t rb_idP,
const rlc_info_t rlc_infoP)
{
rlc_ue_t *ue;
int i;
if (mbms_flagP) {
LOG_E(RLC, "%s:%d:%s: todo (mbms not supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (actionP != CONFIG_ACTION_REMOVE) {
LOG_E(RLC, "%s:%d:%s: todo (only CONFIG_ACTION_REMOVE supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (ctxt_pP->module_id) {
LOG_E(RLC, "%s:%d:%s: todo (only module_id 0 supported)\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if ((srb_flagP && !(rb_idP >= 1 && rb_idP <= 2)) ||
(!srb_flagP && !(rb_idP >= 1 && rb_idP <= 5))) {
LOG_E(RLC, "%s:%d:%s: bad rb_id (%d) (is_srb %d)\n", __FILE__, __LINE__, __FUNCTION__, (int)rb_idP, srb_flagP);
exit(1);
}
rlc_manager_lock(rlc_ue_manager);
LOG_D(RLC, "%s:%d:%s: remove rb %d (is_srb %d) for UE %d\n", __FILE__, __LINE__, __FUNCTION__, (int)rb_idP, srb_flagP, ctxt_pP->rnti);
ue = rlc_manager_get_ue(rlc_ue_manager, ctxt_pP->rnti);
if (srb_flagP) {
if (ue->srb[rb_idP-1] != NULL) {
ue->srb[rb_idP-1]->delete(ue->srb[rb_idP-1]);
ue->srb[rb_idP-1] = NULL;
} else
LOG_W(RLC, "removing non allocated SRB %d, do nothing\n", (int)rb_idP);
} else {
if (ue->drb[rb_idP-1] != NULL) {
ue->drb[rb_idP-1]->delete(ue->drb[rb_idP-1]);
ue->drb[rb_idP-1] = NULL;
} else
LOG_W(RLC, "removing non allocated DRB %d, do nothing\n", (int)rb_idP);
}
/* remove UE if it has no more RB configured */
for (i = 0; i < 2; i++)
if (ue->srb[i] != NULL)
break;
if (i == 2) {
for (i = 0; i < 5; i++)
if (ue->drb[i] != NULL)
break;
if (i == 5)
rlc_manager_remove_ue(rlc_ue_manager, ctxt_pP->rnti);
}
rlc_manager_unlock(rlc_ue_manager);
return RLC_OP_STATUS_OK;
}
void rrc_rlc_register_rrc (rrc_data_ind_cb_t rrc_data_indP, rrc_data_conf_cb_t rrc_data_confP)
{
/* nothing to do */
}
rlc_op_status_t rrc_rlc_remove_ue (const protocol_ctxt_t* const x)
{
LOG_D(RLC, "%s:%d:%s: remove UE %d\n", __FILE__, __LINE__, __FUNCTION__, x->rnti);
rlc_manager_lock(rlc_ue_manager);
rlc_manager_remove_ue(rlc_ue_manager, x->rnti);
rlc_manager_unlock(rlc_ue_manager);
return RLC_OP_STATUS_OK;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_pdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
/**************************************************************************/
/* RX PDU segment and segment list */
/**************************************************************************/
rlc_rx_pdu_segment_t *rlc_rx_new_pdu_segment(int sn, int so, int size,
int is_last, char *data, int data_offset)
{
rlc_rx_pdu_segment_t *ret = malloc(sizeof(rlc_rx_pdu_segment_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->sn = sn;
ret->so = so;
ret->size = size;
ret->is_last = is_last;
ret->next = NULL;
ret->data = malloc(size);
if (ret->data == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
memcpy(ret->data, data, size);
ret->data_offset = data_offset;
return ret;
}
void rlc_rx_free_pdu_segment(rlc_rx_pdu_segment_t *pdu_segment)
{
free(pdu_segment->data);
free(pdu_segment);
}
rlc_rx_pdu_segment_t *rlc_rx_pdu_segment_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
rlc_rx_pdu_segment_t *list, rlc_rx_pdu_segment_t *pdu_segment)
{
rlc_rx_pdu_segment_t head;
rlc_rx_pdu_segment_t *cur;
rlc_rx_pdu_segment_t *prev;
head.next = list;
cur = list;
prev = &head;
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
while (cur != NULL) {
/* check if 'pdu_segment' is before 'cur' in the list */
if (sn_compare(sn_compare_data, cur->sn, pdu_segment->sn) > 0 ||
(cur->sn == pdu_segment->sn && cur->so > pdu_segment->so)) {
break;
}
prev = cur;
cur = cur->next;
}
prev->next = pdu_segment;
pdu_segment->next = cur;
return head.next;
}
/**************************************************************************/
/* TX PDU management */
/**************************************************************************/
rlc_tx_pdu_segment_t *rlc_tx_new_pdu(void)
{
rlc_tx_pdu_segment_t *ret = calloc(1, sizeof(rlc_tx_pdu_segment_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ret->retx_count = -1;
return ret;
}
void rlc_tx_free_pdu(rlc_tx_pdu_segment_t *pdu)
{
free(pdu);
}
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_append(rlc_tx_pdu_segment_t *list,
rlc_tx_pdu_segment_t *pdu)
{
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
head.next = list;
cur = &head;
while (cur->next != NULL) {
cur = cur->next;
}
cur->next = pdu;
return head.next;
}
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
rlc_tx_pdu_segment_t *list, rlc_tx_pdu_segment_t *pdu_segment)
{
rlc_tx_pdu_segment_t head;
rlc_tx_pdu_segment_t *cur;
rlc_tx_pdu_segment_t *prev;
head.next = list;
cur = list;
prev = &head;
/* order is by 'sn', if 'sn' is the same then order is by 'so' */
while (cur != NULL) {
/* check if 'pdu_segment' is before 'cur' in the list */
if (sn_compare(sn_compare_data, cur->sn, pdu_segment->sn) > 0 ||
(cur->sn == pdu_segment->sn && cur->so > pdu_segment->so)) {
break;
}
prev = cur;
cur = cur->next;
}
prev->next = pdu_segment;
pdu_segment->next = cur;
return head.next;
}
/**************************************************************************/
/* PDU decoder */
/**************************************************************************/
void rlc_pdu_decoder_init(rlc_pdu_decoder_t *decoder, char *buffer, int size)
{
decoder->error = 0;
decoder->byte = 0;
decoder->bit = 0;
decoder->buffer = buffer;
decoder->size = size;
}
static int get_bit(rlc_pdu_decoder_t *decoder)
{
int ret;
if (decoder->byte >= decoder->size) {
decoder->error = 1;
return 0;
}
ret = (decoder->buffer[decoder->byte] >> (7 - decoder->bit)) & 1;
decoder->bit++;
if (decoder->bit == 8) {
decoder->bit = 0;
decoder->byte++;
}
return ret;
}
int rlc_pdu_decoder_get_bits(rlc_pdu_decoder_t *decoder, int count)
{
int ret = 0;
int i;
if (count > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
for (i = 0; i < count; i++) {
ret <<= 1;
ret |= get_bit(decoder);
if (decoder->error) return -1;
}
return ret;
}
void rlc_pdu_decoder_align(rlc_pdu_decoder_t *decoder)
{
if (decoder->bit) {
decoder->bit = 0;
decoder->byte++;
}
}
/**************************************************************************/
/* PDU encoder */
/**************************************************************************/
void rlc_pdu_encoder_init(rlc_pdu_encoder_t *encoder, char *buffer, int size)
{
encoder->byte = 0;
encoder->bit = 0;
encoder->buffer = buffer;
encoder->size = size;
}
static void put_bit(rlc_pdu_encoder_t *encoder, int bit)
{
if (encoder->byte == encoder->size) {
LOG_E(RLC, "%s:%d:%s: fatal, buffer full\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
encoder->buffer[encoder->byte] <<= 1;
if (bit)
encoder->buffer[encoder->byte] |= 1;
encoder->bit++;
if (encoder->bit == 8) {
encoder->bit = 0;
encoder->byte++;
}
}
void rlc_pdu_encoder_put_bits(rlc_pdu_encoder_t *encoder, int value, int count)
{
int i;
int x;
if (count > 31) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
x = 1 << (count - 1);
for (i = 0; i < count; i++, x >>= 1)
put_bit(encoder, value & x);
}
void rlc_pdu_encoder_align(rlc_pdu_encoder_t *encoder)
{
while (encoder->bit)
put_bit(encoder, 0);
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_PDU_H_
#define _RLC_PDU_H_
/**************************************************************************/
/* RX PDU segment and segment list */
/**************************************************************************/
typedef struct rlc_rx_pdu_segment_t {
int sn;
int so;
int size;
int is_last;
char *data;
int data_offset;
struct rlc_rx_pdu_segment_t *next;
} rlc_rx_pdu_segment_t;
rlc_rx_pdu_segment_t *rlc_rx_new_pdu_segment(int sn, int so, int size,
int is_last, char *data, int data_offset);
void rlc_rx_free_pdu_segment(rlc_rx_pdu_segment_t *pdu_segment);
rlc_rx_pdu_segment_t *rlc_rx_pdu_segment_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
rlc_rx_pdu_segment_t *list, rlc_rx_pdu_segment_t *pdu_segment);
/**************************************************************************/
/* TX PDU management */
/**************************************************************************/
typedef struct rlc_tx_pdu_segment_t {
int sn;
void *start_sdu; /* real type is rlc_sdu_t * */
int sdu_start_byte; /* starting byte in 'start_sdu' */
int so; /* starting byte of the segment in full PDU */
int data_size; /* number of data bytes (exclude header) */
int is_segment;
int is_last;
int retx_count;
struct rlc_tx_pdu_segment_t *next;
} rlc_tx_pdu_segment_t;
rlc_tx_pdu_segment_t *rlc_tx_new_pdu(void);
void rlc_tx_free_pdu(rlc_tx_pdu_segment_t *pdu);
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_append(rlc_tx_pdu_segment_t *list,
rlc_tx_pdu_segment_t *pdu);
rlc_tx_pdu_segment_t *rlc_tx_pdu_list_add(
int (*sn_compare)(void *, int, int), void *sn_compare_data,
rlc_tx_pdu_segment_t *list, rlc_tx_pdu_segment_t *pdu_segment);
/**************************************************************************/
/* PDU decoder */
/**************************************************************************/
typedef struct {
int error;
int byte; /* next byte to decode */
int bit; /* next bit in next byte to decode */
char *buffer;
int size;
} rlc_pdu_decoder_t;
void rlc_pdu_decoder_init(rlc_pdu_decoder_t *decoder, char *buffer, int size);
#define rlc_pdu_decoder_in_error(d) ((d)->error == 1)
int rlc_pdu_decoder_get_bits(rlc_pdu_decoder_t *decoder, int count);
void rlc_pdu_decoder_align(rlc_pdu_decoder_t *decoder);
/**************************************************************************/
/* PDU encoder */
/**************************************************************************/
typedef struct {
int byte; /* next byte to encode */
int bit; /* next bit in next byte to encode */
char *buffer;
int size;
} rlc_pdu_encoder_t;
void rlc_pdu_encoder_init(rlc_pdu_encoder_t *encoder, char *buffer, int size);
void rlc_pdu_encoder_put_bits(rlc_pdu_encoder_t *encoder, int value, int count);
void rlc_pdu_encoder_align(rlc_pdu_encoder_t *encoder);
#endif /* _RLC_PDU_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_sdu.h"
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
rlc_sdu_t *rlc_new_sdu(char *buffer, int size, int upper_layer_id)
{
rlc_sdu_t *ret = calloc(1, sizeof(rlc_sdu_t));
if (ret == NULL)
goto oom;
ret->upper_layer_id = upper_layer_id;
ret->data = malloc(size);
if (ret->data == NULL)
goto oom;
memcpy(ret->data, buffer, size);
ret->size = size;
return ret;
oom:
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
void rlc_free_sdu(rlc_sdu_t *sdu)
{
free(sdu->data);
free(sdu);
}
void rlc_sdu_list_add(rlc_sdu_t **list, rlc_sdu_t **end, rlc_sdu_t *sdu)
{
if (*list == NULL) {
*list = sdu;
*end = sdu;
return;
}
(*end)->next = sdu;
*end = sdu;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_SDU_H_
#define _RLC_SDU_H_
typedef struct rlc_sdu_t {
int upper_layer_id;
char *data;
int size;
/* next_byte indicates the starting byte to use to construct a new PDU */
int next_byte;
int acked_bytes;
struct rlc_sdu_t *next;
} rlc_sdu_t;
rlc_sdu_t *rlc_new_sdu(char *buffer, int size, int upper_layer_id);
void rlc_free_sdu(rlc_sdu_t *sdu);
void rlc_sdu_list_add(rlc_sdu_t **list, rlc_sdu_t **end, rlc_sdu_t *sdu);
#endif /* _RLC_SDU_H_ */
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "rlc_ue_manager.h"
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "LOG/log.h"
typedef struct {
pthread_mutex_t lock;
rlc_ue_t **ue_list;
int ue_count;
int enb_flag;
} rlc_ue_manager_internal_t;
rlc_ue_manager_t *new_rlc_ue_manager(int enb_flag)
{
rlc_ue_manager_internal_t *ret;
ret = calloc(1, sizeof(rlc_ue_manager_internal_t));
if (ret == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
if (pthread_mutex_init(&ret->lock, NULL)) abort();
ret->enb_flag = enb_flag;
return ret;
}
int rlc_manager_get_enb_flag(rlc_ue_manager_t *_m)
{
rlc_ue_manager_internal_t *m = _m;
return m->enb_flag;
}
void rlc_manager_lock(rlc_ue_manager_t *_m)
{
rlc_ue_manager_internal_t *m = _m;
if (pthread_mutex_lock(&m->lock)) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
void rlc_manager_unlock(rlc_ue_manager_t *_m)
{
rlc_ue_manager_internal_t *m = _m;
if (pthread_mutex_unlock(&m->lock)) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
/* must be called with lock acquired */
rlc_ue_t *rlc_manager_get_ue(rlc_ue_manager_t *_m, int rnti)
{
/* TODO: optimze */
rlc_ue_manager_internal_t *m = _m;
int i;
for (i = 0; i < m->ue_count; i++)
if (m->ue_list[i]->rnti == rnti)
return m->ue_list[i];
LOG_D(RLC, "%s:%d:%s: new UE %d\n", __FILE__, __LINE__, __FUNCTION__, rnti);
m->ue_count++;
m->ue_list = realloc(m->ue_list, sizeof(rlc_ue_t *) * m->ue_count);
if (m->ue_list == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
m->ue_list[m->ue_count-1] = calloc(1, sizeof(rlc_ue_t));
if (m->ue_list[m->ue_count-1] == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
m->ue_list[m->ue_count-1]->rnti = rnti;
return m->ue_list[m->ue_count-1];
}
/* must be called with lock acquired */
void rlc_manager_remove_ue(rlc_ue_manager_t *_m, int rnti)
{
rlc_ue_manager_internal_t *m = _m;
rlc_ue_t *ue;
int i;
int j;
for (i = 0; i < m->ue_count; i++)
if (m->ue_list[i]->rnti == rnti)
break;
if (i == m->ue_count) {
LOG_D(RLC, "%s:%d:%s: warning: ue %d not found\n",
__FILE__, __LINE__, __FUNCTION__,
rnti);
return;
}
ue = m->ue_list[i];
for (j = 0; j < 2; j++)
if (ue->srb[j] != NULL)
ue->srb[j]->delete(ue->srb[j]);
for (j = 0; j < 5; j++)
if (ue->drb[j] != NULL)
ue->drb[j]->delete(ue->drb[j]);
free(ue);
m->ue_count--;
if (m->ue_count == 0) {
free(m->ue_list);
m->ue_list = NULL;
return;
}
memmove(&m->ue_list[i], &m->ue_list[i+1],
(m->ue_count - i) * sizeof(rlc_ue_t *));
m->ue_list = realloc(m->ue_list, m->ue_count * sizeof(rlc_ue_t *));
if (m->ue_list == NULL) {
LOG_E(RLC, "%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
}
/* must be called with lock acquired */
void rlc_ue_add_srb_rlc_entity(rlc_ue_t *ue, int srb_id, rlc_entity_t *entity)
{
if (srb_id < 1 || srb_id > 2) {
LOG_E(RLC, "%s:%d:%s: fatal, bad srb id\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
srb_id--;
if (ue->srb[srb_id] != NULL) {
LOG_E(RLC, "%s:%d:%s: fatal, srb already present\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ue->srb[srb_id] = entity;
}
/* must be called with lock acquired */
void rlc_ue_add_drb_rlc_entity(rlc_ue_t *ue, int drb_id, rlc_entity_t *entity)
{
if (drb_id < 1 || drb_id > 5) {
LOG_E(RLC, "%s:%d:%s: fatal, bad drb id\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
drb_id--;
if (ue->drb[drb_id] != NULL) {
LOG_E(RLC, "%s:%d:%s: fatal, drb already present\n",
__FILE__, __LINE__, __FUNCTION__);
exit(1);
}
ue->drb[drb_id] = entity;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#ifndef _RLC_UE_MANAGER_H_
#define _RLC_UE_MANAGER_H_
#include "rlc_entity.h"
typedef void rlc_ue_manager_t;
typedef struct rlc_ue_t {
int rnti;
int module_id; /* necesarry for the L2 simulator - not clean, to revise */
rlc_entity_t *srb[2];
rlc_entity_t *drb[5];
} rlc_ue_t;
/***********************************************************************/
/* manager functions */
/***********************************************************************/
rlc_ue_manager_t *new_rlc_ue_manager(int enb_flag);
int rlc_manager_get_enb_flag(rlc_ue_manager_t *m);
void rlc_manager_lock(rlc_ue_manager_t *m);
void rlc_manager_unlock(rlc_ue_manager_t *m);
rlc_ue_t *rlc_manager_get_ue(rlc_ue_manager_t *m, int rnti);
void rlc_manager_remove_ue(rlc_ue_manager_t *m, int rnti);
/***********************************************************************/
/* ue functions */
/***********************************************************************/
void rlc_ue_add_srb_rlc_entity(rlc_ue_t *ue, int srb_id, rlc_entity_t *entity);
void rlc_ue_add_drb_rlc_entity(rlc_ue_t *ue, int drb_id, rlc_entity_t *entity);
#endif /* _RLC_UE_MANAGER_H_ */
#ifndef _LOG_H_
#define _LOG_H_
#include <stdio.h>
#define LOG_E(x, ...) printf(__VA_ARGS__)
#define LOG_D(x, ...) printf(__VA_ARGS__)
#define LOG_W(x, ...) printf(__VA_ARGS__)
#endif /* _LOG_H_ */
CC=gcc
CFLAGS=-Wall -g --coverage -I.
LIB=rlc_entity.o rlc_entity_am.o rlc_entity_um.o rlc_pdu.o rlc_sdu.o
tests:
@./run_tests.sh
all: clean_run $(TEST).run
%.run: $(TEST).bin
#valgrind ./$(TEST).bin > $(TEST).run_pre 2> $(TEST).valgrind
./$(TEST).bin > $(TEST).run_pre
grep ^TEST $(TEST).run_pre > $(TEST).run
gunzip -c $(TEST).txt.gz > $(TEST).txt
diff -q $(TEST).txt $(TEST).run
$(TEST).bin: $(TEST).o $(LIB)
$(CC) $(CFLAGS) -o $@ $^
%.o: ../%.c
$(CC) $(CFLAGS) -I.. -c -o $@ $<
$(TEST).o: test.c
$(CC) $(CFLAGS) -c -o $@ $< -DTEST='"$(TEST).h"'
clean_run:
rm -f $(TEST).run $(TEST).bin $(TEST).o
clean:
rm -f *.o *.bin *.run *.run_pre *.gcov *.gcda *.gcno test*.txt a.out \
*.valgrind
To run tests, simply type: make
Each test is made of:
testXX.h definition of the test
testXX.txt.gz compressed expected output of the test
At runtime, we generate:
testXX.run actual output of the test
test.c is the test driving program.
Only the output lines of the test program starting with "TEST" are
stored into testXX.txt and testXX.run.
A test is considered a success if testXX.txt and testXX.run are equal.
Only failed tests are reported.
How to define a new test?
1 - get the ID
Look in the file run_tests.sh, the variable test_count gives you
the number of existing tests. The ID of your test has to be
test_count + 1.
2 - create files
Create the file test<ID>.h containing the test, then generate test<ID>.run
by running 'make all TEST=test<ID>' and copy test<ID>.run to test<ID>.txt.
Then compress this file (gzip -9 test<ID>.txt). Be sure that the output
is correct, of course.
For the file names, replace <ID> by the actual number of the test.
For example, if your test ID is 47, then name the files test47.h and
test47.txt. And run 'make all TEST=test47' to generate test47.run.
The available instructions for a test are described at the top of test.c.
/* gcc -Wall make_pdu.c -I.. ../rlc_pdu.c */
#include "rlc_pdu.h"
#include <stdio.h>
int main(void)
{
char out[100];
rlc_pdu_encoder_t e;
int i;
rlc_pdu_encoder_init(&e, out, 100);
rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
rlc_pdu_encoder_put_bits(&e, 10, 10); // nack_sn
rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
rlc_pdu_encoder_put_bits(&e, 0, 1); // e2
rlc_pdu_encoder_align(&e);
for (i = 0; i < e.byte; i++) printf(" %2.2x", (unsigned char)e.buffer[i]);
printf("\n");
return 0;
}
#!/bin/sh
test_count=45
for i in `seq $test_count`
do
make all TEST=test$i >/dev/null 2>/dev/null
if [ $? != 0 ]
then
echo TEST $i FAILURE
fi
done
#include "../rlc_entity.h"
#include "../rlc_entity_am.h"
#include "../rlc_entity_um.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <sys/wait.h>
#include <unistd.h>
/*
* ENB_AM <rx_maxsize> <tx_maxsize> <t_reordering> <t_status_prohibit>
* <t_poll_retransmit> <poll_pdu> <poll_byte> <max_retx_threshold>
* create the eNB RLC AM entity with given parameters
*
* UE_AM <rx_maxsize> <tx_maxsize> <t_reordering> <t_status_prohibit>
* <t_poll_retransmit> <poll_pdu> <poll_byte> <max_retx_threshold>
* create the UE RLC AM entity with given parameters
*
* ENB_UM <rx_maxsize> <tx_maxsize> <t_reordering> <sn_field_length>
* create the eNB RLC UM entity with given parameters
*
* UE_UM <rx_maxsize> <tx_maxsize> <t_reordering> <sn_field_length>
* create the UE RLC UM entity with given parameters
*
* TIME <time>
* following actions to be performed at time <time>
* <time> starts at 1
* You must end your test definition with a line 'TIME, -1'.
*
* ENB_SDU <id> <size>
* send an SDU to eNB with id <i> and size <size>
* the SDU is [00 01 ... ff 01 ...]
* (ie. start byte is 00 then we increment for each byte, loop if needed)
*
* UE_SDU <id> <size>
* same as ENB_SDU but the SDU is sent to the UE
*
* ENB_PDU <size> <'size' bytes>
* send a custom PDU from eNB to UE (eNB does not see this PDU at all)
*
* UE_PDU <size> <'size' bytes>
* send a custom PDU from UE to eNB (UE does not see this PDU at all)
*
* ENB_PDU_SIZE <size>
* set 'enb_pdu_size'
*
* UE_PDU_SIZE <size>
* set 'ue_pdu_size'
*
* ENB_RECV_FAILS <fails>
* set the 'enb_recv_fails' flag to <fails>
* (1: recv will fail, 0: recv will succeed)
*
* UE_RECV_FAILS <fails>
* same as ENB_RECV_FAILS but for 'ue_recv_fails'
*
* MUST_FAIL
* to be used as first command after the first TIME to indicate
* that the test must fail (ie. exit with non zero, crash not allowed)
*
* ENB_BUFFER_STATUS
* call buffer_status for eNB and print result
*
* UE_BUFFER_STATUS
* call buffer_status for UE and print result
*
* ENB_DISCARD_SDU <sdu ID>
* discards given SDU
*
* UE_DISCARD_SDU <sdu ID>
* discards given SDU
*
* RE_ESTABLISH
* re-establish both eNB and UE
*/
enum action {
ENB_AM, UE_AM,
ENB_UM, UE_UM,
TIME, ENB_SDU, UE_SDU, ENB_PDU, UE_PDU,
ENB_PDU_SIZE, UE_PDU_SIZE,
ENB_RECV_FAILS, UE_RECV_FAILS,
MUST_FAIL,
ENB_BUFFER_STATUS, UE_BUFFER_STATUS,
ENB_DISCARD_SDU, UE_DISCARD_SDU,
RE_ESTABLISH
};
int test[] = {
/* TEST is defined at compilation time */
#include TEST
};
void deliver_sdu_enb_am(void *deliver_sdu_data, struct rlc_entity_t *_entity,
char *buf, int size)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: ENB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_enb_um(void *deliver_sdu_data, struct rlc_entity_t *_entity,
char *buf, int size)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
printf("TEST: ENB: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_enb(void *successful_delivery_data,
rlc_entity_t *_entity, int sdu_id)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: ENB: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_enb(void *max_retx_reached_data, rlc_entity_t *_entity)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: ENB: %"PRIu64": max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
void deliver_sdu_ue_am(void *deliver_sdu_data, struct rlc_entity_t *_entity,
char *buf, int size)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void deliver_sdu_ue_um(void *deliver_sdu_data, struct rlc_entity_t *_entity,
char *buf, int size)
{
rlc_entity_um_t *entity = (rlc_entity_um_t *)_entity;
printf("TEST: UE: %"PRIu64": deliver SDU size %d [",
entity->t_current, size);
for (int i = 0; i < size; i++) printf(" %2.2x", (unsigned char)buf[i]);
printf("]\n");
}
void successful_delivery_ue(void *successful_delivery_data,
rlc_entity_t *_entity, int sdu_id)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64": SDU %d was successfully delivered.\n",
entity->t_current, sdu_id);
}
void max_retx_reached_ue(void *max_retx_reached_data, rlc_entity_t *_entity)
{
rlc_entity_am_t *entity = (rlc_entity_am_t *)_entity;
printf("TEST: UE: %"PRIu64", max RETX reached! radio link failure!\n",
entity->t_current);
exit(1);
}
int test_main(void)
{
rlc_entity_t *enb = NULL;
rlc_entity_t *ue = NULL;
int i;
int k;
char *sdu;
char *pdu;
rlc_entity_buffer_status_t buffer_status;
int enb_do_buffer_status = 0;
int ue_do_buffer_status = 0;
int size;
int pos;
int next_byte_enb = 0;
int next_byte_ue = 0;
int enb_recv_fails = 0;
int ue_recv_fails = 0;
int enb_pdu_size = 1000;
int ue_pdu_size = 1000;
sdu = malloc(16001);
pdu = malloc(3000);
if (sdu == NULL || pdu == NULL) {
printf("out of memory\n");
exit(1);
}
for (i = 0; i < 16001; i++)
sdu[i] = i & 255;
pos = 0;
if (test[pos] != TIME) {
printf("%s:%d:%s: fatal\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
for (i = 1; i < 1000; i++) {
if (i == test[pos+1]) {
pos += 2;
while (test[pos] != TIME)
switch (test[pos]) {
default: printf("fatal: unknown action\n"); exit(1);
case ENB_AM:
enb = new_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_enb_am, NULL,
successful_delivery_enb, NULL,
max_retx_reached_enb, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8]);
pos += 9;
break;
case UE_AM:
ue = new_rlc_entity_am(test[pos+1], test[pos+2],
deliver_sdu_ue_am, NULL,
successful_delivery_ue, NULL,
max_retx_reached_ue, NULL,
test[pos+3], test[pos+4], test[pos+5],
test[pos+6], test[pos+7], test[pos+8]);
pos += 9;
break;
case ENB_UM:
enb = new_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_enb_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case UE_UM:
ue = new_rlc_entity_um(test[pos+1], test[pos+2],
deliver_sdu_ue_um, NULL,
test[pos+3], test[pos+4]);
pos += 5;
break;
case ENB_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_enb++)
sdu[k] = next_byte_enb;
printf("TEST: ENB: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
enb->recv_sdu(enb, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case UE_SDU:
for (k = 0; k < test[pos+2]; k++, next_byte_ue++)
sdu[k] = next_byte_ue;
printf("TEST: UE: %d: recv_sdu (id %d): size %d: [",
i, test[pos+1], test[pos+2]);
for (k = 0; k < test[pos+2]; k++)
printf(" %2.2x", (unsigned char)sdu[k]);
printf("]\n");
ue->recv_sdu(ue, sdu, test[pos+2], test[pos+1]);
pos += 3;
break;
case ENB_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: ENB: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case UE_PDU:
for (k = 0; k < test[pos+1]; k++)
pdu[k] = test[pos+2+k];
printf("TEST: UE: %d: custom PDU: size %d: [", i, test[pos+1]);
for (k = 0; k < test[pos+1]; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!enb_recv_fails)
enb->recv_pdu(enb, pdu, test[pos+1]);
pos += 2 + test[pos+1];
break;
case ENB_PDU_SIZE:
enb_pdu_size = test[pos+1];
pos += 2;
break;
case UE_PDU_SIZE:
ue_pdu_size = test[pos+1];
pos += 2;
break;
case ENB_RECV_FAILS:
enb_recv_fails = test[pos+1];
pos += 2;
break;
case UE_RECV_FAILS:
ue_recv_fails = test[pos+1];
pos += 2;
break;
case MUST_FAIL:
/* do nothing, only used by caller */
pos++;
break;
case ENB_BUFFER_STATUS:
enb_do_buffer_status = 1;
pos++;
break;
case UE_BUFFER_STATUS:
ue_do_buffer_status = 1;
pos++;
break;
case ENB_DISCARD_SDU:
printf("TEST: ENB: %d: discard SDU %d\n", i, test[pos+1]);
enb->discard_sdu(enb, test[pos+1]);
pos += 2;
break;
case UE_DISCARD_SDU:
printf("TEST: UE: %d: discard SDU %d\n", i, test[pos+1]);
ue->discard_sdu(ue, test[pos+1]);
pos += 2;
break;
case RE_ESTABLISH:
printf("TEST: %d: re-establish eNB and UE\n", i);
enb->reestablishment(enb);
ue->reestablishment(ue);
pos++;
break;
}
}
enb->set_time(enb, i);
ue->set_time(ue, i);
if (enb_do_buffer_status) {
enb_do_buffer_status = 0;
buffer_status = enb->buffer_status(enb, enb_pdu_size);
printf("TEST: ENB: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = enb->generate_pdu(enb, pdu, enb_pdu_size);
if (size) {
printf("TEST: ENB: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!ue_recv_fails)
ue->recv_pdu(ue, pdu, size);
}
if (ue_do_buffer_status) {
ue_do_buffer_status = 0;
buffer_status = ue->buffer_status(ue, ue_pdu_size);
printf("TEST: UE: %d: buffer_status: status_size %d tx_size %d retx_size %d\n",
i,
buffer_status.status_size,
buffer_status.tx_size,
buffer_status.retx_size);
}
size = ue->generate_pdu(ue, pdu, ue_pdu_size);
if (size) {
printf("TEST: UE: %d: generate_pdu: size %d: [", i, size);
for (k = 0; k < size; k++) printf(" %2.2x", (unsigned char)pdu[k]);
printf("]\n");
if (!enb_recv_fails)
enb->recv_pdu(enb, pdu, size);
}
}
enb->delete(enb);
ue->delete(ue);
free(sdu);
free(pdu);
return 0;
}
void usage(void)
{
printf("options:\n");
printf(" -no-fork\n");
printf(" don't fork (to ease debugging with gdb)\n");
exit(0);
}
int main(int n, char **v)
{
int must_fail = 0;
int son;
int status;
int i;
int no_fork = 0;
for (i = 1; i < n; i++) {
if (!strcmp(v[i], "-no-fork")) { no_fork = 1; continue; }
usage();
}
if (test[2] == MUST_FAIL)
must_fail = 1;
if (no_fork) return test_main();
son = fork();
if (son == -1) {
perror("fork");
return 1;
}
if (son == 0)
return test_main();
if (wait(&status) == -1) {
perror("wait");
return 1;
}
/* child must quit properly */
if (!WIFEXITED(status))
return 1;
/* child must fail if expected to */
if (must_fail && WEXITSTATUS(status) == 0)
return 1;
/* child must not fail if not expected to */
if (!must_fail && WEXITSTATUS(status))
return 1;
return 0;
}
/*
* basic am test:
* at time 1, eNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
UE_BUFFER_STATUS,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* rlc am test resegmentation of PDU segment with several SDUs
* eNB sends 3 SDUs [1..10] [11.20] [21..30], not received
* eNB retx with smaller PDUs, not received
* eNB retx with still smaller PDUs, not received
* then reception on, all passes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
TIME, 2,
ENB_PDU_SIZE, 25,
TIME, 48,
ENB_PDU_SIZE, 15,
TIME, 100,
UE_RECV_FAILS, 0,
ENB_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test function rlc_am_reassemble_next_segment
* in r->pdu_byte >= r->so + (r->sdu_offset - r->start->data_offset)
* + r->sdu_len
* when case 'if (r->e)' is false
* eNB sends 3 SDUs [1..10] [11.20] [21..30], not received
* eNB retx with smaller PDUs, not received
* eNB retx with still smaller PDUs, not received
* then UE reception on
* then custom PDUs, first a small part of head of original PDU
* then a bigger part, covering the first part
* so that the beginning of this part triggers the 'while'
* then eNB reception on, all passes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
TIME, 2,
ENB_PDU_SIZE, 25,
TIME, 48,
ENB_PDU_SIZE, 15,
TIME, 95,
ENB_BUFFER_STATUS,
TIME, 99,
UE_RECV_FAILS, 0,
ENB_PDU, 14, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
ENB_PDU, 25, 0xec, 0x00, 0x00, 0x00, 0x00, 0xa0, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12,
TIME, 100,
ENB_RECV_FAILS, 0,
TIME, 134,
UE_BUFFER_STATUS,
TIME, -1
/*
* rlc am test function rlc_am_reassemble_next_segment
* in r->pdu_byte >= r->so + (r->sdu_offset - r->start->data_offset)
* + r->sdu_len
* when case 'if (r->e)' is true
* eNB sends 4 SDUs [1..5] [6..10] [11.20] [21..30], not received
* eNB retx with smaller PDUs, not received
* eNB retx with still smaller PDUs, not received
* then UE reception on
* then custom PDUs, first a small part of head of original PDU
* then a bigger part, covering the first part
* so that the beginning of this part triggers the 'while'
* then eNB reception on, all passes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 5,
ENB_SDU, 1, 5,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
TIME, 2,
ENB_PDU_SIZE, 25,
TIME, 48,
ENB_PDU_SIZE, 15,
TIME, 99,
UE_RECV_FAILS, 0,
ENB_PDU, 15, 0xec, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
ENB_PDU, 25, 0xec, 0x00, 0x00, 0x00, 0x80, 0x50, 0x05, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
TIME, 100,
ENB_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test function process_received_ack with something in
* the retransmit_list to put in the ack_list
* eNB sends 4 PDUs, not received
* eNB retransmits 4th PDU, received, ACKed with NACKs for PDU 1, 2, 3
* UE receives custom PDU for 1, 2, 3, 4 (they are not sent by eNB)
* (4 resent to have the P bit set)
* UE sends ACK for all, eNB puts from retransmit_list to ack_list
*
* Maybe not very realistic (custom PDUs).
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_PDU_SIZE, 12,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
TIME, 10,
UE_RECV_FAILS, 0,
ENB_RECV_FAILS, 0,
TIME, 87,
ENB_PDU, 12, 0x80, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
ENB_PDU, 12, 0x80, 0x01, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
ENB_PDU, 12, 0x80, 0x02, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
ENB_PDU, 12, 0xa0, 0x03, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
TIME, -1
/*
* rlc am test max_retx_reached
* eNB sends PDU, never received
*/
TIME, 1,
MUST_FAIL,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 10,
TIME, -1
/*
* rlc am test so_overlap
* eNB sends PDU, not received
* then PDU is segmented in 3 parts, part 1 & 3 not received,
* then we generate a fake control PDU from UE to eNB that
* contains NACK with so_start/so_end being inside part 2.
*
* code to generate fake control PDU:
* rlc_pdu_encoder_init(&e, out, 100);
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
* rlc_pdu_encoder_put_bits(&e, 2, 10); // ack_sn
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 1, 10); // nack_sn
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e2
* rlc_pdu_encoder_put_bits(&e, 14, 15); // so_start
* rlc_pdu_encoder_put_bits(&e, 16, 15); // so_end
* rlc_pdu_encoder_align(&e);
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 8,
ENB_RECV_FAILS, 1,
TIME, 2,
UE_RECV_FAILS, 1,
ENB_SDU, 1, 30,
TIME, 20,
ENB_PDU_SIZE, 14,
TIME, 48,
UE_RECV_FAILS, 0,
TIME, 49,
UE_RECV_FAILS, 1,
TIME, 50,
UE_RECV_FAILS, 0,
TIME, 60,
ENB_RECV_FAILS, 0,
UE_PDU, 8, 0x00, 0x0a, 0x00, 0xa0, 0x03, 0x80, 0x08, 0x00,
TIME, 70,
UE_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test process_received_nack
* Same events as for test15 except the fake control PDU
* does not ACK anything (ack_sn = 0) so that PDU in the
* wait_list are not transfered into the ack_list and
* we cover the case:
* } else {
* prev = cur;
* cur = cur->next;
* }
* for the wait_list case.
*
* code to generate fake control PDU:
* rlc_pdu_encoder_init(&e, out, 100);
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
* rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 1, 10); // nack_sn
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e2
* rlc_pdu_encoder_put_bits(&e, 14, 15); // so_start
* rlc_pdu_encoder_put_bits(&e, 16, 15); // so_end
* rlc_pdu_encoder_align(&e);
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 8,
ENB_RECV_FAILS, 1,
TIME, 2,
UE_RECV_FAILS, 1,
ENB_SDU, 1, 30,
TIME, 20,
ENB_PDU_SIZE, 14,
TIME, 48,
UE_RECV_FAILS, 0,
TIME, 49,
UE_RECV_FAILS, 1,
TIME, 50,
UE_RECV_FAILS, 0,
TIME, 60,
ENB_RECV_FAILS, 0,
UE_PDU, 8, 0x00, 0x02, 0x00, 0xa0, 0x03, 0x80, 0x08, 0x00,
TIME, 70,
UE_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test function process_received_nack
* case 'check that VT(A) <= sn < VT(S)'
* eNB sends PDU, not received, resends segmented
* we generate a fake control PDU containing nack_sn == 10,
* to fail the 'check ...' and cover the return.
*
* code to generate fake control PDU:
* rlc_pdu_encoder_init(&e, out, 100);
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 3); // CPT
* rlc_pdu_encoder_put_bits(&e, 0, 10); // ack_sn
* rlc_pdu_encoder_put_bits(&e, 1, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 10, 10); // nack_sn
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e1
* rlc_pdu_encoder_put_bits(&e, 0, 1); // e2
* rlc_pdu_encoder_align(&e);
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 30,
ENB_RECV_FAILS, 1,
TIME, 20,
ENB_PDU_SIZE, 14,
TIME, 60,
ENB_RECV_FAILS, 0,
UE_PDU, 4, 0x00, 0x02, 0x05, 0x00,
TIME, -1
/*
* test rlc am simulate rx pdu buffer full
* eNB sends too big PDU to UE, rejected because buffer full
*/
TIME, 1,
MUST_FAIL,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 10, 10, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
TIME, -1
/*
* test rlc am bad PDU
* eNB sends custom PDUs to UE, all of them are wrong for a reason or another
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
/* data PDU, LI == 0
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 11); // LI
*/
ENB_PDU, 4, 0x84, 0x00, 0x00, 0x00,
/* data PDU, no data
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
*/
ENB_PDU, 2, 0x80, 0x00,
/* data PDU, LI == 2 > data size == 1
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 10); // SN
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 2, 11); // LI
* rlc_pdu_encoder_align(&e);
* rlc_pdu_encoder_put_bits(&e, 0, 8); // 1 byte of data
*/
ENB_PDU, 5, 0x84, 0x00, 0x00, 0x20, 0x00,
/* control PDU, CPT != 0
* rlc_pdu_encoder_put_bits(&e, 0, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 2, 3); // CPT
*/
ENB_PDU, 1, 0x20,
/* data PDU, but only 1 byte
* rlc_pdu_encoder_put_bits(&e, 1, 1); // D/C
* rlc_pdu_encoder_put_bits(&e, 0, 1); // RF
* rlc_pdu_encoder_put_bits(&e, 0, 1); // P
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
*/
ENB_PDU, 1, 0x84,
TIME, -1
/*
* basic am test:
* at time 1, eNB receives an SDU of 16000 bytes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 16000,
TIME, -1
/*
* rlc am test full tx window
* for that eNB sends a lot of small PDUs
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 513,
ENB_PDU_SIZE, 3,
ENB_RECV_FAILS, 1,
ENB_BUFFER_STATUS,
TIME, 511,
UE_BUFFER_STATUS,
TIME, 512,
UE_BUFFER_STATUS,
TIME, 513,
UE_BUFFER_STATUS,
TIME, 557,
ENB_BUFFER_STATUS,
TIME, 558,
ENB_BUFFER_STATUS,
TIME, 559,
ENB_BUFFER_STATUS,
TIME, 600,
ENB_BUFFER_STATUS,
ENB_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test big SDU (size > 2047)
* first generate SDU with exactly 2047 bytes
* later on generate SDU with exactly 2048 bytes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 20,
ENB_SDU, 1, 2047,
ENB_SDU, 2, 20,
ENB_PDU_SIZE, 2200,
TIME, 10,
ENB_SDU, 3, 20,
ENB_SDU, 4, 2048,
ENB_SDU, 5, 20,
TIME, -1
/*
* am test: ask for retx with TX buffer too small
* then ask for status with buffer too small
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 100,
UE_RECV_FAILS, 1,
TIME, 47,
ENB_PDU_SIZE, 4,
ENB_BUFFER_STATUS,
UE_BUFFER_STATUS,
TIME, 48,
ENB_PDU_SIZE, 1000,
UE_PDU_SIZE, 1,
UE_BUFFER_STATUS,
UE_RECV_FAILS, 0,
TIME, 49,
UE_BUFFER_STATUS,
TIME, 50,
UE_PDU_SIZE, 1000,
UE_BUFFER_STATUS,
TIME, -1
/*
* am test: basic test with poll_byte == 1
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, 1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, 1, 4,
ENB_SDU, 0, 30,
ENB_PDU_SIZE, 10,
TIME, -1
/*
* am test: basic test with poll_pdu == 2
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, 2, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, 2, -1, 4,
ENB_SDU, 0, 50,
ENB_PDU_SIZE, 10,
TIME, -1
/*
* am test: reject SDU because not enough room in rx buffer
*/
TIME, 1,
ENB_AM, 10, 10, 35, 0, 45, -1, -1, 4,
UE_AM, 10, 10, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 50,
TIME, -1
/*
* am test: test function check_t_poll_retransmit
* case 'PDU with SN = VT(S)-1 not found?'
* eNB sends some PDUs, UE receives none
* then UE receives the first retransmitted PDU and nothing more
* until poll retransmit occurs again in the eNB to trigger the case
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
UE_RECV_FAILS, 1,
TIME, 2,
ENB_SDU, 1, 10,
TIME, 3,
ENB_SDU, 2, 10,
TIME, 4,
ENB_SDU, 3, 10,
TIME, 50,
UE_RECV_FAILS, 0,
TIME, 51,
UE_RECV_FAILS, 1,
TIME, 100,
UE_RECV_FAILS, 0,
TIME, -1
/*
* am test: test function check_t_poll_retransmit
* case 'do we meet conditions of 36.322 5.2.2.3?'
* eNB sends one PDU, UE does not receive
* just before calling check_t_poll_retransmit, eNB receives a new SDU
* for the function 'check_poll_after_pdu_assembly' to fail
* then UE receives all what eNB sends
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
UE_RECV_FAILS, 1,
TIME, 47,
ENB_SDU, 1, 10,
UE_RECV_FAILS, 0,
TIME, -1
/*
* am test: test function check_t_reordering,
* case 'update VR(MS) to first SN >= VR(X) for which not
* all PDU segments have been received'
* eNB sends 3 PDUs, first not received, two others received
* later on, everything is received
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
UE_RECV_FAILS, 1,
TIME, 2,
UE_RECV_FAILS, 0,
ENB_SDU, 1, 10,
TIME, 3,
ENB_SDU, 2, 10,
TIME, -1
/*
* am test: test function check_t_reordering,
* case 'VR(H) > VR(MS)'
* eNB sends 4 PDUs, only 1st and 3rd are received
* later on, everything is received
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
UE_RECV_FAILS, 1,
TIME, 2,
UE_RECV_FAILS, 0,
ENB_SDU, 1, 10,
TIME, 3,
UE_RECV_FAILS, 1,
ENB_SDU, 2, 10,
TIME, 4,
UE_RECV_FAILS, 0,
ENB_SDU, 3, 10,
TIME, -1
/*
* basic am test:
* at time 1, eNB receives an SDU of 16001 bytes
*/
TIME, 1,
MUST_FAIL,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 16001,
TIME, -1
/*
* am test: test function generate_status
* enter the while loop 'go to highest full sn+1 for ACK'
* eNB sends several PDUs, only the last is received
* UE sends status PDU of a chosen size that let the code enter the while
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 70,
ENB_PDU_SIZE, 12,
UE_RECV_FAILS, 1,
TIME, 7,
UE_RECV_FAILS, 0,
UE_PDU_SIZE, 12,
TIME, -1
/*
* um test: several SDUs in a PDU (field length 5 bits)
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_SDU, 0, 10,
ENB_SDU, 1, 20,
ENB_SDU, 2, 30,
TIME, -1
/*
* um test: several SDUs in a PDU (field length 10 bits)
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 10,
UE_UM, 100000, 100000, 35, 10,
ENB_SDU, 0, 10,
ENB_SDU, 1, 20,
ENB_SDU, 2, 30,
TIME, -1
/*
* um test: test function rlc_um_reassemble_pdu, discard SDU
* case '!(fi & 0x02'
* eNB sends 33 PDUs covering 1 SDU, only PDU 0 received (with SN=0 and FI=1)
* then eNB sends 1 PDU covering 1 SDU (so SN=1 and FI=0 for this one)
* received by UE
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_SDU, 0, 33,
ENB_PDU_SIZE, 2,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 34,
UE_RECV_FAILS, 0,
ENB_SDU, 1, 1,
TIME, -1
/*
* um test: trigger some cases in rlc_um_reception_actions
* eNB sends several PDUs, only the beginning PDUs and ending PDUs are
* received. Middle PDUs are not.
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_SDU, 0, 40,
ENB_PDU_SIZE, 2,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 8,
UE_RECV_FAILS, 0,
TIME, -1
/*
* um: discard PDU because rx buffer full
* eNB sends a PDU too big
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 10, 10, 35, 5,
ENB_SDU, 0, 40,
TIME, -1
/*
* um: discard according to 36.322 5.1.2.2.2
* eNB sends many PDUs. 1st is received, then not, then again.
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_SDU, 0, 33,
ENB_PDU_SIZE, 2,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 22,
UE_RECV_FAILS, 0,
TIME, -1
/*
* um: some wrong PDUs
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
/* LI == 0
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 5); // SN
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 11); // LI
*/
ENB_PDU, 3, 0x20, 0x00, 0x00,
/* no data
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 5); // SN
*/
ENB_PDU, 1, 0x00,
/* LI == 2 >= data_size == 1
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 5); // SN
* rlc_pdu_encoder_put_bits(&e, 0, 1); // E
* rlc_pdu_encoder_put_bits(&e, 2, 11); // LI
* rlc_pdu_encoder_align(&e);
* rlc_pdu_encoder_put_bits(&e, 0, 8); // 1 byte of data
*/
ENB_PDU, 4, 0x20, 0x00, 0x20, 0x00,
/* PDU with E == 1 but has size 1 byte only (truncated PDU)
* rlc_pdu_encoder_put_bits(&e, 0, 2); // FI
* rlc_pdu_encoder_put_bits(&e, 1, 1); // E
* rlc_pdu_encoder_put_bits(&e, 0, 5); // SN
*/
ENB_PDU, 1, 0x20,
TIME, -1
/*
* um: test some cases of functions tx_pdu_size and rlc_entity_um_generate_pdu
* eNB has too much data to fit in one PDU
* then later eNB wants to send an SDU of size > 2047
* then later eNB sends several SDUs in one PDU
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_PDU_SIZE, 2050,
ENB_SDU, 0, 1500,
ENB_SDU, 1, 1500,
ENB_SDU, 2, 10,
TIME, 10,
ENB_SDU, 3, 2048,
ENB_SDU, 4, 10,
TIME, 20,
ENB_SDU, 5, 10,
ENB_SDU, 6, 10,
ENB_SDU, 7, 10,
ENB_SDU, 8, 10,
TIME, -1
/*
* um: SDU too big
*/
TIME, 1,
MUST_FAIL,
ENB_UM, 10, 10, 35, 5,
UE_UM, 100, 100, 35, 5,
ENB_SDU, 0, 16001,
TIME, -1
/*
* basic um test: UE field length 5 bits
* at time 1, eNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
ENB_SDU, 0, 10,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* um: not enough room in SDU list
*/
TIME, 1,
ENB_UM, 10, 10, 35, 5,
UE_UM, 100, 100, 35, 5,
ENB_SDU, 0, 20,
ENB_BUFFER_STATUS,
TIME, -1
/*
* um: test function check_t_reordering
* eNB sends PDUs, UE receives some and some not
*/
TIME, 1,
ENB_UM, 10000, 10000, 35, 5,
UE_UM, 10000, 10000, 35, 5,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
ENB_SDU, 4, 10,
ENB_SDU, 5, 10,
ENB_SDU, 6, 10,
ENB_SDU, 7, 10,
ENB_SDU, 8, 10,
ENB_SDU, 9, 10,
ENB_SDU, 10, 10,
ENB_SDU, 11, 10,
ENB_SDU, 12, 10,
ENB_SDU, 13, 10,
ENB_SDU, 14, 10,
ENB_SDU, 15, 10,
ENB_SDU, 16, 10,
ENB_SDU, 17, 10,
ENB_SDU, 18, 10,
ENB_SDU, 19, 10,
ENB_SDU, 20, 10,
ENB_SDU, 21, 10,
ENB_SDU, 22, 10,
ENB_SDU, 23, 10,
ENB_SDU, 24, 10,
ENB_SDU, 25, 10,
ENB_PDU_SIZE, 40,
TIME, 2,
UE_RECV_FAILS, 1,
TIME, 3,
UE_RECV_FAILS, 0,
TIME, 6,
UE_RECV_FAILS, 1,
TIME, 7,
UE_RECV_FAILS, 0,
TIME, 8,
UE_RECV_FAILS, 1,
TIME, -1
/*
* am test: test rlc_entity_am_discard_sdu
* eNB and UE get some SDU, later on some are discarded
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
ENB_PDU_SIZE, 23,
TIME, 2,
ENB_DISCARD_SDU, 0,
ENB_DISCARD_SDU, 2,
ENB_DISCARD_SDU, 3,
ENB_DISCARD_SDU, 1,
TIME, 10,
UE_SDU, 0, 5,
UE_SDU, 1, 5,
UE_SDU, 2, 5,
UE_SDU, 3, 5,
UE_SDU, 4, 5,
UE_SDU, 5, 5,
UE_PDU_SIZE, 13,
TIME, 12,
UE_DISCARD_SDU, 3,
UE_DISCARD_SDU, 1,
UE_DISCARD_SDU, 0,
UE_DISCARD_SDU, 5,
UE_DISCARD_SDU, 4,
UE_DISCARD_SDU, 2,
TIME, 30,
UE_SDU, 6, 5,
UE_DISCARD_SDU, 6,
TIME, 31,
UE_SDU, 7, 8,
TIME, -1
/*
* um test: test rlc_entity_um_discard_sdu
* eNB and UE get some SDU, later on some are discarded
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 10,
UE_UM, 100000, 100000, 35, 10,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
ENB_PDU_SIZE, 23,
TIME, 2,
ENB_DISCARD_SDU, 0,
ENB_DISCARD_SDU, 2,
ENB_DISCARD_SDU, 3,
ENB_DISCARD_SDU, 1,
TIME, 10,
UE_SDU, 0, 5,
UE_SDU, 1, 5,
UE_SDU, 2, 5,
UE_SDU, 3, 5,
UE_SDU, 4, 5,
UE_SDU, 5, 5,
UE_PDU_SIZE, 13,
TIME, 12,
UE_DISCARD_SDU, 3,
UE_DISCARD_SDU, 1,
UE_DISCARD_SDU, 0,
UE_DISCARD_SDU, 5,
UE_DISCARD_SDU, 4,
UE_DISCARD_SDU, 2,
TIME, 30,
UE_SDU, 6, 5,
UE_DISCARD_SDU, 6,
TIME, 31,
UE_SDU, 7, 8,
TIME, -1
/*
* am: test function rlc_entity_am_reestablishment
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
RE_ESTABLISH,
TIME, 2,
ENB_SDU, 0, 10,
RE_ESTABLISH,
TIME, 3,
ENB_SDU, 0, 40,
ENB_PDU_SIZE, 14,
UE_RECV_FAILS, 1,
TIME, 4,
UE_RECV_FAILS, 0,
TIME, 10,
RE_ESTABLISH,
TIME, -1
/*
* um: test function rlc_entity_am_reestablishment
* and also the function clear_entity, case 'while (cur_rx != NULL)'
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 5,
UE_UM, 100000, 100000, 35, 5,
RE_ESTABLISH,
TIME, 2,
ENB_SDU, 0, 10,
RE_ESTABLISH,
TIME, 3,
ENB_SDU, 0, 10,
ENB_SDU, 0, 10,
ENB_SDU, 0, 10,
ENB_SDU, 0, 10,
ENB_PDU_SIZE, 14,
TIME, 5,
UE_RECV_FAILS, 1,
TIME, 6,
UE_RECV_FAILS, 0,
TIME, 10,
RE_ESTABLISH,
TIME, 998,
ENB_SDU, 0, 10,
ENB_SDU, 0, 10,
UE_RECV_FAILS, 1,
TIME, 999,
UE_RECV_FAILS, 0,
TIME, -1
/*
* basic um test: UE field length 10 bits
* at time 1, eNB receives an SDU of 10 bytes
* at time 10, UE receives an SDU of 5 bytes
*/
TIME, 1,
ENB_UM, 100000, 100000, 35, 10,
UE_UM, 100000, 100000, 35, 10,
ENB_SDU, 0, 10,
TIME, 10,
UE_SDU, 0, 5,
TIME, -1
/*
* rlc am test function segment_already_received
* eNB sends SDU [1..900], not received
* eNB retx with smaller PDUs [1..600] [601..900]
* [1..600] is received but ACK/NACK not
* eNB retx with still smaller PDUs [1..400] [401..600] [601..900]
* all is received, ACKs/NACKs go through
*
* this test will fail if NACK mechanism uses SOstart/SOend
* (not implemented for the moment)
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 900,
TIME, 2,
ENB_PDU_SIZE, 600,
UE_RECV_FAILS, 0,
TIME, 48,
UE_RECV_FAILS, 1,
ENB_PDU_SIZE, 400,
TIME, 90,
UE_RECV_FAILS, 0,
ENB_RECV_FAILS, 0,
TIME, -1
/*
* rlc am test function rlc_am_segment_full
* eNB sends SDU [1..900], not received
* eNB retx with smaller PDUs [1..600] [601..900]
* nothing received
* eNB retx with still smaller PDUs [1..400] [401..600] [601..900]
* [401..600] received, ACK goes through
* link clean, all goes through
*
* this test will fail if NACK mechanism uses SOstart/SOend
* (not implemented for the moment)
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 900,
TIME, 2,
ENB_PDU_SIZE, 600,
TIME, 48,
ENB_PDU_SIZE, 400,
TIME, 95,
UE_RECV_FAILS, 0,
ENB_RECV_FAILS, 0,
TIME, -1
/*
* basic am test:
* at time 1, eNB receives 10 SDUs of 10 bytes
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
ENB_SDU, 0, 10,
ENB_SDU, 1, 10,
ENB_SDU, 2, 10,
ENB_SDU, 3, 10,
ENB_SDU, 4, 10,
ENB_SDU, 5, 10,
ENB_SDU, 6, 10,
ENB_SDU, 7, 10,
ENB_SDU, 8, 10,
ENB_SDU, 9, 10,
TIME, -1
/*
* rlc am test function rlc_am_reassemble_next_segment
* case 'if pdu_byte is not in [so .. so+len-1]'
* eNB sends SDU [1..30], not received
* eNB retx with smaller PDUs [1..21] [22..30], not received
* eNB retx with still smaller PDUs [1..11] [12..21] [22..30], not received
* custom PDU [12..21] sent to UE, received
* custom PDU [1..21] sent to UE, received
*
* Not sure if in a real setup [12..21] is sent and then [1..21] is sent.
* In the current RLC implementation, this is impossible. If we send [12..21]
* it means [1..21] has been split and so we won't sent it later on.
* Maybe with HARQ retransmissions in PHY/MAC in bad radio conditions?
*
* this test will fail if NACK mechanism uses SOstart/SOend
* (not implemented for the moment)
*/
TIME, 1,
ENB_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_AM, 100000, 100000, 35, 0, 45, -1, -1, 4,
UE_RECV_FAILS, 1,
ENB_RECV_FAILS, 1,
ENB_SDU, 0, 30,
TIME, 2,
ENB_PDU_SIZE, 25,
TIME, 48,
ENB_PDU_SIZE, 15,
TIME, 100,
UE_RECV_FAILS, 0,
ENB_RECV_FAILS, 0,
ENB_PDU, 14, 0xd8, 0x00, 0x00, 0x0b, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
TIME, 101,
ENB_PDU, 25, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
TIME, -1
......@@ -2134,6 +2134,8 @@ rrc_eNB_generate_RRCConnectionRelease(
{
uint8_t buffer[RRC_BUF_SIZE];
uint16_t size = 0;
int release_num;
memset(buffer, 0, RRC_BUF_SIZE);
T(T_ENB_RRC_CONNECTION_RELEASE, T_INT(ctxt_pP->module_id), T_INT(ctxt_pP->frame),
T_INT(ctxt_pP->subframe), T_INT(ctxt_pP->rnti));
......@@ -2161,12 +2163,9 @@ rrc_eNB_generate_RRCConnectionRelease(
ue_context_pP->ue_context.rnti,
rrc_eNB_mui,
size);
pthread_mutex_lock(&rrc_release_freelist);
while (pthread_mutex_trylock(&rrc_release_freelist)) {
/* spin... */
}
for (uint16_t release_num = 0; release_num < NUMBER_OF_UE_MAX; release_num++) {
for (release_num = 0; release_num < NUMBER_OF_UE_MAX; release_num++) {
if (rrc_release_info.RRC_release_ctrl[release_num].flag == 0) {
if (ue_context_pP->ue_context.ue_release_timer_s1 > 0) {
rrc_release_info.RRC_release_ctrl[release_num].flag = 1;
......@@ -2186,6 +2185,12 @@ rrc_eNB_generate_RRCConnectionRelease(
}
}
/* TODO: what to do if RRC_release_ctrl is full? For now, exit. */
if (release_num == NUMBER_OF_UE_MAX) {
LOG_E(RRC, "fatal: rrc_release_info.RRC_release_ctrl is full\n");
exit(1);
}
pthread_mutex_unlock(&rrc_release_freelist);
if (NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) {
......@@ -3512,6 +3517,35 @@ void rrc_eNB_generate_defaultRRCConnectionReconfiguration(const protocol_ctxt_t
size,
buffer,
PDCP_TRANSMISSION_MODE_CONTROL);
/* Refresh SRBs/DRBs */
rrc_pdcp_config_asn1_req(ctxt_pP,
*SRB_configList2, // NULL,
*DRB_configList,
NULL,
0xff, // already configured during the securitymodecommand
NULL,
NULL,
NULL
#if (LTE_RRC_VERSION >= MAKE_VERSION(9, 0, 0))
, (LTE_PMCH_InfoList_r9_t *) NULL
#endif
, NULL);
/* Refresh SRBs/DRBs */
if (!NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) {
rrc_rlc_config_asn1_req(ctxt_pP,
*SRB_configList2, // NULL,
*DRB_configList,
NULL
#if (LTE_RRC_VERSION >= MAKE_VERSION(9, 0, 0))
, (LTE_PMCH_InfoList_r9_t *) NULL,
0,
0
#endif
);
}
free(Sparams);
Sparams = NULL;
free(quantityConfig->quantityConfigEUTRA->filterCoefficientRSRP);
......@@ -5192,13 +5226,13 @@ rrc_eNB_generate_HO_RRCConnectionReconfiguration(const protocol_ctxt_t *const ct
DRB_rlc_config = CALLOC(1, sizeof(*DRB_rlc_config));
DRB_config->rlc_Config = DRB_rlc_config;
#ifdef RRC_DEFAULT_RAB_IS_AM
DRB_rlc_config->present = RLC_Config_PR_am;
DRB_rlc_config->choice.am.ul_AM_RLC.t_PollRetransmit = T_PollRetransmit_ms50;
DRB_rlc_config->choice.am.ul_AM_RLC.pollPDU = PollPDU_p16;
DRB_rlc_config->choice.am.ul_AM_RLC.pollByte = PollByte_kBinfinity;
DRB_rlc_config->choice.am.ul_AM_RLC.maxRetxThreshold = UL_AM_RLC__maxRetxThreshold_t8;
DRB_rlc_config->choice.am.dl_AM_RLC.t_Reordering = T_Reordering_ms35;
DRB_rlc_config->choice.am.dl_AM_RLC.t_StatusProhibit = T_StatusProhibit_ms25;
DRB_rlc_config->present = LTE_RLC_Config_PR_am;
DRB_rlc_config->choice.am.ul_AM_RLC.t_PollRetransmit = LTE_T_PollRetransmit_ms50;
DRB_rlc_config->choice.am.ul_AM_RLC.pollPDU = LTE_PollPDU_p16;
DRB_rlc_config->choice.am.ul_AM_RLC.pollByte = LTE_PollByte_kBinfinity;
DRB_rlc_config->choice.am.ul_AM_RLC.maxRetxThreshold = LTE_UL_AM_RLC__maxRetxThreshold_t8;
DRB_rlc_config->choice.am.dl_AM_RLC.t_Reordering = LTE_T_Reordering_ms35;
DRB_rlc_config->choice.am.dl_AM_RLC.t_StatusProhibit = LTE_T_StatusProhibit_ms25;
#else
DRB_rlc_config->present = LTE_RLC_Config_PR_um_Bi_Directional;
DRB_rlc_config->choice.um_Bi_Directional.ul_UM_RLC.sn_FieldLength = LTE_SN_FieldLength_size10;
......@@ -6029,6 +6063,35 @@ rrc_eNB_generate_HO_RRCConnectionReconfiguration(const protocol_ctxt_t *const ct
ue_context_pP->ue_context.rnti,
rrc_eNB_mui,
size);
/* Refresh SRBs/DRBs */
rrc_pdcp_config_asn1_req(ctxt_pP,
*SRB_configList2, // NULL,
*DRB_configList,
NULL,
0xff, // already configured during the securitymodecommand
NULL,
NULL,
NULL
#if (LTE_RRC_VERSION >= MAKE_VERSION(9, 0, 0))
, (LTE_PMCH_InfoList_r9_t *) NULL
#endif
, NULL);
/* Refresh SRBs/DRBs */
if (!NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) {
rrc_rlc_config_asn1_req(ctxt_pP,
*SRB_configList2, // NULL,
*DRB_configList,
NULL
#if (LTE_RRC_VERSION >= MAKE_VERSION(9, 0, 0))
, (LTE_PMCH_InfoList_r9_t *) NULL,
0,
0
#endif
);
}
free(quantityConfig->quantityConfigEUTRA->filterCoefficientRSRQ);
quantityConfig->quantityConfigEUTRA->filterCoefficientRSRQ = NULL;
free(quantityConfig->quantityConfigEUTRA->filterCoefficientRSRP);
......@@ -6072,12 +6135,14 @@ rrc_eNB_configure_rbs_handover(struct rrc_eNB_ue_context_s *ue_context_p, protoc
NULL,
NULL,
(LTE_PMCH_InfoList_r9_t *) NULL, NULL);
if (!NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) {
rrc_rlc_config_asn1_req(ctxt_pP,
ue_context_p->ue_context.SRB_configList,
(LTE_DRB_ToAddModList_t *) NULL,
(LTE_DRB_ToReleaseList_t *) NULL,
(LTE_PMCH_InfoList_r9_t *) NULL, 0, 0
);
}
if (EPC_MODE_ENABLED) {
rrc_eNB_process_security (
......@@ -6212,6 +6277,7 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete(
(LTE_PMCH_InfoList_r9_t *) NULL,
NULL);
/* Refresh SRBs/DRBs */
if (!NODE_IS_CU(RC.rrc[ctxt_pP->module_id]->node_type)) {
rrc_rlc_config_asn1_req(ctxt_pP,
SRB_configList, // NULL,
DRB_configList,
......@@ -6220,6 +6286,7 @@ rrc_eNB_process_RRCConnectionReconfigurationComplete(
0,
0
);
}
/* Set the SRB active in UE context */
if (SRB_configList != NULL) {
......@@ -8166,6 +8233,143 @@ void rrc_enb_init(void) {
memset(&rrc_release_info,0,sizeof(RRC_release_list_t));
}
//-----------------------------------------------------------------------------
void process_successful_rlc_sdu_indication(int instance,
int rnti,
int message_id)
{
int release_num;
int release_total;
RRC_release_ctrl_t *release_ctrl;
/* Check if the message sent was RRC Connection Release.
* If yes then continue the release process.
*/
pthread_mutex_lock(&rrc_release_freelist);
if (rrc_release_info.num_UEs > 0) {
release_total = 0;
for (release_num = 0, release_ctrl = &rrc_release_info.RRC_release_ctrl[0];
release_num < NUMBER_OF_UE_MAX;
release_num++, release_ctrl++) {
if(release_ctrl->flag > 0) {
release_total++;
} else {
continue;
}
if (release_ctrl->flag == 1 && release_ctrl->rnti == rnti && release_ctrl->rrc_eNB_mui == message_id) {
release_ctrl->flag = 3;
LOG_D(MAC,"DLSCH Release send:index %d rnti %x mui %d flag 1->3\n",
release_num,
rnti,
message_id);
break;
}
if (release_ctrl->flag == 2 && release_ctrl->rnti == rnti && release_ctrl->rrc_eNB_mui == message_id) {
release_ctrl->flag = 4;
LOG_D(MAC, "DLSCH Release send:index %d rnti %x mui %d flag 2->4\n",
release_num,
rnti,
message_id);
break;
}
if(release_total >= rrc_release_info.num_UEs)
break;
}
}
pthread_mutex_unlock(&rrc_release_freelist);
}
//-----------------------------------------------------------------------------
void process_unsuccessful_rlc_sdu_indication(int instance, int rnti)
{
int release_num;
int release_total;
RRC_release_ctrl_t *release_ctrl;
/* radio link failure detected by RLC layer, remove UE properly */
pthread_mutex_lock(&rrc_release_freelist);
/* first, check if the rnti is in the list rrc_release_info.RRC_release_ctrl */
if (rrc_release_info.num_UEs > 0) {
release_total = 0;
for (release_num = 0, release_ctrl = &rrc_release_info.RRC_release_ctrl[0];
release_num < NUMBER_OF_UE_MAX;
release_num++, release_ctrl++) {
if(release_ctrl->flag > 0) {
release_total++;
} else {
continue;
}
if (release_ctrl->flag == 1 && release_ctrl->rnti == rnti) {
release_ctrl->flag = 3;
LOG_D(MAC,"DLSCH Release send:index %d rnti %x flag 1->3\n",
release_num,
rnti);
goto done;
}
if (release_ctrl->flag == 2 && release_ctrl->rnti == rnti) {
release_ctrl->flag = 4;
LOG_D(MAC, "DLSCH Release send:index %d rnti %x flag 2->4\n",
release_num,
rnti);
goto done;
}
if(release_total >= rrc_release_info.num_UEs)
break;
}
}
/* it's not in the list, put it with flag = 4 */
for (release_num = 0; release_num < NUMBER_OF_UE_MAX; release_num++) {
if (rrc_release_info.RRC_release_ctrl[release_num].flag == 0) {
rrc_release_info.RRC_release_ctrl[release_num].flag = 4;
rrc_release_info.RRC_release_ctrl[release_num].rnti = rnti;
rrc_release_info.RRC_release_ctrl[release_num].rrc_eNB_mui = -1; /* not defined */
rrc_release_info.num_UEs++;
LOG_D(RRC, "radio link failure detected: index %d rnti %x flag %d \n",
release_num,
rnti,
rrc_release_info.RRC_release_ctrl[release_num].flag);
break;
}
}
/* TODO: what to do if rrc_release_info.RRC_release_ctrl is full? */
if (release_num == NUMBER_OF_UE_MAX) {
LOG_E(RRC, "fatal: radio link failure: rrc_release_info.RRC_release_ctrl is full\n");
exit(1);
}
done:
pthread_mutex_unlock(&rrc_release_freelist);
}
//-----------------------------------------------------------------------------
void process_rlc_sdu_indication(int instance,
int rnti,
int is_successful,
int srb_id,
int message_id)
{
if (is_successful)
process_successful_rlc_sdu_indication(instance, rnti, message_id);
else
process_unsuccessful_rlc_sdu_indication(instance, rnti);
}
//-----------------------------------------------------------------------------
int add_ue_to_remove(struct rrc_eNB_ue_context_s **ue_to_be_removed,
int removed_ue_count,
......@@ -8868,6 +9072,13 @@ void *rrc_enb_process_itti_msg(void *notUsed) {
rrc_eNB_process_M2AP_MCE_CONFIGURATION_UPDATE(&ctxt,&M2AP_MCE_CONFIGURATION_UPDATE(msg_p));
break;
case RLC_SDU_INDICATION:
process_rlc_sdu_indication(instance,
RLC_SDU_INDICATION(msg_p).rnti,
RLC_SDU_INDICATION(msg_p).is_successful,
RLC_SDU_INDICATION(msg_p).srb_id,
RLC_SDU_INDICATION(msg_p).message_id);
break;
default:
LOG_E(RRC, "[eNB %d] Received unexpected message %s\n", instance, msg_name_p);
......
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