/******************************************************************************* 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 <http://www.gnu.org/licenses/>. 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 #include "T.h" #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) { #if MEGALOG printf("SCHEDULER fapi_dl_tpc rnti %x (%d) not existing in MAC\n", dci->rnti, dci->rnti); #endif dci->tpc = 0; return; } 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) { #if MEGALOG 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); #endif 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; } } #if MEGALOG 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); #endif } 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) { #if MEGALOG printf("SCHEDULER fapi_ul_tpc rnti %x (%d) not existing in MAC\n", dci->rnti, dci->rnti); #endif dci->tpc = 0; return; } 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) { #if MEGALOG 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); #endif 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; } #if MEGALOG 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); #endif } /* this structure is used to store downlink ack/nack information * to be sent to FAPI by SchedDlTriggerReq */ /* TODO: lock access to it or not? */ static struct { struct { int rnti; int CC_id; 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_frame; /* the frame where the ACK/NACK has been received */ 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 */ void fapi_dl_ack_nack(int CC_id, int rnti, int harq_pid, int transport_block, int ack) { int pos = fapi_dl_ack_nack_data.count; #if MEGALOG printf("GOT DOWNLINK ack %d for rnti %x harq_pid %d cc %d transport_block %d\n", ack, rnti, harq_pid, CC_id, transport_block); #endif /* 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].CC_id = CC_id; 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 array is used to deal with Msg4 ack/nack * once we get an ACK, the UE is considered configured * if we get a NACK, we have to deal with retransmission of Msg4 (TODO) */ unsigned char waiting_for_msg4_ack[65536]; /* 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 harq_pid, int rnti, int ack) { #if MEGALOG printf("GOT UPLINK ack %d for rnti %x harq_pid %d (f/sf %d/%d)\n", ack, rnti, harq_pid, frame, subframe); #endif 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_frame = frame; 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; if (waiting_for_msg4_ack[rnti] == 1) { /* TODO: deal with module_id != 0, CC_id != 0 */ eNB_MAC_INST *eNB = &eNB_mac_inst[0 /*module_idP*/]; RA_TEMPLATE *RA_template; int i, UE_id; waiting_for_msg4_ack[rnti] = 0; for (i = 0; i < NB_RA_PROC_MAX; i++) { RA_template = (RA_TEMPLATE *)&eNB->common_channels[0 /* 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->RA_active = FALSE; RA_template->wait_ack_Msg4=0; fprintf(stderr, "f/sf %d/%d got ack value %d for RA Msg4 for UE %d\n", frame, subframe, ack, rnti); UE_id = find_UE_id(0 /*module_idP */,RA_template->rnti); if (UE_id == -1) abort(); eNB_mac_inst[0 /* module_idP */].UE_list.UE_template[UE_PCCID(0 /* module_idP */,UE_id)][UE_id].configured=TRUE; } /* 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++; /* TODO: get proper SINR information * for the moment: let's report a sinr of 20 (arbitrary) * for every RBs */ fapi_interface_t *fapi = eNB_mac_inst[0 /* TODO: get correct module_id*/].fapi; struct SchedUlCqiInfoReqParameters params; struct UlCqi_s cqi; int i; cqi.rnti = rnti; for (i = 0; i < 100; i++) cqi.sinr[i] = 20<<3; cqi.type = ff_PUSCH; cqi.servCellIndex = 0; /* TODO: get correct cell index */ params.sfnSf = frame * 16 + subframe; params.nr_ulCqiList = 1; params.ulCqiList = &cqi; params.nr_vendorSpecificList = 0; params.vendorSpecificList = NULL; SchedUlCqiInfoReq(fapi->sched, ¶ms); } /* 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) { #if MEGALOG printf("GOT lcid %d length %d (f/sf %d/%d)\n", lcid, length, frame, subframe); #endif 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; } void fapi_dl_cqi_report(int module_id, int rnti, int frame, int subframe, int cqi_wideband, int *cqi_subband, int rank_indication) { /* TODO: 2 TBs, other reporting modes - we suppose 3-0 (see 36.213 7.2.1) */ fapi_interface_t *fapi; struct SchedDlCqiInfoReqParameters params; struct CqiListElement_s cqi; int i; fapi = eNB_mac_inst[module_id].fapi; cqi.rnti = rnti; cqi.csiReport.ri = rank_indication; cqi.csiReport.mode = A30; /* TODO: get real value */ cqi.csiReport.report.A30Csi.wbCqi = cqi_wideband; for (i = 0; i < MAX_HL_SB; i++) cqi.csiReport.report.A30Csi.sbCqi[i] = cqi_subband[i]; /* TODO: remove this oaisim fix * value 0 is not allowed in wideband CQI reporting, * see 36.213 7.2.3 table 7.2.3-1 * but we get value 0 in oaisim */ if (cqi.csiReport.report.A30Csi.wbCqi == 0) cqi.csiReport.report.A30Csi.wbCqi++; cqi.servCellIndex = 0; /* TODO: get correct value */ params.sfnSf = frame * 16 + subframe; params.nrcqiList = 1; params.cqiList = &cqi; params.nr_vendorSpecificList = 0; params.vendorSpecificList = NULL; //fprintf(stderr, "fsf %d/%d CQI wideband %d rank_indication %d\n", frame, subframe, cqi_wideband, rank_indication); //for (i = 0; i < MAX_HL_SB; i++) //fprintf(stderr, " subband %d: %d\n", i, cqi_subband[i]); SchedDlCqiInfoReq(fapi->sched, ¶ms); /* TODO: get rid of this hack */ cqi.rnti = rnti; cqi.csiReport.ri = rank_indication; cqi.csiReport.mode = A30; /* TODO: get real value */ cqi.csiReport.report.A30Csi.wbCqi = cqi_wideband; for (i = 0; i < MAX_HL_SB; i++) cqi.csiReport.report.A30Csi.sbCqi[i] = cqi_subband[i]; if (cqi.csiReport.report.A30Csi.wbCqi == 0) cqi.csiReport.report.A30Csi.wbCqi++; cqi.servCellIndex = 1; /* TODO: get correct value */ params.sfnSf = frame * 16 + subframe; params.nrcqiList = 1; params.cqiList = &cqi; params.nr_vendorSpecificList = 0; params.vendorSpecificList = NULL; SchedDlCqiInfoReq(fapi->sched, ¶ms); } #define CONVERT_DL_1A_FDD(bandwidth) \ static void fapi_convert_dl_1A_ ## bandwidth ## MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) \ { \ DCI1A_ ## bandwidth ## MHz_FDD_t *d = (DCI1A_ ## bandwidth ## MHz_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_ ## bandwidth ## MHz_FDD_t; \ a->format = format1A; \ } CONVERT_DL_1A_FDD(5); /* defines fapi_convert_dl_1A_5MHz_FDD */ CONVERT_DL_1A_FDD(10); /* defines fapi_convert_dl_1A_10MHz_FDD */ CONVERT_DL_1A_FDD(20); /* defines fapi_convert_dl_1A_20MHz_FDD */ static uint32_t revert(uint32_t x, int len) {return x; int i; int ret = 0; for (i = 0; i < len; i++) { ret <<= 1; ret |= x & 1; x >>= 1; } return ret; } int cqi_ready[65536]; #define CONVERT_DL_1_FDD(bandwidth) \ static void fapi_convert_dl_1_ ## bandwidth ## MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) \ { \ DCI1_ ## bandwidth ## MHz_FDD_t *d = (DCI1_ ## bandwidth ## MHz_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_ ## bandwidth ## MHz_FDD_t; \ a->format = format1; \ } CONVERT_DL_1_FDD(5); /* defines fapi_convert_dl_1_5MHz_FDD */ CONVERT_DL_1_FDD(10); /* defines fapi_convert_dl_1_10MHz_FDD */ CONVERT_DL_1_FDD(20); /* defines fapi_convert_dl_1_20MHz_FDD */ 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); if (dci->format == ONE && dci->resAlloc != 0) { printf("ERROR: resAlloc must be 0\n"); abort(); } /* TODO: handle all bandwidths */ switch (PHY_vars_eNB_g[module_id][CC_id]->lte_frame_parms.N_RB_DL) { case 25: 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(); } break; case 50: switch (dci->format) { case ONE_A: fapi_convert_dl_1A_10MHz_FDD(dci, a); break; case ONE: fapi_convert_dl_1_10MHz_FDD(dci, a); break; default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort(); } break; case 100: switch (dci->format) { case ONE_A: fapi_convert_dl_1A_20MHz_FDD(dci, a); break; case ONE: fapi_convert_dl_1_20MHz_FDD(dci, a); break; default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort(); } 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; } #define CONVERT_UL_FDD(bandwidth) \ static void fapi_convert_ul_ ## bandwidth ## MHz_FDD(module_id_t module_idP, int CC_id, \ struct UlDciListElement_s *dci, DCI_ALLOC_t *a) \ { \ DCI0_ ## bandwidth ## MHz_FDD_t *d = (DCI0_ ## bandwidth ## MHz_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; /* TODO: this may be wrong, */ \ /* see openair1/PHY/LTE_TRANSPORT/dci_tools.c:generate_eNB_ulsch_params_from_dci */ \ /* there is a translation between those */ \ d->cqi_req = dci->cqiRequest; \ if (!cqi_ready[dci->rnti]) d->cqi_req = 0; \ /* TODO: do not disable CQI request */ \ d->cqi_req = 0; \ /*if (dci->cqiRequest) fprintf(stderr, "cqi req (ready %d) (d->cqi_req %d)!!\n", cqi_ready[dci->rnti], d->cqi_req);*/ \ d->padding = 0; \ \ a->dci_length = sizeof_DCI0_ ## bandwidth ## MHz_FDD_t; \ a->format = format0; \ } CONVERT_UL_FDD(5); /* defines fapi_convert_ul_5MHz_FDD */ CONVERT_UL_FDD(10); /* defines fapi_convert_ul_10MHz_FDD */ CONVERT_UL_FDD(20); /* defines fapi_convert_ul_20MHz_FDD */ 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); if (dci->hopping != 0) { printf("ERROR: hopping must be 0\n"); abort(); } /* TODO: handle all bandwidths */ switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_UL) { case 25: fapi_convert_ul_5MHz_FDD(module_idP, CC_id, dci, a); break; case 50: fapi_convert_ul_10MHz_FDD(module_idP, CC_id, dci, a); break; case 100: fapi_convert_ul_20MHz_FDD(module_idP, CC_id, 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; a->rnti = dci->rnti; #if MEGALOG printf("FAPI to MAC ul DCI ue %d rbStart %d rbLen %d tbs %d CCE %d mcs %d ndi %d tpc %d cshift %d cqi_req %d aggrLevel %d\n", dci->rnti, dci->rbStart, dci->rbLen, dci->tbSize, dci->cceIndex, dci->mcs, dci->ndi, dci->tpc, dci->n2Dmrs, dci->cqiRequest, dci->aggrLevel); #endif } /* 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(); } if (grant & 0x80000) { LOG_E(MAC, "uplink grant has hopping flag set (0x%x)\n", grant); exit(1); grant &= ~0x80000; } 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); if (UE_id == -1) abort(); 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 < msg4_header_length+rrc_sdu_length) abort(); 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(RRC,"[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, /* TODO: scell bitmap command */ 0, 0); 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); T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_idP), T_INT(CC_id), T_INT(RA_template->rnti), T_INT(frameP), T_INT(subframeP), T_INT(0), T_BUFFER(&eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0], TBsize)); 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++; waiting_for_msg4_ack[RA_template->rnti] = 1; 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 MCS 29-31 in the PHY layer * in the meantime, this array stores the latest mcs used for each rnti/servcell/harq_pid */ static unsigned char latest_mcs[65536][2][10]; static unsigned int latest_rbbitmap[65536][2][10]; /* TODO: deal with more than one transport block */ static void fapi_schedule_retransmission_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++; /* TODO: handle the case where CC_id != servCellIndex */ if (CC_id != d->servCellIndex) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } #if MEGALOG static char *X[] = {"ONE", "ONE_A", "ONE_B", "ONE_C", "ONE_D", "TWO", "TWO_A", "TWO_B"}; static char *Y[] = {"VRB_DISTRIBUTED", "VRB_LOCALIZED"}; printf("RETR fsf %d/%d rnti %d rbBitmap %x rbShift %d rbgSubset %d resAlloc %d nr_of_tbs %d tbsSize[0] %d mcs[0] %d (latest %d) ndi[0] %d rv[0] %d cceIndex %d aggrLevel %d precodingInfo %d format %s tpc %d harqProcess %d vrbFormat %s tbSwap %d spsRelease %d preambleIndex %d prachMaskIndex %d nGap %d dlPowerOffset %d pdcchPowerOffset %d cifPresent %d\n", frame, subframe, d->dci.rnti, d->dci.rbBitmap, d->dci.rbShift, d->dci.rbgSubset, d->dci.resAlloc, d->dci.nr_of_tbs, d->dci.tbsSize[0], d->dci.mcs[0], latest_mcs[d->rnti][CC_id][d->dci.harqProcess], d->dci.ndi[0], d->dci.rv[0], d->dci.cceIndex, d->dci.aggrLevel, d->dci.precodingInfo, X[d->dci.format], d->dci.tpc, d->dci.harqProcess, Y[d->dci.vrbFormat], d->dci.tbSwap, d->dci.spsRelease, d->dci.preambleIndex, d->dci.prachMaskIndex, d->dci.nGap+1, d->dci.dlPowerOffset, d->dci.pdcchPowerOffset, d->dci.cifPresent); #endif /* TODO: deal with MCS 29-31 in the PHY layer * in the meantime, let's replace with the last used MCS */ d->dci.mcs[0] = latest_mcs[d->rnti][CC_id][d->dci.harqProcess]; /* TODO: remove this terrible hack! */ d->dci.rbBitmap = latest_rbbitmap[d->rnti][CC_id][d->dci.harqProcess]; fapi_convert_dl_dci(module_id, CC_id, &d->dci, a); #if MEGALOG printf("RUN fapi_schedule_retransmission_ue\n"); #endif if (d->nr_rlcPDU_List[0] != 0) { /*printf("%s:%d:%s: error?\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[1]) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->servCellIndex != 0 && d->servCellIndex != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* activate/deactive 2nd cell * TODO: do it better, only activate at subframe+8 if ACK received at subframe +4 */ if (d->ceBitmap[0]) { if (d->ceBitmap[0] != ff_AD) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } LOG_I(MAC, "FAPI: f/sf %d/%d rnti %d ca_activate %2.2x\n", frame, subframe, d->rnti, d->activationDeactivationCE); //mac_xface->ca_activate(module_id, d->rnti, d->activationDeactivationCE); /* TODO: remove this hack */ mac_xface->ca_activate(module_id, d->rnti, 2); } UE_id = find_UE_id(module_id, d->rnti); if (UE_id == -1) { #if MEGALOG printf("SCHEDULER fapi_schedule_retransmission_ue rnti %x (%d) not existing in MAC\n", d->rnti, d->rnti); #endif abort(); } add_ue_dlsch_info(module_id, CC_id, UE_id, subframe, /* S_DL_SCHEDULED */ S_DL_WAITING); } /* 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]; /* retransmission? */ if (d->dci.mcs[0] >= 29 && d->dci.mcs[0] <= 31) { fapi_schedule_retransmission_ue(module_id, CC_id, frame, subframe, d); return; } if (d->nr_rlcPDU_List[0] > 1) { printf("%s:%d:%s: TODO (d->nr_rlcPDU_List[0] %d)\n", __FILE__, __LINE__, __FUNCTION__, d->nr_rlcPDU_List[0]); return; } #if MEGALOG static char *X[] = {"ONE", "ONE_A", "ONE_B", "ONE_C", "ONE_D", "TWO", "TWO_A", "TWO_B"}; static char *Y[] = {"VRB_DISTRIBUTED", "VRB_LOCALIZED"}; printf("fsf %d/%d rnti %d rbBitmap %x rbShift %d rbgSubset %d resAlloc %d nr_of_tbs %d tbsSize[0] %d mcs[0] %d ndi[0] %d rv[0] %d cceIndex %d aggrLevel %d precodingInfo %d format %s tpc %d harqProcess %d vrbFormat %s tbSwap %d spsRelease %d preambleIndex %d prachMaskIndex %d nGap %d dlPowerOffset %d pdcchPowerOffset %d cifPresent %d\n", frame, subframe, d->dci.rnti, d->dci.rbBitmap, d->dci.rbShift, d->dci.rbgSubset, d->dci.resAlloc, d->dci.nr_of_tbs, d->dci.tbsSize[0], d->dci.mcs[0], d->dci.ndi[0], d->dci.rv[0], d->dci.cceIndex, d->dci.aggrLevel, d->dci.precodingInfo, X[d->dci.format], d->dci.tpc, d->dci.harqProcess, Y[d->dci.vrbFormat], d->dci.tbSwap, d->dci.spsRelease, d->dci.preambleIndex, d->dci.prachMaskIndex, d->dci.nGap+1, d->dci.dlPowerOffset, d->dci.pdcchPowerOffset, d->dci.cifPresent); #endif /* 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); #if MEGALOG printf("RUN fapi_schedule_ue\n"); #endif /* TODO: support more than one RLC PDU */ 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[1]) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } if (d->servCellIndex != 0 && d->servCellIndex != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* TODO: handle the case where CC_id != servCellIndex */ if (CC_id != d->servCellIndex) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* activate/deactive 2nd cell * TODO: do it better, only activate at subframe+8 if ACK received at subframe +4 */ if (d->ceBitmap[0]) { if (d->ceBitmap[0] != ff_AD) { printf("%s:%d:%s: TODO (bitmap %d)\n", __FILE__, __LINE__, __FUNCTION__, d->ceBitmap[0]); abort(); } LOG_I(MAC, "FAPI: f/sf %d/%d rnti %d ca_activate %2.2x\n", frame, subframe, d->rnti, d->activationDeactivationCE); //mac_xface->ca_activate(module_id, d->rnti, d->activationDeactivationCE); /* TODO: remove this hack */ mac_xface->ca_activate(module_id, d->rnti, 2); } /* TODO: deal with MCS 29-31 * in the meantime, we store the latest used mcs for each rnti/harq_pid */ latest_mcs[d->rnti][CC_id][d->dci.harqProcess] = d->dci.mcs[0]; latest_rbbitmap[d->rnti][CC_id][d->dci.harqProcess] = d->dci.rbBitmap; 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++) { if (d->rlcPduList[0][i].logicalChannelIdentity > 3) { printf("%s:%d:%s: bad lcid %d\n", __FILE__, __LINE__, __FUNCTION__, d->rlcPduList[0][i].logicalChannelIdentity); abort(); } 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); #if MEGALOG 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); #endif /* TODO: what if we get 0? */ 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(); } //if (d->rlcPduList[0][i].size == 2) { // printf("yo\n"); //} 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) LOG_W(MAC, "FAPI f/sf %d/%d: RLC returned length 0 while asked %d for channel ID %d\n", frame, subframe, d->rlcPduList[0][i].size, d->rlcPduList[0][i].logicalChannelIdentity); 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++; #if MEGALOG printf("FILLED %d bytes\n", output_length); #endif } /* get size of header and payload */ header_size = 0; payload_size = 0; /* TODO: 2nd TB */ if (d->ceBitmap[0] & ff_AD) { header_size++; payload_size++; } 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; #if MEGALOG printf("PADDING_SIZE %d\n", padding_size); #endif //if (padding_size == 3) { printf("padding size 3\n"); abort(); } UE_id = find_UE_id(module_id, d->rnti); if (UE_id == -1) { #if MEGALOG printf("SCHEDULER fapi_schedule_ue rnti %x (%d) not existing in MAC\n", d->rnti, d->rnti); #endif abort(); } /* 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, /* TODO: remove this hack */ 2 /*d->activationDeactivationCE*/, (d->ceBitmap[0] & ff_AD) != 0); /* fill payload */ memcpy(&UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset], dlsch_buffer, dlsch_filled); T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(d->rnti), T_INT(frame), T_INT(subframe), T_INT(d->dci.harqProcess), T_BUFFER(UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], tbs)); #if MEGALOG printf("BLOCK (%d)", tbs); for (i = 0; i < tbs; i++) printf(" %2.2x", (unsigned char)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][i]); printf("\n"); #endif 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; } static char *dci_format_to_string(DCI_format_t f) { switch (f) { case format0: return "format 0"; case format1: return "format 1"; case format1A: return "format 1A"; case format1B: return "format 1B"; case format1C: return "format 1C"; case format1D: return "format 1D"; case format1E_2A_M10PRB: return "format 1E_2A_M10PRB"; case format2: return "format 2"; case format2A: return "format 2A"; case format2B: return "format 2B"; case format2C: return "format 2C"; case format2D: return "format 2D"; case format3: return "format 3"; } printf("%s:%d: unhandled DCI format\n", __FILE__, __LINE__); abort(); } static int next_frame = 0; static int next_subframe = 0; static pthread_mutex_t fmut = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t fcond = PTHREAD_COND_INITIALIZER; void eNB_dlsch_ulsch_scheduler(module_id_t module_idP,uint8_t cooperation_flag, frame_t frameP, sub_frame_t subframeP) //, int calibration_flag) { { if (pthread_mutex_lock(&fmut)) abort(); 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; VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_IN); #if MEGALOG printf("SCHEDULER called for f/sf %d/%d\n", frameP, subframeP); #endif #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_id<MAX_NUM_CCs; CC_id++) { DCI_pdu[CC_id] = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; //DCI_pdu[CC_id]->nCCE=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) { /* TODO: try to sync this part with develop */ //mac_remove_ue(module_idP, i, frameP, subframeP); printf("%s:%d: TODO: sync with develop\n", __FILE__, __LINE__); abort(); } i = next_i; } // clear DCI and BCCH contents before scheduling for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { DCI_pdu[CC_id]->Num_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_id<MAX_NUM_CCs; CC_id++) { for (i=0; i < NB_RA_PROC_MAX; i++) { if (eNB->common_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) + 1; LOG_D(MAC, "calling SchedDlRlcBufferReq on CCCH rnti %x queue_size %d\n", rlc.rnti, rlc.rlcTransmissionQueueSize); #if MEGALOG printf("MAC to FAPI downlink BUF CCCH %d\n", rlc.rlcTransmissionQueueSize); #endif 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_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, 0); rlc.logicalChannelIdentity = DCCH; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; if (rlc.rlcTransmissionQueueSize == 2) rlc.rlcTransmissionQueueSize = 10; LOG_D(MAC, "calling SchedDlRlcBufferReq on DCCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); #if MEGALOG printf("MAC to FAPI downlink BUF DCCH %d\n", rlc_status.bytes_in_buffer); #endif /* DCCH+1 (srb 2, lcid 2) */ rlc_status = mac_rlc_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH+1, 0); rlc.logicalChannelIdentity = DCCH+1; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; LOG_D(MAC, "calling SchedDlRlcBufferReq on DCCH+1 rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); #if MEGALOG printf("MAC to FAPI downlink BUF DCCH+1 %d\n", rlc_status.bytes_in_buffer); #endif /* DTCH (drb 1, lcid 3) */ rlc_status = mac_rlc_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DTCH, 0); rlc.logicalChannelIdentity = DTCH; rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer; LOG_D(MAC, "calling SchedDlRlcBufferReq on DTCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer); SchedDlRlcBufferReq(fapi->sched, &rlc); #if MEGALOG printf("MAC to FAPI downlink BUF DTCH %d\n", rlc_status.bytes_in_buffer); #endif } 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] ? ff_ACK : ff_NACK; /* TODO: more than 1 TB */ dlinfo[i].servCellIndex = fapi_dl_ack_nack_data.ack[i].CC_id; /* TODO: get real value for the servCellIndex */ #if MEGALOG printf("MAC to FAPI downlink ack/nack from PHY CC %d f/sf %d/%d rnti %x harq %d ack %d\n", dlinfo[i].servCellIndex, frameP, subframeP, dlinfo[i].rnti, dlinfo[i].harqProcessId, fapi_dl_ack_nack_data.ack[i].ack[0]); #endif } 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_D(MAC, "calling SchedDlTriggerReq\n"); SchedDlTriggerReq(fapi->sched, &dlreq); LOG_D(MAC, "calling SchedDlConfigInd\n"); SchedDlConfigInd(fapi, &dlind); LOG_D(MAC, "SchedDlConfigInd returns dlind.nr_buildDataList %d f/sf %d/%d\n", dlind.nr_buildDataList, frameP, subframeP); LOG_D(MAC, "SchedDlConfigInd returns dlind.nr_buildRARList %d f/sf %d/%d\n", dlind.nr_buildRARList, frameP, subframeP); LOG_D(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 MEGALOG 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); #endif switch (dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity) { case 0: /* CCCH */ if (dlind.buildDataList[i].nr_rlcPDU_List[0] != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); } /* 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); } #if MEGALOG printf("FAPI to MAC downlink schedule RAR ue %x f/sf %d/%d\n", dlind.buildRarList[0].rnti, frameP, subframeP); #endif 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); } for (i = 0; i < dlind.nr_buildBroadcastList; i++) { if (dlind.buildBroadcastList[i].type == ff_PCCH) { printf("%s:%d: PCCH: todo\n", __FUNCTION__, __LINE__); exit(0); } #if MEGALOG printf("FAPI to MAC downlink schedule SI %d CC %d f/sf %d/%d\n", dlind.buildBroadcastList[i].index, dlind.buildBroadcastList[i].carrierIndex, frameP, subframeP); #endif fapi_schedule_SI(module_idP, dlind.buildBroadcastList[i].carrierIndex, frameP, subframeP, dlind.buildBroadcastList[i].index, &dlind.buildBroadcastList[i].dci); } /* TODO: do it correctly */ if (dlind.nr_ofdmSymbolsCount != 0) { if (dlind.nr_ofdmSymbolsCount != MAX_NUM_CCs) { printf("%s:%d:%s: what to do?\n", __FILE__, __LINE__, __FUNCTION__); abort(); } for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) { if (dlind.nrOfPdcchOfdmSymbols[CC_id] != NULL) { int cc = dlind.nrOfPdcchOfdmSymbols[CC_id]->carrierIndex; DCI_pdu[cc]->num_pdcch_symbols = dlind.nrOfPdcchOfdmSymbols[CC_id]->pdcchOfdmSymbolCount; #if MEGALOG 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); #endif } } } #if 0 // Allocate CCEs for good after scheduling is done for (CC_id=0;CC_id<MAX_NUM_CCs;CC_id++) allocate_CCEs(module_idP,CC_id,subframeP,0); #endif /*************************************************/ /* uplink scheduling */ /*************************************************/ ulreq.sfnSf = frameP * 16 + subframeP; ulreq.nr_ulInfoList = 0; ulreq.ulInfoList = NULL; ulreq.nr_vendorSpecificList = 0; ulreq.vendorSpecificList = NULL; /* fill ulInfoList */ ulsf = (subframeP + 10 - 3) % 10; if (fapi_ul_ack_nack_data[ulsf].count) { ulreq.nr_ulInfoList = fapi_ul_ack_nack_data[ulsf].count; ulreq.ulInfoList = ulinfo; for (i = 0; i < ulreq.nr_ulInfoList; i++) { #if MEGALOG printf("MAC to FAPI uplink acknack ue %x f/sf %d/%d ulsf %d [reception frame/subframe %d/%d] ack %d\n", fapi_ul_ack_nack_data[ulsf].ack[i].rnti, frameP, subframeP, ulsf, fapi_ul_ack_nack_data[ulsf].ack[i].reception_frame, fapi_ul_ack_nack_data[ulsf].ack[i].reception_subframe, fapi_ul_ack_nack_data[ulsf].ack[i].ack); #endif ulinfo[i].puschTransmissionTimestamp = fapi_ul_ack_nack_data[ulsf].ack[i].reception_frame * 16 + fapi_ul_ack_nack_data[ulsf].ack[i].reception_subframe; ulinfo[i].rnti = fapi_ul_ack_nack_data[ulsf].ack[i].rnti; ulinfo[i].receptionStatus = fapi_ul_ack_nack_data[ulsf].ack[i].ack == 1 ? Ok : NotOk; ulinfo[i].tpc = 0; /* TODO */ ulinfo[i].servCellIndex = 0; /* TODO: get correct value */ for (j = 0; j < MAX_LC_LIST+1; j++) { ulinfo[i].ulReception[j] = fapi_ul_ack_nack_data[ulsf].ack[i].length[j]; #if MEGALOG printf("MAC to FAPI uplink ue %x f/sf %d/%d lcid %d size acked %d\n", fapi_ul_ack_nack_data[ulsf].ack[i].rnti, frameP, subframeP, j, fapi_ul_ack_nack_data[ulsf].ack[i].length[j]); #endif } } fapi_ul_ack_nack_data[ulsf].count = 0; } LOG_D(MAC, "calling SchedUlTriggerReq\n"); SchedUlTriggerReq(fapi->sched, &ulreq); LOG_D(MAC, "calling SchedUlConfigInd\n"); SchedUlConfigInd(fapi, &ulind); /* a hack to report nice CQI all the time it's asked */ for (i = 0; i < ulind.nr_dciList; i++) { struct UlDciListElement_s *dci = &ulind.dciList[i]; if (dci->cqiRequest) { int cqi_subband[13]; int k; for (k = 0; k < 13; k++) cqi_subband[k] = 14; fapi_dl_cqi_report(0, dci->rnti, frameP + (subframeP > 6 ? 1 : 0), (subframeP + 4) % 10, 14, cqi_subband, 0); } } #if MEGALOG printf("FAPI to MAC uplink nr_dclList %d nr_phichList %d\n", ulind.nr_dciList, ulind.nr_phichList); #endif 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; #if MEGALOG printf("FAPI to MAC uplink schedule ue %x ndi %d (fsf %d %d) rbstart %d rblen %d tbsize %d mcs %d\n", ulind.dciList[i].rnti, ulind.dciList[i].ndi, frameP, subframeP, ulind.dciList[i].rbStart, ulind.dciList[i].rbLen, ulind.dciList[i].tbSize, ulind.dciList[i].mcs); #endif fapi_schedule_uplink(module_idP, CC_id, &ulind.dciList[i]); } #if MEGALOG int cc; for (cc = 0; cc < 2; cc++) { printf("RECAP dci pdu count %d cc %d\n", eNB_mac_inst[0].common_channels[cc].DCI_pdu.Num_common_dci, cc); for (i = 0; i < eNB_mac_inst[0].common_channels[cc].DCI_pdu.Num_common_dci; i++) { printf(" RECAP %i rnti %x %s dci pdu %s\n", i, eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].rnti, dci_format_to_string(eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].format), binary(*(uint32_t *)eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].dci_pdu) ); } } printf("RECAP phich count %d\n", ulind.nr_phichList); for (i = 0; i < ulind.nr_phichList; i++) { printf(" RECAP %i rnti %x %s\n", i, ulind.phichList[i].rnti, ulind.phichList[i].phich == ACK ? "ACK" : "NACK"); } #endif global_subframe++; if (pthread_mutex_unlock(&fmut)) abort(); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_OUT); } #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; void *DLSCH_dci=NULL; int size_bits=0,size_bytes=0; 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_id<MAX_NUM_CCs; CC_id++) { DCI_pdu[CC_id] = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu; 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]; PHY_vars_eNB_g[module_idP][CC_id]->pusch_stats_bsr[i][(frameP*10)+subframeP]=-63; if (i==UE_list->head) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_BSR,PHY_vars_eNB_g[module_idP][CC_id]->pusch_stats_bsr[i][(frameP*10)+subframeP]); // increment this, it is cleared when we receive an sdu eNB_mac_inst[module_idP].UE_list.UE_sched_ctrl[i].ul_inactivity_timer++; eNB_mac_inst[module_idP].UE_list.UE_sched_ctrl[i].cqi_req_timer++; if (mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)==NULL) { // mac_remove_ue(module_idP, i, frameP, subframeP); } else { // check uplink failure if ((UE_list->UE_sched_ctrl[i].ul_failure_timer>0)&& (UE_list->UE_sched_ctrl[i].ul_out_of_sync==0)) { LOG_D(MAC,"UE %d rnti %x: UL Failure timer %d \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer); if (UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent==0) { UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent=1; // add a format 1A dci for this UE to request an RA procedure (only one UE per subframe) LOG_D(MAC,"UE %d rnti %x: sending PDCCH order for RAPROC (failure timer %d) \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer); DLSCH_dci = (void *)UE_list->UE_template[CC_id][i].DLSCH_DCI[0]; *(uint32_t*)DLSCH_dci = 0; if (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.frame_type == TDD) { switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL) { case 6: ((DCI1A_1_5MHz_TDD_1_6_t*)DLSCH_dci)->type = 1; ((DCI1A_1_5MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 31; size_bytes = sizeof(DCI1A_1_5MHz_TDD_1_6_t); size_bits = sizeof_DCI1A_1_5MHz_TDD_1_6_t; break; case 25: ((DCI1A_5MHz_TDD_1_6_t*)DLSCH_dci)->type = 1; ((DCI1A_5MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 511; size_bytes = sizeof(DCI1A_5MHz_TDD_1_6_t); size_bits = sizeof_DCI1A_5MHz_TDD_1_6_t; break; case 50: ((DCI1A_10MHz_TDD_1_6_t*)DLSCH_dci)->type = 1; ((DCI1A_10MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 2047; size_bytes = sizeof(DCI1A_10MHz_TDD_1_6_t); size_bits = sizeof_DCI1A_10MHz_TDD_1_6_t; break; case 100: ((DCI1A_20MHz_TDD_1_6_t*)DLSCH_dci)->type = 1; ((DCI1A_20MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 8191; size_bytes = sizeof(DCI1A_20MHz_TDD_1_6_t); size_bits = sizeof_DCI1A_20MHz_TDD_1_6_t; break; } } else { // FDD switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL) { case 6: ((DCI1A_1_5MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_1_5MHz_FDD_t*)DLSCH_dci)->rballoc = 31; size_bytes = sizeof(DCI1A_1_5MHz_FDD_t); size_bits = sizeof_DCI1A_1_5MHz_FDD_t; break; case 15:/* ((DCI1A_2_5MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_2_5MHz_FDD_t*)DLSCH_dci)->rballoc = 31; size_bytes = sizeof(DCI1A_1_5MHz_FDD_t); size_bits = sizeof_DCI1A_1_5MHz_FDD_t;*/ break; case 25: ((DCI1A_5MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_5MHz_FDD_t*)DLSCH_dci)->rballoc = 511; size_bytes = sizeof(DCI1A_5MHz_FDD_t); size_bits = sizeof_DCI1A_5MHz_FDD_t; break; case 50: ((DCI1A_10MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_10MHz_FDD_t*)DLSCH_dci)->rballoc = 2047; size_bytes = sizeof(DCI1A_10MHz_FDD_t); size_bits = sizeof_DCI1A_10MHz_FDD_t; break; case 75: /* ((DCI1A_15MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_15MHz_FDD_t*)DLSCH_dci)->rballoc = 2047; size_bytes = sizeof(DCI1A_10MHz_FDD_t); size_bits = sizeof_DCI1A_10MHz_FDD_t;*/ break; case 100: ((DCI1A_20MHz_FDD_t*)DLSCH_dci)->type = 1; ((DCI1A_20MHz_FDD_t*)DLSCH_dci)->rballoc = 8191; size_bytes = sizeof(DCI1A_20MHz_FDD_t); size_bits = sizeof_DCI1A_20MHz_FDD_t; break; } } add_ue_spec_dci(DCI_pdu[CC_id], DLSCH_dci, rnti, size_bytes, process_ue_cqi (module_idP,i),//aggregation, size_bits, format1A, 0); } else { // ra_pdcch_sent==1 LOG_D(MAC,"UE %d rnti %x: sent PDCCH order for RAPROC waiting (failure timer %d) \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer); if ((UE_list->UE_sched_ctrl[i].ul_failure_timer % 40) == 0) UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent=0; // resend every 4 frames } UE_list->UE_sched_ctrl[i].ul_failure_timer++; // check threshold if (UE_list->UE_sched_ctrl[i].ul_failure_timer > 200) { // inform RRC of failure and clear timer LOG_I(MAC,"UE %d rnti %x: UL Failure after repeated PDCCH orders: Triggering RRC \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer); mac_eNB_rrc_ul_failure(module_idP,CC_id,frameP,subframeP,rnti); UE_list->UE_sched_ctrl[i].ul_failure_timer=0; UE_list->UE_sched_ctrl[i].ul_out_of_sync=1; } } } // ul_failure_timer>0 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_id<MAX_NUM_CCs; CC_id++) { DCI_pdu[CC_id]->Num_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_id<MAX_NUM_CCs; CC_id++) { if (eNB_mac_inst[module_idP].common_channels[CC_id].MBMS_flag >0) { 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_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<MAX_NUM_CCs;CC_id++) allocate_CCEs(module_idP,CC_id,subframeP,0); /* int dummy=0; for (i=0; i<DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci; i++) if (DCI_pdu[CC_id]->dci_alloc[i].rnti==2) dummy=1; if (dummy==1) for (i=0; i<DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci; i++) LOG_I(MAC,"Frame %d, subframe %d: DCI %d/%d, format %d, rnti %x, NCCE %d(num_pdcch_symb %d)\n", frameP,subframeP,i,DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci, DCI_pdu[CC_id]->dci_alloc[i].format, DCI_pdu[CC_id]->dci_alloc[i].rnti, DCI_pdu[CC_id]->dci_alloc[i].firstCCE, DCI_pdu[CC_id]->num_pdcch_symbols); LOG_D(MAC,"frameP %d, subframeP %d\n",frameP,subframeP); */ stop_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_OUT); } #endif /* FAPI */