/* * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The OpenAirInterface Software Alliance licenses this file to You under * the OAI Public License, Version 1.1 (the "License"); you may not use this file * except in compliance with the License. * You may obtain a copy of the License at * * http://www.openairinterface.org/?page_id=698 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. *------------------------------------------------------------------------------- * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ /*! \file flexran_agent_scheduler_dataplane.c * \brief data plane procedures related to eNB scheduling * \author Xenofon Foukas * \date 2016 * \email: x.foukas@sms.ed.ac.uk * \version 0.1 * @ingroup _mac */ #include "assertions.h" #include "PHY/defs.h" #include "PHY/extern.h" #include "SCHED/defs.h" #include "SCHED/extern.h" #include "LAYER2/MAC/flexran_agent_mac_proto.h" #include "LAYER2/MAC/defs.h" #include "LAYER2/MAC/proto.h" #include "LAYER2/MAC/extern.h" #include "LAYER2/MAC/flexran_dci_conversions.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 "header.pb-c.h" #include "flexran.pb-c.h" #include "flexran_agent_extern.h" #include "flexran_agent_common.h" #include "SIMULATION/TOOLS/defs.h" // for taus void flexran_apply_dl_scheduling_decisions(mid_t mod_id, uint32_t frame, uint32_t subframe, int *mbsfn_flag, Protocol__FlexranMessage *dl_scheduling_info) { Protocol__FlexDlMacConfig *mac_config = dl_scheduling_info->dl_mac_config_msg; // Check if there is anything to schedule for random access if (mac_config->n_dl_rar > 0) { /*TODO: call the random access data plane function*/ } // Check if there is anything to schedule for paging/broadcast if (mac_config->n_dl_broadcast > 0) { /*TODO: call the broadcast/paging data plane function*/ } // Check if there is anything to schedule for the UEs if (mac_config->n_dl_ue_data > 0) { flexran_apply_ue_spec_scheduling_decisions(mod_id, frame, subframe, mbsfn_flag, mac_config->n_dl_ue_data, mac_config->dl_ue_data); } } void flexran_apply_ue_spec_scheduling_decisions(mid_t mod_id, uint32_t frame, uint32_t subframe, int *mbsfn_flag, uint32_t n_dl_ue_data, Protocol__FlexDlData **dl_ue_data) { uint8_t CC_id; int UE_id; mac_rlc_status_resp_t rlc_status; unsigned char ta_len=0; unsigned char header_len = 0, header_len_tmp = 0; unsigned char sdu_lcids[11],offset,num_sdus=0; uint16_t nb_rb; uint16_t TBS, sdu_lengths[11],rnti,padding=0,post_padding=0; unsigned char dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES]; uint8_t round = 0; uint8_t harq_pid = 0; // LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]; LTE_eNB_UE_stats *eNB_UE_stats = NULL; uint16_t sdu_length_total = 0; short ta_update = 0; eNB_MAC_INST *eNB = &eNB_mac_inst[mod_id]; UE_list_t *UE_list = &eNB->UE_list; // static int32_t tpc_accumulated=0; UE_sched_ctrl *ue_sched_ctl; int last_sdu_header_len = 0; int i, j; Protocol__FlexDlData *dl_data; Protocol__FlexDlDci *dl_dci; uint32_t rlc_size, n_lc, lcid; // For each UE-related command for (i = 0; i < n_dl_ue_data; i++) { dl_data = dl_ue_data[i]; dl_dci = dl_data->dl_dci; CC_id = dl_data->serv_cell_index; // frame_parms[CC_id] = mac_xface->get_lte_frame_parms(mod_id, CC_id); rnti = dl_data->rnti; UE_id = find_ue(rnti, PHY_vars_eNB_g[mod_id][CC_id]); ue_sched_ctl = &UE_list->UE_sched_ctrl[UE_id]; eNB_UE_stats = mac_xface->get_eNB_UE_stats(mod_id,CC_id,rnti); round = dl_dci->rv[0]; harq_pid = dl_dci->harq_process; //LOG_I(FLEXRAN_AGENT, "[Frame %d][Subframe %d] Scheduling harq %d\n", frame, subframe, harq_pid); // LOG_I(FLEXRAN_AGENT, "[Frame %d][Subframe %d]Now scheduling harq_pid %d (round %d)\n", frame, subframe, harq_pid, round); // If this is a new transmission if (round == 0) { // First we have to deal with the creation of the PDU based on the message instructions rlc_status.bytes_in_buffer = 0; TBS = dl_dci->tbs_size[0]; if (dl_data->n_ce_bitmap > 0) { //Check if there is TA command and set the length appropriately ta_len = (dl_data->ce_bitmap[0] & PROTOCOL__FLEX_CE_TYPE__FLPCET_TA) ? 2 : 0; } num_sdus = 0; sdu_length_total = 0; n_lc = dl_data->n_rlc_pdu; // Go through each one of the channel commands and create SDUs header_len = 0; last_sdu_header_len = 0; for (j = 0; j < n_lc; j++) { sdu_lengths[j] = 0; lcid = dl_data->rlc_pdu[j]->rlc_pdu_tb[0]->logical_channel_id; rlc_size = dl_data->rlc_pdu[j]->rlc_pdu_tb[0]->size; LOG_D(MAC,"[TEST] [eNB %d] [Frame %d] [Subframe %d], LCID %d, CC_id %d, Requesting %d bytes from RLC (RRC message)\n", mod_id, frame, subframe, lcid, CC_id, rlc_size); if (rlc_size > 0) { rlc_status = mac_rlc_status_ind(mod_id, rnti, mod_id, frame, subframe, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, 0 #ifdef Rel14 ,0, 0 #endif ); if (rlc_status.bytes_in_buffer > 0) { if (rlc_status.bytes_in_buffer < rlc_size) { rlc_size = rlc_status.bytes_in_buffer; } if (rlc_size <= 2) { rlc_size = 3; } rlc_status = mac_rlc_status_ind(mod_id, rnti, mod_id, frame, subframe, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, rlc_size // transport block set size #ifdef Rel14 ,0, 0 #endif ); LOG_D(MAC, "[TEST] RLC can give %d bytes for LCID %d during second call\n", rlc_status.bytes_in_buffer, lcid); if (rlc_status.bytes_in_buffer > 0) { sdu_lengths[j] = mac_rlc_data_req(mod_id, rnti, mod_id, frame, ENB_FLAG_YES, MBMS_FLAG_NO, lcid, rlc_size, //not used (char *)&dlsch_buffer[sdu_length_total] #ifdef Rel14 ,0, 0 #endif ); LOG_D(MAC,"[eNB %d][LCID %d] CC_id %d Got %d bytes from RLC\n",mod_id, lcid, CC_id, sdu_lengths[j]); sdu_length_total += sdu_lengths[j]; sdu_lcids[j] = lcid; UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[lcid] += 1; UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[lcid] += sdu_lengths[j]; if (sdu_lengths[j] < 128) { header_len += 2; last_sdu_header_len = 2; } else { header_len += 3; last_sdu_header_len = 3; } num_sdus++; } } } } // SDU creation end if (((sdu_length_total + header_len + ta_len) > 0)) { header_len_tmp = header_len; // If we have only a single SDU, header length becomes 1 if ((num_sdus) == 1) { //if (header_len == 2 || header_len == 3) { header_len = 1; } else { header_len = (header_len - last_sdu_header_len) + 1; } // If we need a 1 or 2 bit padding or no padding at all if ((TBS - header_len - sdu_length_total - ta_len) <= 2 || (TBS - header_len - sdu_length_total - ta_len) > TBS) { //protect from overflow padding = (TBS - header_len - sdu_length_total - ta_len); post_padding = 0; } else { // The last sdu needs to have a length field, since we add padding padding = 0; header_len = header_len_tmp; post_padding = TBS - sdu_length_total - header_len - ta_len; // 1 is for the postpadding header } if (ta_len > 0) { // Reset the measurement ta_update = flexran_get_TA(mod_id, UE_id, CC_id); ue_sched_ctl->ta_timer = 20; eNB_UE_stats->timing_advance_update = 0; } else { ta_update = 0; } // If there is nothing to schedule, just leave if ((sdu_length_total) <= 0) { harq_pid_updated[UE_id][harq_pid] = 1; harq_pid_round[UE_id][harq_pid] = 0; continue; } // LOG_I(FLEXRAN_AGENT, "[Frame %d][Subframe %d] TBS is %d and bytes are %d\n", frame, subframe, TBS, sdu_length_total); offset = generate_dlsch_header((unsigned char*)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], num_sdus, //num_sdus sdu_lengths, // sdu_lcids, 255, // no drx ta_update, // timing advance NULL, // contention res id padding, post_padding); #ifdef DEBUG_eNB_SCHEDULER LOG_T(MAC,"[eNB %d] First 16 bytes of DLSCH : \n"); for (i=0; i<16; i++) { LOG_T(MAC,"%x.",dlsch_buffer[i]); } LOG_T(MAC,"\n"); #endif // cycle through SDUs and place in dlsch_buffer memcpy(&UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset],dlsch_buffer,sdu_length_total); // memcpy(&eNB_mac_inst[0].DLSCH_pdu[0][0].payload[0][offset],dcch_buffer,sdu_lengths[0]); // fill remainder of DLSCH with random data for (j=0; j<(TBS-sdu_length_total-offset); j++) { UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset+sdu_length_total+j] = (char)(taus()&0xff); } //eNB_mac_inst[0].DLSCH_pdu[0][0].payload[0][offset+sdu_lengths[0]+j] = (char)(taus()&0xff); if (opt_enabled == 1) { trace_pdu(1, (uint8_t *)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], TBS, mod_id, 3, UE_RNTI(mod_id, UE_id), eNB->frame, eNB->subframe,0,0); LOG_D(OPT,"[eNB %d][DLSCH] CC_id %d Frame %d rnti %x with size %d\n", mod_id, CC_id, frame, UE_RNTI(mod_id,UE_id), TBS); } // store stats eNB->eNB_stats[CC_id].dlsch_bytes_tx+=sdu_length_total; eNB->eNB_stats[CC_id].dlsch_pdus_tx+=1; UE_list->eNB_UE_stats[CC_id][UE_id].dl_cqi= eNB_UE_stats->DL_cqi[0]; UE_list->eNB_UE_stats[CC_id][UE_id].crnti= rnti; UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status=mac_eNB_get_rrc_status(mod_id, rnti); UE_list->eNB_UE_stats[CC_id][UE_id].harq_pid = harq_pid; UE_list->eNB_UE_stats[CC_id][UE_id].harq_round = round; //nb_rb = UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid]; //Find the number of resource blocks and set them to the template for retransmissions nb_rb = get_min_rb_unit(mod_id, CC_id); uint16_t stats_tbs = mac_xface->get_TBS_DL(dl_dci->mcs[0], nb_rb); while (stats_tbs < TBS) { nb_rb += get_min_rb_unit(mod_id, CC_id); stats_tbs = mac_xface->get_TBS_DL(dl_dci->mcs[0], nb_rb); } // LOG_I(FLEXRAN_AGENT, "The MCS was %d\n", dl_dci->mcs[0]); UE_list->eNB_UE_stats[CC_id][UE_id].rbs_used = nb_rb; UE_list->eNB_UE_stats[CC_id][UE_id].total_rbs_used += nb_rb; UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_mcs1=dl_dci->mcs[0]; UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_mcs2=dl_dci->mcs[0]; UE_list->eNB_UE_stats[CC_id][UE_id].TBS = TBS; UE_list->eNB_UE_stats[CC_id][UE_id].overhead_bytes= TBS - sdu_length_total; UE_list->eNB_UE_stats[CC_id][UE_id].total_sdu_bytes+= sdu_length_total; UE_list->eNB_UE_stats[CC_id][UE_id].total_pdu_bytes+= TBS; UE_list->eNB_UE_stats[CC_id][UE_id].total_num_pdus+=1; //eNB_UE_stats->dlsch_mcs1 = cqi_to_mcs[eNB_UE_stats->DL_cqi[0]]; //eNB_UE_stats->dlsch_mcs1 = cmin(eNB_UE_stats->dlsch_mcs1, openair_daq_vars.target_ue_dl_mcs); } else { LOG_D(FLEXRAN_AGENT, "No need to schedule a dci after all. Just drop it\n"); harq_pid_updated[UE_id][harq_pid] = 1; harq_pid_round[UE_id][harq_pid] = 0; continue; } } else { // No need to create anything apart of DCI in case of retransmission /*TODO: Must add these */ // eNB_UE_stats->dlsch_trials[round]++; //UE_list->eNB_UE_stats[CC_id][UE_id].num_retransmission+=1; //UE_list->eNB_UE_stats[CC_id][UE_id].rbs_used_retx=nb_rb; //UE_list->eNB_UE_stats[CC_id][UE_id].total_rbs_used_retx+=nb_rb; //UE_list->eNB_UE_stats[CC_id][UE_id].ncce_used_retx=nCCECC_id]; } // UE_list->UE_template[CC_id][UE_id].oldNDI[dl_dci->harq_process] = dl_dci->ndi[0]; // eNB_UE_stats->dlsch_mcs1 = dl_dci->mcs[0]; //Fill the proper DCI of OAI flexran_fill_oai_dci(mod_id, CC_id, rnti, dl_dci); } } void flexran_fill_oai_dci(mid_t mod_id, uint32_t CC_id, uint32_t rnti, Protocol__FlexDlDci *dl_dci) { void *DLSCH_dci = NULL; DCI_PDU *DCI_pdu; unsigned char harq_pid = 0; // unsigned char round = 0; LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]; int size_bits = 0, size_bytes = 0; eNB_MAC_INST *eNB = &eNB_mac_inst[mod_id]; UE_list_t *UE_list = &eNB->UE_list; LTE_eNB_UE_stats *eNB_UE_stats = NULL; int UE_id = find_ue(rnti, PHY_vars_eNB_g[mod_id][CC_id]); uint32_t format; harq_pid = dl_dci->harq_process; // round = dl_dci->rv[0]; // Note this code is for a specific DCI format DLSCH_dci = (void *)UE_list->UE_template[CC_id][UE_id].DLSCH_DCI[harq_pid]; DCI_pdu = &eNB->common_channels[CC_id].DCI_pdu; frame_parms[CC_id] = mac_xface->get_lte_frame_parms(mod_id, CC_id); if (dl_dci->has_tpc == 1) { // Check if tpc has been set and reset measurement */ if ((dl_dci->tpc == 0) || (dl_dci->tpc == 2)) { eNB_UE_stats = mac_xface->get_eNB_UE_stats(mod_id, CC_id, rnti); eNB_UE_stats->Po_PUCCH_update = 0; } } switch (frame_parms[CC_id]->N_RB_DL) { case 6: if (frame_parms[CC_id]->frame_type == TDD) { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_TDD_1(DCI1_1_5MHz_TDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_1_5MHz_TDD_t); size_bits = sizeof_DCI1_1_5MHz_TDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } else { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_FDD_1(DCI1_1_5MHz_FDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_1_5MHz_FDD_t); size_bits = sizeof_DCI1_1_5MHz_FDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } break; case 25: if (frame_parms[CC_id]->frame_type == TDD) { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_TDD_1(DCI1_5MHz_TDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_5MHz_TDD_t); size_bits = sizeof_DCI1_5MHz_TDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } else { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_FDD_1(DCI1_5MHz_FDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_5MHz_FDD_t); size_bits = sizeof_DCI1_5MHz_FDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } break; case 50: if (frame_parms[CC_id]->frame_type == TDD) { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_TDD_1(DCI1_10MHz_TDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_10MHz_TDD_t); size_bits = sizeof_DCI1_10MHz_TDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } else { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_FDD_1(DCI1_10MHz_FDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_10MHz_FDD_t); size_bits = sizeof_DCI1_10MHz_FDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } break; case 100: if (frame_parms[CC_id]->frame_type == TDD) { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_TDD_1(DCI1_20MHz_TDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_20MHz_TDD_t); size_bits = sizeof_DCI1_20MHz_TDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } else { if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1) { FILL_DCI_FDD_1(DCI1_20MHz_FDD_t, DLSCH_dci, dl_dci); size_bytes = sizeof(DCI1_20MHz_FDD_t); size_bits = sizeof_DCI1_20MHz_FDD_t; } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A) { //TODO } else if (dl_dci->format == PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D) { //TODO } } break; } //Set format to the proper type switch(dl_dci->format) { case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1: format = format1; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1A: format = format1A; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1B: format = format1B; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1C: format = format1C; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_1D: format = format1E_2A_M10PRB; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2: format = format2; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2A: format = format2A; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_2B: format = format2B; break; case PROTOCOL__FLEX_DCI_FORMAT__FLDCIF_3: format = 3; break; default: /*TODO: Need to deal with unsupported DCI type*/ return; } add_ue_spec_dci(DCI_pdu, DLSCH_dci, rnti, size_bytes, dl_dci->aggr_level, size_bits, format, 0); }