/******************************************************************************* OpenAirInterface Copyright(c) 1999 - 2014 Eurecom OpenAirInterface is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenAirInterface is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenAirInterface.The full GNU General Public License is included in this distribution in the file called "COPYING". If not, see . Contact Information OpenAirInterface Admin: openair_admin@eurecom.fr OpenAirInterface Tech : openair_tech@eurecom.fr OpenAirInterface Dev : openair4g-devel@lists.eurecom.fr Address : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE *******************************************************************************/ /*! \file eNB_scheduler.c * \brief eNB scheduler top level function operates on per subframe basis * \author Navid Nikaein and Raymond Knopp * \date 2010 - 2014 * \email: navid.nikaein@eurecom.fr * \version 0.5 * @ingroup _mac */ #include "assertions.h" #include "PHY/defs.h" #include "PHY/extern.h" #include "SCHED/defs.h" #include "SCHED/extern.h" #include "LAYER2/MAC/defs.h" #include "LAYER2/MAC/extern.h" #include "LAYER2/MAC/proto.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 "RRC/LITE/extern.h" #include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h" //#include "LAYER2/MAC/pre_processor.c" #include "pdcp.h" #if defined(ENABLE_ITTI) # include "intertask_interface.h" #endif #define ENABLE_MAC_PAYLOAD_DEBUG #define DEBUG_eNB_SCHEDULER 1 //#define DEBUG_HEADER_PARSING 1 //#define DEBUG_PACKET_TRACE 1 /* #ifndef USER_MODE #define msg debug_msg #endif */ #if FAPI /* global variables to deal with TPC command - we send one only once per frame maximum */ static long global_subframe; /* global subframe - incremented at each TTI */ static long ue_last_pucch_tpc[NUMBER_OF_UE_MAX]; /* last global subframe where a PUCCH TPC was sent to the UE */ static long ue_last_pusch_tpc[NUMBER_OF_UE_MAX]; /* last global subframe where a PUSCH TPC was sent to the UE */ static void fapi_dl_tpc(int module_id, int CC_id, struct DlDciListElement_s *dci) { int UE_id = find_UE_id(module_id, dci->rnti); LTE_eNB_UE_stats *eNB_UE_stats = NULL; int32_t normalized_rx_power; int32_t target_rx_power; static int tpc_accumulated = 0; if (UE_id == -1) { printf("%s:%d: rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); } // this assumes accumulated tpc // make sure that we are only sending a tpc update once a frame, otherwise the control loop will freak out if (global_subframe < ue_last_pucch_tpc[UE_id] + 10) { printf("TPC PUCCH global sf %ld (%ld/%ld) UE %x: no TPC, last one is too recent (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, tpc_accumulated); dci->tpc = 0; return; } eNB_UE_stats = mac_xface->get_eNB_UE_stats(module_id, CC_id, dci->rnti); if (eNB_UE_stats == NULL) { printf("%s:%d: stats for rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); } normalized_rx_power = eNB_UE_stats->Po_PUCCH_dBm; target_rx_power = mac_xface->get_target_pucch_rx_power(module_id, CC_id) + 20; if (eNB_UE_stats->Po_PUCCH_update == 1) { eNB_UE_stats->Po_PUCCH_update = 0; ue_last_pucch_tpc[UE_id] = global_subframe; if (normalized_rx_power > target_rx_power + 1) { dci->tpc = -1; tpc_accumulated--; } else if (normalized_rx_power < target_rx_power - 1) { dci->tpc = 1; tpc_accumulated++; } else { dci->tpc = 0; } } printf("TPC PUCCH global sf %ld (%ld/%ld) UE %x: TPC %d (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, dci->tpc, tpc_accumulated); } static void fapi_ul_tpc(int module_id, int CC_id, struct UlDciListElement_s *dci) { int UE_id = find_UE_id(module_id, dci->rnti); LTE_eNB_UE_stats *eNB_UE_stats = NULL; int32_t normalized_rx_power; int32_t target_rx_power; static int tpc_accumulated = 0; if (UE_id == -1) { printf("%s:%d: rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); } // this assumes accumulated tpc // make sure that we are only sending a tpc update once a frame, otherwise the control loop will freak out if (global_subframe < ue_last_pusch_tpc[UE_id] + 10) { printf("TPC PUSCH global sf %ld (%ld/%ld) UE %x: no TPC, last one is too recent (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, tpc_accumulated); dci->tpc = 0; return; } eNB_UE_stats = mac_xface->get_eNB_UE_stats(module_id, CC_id, dci->rnti); if (eNB_UE_stats == NULL) { printf("%s:%d: stats for rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); } normalized_rx_power = eNB_UE_stats->UL_rssi[0]; target_rx_power = mac_xface->get_target_pusch_rx_power(module_id, CC_id); if (normalized_rx_power > target_rx_power + 1) { dci->tpc = -1; tpc_accumulated--; } else if (normalized_rx_power < target_rx_power - 1) { dci->tpc = 1; tpc_accumulated++; } else { dci->tpc = 0; } if (dci->tpc != 0) { ue_last_pusch_tpc[UE_id] = global_subframe; } printf("TPC PUSCH global sf %ld (%ld/%ld) UE %x: TPC %d (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, dci->tpc, tpc_accumulated); } /* this structure is used to store downlink ack/nack information * to be sent to FAPI by SchedDlTriggerReq */ /* TODO: lock access to it or not? */ /* TODO: do it per CC */ static struct { struct { int rnti; int harq_pid; int ack[MAX_TB_LIST]; int ack_count; } ack[MAX_DL_INFO_LIST]; int count; } fapi_dl_ack_nack_data; /* this array is used to store uplink ack/nack information * to be sent to FAPI by SchedUlTriggerReq * one element per TTI */ /* TODO: do it per CC */ static struct { struct { int reception_subframe; /* the subframe where the ACK/NACK has been received */ int rnti; int ack; int length[MAX_LC_LIST+1]; } ack[MAX_UL_INFO_LIST]; int count; } fapi_ul_ack_nack_data[10]; /* this function is called by the PHY to signal UE's ACK/NACK */ /* TODO: do it per CC */ void fapi_dl_ack_nack(int rnti, int harq_pid, int transport_block, int ack) { int pos = fapi_dl_ack_nack_data.count; printf("GOT DOWNLINK ack %d for rnti %x harq_pid %d transport_block %d\n", ack, rnti, harq_pid, transport_block); /* TODO: handle more than 1 TB */ if (transport_block) { printf("%s:%d:%s: TODO: tb != 0\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (pos == MAX_DL_INFO_LIST) { LOG_E(MAC, "fapi_dl_ack_nack: full!\n"); abort(); } fapi_dl_ack_nack_data.ack[pos].rnti = rnti; fapi_dl_ack_nack_data.ack[pos].harq_pid = harq_pid; fapi_dl_ack_nack_data.ack[pos].ack[0] = ack; /* TODO: use transport_block here */ fapi_dl_ack_nack_data.ack[pos].ack_count = 1; /* TODO: take care of transport block */ fapi_dl_ack_nack_data.count++; } /* this function is called by the PHY to inform about correct or wrong * reception by the eNodeB of an uplink UE transmission */ /* TODO: do it per CC */ void fapi_ul_ack_nack(int frame, int subframe, int rnti, int ack) { printf("GOT UPLINK ack %d for rnti %x (f/sf %d/%d)\n", ack, rnti, frame, subframe); int pos = fapi_ul_ack_nack_data[subframe].count; if (pos == MAX_UL_INFO_LIST) { LOG_E(MAC, "fapi_ul_ack_nack: full! (f/sf %d/%d)\n", frame, subframe); abort(); } fapi_ul_ack_nack_data[subframe].ack[pos].reception_subframe = subframe; fapi_ul_ack_nack_data[subframe].ack[pos].rnti = rnti; fapi_ul_ack_nack_data[subframe].ack[pos].ack = ack; /* the values in length are set later in the function fapi_ul_lc_length */ memset(fapi_ul_ack_nack_data[subframe].ack[pos].length, 0, sizeof(int) * MAX_LC_LIST+1); fapi_ul_ack_nack_data[subframe].count++; } /* this function is called by rx_sdu to indicate the number of * bytes received by the given Logical Channel */ void fapi_ul_lc_length(int frame, int subframe, int lcid, int length, int rnti) { printf("GOT lcid %d length %d (f/sf %d/%d)\n", lcid, length, frame, subframe); int pos = fapi_ul_ack_nack_data[subframe].count - 1; if (pos < 0) { printf("%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* TODO: remove this check (and the rnti parameter)? */ if (rnti != fapi_ul_ack_nack_data[subframe].ack[pos].rnti) { printf("%s:%d:%s: fatal error: LCID %d wrong RNTI %x (expected %x)\n", __FILE__, __LINE__, __FUNCTION__, lcid, rnti, fapi_ul_ack_nack_data[subframe].ack[pos].rnti); abort(); } if (lcid < 0 || lcid > 3) { printf("%s:%d:%s: fatal error: unhandled LCID %d\n", __FILE__, __LINE__, __FUNCTION__, lcid); abort(); } fapi_ul_ack_nack_data[subframe].ack[pos].length[lcid] = length; } static void fapi_convert_dl_1A_5MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) { DCI1A_5MHz_FDD_t *d = (DCI1A_5MHz_FDD_t *)a->dci_pdu; if (dci->nr_of_tbs != 1) { printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); exit(1); } d->type = 1; /* type = 0 => DCI Format 0, type = 1 => DCI Format 1A */ d->vrb_type = dci->vrbFormat == VRB_LOCALIZED ? 0 : dci->vrbFormat == VRB_DISTRIBUTED ? 1 : (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0); d->rballoc = dci->rbBitmap; d->mcs = dci->mcs[0]; /* TODO: take care of transport block index */ d->harq_pid = dci->harqProcess; d->ndi = dci->ndi[0]; /* TODO: take care of transport block index */ d->rv = dci->rv[0]; /* TODO: take care of transport block index */ d->TPC = dci->tpc == -1 ? 0 : /* see 36.213 table 5.1.2.1-1 */ dci->tpc == 0 ? 1 : dci->tpc == 1 ? 2 : dci->tpc == 3 ? 3 : (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0); d->padding = 0; a->dci_length = sizeof_DCI1A_5MHz_FDD_t; a->format = format1A; } static uint32_t revert(uint32_t x, int len) { int i; int ret = 0; for (i = 0; i < len; i++) { ret <<= 1; ret |= x & 1; x >>= 1; } return ret; } static void fapi_convert_dl_1_5MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) { DCI1_5MHz_FDD_t *d = (DCI1_5MHz_FDD_t *)a->dci_pdu; if (dci->nr_of_tbs != 1) { printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); exit(1); } d->rah = dci->resAlloc == 0 ? 0 : dci->resAlloc == 1 ? 1 : (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0); d->rballoc = revert(dci->rbBitmap, 13); d->mcs = dci->mcs[0]; /* TODO: take care of transport block index */ d->harq_pid = dci->harqProcess; d->ndi = dci->ndi[0]; /* TODO: take care of transport block index */ d->rv = dci->rv[0]; /* TODO: take care of transport block index */ d->TPC = dci->tpc == -1 ? 0 : /* see 36.213 table 5.1.2.1-1 */ dci->tpc == 0 ? 1 : dci->tpc == 1 ? 2 : dci->tpc == 3 ? 3 : (printf("%s:%d: error (dci->tpc = %d)\n", __FUNCTION__, __LINE__, dci->tpc), abort(), 0); d->dummy = 0; a->dci_length = sizeof_DCI1_5MHz_FDD_t; a->format = format1; } static void fapi_convert_dl_dci(int module_id, int CC_id, struct DlDciListElement_s *dci, DCI_ALLOC_t *a) { /* set TPC in the DCI */ /* TODO: remove it if/when the scheduler does it */ if (dci->rnti != 0) fapi_dl_tpc(module_id, CC_id, dci); /* 5MHz FDD supposed, not checked */ switch (dci->format) { case ONE_A: fapi_convert_dl_1A_5MHz_FDD(dci, a); break; case ONE: fapi_convert_dl_1_5MHz_FDD(dci, a); break; default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort(); } a->L = dci->aggrLevel == 1 ? 0 : dci->aggrLevel == 2 ? 1 : dci->aggrLevel == 4 ? 2 : dci->aggrLevel == 8 ? 3 : (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0); a->firstCCE = dci->cceIndex; a->ra_flag = 0; /* TODO: set to 1 only by fapi_schedule_RAR, is it ok? */ a->rnti = dci->rnti; } static void fapi_convert_ul_5MHz_FDD(module_id_t module_idP, int CC_id, struct UlDciListElement_s *dci, DCI_ALLOC_t *a) { DCI0_5MHz_FDD_t *d = (DCI0_5MHz_FDD_t *)a->dci_pdu; d->type = 0; /* type = 0 => DCI Format 0, type = 1 => DCI Format 1A */ d->hopping = dci->hopping; d->rballoc = mac_xface->computeRIV(PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL, dci->rbStart, dci->rbLen); d->mcs = dci->mcs; d->ndi = dci->ndi; d->TPC = dci->tpc == -1 ? 0 : /* see 36.213 table 5.1.1.1-2, accumulated case supposed */ dci->tpc == 0 ? 1 : dci->tpc == 1 ? 2 : dci->tpc == 3 ? 3 : (printf("%s:%d: error (tpc = %d)\n", __FUNCTION__, __LINE__, dci->tpc), abort(), 0); d->cshift = dci->n2Dmrs; d->cqi_req = dci->cqiRequest; d->padding = 0; a->dci_length = sizeof_DCI0_5MHz_FDD_t; a->format = format0; } static void fapi_convert_ul_dci(module_id_t module_idP, int CC_id, struct UlDciListElement_s *dci, DCI_ALLOC_t *a) { /* set TPC in the DCI */ /* TODO: remove it if/when the scheduler does it */ fapi_ul_tpc(module_idP, CC_id, dci); /* 5MHz FDD supposed, not checked */ fapi_convert_ul_5MHz_FDD(module_idP, CC_id, dci, a); a->L = dci->aggrLevel == 1 ? 0 : dci->aggrLevel == 2 ? 1 : dci->aggrLevel == 4 ? 2 : dci->aggrLevel == 8 ? 3 : (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0); a->firstCCE = dci->cceIndex; a->ra_flag = 0; a->rnti = dci->rnti; } /* index 0 for SIB1, 1 for SIB23 */ static void fapi_schedule_SI(module_id_t module_idP, int CC_id, frame_t frameP, sub_frame_t subframeP, uint8_t index, struct DlDciListElement_s *dci) { eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP]; DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci]; int bcch_sdu_length; if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */ dci_pdu->Num_common_dci++; fapi_convert_dl_dci(module_idP, CC_id, dci, a); /* bug in libscheduler.a? rnti is 0 */ a->rnti = SI_RNTI; if (index == 0) bcch_sdu_length = mac_rrc_get_SIB1(module_idP, CC_id, &eNB->common_channels[CC_id].BCCH_pdu.payload[0]); else if (index == 1) bcch_sdu_length = mac_rrc_get_SIB23(module_idP, CC_id, &eNB->common_channels[CC_id].BCCH_pdu.payload[0]); else { printf("%s:%d: fatal error\n", __FUNCTION__, __LINE__); abort(); } eNB->eNB_stats[CC_id].total_num_bcch_pdu+=1; eNB->eNB_stats[CC_id].bcch_buffer=bcch_sdu_length; eNB->eNB_stats[CC_id].total_bcch_buffer+=bcch_sdu_length; eNB->eNB_stats[CC_id].bcch_mcs=dci->mcs[0]; /* TODO: take care of transport block index */ } static void fapi_schedule_RAR(int module_idP, int CC_id, frame_t frameP, sub_frame_t subframeP, uint16_t rnti, uint32_t grant, struct DlDciListElement_s *dci) { eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP]; DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci]; RA_TEMPLATE *RA_template = NULL; int i; if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); } for (i = 0; i < NB_RA_PROC_MAX; i++) { RA_template = (RA_TEMPLATE *)&eNB->common_channels[CC_id].RA_template[i]; if (RA_template->RA_active != TRUE) continue; if (RA_template->rnti != rnti) continue; break; } if (i == NB_RA_PROC_MAX) { printf("%s:%d:%s: possible?\n", __FILE__, __LINE__, __FUNCTION__); abort(); } RA_template->generate_rar = 1; RA_template->UL_grant = grant; /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */ dci_pdu->Num_common_dci++; fapi_convert_dl_dci(module_idP, CC_id, dci, a); a->ra_flag = 1; /* bug in libscheduler.a? rnti is 0 */ a->rnti = RA_template->RA_rnti; } static void fapi_schedule_RA_Msg4(int module_idP, int CC_id, int RA, int frameP, int subframeP, struct DlDciListElement_s *dci, int pdu_maxsize) { int UE_id; int rrc_sdu_length; eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP]; RA_TEMPLATE *RA_template; int msg4_header_length; int msg4_padding, msg4_post_padding; int TBsize; DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci]; int offset; unsigned char lcid; if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); } RA_template = (RA_TEMPLATE *)&eNB->common_channels[CC_id].RA_template[RA]; UE_id = find_UE_id(module_idP,RA_template->rnti); rrc_sdu_length = mac_rrc_data_req(module_idP, CC_id, frameP, CCCH, 1, // 1 transport block &eNB->common_channels[CC_id].CCCH_pdu.payload[0], ENB_FLAG_YES, module_idP, 0); // not used in this case if (rrc_sdu_length <= 0) { LOG_E(MAC, "fapi_schedule_RA_Msg4: rrc_sdu_length (%d) is not > 0\n", rrc_sdu_length); abort(); } if (rrc_sdu_length > pdu_maxsize) { LOG_E(MAC, "fapi_schedule_RA_Msg4: rrc_sdu_length (%d) > pdu_maxsize (%d)\n", rrc_sdu_length, pdu_maxsize); abort(); } msg4_header_length = 1+6+1; // CR header, CR CE, SDU header TBsize = dci->tbsSize[CC_id] / 8; if ((TBsize - rrc_sdu_length - msg4_header_length) <= 2) { msg4_padding = TBsize - rrc_sdu_length - msg4_header_length; msg4_post_padding = 0; } else { msg4_padding = 0; msg4_post_padding = TBsize - rrc_sdu_length - msg4_header_length -1; } LOG_I(MAC,"[eNB %d][RAPROC] CC_id %d Frame %d subframeP %d Msg4 : TBS %d, sdu_len %d, msg4_header %d, msg4_padding %d, msg4_post_padding %d\n", module_idP, CC_id, frameP, subframeP, TBsize, rrc_sdu_length, msg4_header_length, msg4_padding, msg4_post_padding); lcid = 0; offset = generate_dlsch_header((unsigned char*)eNB->UE_list.DLSCH_pdu[CC_id][0][(unsigned char)UE_id].payload[0], 1, //num_sdus (unsigned short*)&rrc_sdu_length, // &lcid, // sdu_lcid 255, // no drx 0, // no timing advance RA_template->cont_res_id, // contention res id msg4_padding, // no padding msg4_post_padding); memcpy((void*)&eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0][offset], &eNB->common_channels[CC_id].CCCH_pdu.payload[0], rrc_sdu_length); RA_template->generate_Msg4=0; RA_template->wait_ack_Msg4=1; RA_template->RA_active = FALSE; /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */ dci_pdu->Num_common_dci++; fapi_convert_dl_dci(module_idP, CC_id, dci, a); } /* returns 1 if the given LCID has a MAC header of fixed size (for DL-SCH) */ static int fixed_size(int lcid) { /* see 36.321, especially table 6.2.1-1 (rel. 10 is used here) */ return lcid >= 27; } /* TODO: deal with more than one transport block */ static void fapi_schedule_ue(int module_id, int CC_id, int frame, int subframe, struct BuildDataListElement_s *d) { int header_size; int payload_size; int padding_size; int tbs; int i; mac_rlc_status_resp_t rlc_status; unsigned char dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES]; int dlsch_filled = 0; int output_length; int UE_id; eNB_MAC_INST *eNB = &eNB_mac_inst[module_id]; UE_list_t *UE_list = &eNB->UE_list; int num_sdus; unsigned short sdu_lengths[MAX_RLC_PDU_LIST]; unsigned char sdu_lcids[MAX_RLC_PDU_LIST]; int offset; DCI_PDU *dci_pdu = &eNB->common_channels[CC_id].DCI_pdu; DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci]; /* generate DCI */ if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */ dci_pdu->Num_common_dci++; fapi_convert_dl_dci(module_id, CC_id, &d->dci, a); printf("RUN fapi_schedule_ue\n"); if (d->nr_rlcPDU_List[0] != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->nr_rlcPDU_List[1] != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->ceBitmap[0]) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->ceBitmap[1]) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->servCellIndex != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } tbs = d->dci.tbsSize[0] / 8; num_sdus = 0; /* get DLSCH buffer and adjust size according to what RLC says */ for (i = 0; i < d->nr_rlcPDU_List[0]; i++) { rlc_status = mac_rlc_status_ind( module_id, d->rnti, module_id, frame, ENB_FLAG_YES, MBMS_FLAG_NO, d->rlcPduList[0][i].logicalChannelIdentity, d->rlcPduList[0][i].size); printf("RLC_SIZE in fapi_schedule_ue %d (asked %d) lcid %d rnti %x f/sf %d/%d\n", rlc_status.bytes_in_buffer, d->rlcPduList[0][i].size, d->rlcPduList[0][i].logicalChannelIdentity, d->rnti, frame, subframe); if (rlc_status.bytes_in_buffer <= 0) { printf("%s:%d:%s: error\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (rlc_status.bytes_in_buffer > d->rlcPduList[0][i].size) abort(); /* that can't happen */ if (dlsch_filled + d->rlcPduList[0][i].size > MAX_DLSCH_PAYLOAD_BYTES) { printf("dlsch buffer filled too much\n"); abort(); } output_length = mac_rlc_data_req( module_id, d->rnti, module_id, frame, ENB_FLAG_YES, MBMS_FLAG_NO, d->rlcPduList[0][i].logicalChannelIdentity, (char *)&dlsch_buffer[dlsch_filled]); if (output_length <= 0 || output_length > d->rlcPduList[0][i].size) abort(); d->rlcPduList[0][i].size = output_length; dlsch_filled += output_length; sdu_lengths[num_sdus] = output_length; sdu_lcids[num_sdus] = d->rlcPduList[0][i].logicalChannelIdentity; num_sdus++; printf("FILLED %d bytes\n", output_length); } /* get size of header and payload */ header_size = 0; payload_size = 0; if (d->nr_rlcPDU_List[0]) { header_size++; payload_size += d->rlcPduList[0][0].size; } for (i = 1; i < d->nr_rlcPDU_List[0]; i++) { if (!fixed_size(d->rlcPduList[0][i-1].logicalChannelIdentity)) { header_size++; if (d->rlcPduList[0][i-1].size >= 128) header_size++; } header_size++; payload_size += d->rlcPduList[0][i].size; } padding_size = tbs - header_size - payload_size; printf("PADDING_SIZE %d\n", padding_size); UE_id = find_UE_id(module_id, d->rnti); /* generate dlsch header */ offset = generate_dlsch_header((unsigned char*)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], num_sdus, sdu_lengths, sdu_lcids, 255, // no drx 0, /* TODO: timing advance */ NULL, // contention res id padding_size <= 2 ? padding_size : 0, padding_size > 2 ? padding_size : 0); /* fill payload */ memcpy(&UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset], dlsch_buffer, dlsch_filled); add_ue_dlsch_info(module_id, CC_id, UE_id, subframe, /* S_DL_SCHEDULED */ S_DL_WAITING); } static void fapi_schedule_uplink(int module_idP, int CC_id, struct UlDciListElement_s *dci) { DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci]; if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */ dci_pdu->Num_common_dci++; fapi_convert_ul_dci(module_idP, CC_id, dci, a); } char *binary(unsigned x) { static char r[33]; char *s = r+31; int i; r[32] = 0; for (i = 0; i < 32; i++) { *s = '0' + (x&1); s--; x >>= 1; } return r; } void eNB_dlsch_ulsch_scheduler(module_id_t module_idP,uint8_t cooperation_flag, frame_t frameP, sub_frame_t subframeP) //, int calibration_flag) { { int CC_id; int i, j; DCI_PDU *DCI_pdu[MAX_NUM_CCs]; int mbsfn_status[MAX_NUM_CCs]; UE_list_t *UE_list = &eNB_mac_inst[module_idP].UE_list; fapi_interface_t *fapi; struct SchedDlTriggerReqParameters dlreq; struct SchedDlConfigIndParameters dlind; struct DlInfoListElement_s dlinfo[MAX_DL_INFO_LIST]; protocol_ctxt_t ctxt; int UE_id; eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP]; struct SchedUlTriggerReqParameters ulreq; struct SchedUlConfigIndParameters ulind; struct UlInfoListElement_s ulinfo[MAX_UL_INFO_LIST]; int ulsf; printf("SCHEDULER called for f/sf %d/%d\n", frameP, subframeP); #if defined(ENABLE_ITTI) while (1) { // Checks if a message has been sent to MAC sub-task MessageDef *msg_p; int result; itti_poll_msg (TASK_MAC_ENB, &msg_p); if (msg_p == NULL) break; result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p); AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); } #endif for (CC_id=0; CC_idnCCE=0; DCI_pdu[CC_id]->num_pdcch_symbols=1; mbsfn_status[CC_id]=0; // clear vrb_map memset(eNB_mac_inst[module_idP].common_channels[CC_id].vrb_map,0,100); } // refresh UE list based on UEs dropped by PHY in previous subframe i = UE_list->head; while (i>=0) { int rnti, next_i; rnti = UE_RNTI(module_idP, i); CC_id = UE_PCCID(module_idP, i); next_i= UE_list->next[i]; if (mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)==NULL) { mac_remove_ue(module_idP, i, frameP, subframeP); } i = next_i; } // clear DCI and BCCH contents before scheduling for (CC_id=0; CC_idNum_common_dci = 0; DCI_pdu[CC_id]->Num_ue_spec_dci = 0; #ifdef Rel10 eNB_mac_inst[module_idP].common_channels[CC_id].mcch_active = 0; #endif eNB_mac_inst[module_idP].frame = frameP; eNB_mac_inst[module_idP].subframe = subframeP; } /* run PDCP */ PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frameP, 0, module_idP); pdcp_run(&ctxt); // check HO rrc_rx_tx(&ctxt, 0, // eNB index, unused in eNB CC_id); /* MBMS thing not done */ mbsfn_status[0]++; /* avoid gcc warning */ /* let's FAPI schedule! */ fapi = eNB_mac_inst[module_idP].fapi; /*************************************************/ /* downlink scheduling */ /*************************************************/ /* check RA - is there one in generate_Msg4 mode with some CCCH data ready in the RRC Srb0? */ for (CC_id=0; CC_idcommon_channels[CC_id].RA_template[i].RA_active == TRUE && eNB->common_channels[CC_id].RA_template[i].generate_Msg4 == 1 && mac_rrc_get_ccch_size(module_idP, CC_id) > 0) { struct SchedDlRlcBufferReqParameters rlc; memset(&rlc, 0, sizeof(rlc)); rlc.rnti = eNB->common_channels[CC_id].RA_template[i].rnti; rlc.logicalChannelIdentity = CCCH; rlc.rlcTransmissionQueueSize = mac_rrc_get_ccch_size(module_idP, CC_id); LOG_I(MAC, "calling SchedDlRlcBufferReq on CCCH rnti %x queue_size %d\n", rlc.rnti, rlc.rlcTransmissionQueueSize); printf("MAC to FAPI downlink BUF CCCH %d\n", rlc.rlcTransmissionQueueSize); SchedDlRlcBufferReq(fapi->sched, &rlc); /* let's report only once, so put generate_Msg4 to 2 * (we will generate later on, when FAPI tells us to do so) */ eNB->common_channels[CC_id].RA_template[i].generate_Msg4 = 2; } } } /* update RLC buffers status in the scheduler */ for (UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) { struct SchedDlRlcBufferReqParameters rlc; mac_rlc_status_resp_t rlc_status; memset(&rlc, 0, sizeof(rlc)); rlc.rnti = UE_RNTI(module_idP, UE_id); /* this code is wrong: we should loop over all configured RaB */ /* DCCH (srb 1, lcid 1) */ rlc_status = mac_rlc_get_buffer_occupancy(module_idP, rlc.rnti, frameP, DCCH); rlc.logicalChannelIdentity = DCCH; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; LOG_I(MAC, "calling SchedDlRlcBufferReq on DCCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); printf("MAC to FAPI downlink BUF DCCH %d\n", rlc_status.bytes_in_buffer); /* DCCH+1 (srb 2, lcid 2) */ rlc_status = mac_rlc_get_buffer_occupancy(module_idP, rlc.rnti, frameP, DCCH+1); rlc.logicalChannelIdentity = DCCH+1; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; LOG_I(MAC, "calling SchedDlRlcBufferReq on DCCH+1 rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); printf("MAC to FAPI downlink BUF DCCH+1 %d\n", rlc_status.bytes_in_buffer); /* DTCH (drb 1, lcid 3) */ rlc_status = mac_rlc_get_buffer_occupancy(module_idP, rlc.rnti, frameP, DTCH); rlc.logicalChannelIdentity = DTCH; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; LOG_I(MAC, "calling SchedDlRlcBufferReq on DTCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); printf("MAC to FAPI downlink BUF DTCH %d\n", rlc_status.bytes_in_buffer); } dlreq.sfnSf = frameP * 16 + subframeP; dlreq.nr_dlInfoList = 0; dlreq.dlInfoList = NULL; dlreq.nr_vendorSpecificList = 0; dlreq.vendorSpecificList = NULL; /* fill harq feedback data */ /* TODO: deal with more than one TB */ for (i = 0; i < fapi_dl_ack_nack_data.count; i++) { dlinfo[i].rnti = fapi_dl_ack_nack_data.ack[i].rnti; dlinfo[i].harqProcessId = fapi_dl_ack_nack_data.ack[i].harq_pid; dlinfo[i].nr_harqStatus = 1; /* TODO: deal with more than 1 TB */ dlinfo[i].harqStatus[0] = fapi_dl_ack_nack_data.ack[i].ack[0]; /* TODO: more than 1 TB */ dlinfo[i].servCellIndex = 0; /* TODO: get real value for the servCellIndex */ printf("MAC to FAPI downlink ack/nack from PHY f/sf %d/%d rnti %x harq %d ack %d\n", frameP, subframeP, dlinfo[i].rnti, dlinfo[i].harqProcessId, dlinfo[i].harqStatus[0]); } if (fapi_dl_ack_nack_data.count) { dlreq.nr_dlInfoList = fapi_dl_ack_nack_data.count; dlreq.dlInfoList = dlinfo; } fapi_dl_ack_nack_data.count = 0; LOG_I(MAC, "calling SchedDlTriggerReq\n"); SchedDlTriggerReq(fapi->sched, &dlreq); LOG_I(MAC, "calling SchedDlConfigInd\n"); SchedDlConfigInd(fapi, &dlind); LOG_I(MAC, "SchedDlConfigInd returns dlind.nr_buildDataList %d f/sf %d/%d\n", dlind.nr_buildDataList, frameP, subframeP); LOG_I(MAC, "SchedDlConfigInd returns dlind.nr_buildRARList %d f/sf %d/%d\n", dlind.nr_buildRARList, frameP, subframeP); LOG_I(MAC, "SchedDlConfigInd returns dlind.nr_buildBroadcastList %d f/sf %d/%d\n", dlind.nr_buildBroadcastList, frameP, subframeP); /* TODO: rewrite. All should go into fapi_schedule_ue where special cases should be handled */ for (i = 0; i < dlind.nr_buildDataList; i++) { if (dlind.buildDataList[i].ceBitmap[1] != 0 || dlind.buildDataList[i].nr_rlcPDU_List[1] != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (dlind.buildDataList[i].nr_rlcPDU_List[0] != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } printf("FAPI to MAC downlink schedule ue %x channel %d f/sf %d/%d\n", dlind.buildDataList[i].rnti, dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity, frameP, subframeP); switch (dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity) { case 0: /* CCCH */ /* TODO: get the right CC_id from servCellIndex, depending on the UE rnti/pcell/scell settings */ CC_id = dlind.buildDataList[i].servCellIndex; /* look for an active RA with generate_Msg4 == 2 for this rnti */ for (j=0; j < NB_RA_PROC_MAX; j++) { if (eNB->common_channels[CC_id].RA_template[j].RA_active == TRUE && eNB->common_channels[CC_id].RA_template[j].generate_Msg4 == 2 && eNB->common_channels[CC_id].RA_template[j].rnti == dlind.buildDataList[i].rnti) { if (dlind.buildDataList[i].ceBitmap[CC_id] != ff_CR) { printf("%s:%d:%s: TODO)\n", __FILE__, __LINE__, __FUNCTION__); abort(); } fapi_schedule_RA_Msg4(module_idP, CC_id, j, frameP, subframeP, &dlind.buildDataList[i].dci, dlind.buildDataList[i].rlcPduList[0][0].size); break; } } if (j == NB_RA_PROC_MAX) { printf("%s:%d:%s: possible?\n", __FILE__, __LINE__, __FUNCTION__); abort(); } break; case 1: /* DCCH (SRB1) */ case 2: /* DCCH+1 (SRB2) */ case 3: /* DTCH (DRB1) */ /* TODO: get the correct CC_id from servCellIndex */ fapi_schedule_ue(module_idP, dlind.buildDataList[i].servCellIndex /* CC_id */, frameP, subframeP, &dlind.buildDataList[i]); break; default: printf("%s:%d:%s: bad channel ID (%d)\n", __FILE__, __LINE__, __FUNCTION__, dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity); abort(); } } if (dlind.nr_buildRARList) { if (dlind.nr_buildRARList != 1) { printf("%s:%d: more than 1 RAR, todo\n", __FUNCTION__, __LINE__); exit(0); } if (dlind.buildRarList[0].carrierIndex != 0) { printf("%s:%d: 2nd CC: todo properly\n", __FUNCTION__, __LINE__); exit(0); } /* force bit 0 of grant to 0 * according to 36.213 6.2 the value * is reserved but openair needs it to be 0 * this is for contention-based RA * maybe we will need to review that if/when we do contention-free RA */ printf("FAPI to MAC downlink schedule RAR ue %x f/sf %d/%d\n", dlind.buildRarList[0].rnti, frameP, subframeP); dlind.buildRarList[0].grant &= ~1; fapi_schedule_RAR(module_idP, dlind.buildRarList[0].carrierIndex, frameP, subframeP, dlind.buildRarList[0].rnti, dlind.buildRarList[0].grant, &dlind.buildRarList[0].dci); } if (dlind.nr_buildBroadcastList) { if (dlind.nr_buildBroadcastList != 1) { printf("%s:%d: more than 1 broadcast SI, todo\n", __FUNCTION__, __LINE__); exit(0); } if (dlind.buildBroadcastList[0].type == ff_PCCH) { printf("%s:%d: PCCH: todo\n", __FUNCTION__, __LINE__); exit(0); } if (dlind.buildBroadcastList[0].carrierIndex != 0) { printf("%s:%d: 2nd CC: todo properly\n", __FUNCTION__, __LINE__); exit(0); } printf("FAPI to MAC downlink schedule SI %d f/sf %d/%d\n", dlind.buildBroadcastList[0].index, frameP, subframeP); fapi_schedule_SI(module_idP, dlind.buildBroadcastList[0].carrierIndex, frameP, subframeP, dlind.buildBroadcastList[0].index, &dlind.buildBroadcastList[0].dci); } /* TODO: do it correctly */ if (dlind.nr_ofdmSymbolsCount != 0) { if (dlind.nr_ofdmSymbolsCount != 1) { printf("%s:%d:%s: what to do?\n", __FILE__, __LINE__, __FUNCTION__); abort(); } for (CC_id=0; CC_idcarrierIndex; DCI_pdu[cc]->num_pdcch_symbols = dlind.nrOfPdcchOfdmSymbols[CC_id]->pdcchOfdmSymbolCount; printf("FAPI to MAC downlink DCI_pdu[%d]->num_pdcch_symbols %d f/sf %d/%d\n", cc, DCI_pdu[cc]->num_pdcch_symbols, frameP, subframeP); } } } #if 0 // Allocate CCEs for good after scheduling is done for (CC_id=0;CC_idsched, &ulreq); LOG_I(MAC, "calling SchedUlConfigInd\n"); SchedUlConfigInd(fapi, &ulind); printf("FAPI to MAC uplink nr_dclList %d nr_phichList %d\n", ulind.nr_dciList, ulind.nr_phichList); for (i = 0; i < ulind.nr_dciList; i++) { /* TODO: get the right CC_id from servCellIndex, depending on the UE rnti/pcell/scell settings */ CC_id = ulind.dciList[i].servCellIndex; printf("FAPI to MAC uplink schedule ue %x ndi %d (fsf %d %d)\n", ulind.dciList[i].rnti, ulind.dciList[i].ndi, frameP, subframeP); fapi_schedule_uplink(module_idP, CC_id, &ulind.dciList[i]); } printf("RECAP dci pdu count %d\n", eNB_mac_inst[0].common_channels[0].DCI_pdu.Num_common_dci); for (i = 0; i < eNB_mac_inst[0].common_channels[0].DCI_pdu.Num_common_dci; i++) { printf(" RECAP %i rnti %x format %d dci pdu %s\n", i, eNB_mac_inst[0].common_channels[0].DCI_pdu.dci_alloc[i].rnti, eNB_mac_inst[0].common_channels[0].DCI_pdu.dci_alloc[i].format, binary(*(uint32_t *)eNB_mac_inst[0].common_channels[0].DCI_pdu.dci_alloc[i].dci_pdu) ); } global_subframe++; } #else /* FAPI */ void eNB_dlsch_ulsch_scheduler(module_id_t module_idP,uint8_t cooperation_flag, frame_t frameP, sub_frame_t subframeP) //, int calibration_flag) { { int mbsfn_status[MAX_NUM_CCs]; protocol_ctxt_t ctxt; #ifdef EXMIMO int ret; #endif #if defined(ENABLE_ITTI) MessageDef *msg_p; const char *msg_name; instance_t instance; int result; #endif DCI_PDU *DCI_pdu[MAX_NUM_CCs]; int CC_id,i,next_i; UE_list_t *UE_list=&eNB_mac_inst[module_idP].UE_list; rnti_t rnti; LOG_D(MAC,"[eNB %d] Frame %d, Subframe %d, entering MAC scheduler (UE_list->head %d)\n",module_idP, frameP, subframeP,UE_list->head); start_meas(&eNB_mac_inst[module_idP].eNB_scheduler); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_IN); for (CC_id=0; CC_idnCCE=0; DCI_pdu[CC_id]->num_pdcch_symbols=1; mbsfn_status[CC_id]=0; // clear vrb_map memset(eNB_mac_inst[module_idP].common_channels[CC_id].vrb_map,0,100); } // refresh UE list based on UEs dropped by PHY in previous subframe i = UE_list->head; while (i>=0) { rnti = UE_RNTI(module_idP, i); CC_id = UE_PCCID(module_idP, i); LOG_D(MAC,"UE %d: rnti %x (%p)\n", i, rnti, mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)); next_i= UE_list->next[i]; if (mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)==NULL) { mac_remove_ue(module_idP, i, frameP, subframeP); } i = next_i; } #if defined(ENABLE_ITTI) do { // Checks if a message has been sent to MAC sub-task itti_poll_msg (TASK_MAC_ENB, &msg_p); if (msg_p != NULL) { msg_name = ITTI_MSG_NAME (msg_p); instance = ITTI_MSG_INSTANCE (msg_p); switch (ITTI_MSG_ID(msg_p)) { case MESSAGE_TEST: LOG_D(MAC, "Received %s\n", ITTI_MSG_NAME(msg_p)); break; case RRC_MAC_BCCH_DATA_REQ: LOG_D(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d\n", msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance, RRC_MAC_BCCH_DATA_REQ (msg_p).frame, RRC_MAC_BCCH_DATA_REQ (msg_p).enb_index); // TODO process BCCH data req. break; case RRC_MAC_CCCH_DATA_REQ: LOG_D(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d\n", msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance, RRC_MAC_CCCH_DATA_REQ (msg_p).frame, RRC_MAC_CCCH_DATA_REQ (msg_p).enb_index); // TODO process CCCH data req. break; #ifdef Rel10 case RRC_MAC_MCCH_DATA_REQ: LOG_D(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d, mbsfn_sync_area %d\n", msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance, RRC_MAC_MCCH_DATA_REQ (msg_p).frame, RRC_MAC_MCCH_DATA_REQ (msg_p).enb_index, RRC_MAC_MCCH_DATA_REQ (msg_p).mbsfn_sync_area); // TODO process MCCH data req. break; #endif default: LOG_E(MAC, "Received unexpected message %s\n", msg_name); break; } result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p); AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result); } } while(msg_p != NULL); #endif // clear DCI and BCCH contents before scheduling for (CC_id=0; CC_idNum_common_dci = 0; DCI_pdu[CC_id]->Num_ue_spec_dci = 0; #ifdef Rel10 eNB_mac_inst[module_idP].common_channels[CC_id].mcch_active =0; #endif eNB_mac_inst[module_idP].frame = frameP; eNB_mac_inst[module_idP].subframe = subframeP; } //if (subframeP%5 == 0) //#ifdef EXMIMO PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frameP, 0,module_idP); pdcp_run(&ctxt); //#endif // check HO rrc_rx_tx(&ctxt, 0, // eNB index, unused in eNB CC_id); #ifdef Rel10 for (CC_id=0; CC_id0) { start_meas(&eNB_mac_inst[module_idP].schedule_mch); mbsfn_status[CC_id] = schedule_MBMS(module_idP,CC_id,frameP,subframeP); stop_meas(&eNB_mac_inst[module_idP].schedule_mch); } } #endif // refresh UE list based on UEs dropped by PHY in previous subframe /* i=UE_list->head; while (i>=0) { next_i = UE_list->next[i]; LOG_T(MAC,"UE %d : rnti %x, stats %p\n",i,UE_RNTI(module_idP,i),mac_xface->get_eNB_UE_stats(module_idP,0,UE_RNTI(module_idP,i))); if (mac_xface->get_eNB_UE_stats(module_idP,0,UE_RNTI(module_idP,i))==NULL) { mac_remove_ue(module_idP,i,frameP); } i=next_i; } */ switch (subframeP) { case 0: // FDD/TDD Schedule Downlink RA transmissions (RA response, Msg4 Contention resolution) // Schedule ULSCH for FDD or subframeP 4 (TDD config 0,3,6) // Schedule Normal DLSCH schedule_RA(module_idP,frameP,subframeP,2); if (mac_xface->lte_frame_parms->frame_type == FDD) { //FDD schedule_ulsch(module_idP,frameP,cooperation_flag,0,4);//,calibration_flag); } else if ((mac_xface->lte_frame_parms->tdd_config == TDD) || //TDD (mac_xface->lte_frame_parms->tdd_config == 3) || (mac_xface->lte_frame_parms->tdd_config == 6)) { //schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,4);//,calibration_flag); } schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 1: // TDD, schedule UL for subframeP 7 (TDD config 0,1) / subframeP 8 (TDD Config 6) // FDD, schedule normal UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD switch (mac_xface->lte_frame_parms->tdd_config) { case 0: case 1: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,7); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 6: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,8); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD schedule_ulsch(module_idP,frameP,cooperation_flag,1,5); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 2: // TDD, nothing // FDD, normal UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == FDD) { //FDD schedule_ulsch(module_idP,frameP,cooperation_flag,2,6); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 3: // TDD Config 2, ULSCH for subframeP 7 // TDD Config 2/5 normal DLSCH // FDD, normal UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == TDD) { switch (mac_xface->lte_frame_parms->tdd_config) { case 2: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,7); // no break here! case 5: schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD schedule_ulsch(module_idP,frameP,cooperation_flag,3,7); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 4: // TDD Config 1, ULSCH for subframeP 8 // TDD Config 1/2/4/5 DLSCH // FDD UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == 1) { // TDD switch (mac_xface->lte_frame_parms->tdd_config) { case 1: // schedule_RA(module_idP,frameP,subframeP); schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,8); // no break here! case 2: // no break here! case 4: // no break here! case 5: schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { if (mac_xface->lte_frame_parms->frame_type == FDD) { //FDD // schedule_RA(module_idP,frameP, subframeP, 0); schedule_ulsch(module_idP, frameP, cooperation_flag, 4, 8); schedule_ue_spec(module_idP, frameP, subframeP, mbsfn_status); fill_DLSCH_dci(module_idP, frameP, subframeP, mbsfn_status); } } break; case 5: // TDD/FDD Schedule SI // TDD Config 0,6 ULSCH for subframes 9,3 resp. // TDD normal DLSCH // FDD normal UL/DLSCH schedule_SI(module_idP,frameP,subframeP); //schedule_RA(module_idP,frameP,subframeP,5); if (mac_xface->lte_frame_parms->frame_type == FDD) { schedule_RA(module_idP,frameP,subframeP,1); schedule_ulsch(module_idP,frameP,cooperation_flag,5,9); schedule_ue_spec(module_idP, frameP, subframeP, mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } else if ((mac_xface->lte_frame_parms->tdd_config == 0) || // TDD Config 0 (mac_xface->lte_frame_parms->tdd_config == 6)) { // TDD Config 6 //schedule_ulsch(module_idP,cooperation_flag,subframeP); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } else { schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 6: // TDD Config 0,1,6 ULSCH for subframes 2,3 // TDD Config 3,4,5 Normal DLSCH // FDD normal ULSCH/DLSCH if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD switch (mac_xface->lte_frame_parms->tdd_config) { case 0: break; case 1: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,2); // schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 6: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3); // schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 5: schedule_RA(module_idP,frameP,subframeP,2); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 3: case 4: schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD // schedule_ulsch(module_idP,frameP,cooperation_flag,6,0); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 7: // TDD Config 3,4,5 Normal DLSCH // FDD Normal UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD switch (mac_xface->lte_frame_parms->tdd_config) { case 3: case 4: schedule_RA(module_idP,frameP,subframeP,3); // 3 = Msg3 subframeP, not schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 5: schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD //schedule_ulsch(module_idP,frameP,cooperation_flag,7,1); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 8: // TDD Config 2,3,4,5 ULSCH for subframeP 2 // // FDD Normal UL/DLSCH if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD switch (mac_xface->lte_frame_parms->tdd_config) { case 2: case 3: case 4: case 5: // schedule_RA(module_idP,subframeP); schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,2); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD //schedule_ulsch(module_idP,frameP,cooperation_flag,8,2); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; case 9: // TDD Config 1,3,4,6 ULSCH for subframes 3,3,3,4 if (mac_xface->lte_frame_parms->frame_type == TDD) { switch (mac_xface->lte_frame_parms->tdd_config) { case 1: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3); schedule_RA(module_idP,frameP,subframeP,7); // 7 = Msg3 subframeP, not schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 3: case 4: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 6: schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,4); //schedule_RA(module_idP,frameP,subframeP); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; case 2: case 5: //schedule_RA(module_idP,frameP,subframeP); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); break; default: break; } } else { //FDD // schedule_ulsch(module_idP,frameP,cooperation_flag,9,3); schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status); fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status); } break; } LOG_D(MAC,"FrameP %d, subframeP %d : Scheduling CCEs\n",frameP,subframeP); // Allocate CCEs for good after scheduling is done for (CC_id=0;CC_id