To minimize migration effort, we follow the logic of legacy macro Rel10 and Rel14 used for conditional compilation of current code. In the future, they might be changed to accommodate finer software configuration, e.g. MAKE_VERSION(12,2,0) or MAKE_VERSION(13,1,0) .... etc.
816 lines
24 KiB
C
816 lines
24 KiB
C
/*
|
|
* 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
|
|
*/
|
|
|
|
/*! \file eNB_scheduler_mch.c
|
|
* \brief procedures related to eNB for the MCH transport channel
|
|
* \author Navid Nikaein and Raymond Knopp
|
|
* \date 2012 - 2014
|
|
* \email: navid.nikaein@eurecom.fr
|
|
* \version 1.0
|
|
* @ingroup _mac
|
|
|
|
*/
|
|
|
|
|
|
#include "LAYER2/MAC/mac.h"
|
|
#include "LAYER2/MAC/mac_proto.h"
|
|
#include "LAYER2/MAC/mac_extern.h"
|
|
#include "UTIL/LOG/log.h"
|
|
#include "UTIL/LOG/vcd_signal_dumper.h"
|
|
#include "UTIL/OPT/opt.h"
|
|
#include "OCG.h"
|
|
#include "OCG_extern.h"
|
|
#include "PHY/LTE_TRANSPORT/transport_common_proto.h"
|
|
|
|
#include "RRC/LTE/rrc_extern.h"
|
|
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
|
|
|
|
//#include "LAYER2/MAC/pre_processor.c"
|
|
#include "pdcp.h"
|
|
#include "assertions.h"
|
|
|
|
#if defined(ENABLE_ITTI)
|
|
#include "intertask_interface.h"
|
|
#endif
|
|
|
|
#include "SIMULATION/TOOLS/sim.h" // for taus
|
|
|
|
#define ENABLE_MAC_PAYLOAD_DEBUG
|
|
#define DEBUG_eNB_SCHEDULER 1
|
|
#include "common/ran_context.h"
|
|
|
|
extern RAN_CONTEXT_t RC;
|
|
|
|
#if (RRC_VERSION >= MAKE_VERSION(10, 0, 0))
|
|
int8_t
|
|
get_mbsfn_sf_alloction(module_id_t module_idP, uint8_t CC_id,
|
|
uint8_t mbsfn_sync_area)
|
|
{
|
|
// currently there is one-to-one mapping between sf allocation pattern and sync area
|
|
if (mbsfn_sync_area > MAX_MBSFN_AREA) {
|
|
LOG_W(MAC,
|
|
"[eNB %d] CC_id %d MBSFN synchronization area %d out of range\n ",
|
|
module_idP, CC_id, mbsfn_sync_area);
|
|
return -1;
|
|
} else if (RC.mac[module_idP]->
|
|
common_channels[CC_id].mbsfn_SubframeConfig[mbsfn_sync_area]
|
|
!= NULL) {
|
|
return mbsfn_sync_area;
|
|
} else {
|
|
LOG_W(MAC,
|
|
"[eNB %d] CC_id %d MBSFN Subframe Config pattern %d not found \n ",
|
|
module_idP, CC_id, mbsfn_sync_area);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
int
|
|
schedule_MBMS(module_id_t module_idP, uint8_t CC_id, frame_t frameP,
|
|
sub_frame_t subframeP)
|
|
{
|
|
|
|
int mcch_flag = 0, mtch_flag = 0, msi_flag = 0;
|
|
int mbsfn_period = 0; // 1<<(RC.mac[module_idP]->mbsfn_SubframeConfig[0]->radioframeAllocationPeriod);
|
|
int mcch_period = 0; //32<<(RC.mac[module_idP]->mbsfn_AreaInfo[0]->mcch_Config_r9.mcch_RepetitionPeriod_r9);
|
|
int mch_scheduling_period =
|
|
8 << (RC.mac[module_idP]->common_channels[CC_id].
|
|
pmch_Config[0]->mch_SchedulingPeriod_r9);
|
|
unsigned char mcch_sdu_length;
|
|
unsigned char header_len_mcch = 0, header_len_msi =
|
|
0, header_len_mtch = 0, header_len_mtch_temp =
|
|
0, header_len_mcch_temp = 0, header_len_msi_temp = 0;
|
|
int ii = 0, msi_pos = 0;
|
|
int mcch_mcs = -1;
|
|
uint16_t TBS, j = -1, padding = 0, post_padding = 0;
|
|
mac_rlc_status_resp_t rlc_status;
|
|
int num_mtch;
|
|
int msi_length, i, k;
|
|
unsigned char sdu_lcids[11], num_sdus = 0, offset = 0;
|
|
uint16_t sdu_lengths[11], sdu_length_total = 0;
|
|
unsigned char mch_buffer[MAX_DLSCH_PAYLOAD_BYTES]; // check the max value, this is for dlsch only
|
|
|
|
COMMON_channels_t *cc = &RC.mac[module_idP]->common_channels[CC_id];
|
|
|
|
cc->MCH_pdu.Pdu_size = 0;
|
|
|
|
for (i = 0; i < cc->num_active_mbsfn_area; i++) {
|
|
// assume, that there is always a mapping
|
|
if ((j = get_mbsfn_sf_alloction(module_idP, CC_id, i)) == -1) {
|
|
return 0;
|
|
}
|
|
|
|
mbsfn_period =
|
|
1 << (cc->mbsfn_SubframeConfig[j]->radioframeAllocationPeriod);
|
|
mcch_period =
|
|
32 << (cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_RepetitionPeriod_r9);
|
|
msi_pos = 0;
|
|
ii = 0;
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d : Checking MBSFN Sync Area %d/%d with SF allocation %d/%d for MCCH and MTCH (mbsfn period %d, mcch period %d)\n",
|
|
module_idP, CC_id, frameP, subframeP, i,
|
|
cc->num_active_mbsfn_area, j, cc->num_sf_allocation_pattern,
|
|
mbsfn_period, mcch_period);
|
|
|
|
|
|
switch (cc->mbsfn_AreaInfo[i]->mcch_Config_r9.signallingMCS_r9) {
|
|
case 0:
|
|
mcch_mcs = 2;
|
|
break;
|
|
|
|
case 1:
|
|
mcch_mcs = 7;
|
|
break;
|
|
|
|
case 2:
|
|
mcch_mcs = 13;
|
|
break;
|
|
|
|
case 3:
|
|
mcch_mcs = 19;
|
|
break;
|
|
}
|
|
|
|
// 1st: Check the MBSFN subframes from SIB2 info (SF allocation pattern i, max 8 non-overlapping patterns exist)
|
|
if (frameP % mbsfn_period == cc->mbsfn_SubframeConfig[j]->radioframeAllocationOffset) { // MBSFN frameP
|
|
if (cc->mbsfn_SubframeConfig[j]->subframeAllocation.present == MBSFN_SubframeConfig__subframeAllocation_PR_oneFrame) { // one-frameP format
|
|
|
|
// Find the first subframeP in this MCH to transmit MSI
|
|
if (frameP % mch_scheduling_period ==
|
|
cc->mbsfn_SubframeConfig[j]->
|
|
radioframeAllocationOffset) {
|
|
while (ii == 0) {
|
|
ii = cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & (0x80 >> msi_pos);
|
|
msi_pos++;
|
|
}
|
|
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d : sync area %d sf allocation pattern %d sf alloc %x msi pos is %d \n",
|
|
module_idP, CC_id, frameP, subframeP, i, j,
|
|
cc->mbsfn_SubframeConfig[j]->
|
|
subframeAllocation.choice.oneFrame.buf[0],
|
|
msi_pos);
|
|
}
|
|
// Check if the subframeP is for MSI, MCCH or MTCHs and Set the correspoding flag to 1
|
|
switch (subframeP) {
|
|
case 1:
|
|
if (cc->tdd_Config == NULL) {
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF1) ==
|
|
MBSFN_FDD_SF1) {
|
|
if (msi_pos == 1) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF1) ==
|
|
MBSFN_FDD_SF1)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
|
if (cc->tdd_Config == NULL) {
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF2) ==
|
|
MBSFN_FDD_SF2) {
|
|
if (msi_pos == 2) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF2) ==
|
|
MBSFN_FDD_SF2)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
|
if (cc->tdd_Config != NULL) { // TDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_TDD_SF3) ==
|
|
MBSFN_TDD_SF3) {
|
|
if (msi_pos == 1) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_TDD_SF3) ==
|
|
MBSFN_TDD_SF3)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
} else { // FDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF3) ==
|
|
MBSFN_FDD_SF3) {
|
|
if (msi_pos == 3) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF3) ==
|
|
MBSFN_FDD_SF3)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 4:
|
|
if (cc->tdd_Config != NULL) {
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_TDD_SF4) ==
|
|
MBSFN_TDD_SF4) {
|
|
if (msi_pos == 2) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_TDD_SF4) ==
|
|
MBSFN_TDD_SF4)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
if (cc->tdd_Config == NULL) {
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF6) ==
|
|
MBSFN_FDD_SF6) {
|
|
if (msi_pos == 4) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF6) ==
|
|
MBSFN_FDD_SF6)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 7:
|
|
if (cc->tdd_Config != NULL) { // TDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_TDD_SF7) ==
|
|
MBSFN_TDD_SF7) {
|
|
if (msi_pos == 3) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_TDD_SF7) ==
|
|
MBSFN_TDD_SF7)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
} else { // FDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF7) ==
|
|
MBSFN_FDD_SF7) {
|
|
if (msi_pos == 5) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF7) ==
|
|
MBSFN_FDD_SF7)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 8:
|
|
if (cc->tdd_Config != NULL) { //TDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_TDD_SF8) ==
|
|
MBSFN_TDD_SF8) {
|
|
if (msi_pos == 4) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_TDD_SF8) ==
|
|
MBSFN_TDD_SF8)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
} else { // FDD
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_FDD_SF8) ==
|
|
MBSFN_FDD_SF8) {
|
|
if (msi_pos == 6) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_FDD_SF8) ==
|
|
MBSFN_FDD_SF8)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
|
if (cc->tdd_Config != NULL) {
|
|
if ((cc->
|
|
mbsfn_SubframeConfig[j]->subframeAllocation.
|
|
choice.oneFrame.buf[0] & MBSFN_TDD_SF9) ==
|
|
MBSFN_TDD_SF9) {
|
|
if (msi_pos == 5) {
|
|
msi_flag = 1;
|
|
}
|
|
|
|
if ((frameP % mcch_period ==
|
|
cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.mcch_Offset_r9)
|
|
&&
|
|
((cc->mbsfn_AreaInfo[i]->
|
|
mcch_Config_r9.sf_AllocInfo_r9.
|
|
buf[0] & MBSFN_TDD_SF9) ==
|
|
MBSFN_TDD_SF9)) {
|
|
mcch_flag = 1;
|
|
}
|
|
|
|
mtch_flag = 1;
|
|
}
|
|
}
|
|
|
|
break;
|
|
} // end switch
|
|
|
|
// sf allocation is non-overlapping
|
|
if ((msi_flag == 1) || (mcch_flag == 1)
|
|
|| (mtch_flag == 1)) {
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d Subframe %d: sync area %d SF alloc %d: msi flag %d, mcch flag %d, mtch flag %d\n",
|
|
module_idP, CC_id, frameP, subframeP, i, j,
|
|
msi_flag, mcch_flag, mtch_flag);
|
|
break;
|
|
}
|
|
} else { // four-frameP format
|
|
}
|
|
}
|
|
} // end of for loop
|
|
|
|
cc->msi_active = 0;
|
|
cc->mcch_active = 0;
|
|
cc->mtch_active = 0;
|
|
|
|
// Calculate the mcs
|
|
if ((msi_flag == 1) || (mcch_flag == 1)) {
|
|
cc->MCH_pdu.mcs = mcch_mcs;
|
|
} else if (mtch_flag == 1) { // only MTCH in this subframeP
|
|
cc->MCH_pdu.mcs = cc->pmch_Config[0]->dataMCS_r9;
|
|
}
|
|
// 2nd: Create MSI, get MCCH from RRC and MTCHs from RLC
|
|
|
|
// there is MSI (MCH Scheduling Info)
|
|
if (msi_flag == 1) {
|
|
// Create MSI here
|
|
uint16_t msi_control_element[29], *msi_ptr;
|
|
|
|
msi_ptr = &msi_control_element[0];
|
|
((MSI_ELEMENT *) msi_ptr)->lcid = MCCH_LCHANID; //MCCH
|
|
|
|
if (mcch_flag == 1) {
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0;
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0;
|
|
} else { // no mcch for this MSP
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0x7; // stop value is 2047
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0xff;
|
|
}
|
|
|
|
msi_ptr += sizeof(MSI_ELEMENT);
|
|
|
|
//Header for MTCHs
|
|
num_mtch = cc->mbms_SessionList[0]->list.count;
|
|
|
|
for (k = 0; k < num_mtch; k++) { // loop for all session in this MCH (MCH[0]) at this moment
|
|
((MSI_ELEMENT *) msi_ptr)->lcid = cc->mbms_SessionList[0]->list.array[k]->logicalChannelIdentity_r9; //mtch_lcid;
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_MSB = 0; // last subframeP of this mtch (only one mtch now)
|
|
((MSI_ELEMENT *) msi_ptr)->stop_sf_LSB = 0xB;
|
|
msi_ptr += sizeof(MSI_ELEMENT);
|
|
}
|
|
|
|
msi_length = msi_ptr - msi_control_element;
|
|
|
|
if (msi_length < 128) {
|
|
header_len_msi = 2;
|
|
} else {
|
|
header_len_msi = 3;
|
|
}
|
|
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d : MSI->MCH, length of MSI is %d bytes \n",
|
|
module_idP, CC_id, frameP, msi_length);
|
|
//LOG_D(MAC,"Scheduler: MSI is transmitted in this subframeP \n" );
|
|
|
|
// LOG_D(MAC,"Scheduler: MSI length is %d bytes\n",msi_length);
|
|
// Store MSI data to mch_buffer[0]
|
|
memcpy((char *) &mch_buffer[sdu_length_total],
|
|
msi_control_element, msi_length);
|
|
|
|
sdu_lcids[num_sdus] = MCH_SCHDL_INFO;
|
|
sdu_lengths[num_sdus] = msi_length;
|
|
sdu_length_total += sdu_lengths[num_sdus];
|
|
LOG_I(MAC, "[eNB %d] CC_id %d Create %d bytes for MSI\n",
|
|
module_idP, CC_id, sdu_lengths[num_sdus]);
|
|
num_sdus++;
|
|
cc->msi_active = 1;
|
|
}
|
|
// there is MCCH
|
|
if (mcch_flag == 1) {
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d Subframe %d: Schedule MCCH MESSAGE (area %d, sfAlloc %d)\n",
|
|
module_idP, CC_id, frameP, subframeP, i, j);
|
|
|
|
mcch_sdu_length = mac_rrc_data_req(module_idP, CC_id, frameP, MCCH, 1, &cc->MCCH_pdu.payload[0],
|
|
i); // this is the mbsfn sync area index
|
|
|
|
if (mcch_sdu_length > 0) {
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d : MCCH->MCH, Received %d bytes from RRC \n",
|
|
module_idP, CC_id, frameP, subframeP, mcch_sdu_length);
|
|
|
|
header_len_mcch = 2;
|
|
|
|
if (cc->tdd_Config != NULL) {
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d: Scheduling MCCH->MCH (TDD) for MCCH message %d bytes (mcs %d )\n",
|
|
module_idP, CC_id, frameP, subframeP,
|
|
mcch_sdu_length, mcch_mcs);
|
|
} else {
|
|
LOG_I(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d: Scheduling MCCH->MCH (FDD) for MCCH message %d bytes (mcs %d)\n",
|
|
module_idP, CC_id, frameP, subframeP,
|
|
mcch_sdu_length, mcch_mcs);
|
|
}
|
|
|
|
cc->mcch_active = 1;
|
|
|
|
memcpy((char *) &mch_buffer[sdu_length_total],
|
|
&cc->MCCH_pdu.payload[0], mcch_sdu_length);
|
|
sdu_lcids[num_sdus] = MCCH_LCHANID;
|
|
sdu_lengths[num_sdus] = mcch_sdu_length;
|
|
|
|
if (sdu_lengths[num_sdus] > 128) {
|
|
header_len_mcch = 3;
|
|
}
|
|
|
|
sdu_length_total += sdu_lengths[num_sdus];
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Got %d bytes for MCCH from RRC \n",
|
|
module_idP, CC_id, sdu_lengths[num_sdus]);
|
|
num_sdus++;
|
|
}
|
|
}
|
|
|
|
TBS =
|
|
get_TBS_DL(cc->MCH_pdu.mcs, to_prb(cc->mib->message.dl_Bandwidth));
|
|
#if (RRC_VERSION >= MAKE_VERSION(10, 0, 0))
|
|
// do not let mcch and mtch multiplexing when relaying is active
|
|
// for sync area 1, so not transmit data
|
|
//if ((i == 0) && ((RC.mac[module_idP]->MBMS_flag != multicast_relay) || (RC.mac[module_idP]->mcch_active==0))) {
|
|
#endif
|
|
|
|
// there is MTCHs, loop if there are more than 1
|
|
if (mtch_flag == 1) {
|
|
// Calculate TBS
|
|
/* if ((msi_flag==1) || (mcch_flag==1)) {
|
|
TBS = mac_xface->get_TBS(mcch_mcs, mac_xface->frame_parms->N_RB_DL);
|
|
}
|
|
else { // only MTCH in this subframeP
|
|
TBS = mac_xface->get_TBS(RC.mac[module_idP]->pmch_Config[0]->dataMCS_r9, mac_xface->frame_parms->N_RB_DL);
|
|
}
|
|
|
|
// get MTCH data from RLC (like for DTCH)
|
|
LOG_D(MAC,"[eNB %d] CC_id %d Frame %d subframe %d: Schedule MTCH (area %d, sfAlloc %d)\n",Mod_id,CC_id,frame,subframe,i,j);
|
|
|
|
header_len_mtch = 3;
|
|
LOG_D(MAC,"[eNB %d], CC_id %d, Frame %d, MTCH->MCH, Checking RLC status (rab %d, tbs %d, len %d)\n",
|
|
Mod_id,CC_id,frame,MTCH,TBS,
|
|
TBS-header_len_mcch-header_len_msi-sdu_length_total-header_len_mtch);
|
|
|
|
rlc_status = mac_rlc_status_ind(Mod_id,frame,1,RLC_MBMS_YES,MTCH+ (maxDRB + 3) * MAX_MOBILES_PER_RG,
|
|
TBS-header_len_mcch-header_len_msi-sdu_length_total-header_len_mtch);
|
|
printf("frame %d, subframe %d, rlc_status.bytes_in_buffer is %d\n",frame,subframe, rlc_status.bytes_in_buffer);
|
|
|
|
*/
|
|
|
|
// get MTCH data from RLC (like for DTCH)
|
|
LOG_D(MAC,
|
|
"[eNB %d] CC_id %d Frame %d subframeP %d: Schedule MTCH (area %d, sfAlloc %d)\n",
|
|
module_idP, CC_id, frameP, subframeP, i, j);
|
|
|
|
header_len_mtch = 3;
|
|
LOG_D(MAC,
|
|
"[eNB %d], CC_id %d, Frame %d, MTCH->MCH, Checking RLC status (rab %d, tbs %d, len %d)\n",
|
|
module_idP, CC_id, frameP, MTCH, TBS,
|
|
TBS - header_len_mcch - header_len_msi - sdu_length_total -
|
|
header_len_mtch);
|
|
|
|
rlc_status =
|
|
mac_rlc_status_ind(module_idP, 0, frameP, subframeP,
|
|
module_idP, ENB_FLAG_YES, MBMS_FLAG_YES,
|
|
MTCH,
|
|
TBS - header_len_mcch - header_len_msi -
|
|
sdu_length_total - header_len_mtch
|
|
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
|
|
,0, 0
|
|
#endif
|
|
);
|
|
|
|
LOG_D(MAC,
|
|
"e-MBMS log channel %u frameP %d, subframeP %d, rlc_status.bytes_in_buffer is %d\n",
|
|
MTCH, frameP, subframeP, rlc_status.bytes_in_buffer);
|
|
|
|
if (rlc_status.bytes_in_buffer > 0) {
|
|
LOG_I(MAC,
|
|
"[eNB %d][MBMS USER-PLANE], CC_id %d, Frame %d, MTCH->MCH, Requesting %d bytes from RLC (header len mtch %d)\n",
|
|
module_idP, CC_id, frameP,
|
|
TBS - header_len_mcch - header_len_msi -
|
|
sdu_length_total - header_len_mtch, header_len_mtch);
|
|
|
|
sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, 0, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_YES, MTCH, 0, //not used
|
|
(char *)
|
|
&mch_buffer[sdu_length_total]
|
|
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
|
|
,0,
|
|
0
|
|
#endif
|
|
);
|
|
|
|
|
|
//sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP,frameP, MBMS_FLAG_NO, MTCH+(MAX_NUM_RB*(MAX_MOBILES_PER_ENB+1)), (char*)&mch_buffer[sdu_length_total]);
|
|
LOG_I(MAC,
|
|
"[eNB %d][MBMS USER-PLANE] CC_id %d Got %d bytes for MTCH %d\n",
|
|
module_idP, CC_id, sdu_lengths[num_sdus], MTCH);
|
|
cc->mtch_active = 1;
|
|
sdu_lcids[num_sdus] = MTCH;
|
|
sdu_length_total += sdu_lengths[num_sdus];
|
|
|
|
if (sdu_lengths[num_sdus] < 128) {
|
|
header_len_mtch = 2;
|
|
}
|
|
|
|
num_sdus++;
|
|
} else {
|
|
header_len_mtch = 0;
|
|
}
|
|
}
|
|
#if (RRC_VERSION >= MAKE_VERSION(10, 0, 0))
|
|
// }
|
|
#endif
|
|
|
|
// FINAL STEP: Prepare and multiplexe MSI, MCCH and MTCHs
|
|
if ((sdu_length_total + header_len_msi + header_len_mcch +
|
|
header_len_mtch) > 0) {
|
|
// Adjust the last subheader
|
|
/* if ((msi_flag==1) || (mcch_flag==1)) {
|
|
RC.mac[module_idP]->MCH_pdu.mcs = mcch_mcs;
|
|
}
|
|
else if (mtch_flag == 1) { // only MTCH in this subframeP
|
|
RC.mac[module_idP]->MCH_pdu.mcs = RC.mac[module_idP]->pmch_Config[0]->dataMCS_r9;
|
|
}
|
|
*/
|
|
header_len_mtch_temp = header_len_mtch;
|
|
header_len_mcch_temp = header_len_mcch;
|
|
header_len_msi_temp = header_len_msi;
|
|
|
|
if (header_len_mtch > 0) {
|
|
header_len_mtch = 1; // remove Length field in the subheader for the last PDU
|
|
} else if (header_len_mcch > 0) {
|
|
header_len_mcch = 1;
|
|
} else {
|
|
header_len_msi = 1;
|
|
}
|
|
|
|
// Calculate the padding
|
|
if ((TBS - header_len_mtch - header_len_mcch - header_len_msi -
|
|
sdu_length_total) < 0) {
|
|
LOG_E(MAC, "Error in building MAC PDU, TBS %d < PDU %d \n",
|
|
TBS,
|
|
header_len_mtch + header_len_mcch + header_len_msi +
|
|
sdu_length_total);
|
|
return 0;
|
|
} else
|
|
if ((TBS - header_len_mtch - header_len_mcch - header_len_msi -
|
|
sdu_length_total) <= 2) {
|
|
padding =
|
|
(TBS - header_len_mtch - header_len_mcch - header_len_msi -
|
|
sdu_length_total);
|
|
post_padding = 0;
|
|
} else { // using post_padding, give back the Length field of subheader for the last PDU
|
|
padding = 0;
|
|
|
|
if (header_len_mtch > 0) {
|
|
header_len_mtch = header_len_mtch_temp;
|
|
} else if (header_len_mcch > 0) {
|
|
header_len_mcch = header_len_mcch_temp;
|
|
} else {
|
|
header_len_msi = header_len_msi_temp;
|
|
}
|
|
|
|
post_padding =
|
|
TBS - sdu_length_total - header_len_msi - header_len_mcch -
|
|
header_len_mtch;
|
|
}
|
|
|
|
// Generate the MAC Header for MCH
|
|
// here we use the function for DLSCH because DLSCH & MCH have the same Header structure
|
|
offset = generate_dlsch_header((unsigned char *) cc->MCH_pdu.payload, num_sdus, sdu_lengths, sdu_lcids, 255, // no drx
|
|
31, // no timing advance
|
|
NULL, // no contention res id
|
|
padding, post_padding);
|
|
|
|
cc->MCH_pdu.Pdu_size = TBS;
|
|
cc->MCH_pdu.sync_area = i;
|
|
cc->MCH_pdu.msi_active = cc->msi_active;
|
|
cc->MCH_pdu.mcch_active = cc->mcch_active;
|
|
cc->MCH_pdu.mtch_active = cc->mtch_active;
|
|
LOG_D(MAC,
|
|
" MCS for this sf is %d (mcch active %d, mtch active %d)\n",
|
|
cc->MCH_pdu.mcs, cc->MCH_pdu.mcch_active,
|
|
cc->MCH_pdu.mtch_active);
|
|
LOG_I(MAC,
|
|
"[eNB %d][MBMS USER-PLANE ] CC_id %d Generate header : sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,padding %d,post_padding %d (mcs %d, TBS %d), header MTCH %d, header MCCH %d, header MSI %d\n",
|
|
module_idP, CC_id, sdu_length_total, num_sdus,
|
|
sdu_lengths[0], sdu_lcids[0], offset, padding, post_padding,
|
|
cc->MCH_pdu.mcs, TBS, header_len_mtch, header_len_mcch,
|
|
header_len_msi);
|
|
// copy SDU to mch_pdu after the MAC Header
|
|
memcpy(&cc->MCH_pdu.payload[offset], mch_buffer, sdu_length_total);
|
|
|
|
// filling remainder of MCH with random data if necessery
|
|
for (j = 0; j < (TBS - sdu_length_total - offset); j++) {
|
|
cc->MCH_pdu.payload[offset + sdu_length_total + j] =
|
|
(char) (taus() & 0xff);
|
|
}
|
|
|
|
/* Tracing of PDU is done on UE side */
|
|
if (opt_enabled == 1) {
|
|
trace_pdu(1, (uint8_t *) cc->MCH_pdu.payload, TBS, module_idP, 6, 0xffff, // M_RNTI = 6 in wirehsark
|
|
RC.mac[module_idP]->frame,
|
|
RC.mac[module_idP]->subframe, 0, 0);
|
|
LOG_D(OPT,
|
|
"[eNB %d][MCH] CC_id %d Frame %d : MAC PDU with size %d\n",
|
|
module_idP, CC_id, frameP, TBS);
|
|
}
|
|
|
|
/*
|
|
for (j=0;j<sdu_length_total;j++)
|
|
printf("%2x.",RC.mac[module_idP]->MCH_pdu.payload[j+offset]);
|
|
printf(" \n"); */
|
|
return 1;
|
|
} else {
|
|
cc->MCH_pdu.Pdu_size = 0;
|
|
cc->MCH_pdu.sync_area = 0;
|
|
cc->MCH_pdu.msi_active = 0;
|
|
cc->MCH_pdu.mcch_active = 0;
|
|
cc->MCH_pdu.mtch_active = 0;
|
|
// for testing purpose, fill with random data
|
|
//for (j=0;j<(TBS-sdu_length_total-offset);j++)
|
|
// RC.mac[module_idP]->MCH_pdu.payload[offset+sdu_length_total+j] = (char)(taus()&0xff);
|
|
return 0;
|
|
}
|
|
|
|
//this is for testing
|
|
/*
|
|
if (mtch_flag == 1) {
|
|
// LOG_D(MAC,"DUY: mch_buffer length so far is : %ld\n", &mch_buffer[sdu_length_total]-&mch_buffer[0]);
|
|
return 1;
|
|
}
|
|
else
|
|
return 0;
|
|
*/
|
|
}
|
|
|
|
MCH_PDU *get_mch_sdu(module_id_t module_idP, int CC_id, frame_t frameP,
|
|
sub_frame_t subframeP)
|
|
{
|
|
// RC.mac[module_idP]->MCH_pdu.mcs=0;
|
|
//LOG_D(MAC," MCH_pdu.mcs is %d\n", RC.mac[module_idP]->MCH_pdu.mcs);
|
|
//#warning "MCH pdu should take the CC_id index"
|
|
return (&RC.mac[module_idP]->common_channels[CC_id].MCH_pdu);
|
|
}
|
|
|
|
#endif
|