/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

/*! \file eNB_scheduler_dlsch.c
 * \brief procedures related to eNB for the DLSCH transport channel
 * \author  Navid Nikaein and Raymond Knopp
 * \date 2010 - 2014
 * \email: navid.nikaein@eurecom.fr
 * \version 1.0
 * @ingroup _mac

 */


#include "LAYER2/MAC/mac.h"
#include "LAYER2/MAC/mac_proto.h"
#include "LAYER2/MAC/mac_extern.h"
#include "UTIL/LOG/log.h"
#include "UTIL/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
#include "OCG.h"
#include "OCG_extern.h"
#include "PHY/LTE_TRANSPORT/transport_common_proto.h"

#include "RRC/LTE/rrc_extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"

//#include "LAYER2/MAC/pre_processor.c"
#include "pdcp.h"

#include "SIMULATION/TOOLS/sim.h"	// for taus

#include "assertions.h"

#if defined(ENABLE_ITTI)
#include "intertask_interface.h"
#endif

#include "ENB_APP/flexran_agent_defs.h"
#include "flexran_agent_ran_api.h"
#include "header.pb-c.h"
#include "flexran.pb-c.h"
#include <dlfcn.h>

#include "T.h"

#define ENABLE_MAC_PAYLOAD_DEBUG
//#define DEBUG_eNB_SCHEDULER 1

extern RAN_CONTEXT_t RC;
extern uint8_t nfapi_mode;


// number of active slices for  past and current time
int n_active_slices = 1;
int n_active_slices_current = 1;

// RB share for each slice for past and current time
float avg_slice_percentage=0.25;
float slice_percentage[MAX_NUM_SLICES] = {1.0, 0.0, 0.0, 0.0};
float slice_percentage_current[MAX_NUM_SLICES] = {1.0, 0.0, 0.0, 0.0};
float total_slice_percentage = 0;
float total_slice_percentage_current = 0;

// MAX MCS for each slice for past and current time
int slice_maxmcs[MAX_NUM_SLICES] = { 28, 28, 28, 28 };
int slice_maxmcs_current[MAX_NUM_SLICES] = { 28, 28, 28, 28 };

int update_dl_scheduler[MAX_NUM_SLICES] = { 1, 1, 1, 1 };
int update_dl_scheduler_current[MAX_NUM_SLICES] = { 1, 1, 1, 1 };

// name of available scheduler
char *dl_scheduler_type[MAX_NUM_SLICES] =
  { "schedule_ue_spec",
    "schedule_ue_spec",
    "schedule_ue_spec",
    "schedule_ue_spec"
  };

// The lists of criteria that enforce the sorting policies of the slices
uint32_t sorting_policy[MAX_NUM_SLICES] = {0x01234, 0x01234, 0x01234, 0x01234};
uint32_t sorting_policy_current[MAX_NUM_SLICES] = {0x01234, 0x01234, 0x01234, 0x01234};

// pointer to the slice specific scheduler
slice_scheduler_dl slice_sched_dl[MAX_NUM_SLICES] = {0};

//------------------------------------------------------------------------------
void
add_ue_dlsch_info(module_id_t module_idP,
		  int CC_id,
		  int UE_id, sub_frame_t subframeP, UE_DLSCH_STATUS status)
//------------------------------------------------------------------------------
{
  //LOG_D(MAC, "%s(module_idP:%d, CC_id:%d, UE_id:%d, subframeP:%d, status:%d) serving_num:%d rnti:%x\n", __FUNCTION__, module_idP, CC_id, UE_id, subframeP, status, eNB_dlsch_info[module_idP][CC_id][UE_id].serving_num, UE_RNTI(module_idP,UE_id));

  eNB_dlsch_info[module_idP][CC_id][UE_id].rnti =
    UE_RNTI(module_idP, UE_id);
  //  eNB_dlsch_info[module_idP][CC_id][ue_mod_idP].weight           = weight;
  eNB_dlsch_info[module_idP][CC_id][UE_id].subframe = subframeP;
  eNB_dlsch_info[module_idP][CC_id][UE_id].status = status;

  eNB_dlsch_info[module_idP][CC_id][UE_id].serving_num++;

}

//------------------------------------------------------------------------------
int
schedule_next_dlue(module_id_t module_idP, int CC_id,
		   sub_frame_t subframeP)
//------------------------------------------------------------------------------
{

  int next_ue;
  UE_list_t *UE_list = &RC.mac[module_idP]->UE_list;

  for (next_ue = UE_list->head; next_ue >= 0;
       next_ue = UE_list->next[next_ue]) {
    if (eNB_dlsch_info[module_idP][CC_id][next_ue].status ==
	S_DL_WAITING) {
      return next_ue;
    }
  }

  for (next_ue = UE_list->head; next_ue >= 0;
       next_ue = UE_list->next[next_ue]) {
    if (eNB_dlsch_info[module_idP][CC_id][next_ue].status == S_DL_BUFFERED) {
      eNB_dlsch_info[module_idP][CC_id][next_ue].status = S_DL_WAITING;
    }
  }

  return (-1);		//next_ue;

}

//------------------------------------------------------------------------------
int
generate_dlsch_header(unsigned char *mac_header,
		      unsigned char num_sdus,
		      unsigned short *sdu_lengths,
		      unsigned char *sdu_lcids,
		      unsigned char drx_cmd,
		      unsigned short timing_advance_cmd,
		      unsigned char *ue_cont_res_id,
		      unsigned char short_padding,
		      unsigned short post_padding)
//------------------------------------------------------------------------------
{

  SCH_SUBHEADER_FIXED *mac_header_ptr = (SCH_SUBHEADER_FIXED *) mac_header;
  uint8_t first_element = 0, last_size = 0, i;
  uint8_t mac_header_control_elements[16], *ce_ptr;

  ce_ptr = &mac_header_control_elements[0];

  // compute header components

  if ((short_padding == 1) || (short_padding == 2)) {
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    first_element = 1;
    last_size = 1;
  }

  if (short_padding == 2) {
    mac_header_ptr->E = 1;
    mac_header_ptr++;
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    last_size = 1;
  }

  if (drx_cmd != 255) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      mac_header_ptr++;
    } else {
      first_element = 1;
    }

    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = DRX_CMD;
    last_size = 1;
  }

  if (timing_advance_cmd != 31) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      mac_header_ptr++;
    } else {
      first_element = 1;
    }

    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = TIMING_ADV_CMD;
    last_size = 1;
    //    msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);
    ((TIMING_ADVANCE_CMD *) ce_ptr)->R = 0;
    AssertFatal(timing_advance_cmd < 64,
		"timing_advance_cmd %d > 63\n", timing_advance_cmd);
    ((TIMING_ADVANCE_CMD *) ce_ptr)->TA = timing_advance_cmd;	//(timing_advance_cmd+31)&0x3f;
    LOG_D(MAC, "timing advance =%d (%d)\n", timing_advance_cmd,
	  ((TIMING_ADVANCE_CMD *) ce_ptr)->TA);
    ce_ptr += sizeof(TIMING_ADVANCE_CMD);
    //msg("offset %d\n",ce_ptr-mac_header_control_elements);
  }

  if (ue_cont_res_id) {
    if (first_element > 0) {
      mac_header_ptr->E = 1;
      /*
	printf("[eNB][MAC] last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);
      */
      mac_header_ptr++;
    } else {
      first_element = 1;
    }

    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = UE_CONT_RES;
    last_size = 1;

    LOG_T(MAC,
	  "[eNB ][RAPROC] Generate contention resolution msg: %x.%x.%x.%x.%x.%x\n",
	  ue_cont_res_id[0], ue_cont_res_id[1], ue_cont_res_id[2],
	  ue_cont_res_id[3], ue_cont_res_id[4], ue_cont_res_id[5]);

    memcpy(ce_ptr, ue_cont_res_id, 6);
    ce_ptr += 6;
    // msg("(cont_res) : offset %d\n",ce_ptr-mac_header_control_elements);
  }
  //msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);

  for (i = 0; i < num_sdus; i++) {
    LOG_T(MAC, "[eNB] Generate DLSCH header num sdu %d len sdu %d\n",
	  num_sdus, sdu_lengths[i]);

    if (first_element > 0) {
      mac_header_ptr->E = 1;
      /*msg("last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
	((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);
      */
      mac_header_ptr += last_size;
      //msg("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);
    } else {
      first_element = 1;
    }

    if (sdu_lengths[i] < 128) {
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->R = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->E = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->F = 0;
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->LCID = sdu_lcids[i];
      ((SCH_SUBHEADER_SHORT *) mac_header_ptr)->L = (unsigned char) sdu_lengths[i];
      last_size = 2;
    } else {
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->R = 0;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->E = 0;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->F = 1;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->LCID = sdu_lcids[i];
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_MSB = ((unsigned short) sdu_lengths[i] >> 8) & 0x7f;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_LSB = (unsigned short) sdu_lengths[i] & 0xff;
      ((SCH_SUBHEADER_LONG *) mac_header_ptr)->padding = 0x00;
      last_size = 3;
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,
	    "[eNB] generate long sdu, size %x (MSB %x, LSB %x)\n",
	    sdu_lengths[i],
	    ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_MSB,
	    ((SCH_SUBHEADER_LONG *) mac_header_ptr)->L_LSB);
#endif
    }
  }

  /*

    printf("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);

    printf("last subheader : %x (R%d,E%d,LCID%d)\n",*(unsigned char*)mac_header_ptr,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->R,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E,
    ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID);


    if (((SCH_SUBHEADER_FIXED*)mac_header_ptr)->LCID < UE_CONT_RES) {
    if (((SCH_SUBHEADER_SHORT*)mac_header_ptr)->F == 0)
    printf("F = 0, sdu len (L field) %d\n",(((SCH_SUBHEADER_SHORT*)mac_header_ptr)->L));
    else
    printf("F = 1, sdu len (L field) %d\n",(((SCH_SUBHEADER_LONG*)mac_header_ptr)->L));
    }
  */
  if (post_padding > 0) {	// we have lots of padding at the end of the packet
    mac_header_ptr->E = 1;
    mac_header_ptr += last_size;
    // add a padding element
    mac_header_ptr->R = 0;
    mac_header_ptr->E = 0;
    mac_header_ptr->LCID = SHORT_PADDING;
    mac_header_ptr++;
  } else {			// no end of packet padding
    // last SDU subhead is of fixed type (sdu length implicitly to be computed at UE)
    mac_header_ptr++;
  }

  //msg("After subheaders %d\n",(uint8_t*)mac_header_ptr - mac_header);

  if ((ce_ptr - mac_header_control_elements) > 0) {
    // printf("Copying %d bytes for control elements\n",ce_ptr-mac_header_control_elements);
    memcpy((void *) mac_header_ptr, mac_header_control_elements,
	   ce_ptr - mac_header_control_elements);
    mac_header_ptr +=
      (unsigned char) (ce_ptr - mac_header_control_elements);
  }
  //msg("After CEs %d\n",(uint8_t*)mac_header_ptr - mac_header);

  return ((unsigned char *) mac_header_ptr - mac_header);
}

//------------------------------------------------------------------------------
void
set_ul_DAI(int module_idP, int UE_idP, int CC_idP, int frameP,
	   int subframeP)
//------------------------------------------------------------------------------
{

  eNB_MAC_INST *eNB = RC.mac[module_idP];
  UE_list_t *UE_list = &eNB->UE_list;
  unsigned char DAI;
  COMMON_channels_t *cc = &eNB->common_channels[CC_idP];
  if (cc->tdd_Config != NULL) {	//TDD
    DAI = (UE_list->UE_template[CC_idP][UE_idP].DAI - 1) & 3;
    LOG_D(MAC,
	  "[eNB %d] CC_id %d Frame %d, subframe %d: DAI %d for UE %d\n",
	  module_idP, CC_idP, frameP, subframeP, DAI, UE_idP);
    // Save DAI for Format 0 DCI

    switch (cc->tdd_Config->subframeAssignment) {
    case 0:
      //      if ((subframeP==0)||(subframeP==1)||(subframeP==5)||(subframeP==6))
      break;

    case 1:
      switch (subframeP) {
      case 0:
      case 1:
	UE_list->UE_template[CC_idP][UE_idP].DAI_ul[7] = DAI;
	break;

      case 4:
        UE_list->UE_template[CC_idP][UE_idP].DAI_ul[8] = DAI;
        break;

      case 5:
      case 6:
        UE_list->UE_template[CC_idP][UE_idP].DAI_ul[2] = DAI;
        break;

      case 9:
        UE_list->UE_template[CC_idP][UE_idP].DAI_ul[3] = DAI;
        break;
      }

    case 2:
      //      if ((subframeP==3)||(subframeP==8))
      //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
      break;

    case 3:

      //if ((subframeP==6)||(subframeP==8)||(subframeP==0)) {
      //  LOG_D(MAC,"schedule_ue_spec: setting UL DAI to %d for subframeP %d => %d\n",DAI,subframeP, ((subframeP+8)%10)>>1);
      //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul[((subframeP+8)%10)>>1] = DAI;
      //}
      switch (subframeP) {
      case 5:
      case 6:
      case 1:
	UE_list->UE_template[CC_idP][UE_idP].DAI_ul[2] = DAI;
	break;

      case 7:
      case 8:
	UE_list->UE_template[CC_idP][UE_idP].DAI_ul[3] = DAI;
	break;

      case 9:
      case 0:
	UE_list->UE_template[CC_idP][UE_idP].DAI_ul[4] = DAI;
	break;

      default:
	break;
      }

      break;

    case 4:
      //      if ((subframeP==8)||(subframeP==9))
      //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
      break;

    case 5:
      //      if (subframeP==8)
      //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
      break;

    case 6:
      //      if ((subframeP==1)||(subframeP==4)||(subframeP==6)||(subframeP==9))
      //  UE_list->UE_template[CC_idP][UE_idP].DAI_ul = DAI;
      break;

    default:
      break;
    }
  }
}

//------------------------------------------------------------------------------
void
schedule_dlsch(module_id_t module_idP,
	        frame_t frameP, sub_frame_t subframeP, int *mbsfn_flag)
//------------------------------------------------------------------------------{
{

  int i = 0;

  total_slice_percentage=0;
  avg_slice_percentage=1.0/n_active_slices;

  // reset the slice percentage for inactive slices
  for (i = n_active_slices; i< MAX_NUM_SLICES; i++) {
    slice_percentage[i]=0;
  }
  for (i = 0; i < n_active_slices; i++) {
    if (slice_percentage[i] < 0 ){
      LOG_W(MAC, "[eNB %d] frame %d subframe %d:invalid slice %d percentage %f. resetting to zero",
	    module_idP, frameP, subframeP, i, slice_percentage[i]);
      slice_percentage[i]=0;
    }
    total_slice_percentage+=slice_percentage[i];
  }

  for (i = 0; i < n_active_slices; i++) {

    // Load any updated functions
    if (update_dl_scheduler[i] > 0 ) {
      slice_sched_dl[i] = dlsym(NULL, dl_scheduler_type[i]);
      update_dl_scheduler[i] = 0 ;
      update_dl_scheduler_current[i] = 0;
      LOG_N(MAC,"update dl scheduler slice %d\n", i);
    }

    if (total_slice_percentage <= 1.0){ // the new total RB share is within the range

      // check if the number of slices has changed, and log
      if (n_active_slices_current != n_active_slices ){
	if ((n_active_slices > 0) && (n_active_slices <= MAX_NUM_SLICES)) {
	  LOG_N(MAC,"[eNB %d]frame %d subframe %d: number of active DL slices has changed: %d-->%d\n",
		module_idP, frameP, subframeP, n_active_slices_current, n_active_slices);

	  n_active_slices_current = n_active_slices;

	} else {
	  LOG_W(MAC,"invalid number of DL slices %d, revert to the previous value %d\n",n_active_slices, n_active_slices_current);
	  n_active_slices = n_active_slices_current;
	}
      }

      // check if the slice rb share has changed, and log the console
      if (slice_percentage_current[i] != slice_percentage[i]){ // new slice percentage
	LOG_N(MAC,"[eNB %d][SLICE %d][DL] frame %d subframe %d: total percentage %f-->%f, slice RB percentage has changed: %f-->%f\n",
	      module_idP, i, frameP, subframeP, total_slice_percentage_current, total_slice_percentage, slice_percentage_current[i], slice_percentage[i]);
	total_slice_percentage_current= total_slice_percentage;
	slice_percentage_current[i] = slice_percentage[i];

      }

      // check if the slice max MCS, and log the console
      if (slice_maxmcs_current[i] != slice_maxmcs[i]){
	if ((slice_maxmcs[i] >= 0) && (slice_maxmcs[i] < 29)){
	  LOG_N(MAC,"[eNB %d][SLICE %d][DL] frame %d subframe %d: slice MAX MCS has changed: %d-->%d\n",
		module_idP, i, frameP, subframeP, slice_maxmcs_current[i], slice_maxmcs[i]);
	  slice_maxmcs_current[i] = slice_maxmcs[i];
	} else {
	  LOG_W(MAC,"[eNB %d][SLICE %d][DL] invalid slice max mcs %d, revert the previous value %d\n",module_idP, i, slice_maxmcs[i],slice_maxmcs_current[i]);
	  slice_maxmcs[i]= slice_maxmcs_current[i];
	}
      }

      // check if a new scheduler, and log the console
      if (update_dl_scheduler_current[i] != update_dl_scheduler[i]){
	LOG_N(MAC,"[eNB %d][SLICE %d][DL] frame %d subframe %d: DL scheduler for this slice is updated: %s \n",
	      module_idP, i, frameP, subframeP, dl_scheduler_type[i]);
	update_dl_scheduler_current[i] = update_dl_scheduler[i];
      }

    } else {
      // here we can correct the values, e.g. reduce proportionally

      if (n_active_slices == n_active_slices_current){
	LOG_W(MAC,"[eNB %d][SLICE %d][DL] invalid total RB share (%f->%f), reduce proportionally the RB share by 0.1\n",
	      module_idP, i, total_slice_percentage_current, total_slice_percentage);
	if (slice_percentage[i] >= avg_slice_percentage){
	  slice_percentage[i]-=0.1;
	  total_slice_percentage-=0.1;
	}
      } else {
	LOG_W(MAC,"[eNB %d][SLICE %d][DL] invalid total RB share (%f->%f), revert the number of slice to its previous value (%d->%d)\n",
	      module_idP, i, total_slice_percentage_current, total_slice_percentage,
	      n_active_slices, n_active_slices_current );
	n_active_slices = n_active_slices_current;
	slice_percentage[i] = slice_percentage_current[i];
      }
    }

    // Check for new sorting policy
    if (sorting_policy_current[i] != sorting_policy[i]) {
      LOG_N(MAC,"[eNB %d][SLICE %d][DL] frame %d subframe %d: UE sorting policy has changed (%x-->%x)\n",
            module_idP, i, frameP, subframeP, sorting_policy_current[i], sorting_policy[i]);
      sorting_policy_current[i] = sorting_policy[i];
    }

    // Run each enabled slice-specific schedulers one by one
    slice_sched_dl[i](module_idP, i, frameP, subframeP, mbsfn_flag/*, dl_info*/);
  }

}

// changes to pre-processor for eMTC

//------------------------------------------------------------------------------
void
schedule_ue_spec(module_id_t module_idP,slice_id_t slice_idP,
		 frame_t frameP, sub_frame_t subframeP, int *mbsfn_flag)
//------------------------------------------------------------------------------
{
  int CC_id;
  int UE_id;
  int aggregation;
  mac_rlc_status_resp_t rlc_status;
  int ta_len = 0;
  unsigned char sdu_lcids[NB_RB_MAX];
  int lcid, offset, num_sdus = 0;
  int nb_rb, nb_rb_temp, nb_available_rb;
  uint16_t sdu_lengths[NB_RB_MAX];
  int TBS, j, rnti, padding = 0, post_padding = 0;
  unsigned char dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES];
  int round = 0;
  int harq_pid = 0;
  eNB_UE_STATS *eNB_UE_stats = NULL;
  int sdu_length_total = 0;

  eNB_MAC_INST *eNB = RC.mac[module_idP];
  COMMON_channels_t *cc = eNB->common_channels;
  UE_list_t *UE_list = &eNB->UE_list;
  int continue_flag = 0;
  int32_t normalized_rx_power, target_rx_power;
  int tpc = 1;
  UE_sched_ctrl *ue_sched_ctl;
  int mcs;
  int i;
  int min_rb_unit[MAX_NUM_CCs];
  int N_RB_DL[MAX_NUM_CCs];
  int total_nb_available_rb[MAX_NUM_CCs];
  int N_RBG[MAX_NUM_CCs];
  nfapi_dl_config_request_body_t *dl_req;
  nfapi_dl_config_request_pdu_t *dl_config_pdu;
  int tdd_sfa;
  int ta_update;
  int header_length_last;
  int header_length_total;

  start_meas(&eNB->schedule_dlsch);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
    (VCD_SIGNAL_DUMPER_FUNCTIONS_SCHEDULE_DLSCH, VCD_FUNCTION_IN);


  // for TDD: check that we have to act here, otherwise return
  if (cc[0].tdd_Config) {
    tdd_sfa = cc[0].tdd_Config->subframeAssignment;
    switch (subframeP) {
    case 0:
      // always continue
      break;
    case 1:
      return;
      break;
    case 2:
      return;
      break;
    case 3:
      if ((tdd_sfa != 2) && (tdd_sfa != 5))
	return;
      break;
    case 4:
      if ((tdd_sfa != 1) && (tdd_sfa != 2) && (tdd_sfa != 4)
	  && (tdd_sfa != 5))
	return;
      break;
    case 5:
      break;
    case 6:
    case 7:
      if ((tdd_sfa != 3) && (tdd_sfa != 4) && (tdd_sfa != 5))
	return;
      break;
    case 8:
      if ((tdd_sfa != 2) && (tdd_sfa != 3) && (tdd_sfa != 4)
	  && (tdd_sfa != 5))
	return;
      break;
    case 9:
      if (tdd_sfa == 0)
	return;
      break;

    }
  }
  //weight = get_ue_weight(module_idP,UE_id);
  aggregation = 2;
  for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
    N_RB_DL[CC_id] = to_prb(cc[CC_id].mib->message.dl_Bandwidth);
    min_rb_unit[CC_id] = get_min_rb_unit(module_idP, CC_id);
    // get number of PRBs less those used by common channels
    total_nb_available_rb[CC_id] = N_RB_DL[CC_id];
    for (i = 0; i < N_RB_DL[CC_id]; i++)
      if (cc[CC_id].vrb_map[i] != 0)
	total_nb_available_rb[CC_id]--;

    N_RBG[CC_id] = to_rbg(cc[CC_id].mib->message.dl_Bandwidth);

    // store the global enb stats:
    eNB->eNB_stats[CC_id].num_dlactive_UEs = UE_list->num_UEs;
    eNB->eNB_stats[CC_id].available_prbs = total_nb_available_rb[CC_id];
    eNB->eNB_stats[CC_id].total_available_prbs += total_nb_available_rb[CC_id];
    eNB->eNB_stats[CC_id].dlsch_bytes_tx = 0;
    eNB->eNB_stats[CC_id].dlsch_pdus_tx = 0;
  }

  /// CALLING Pre_Processor for downlink scheduling (Returns estimation of RBs required by each UE and the allocation on sub-band)

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
      (VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_PREPROCESSOR, VCD_FUNCTION_IN);
  start_meas(&eNB->schedule_dlsch_preprocessor);
  dlsch_scheduler_pre_processor(module_idP,
                                slice_idP,
                                frameP,
                                subframeP,
                                N_RBG,
                                mbsfn_flag);
  stop_meas(&eNB->schedule_dlsch_preprocessor);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
      (VCD_SIGNAL_DUMPER_FUNCTIONS_DLSCH_PREPROCESSOR, VCD_FUNCTION_OUT);

  for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
    LOG_D(MAC, "doing schedule_ue_spec for CC_id %d\n", CC_id);

    dl_req = &eNB->DL_req[CC_id].dl_config_request_body;

    if (mbsfn_flag[CC_id] > 0)
      continue;

    for (UE_id = UE_list->head; UE_id >= 0;
	 UE_id = UE_list->next[UE_id]) {
      continue_flag = 0;	// reset the flag to allow allocation for the remaining UEs
      rnti = UE_RNTI(module_idP, UE_id);
      eNB_UE_stats = &UE_list->eNB_UE_stats[CC_id][UE_id];
      ue_sched_ctl = &UE_list->UE_sched_ctrl[UE_id];


      if (rnti == NOT_A_RNTI) {
	LOG_D(MAC, "Cannot find rnti for UE_id %d (num_UEs %d)\n",
	      UE_id, UE_list->num_UEs);
	continue_flag = 1;
      }

      if (eNB_UE_stats == NULL) {
          LOG_D(MAC, "[eNB] Cannot find eNB_UE_stats\n");
          continue_flag = 1;
      }
      if (!ue_slice_membership(UE_id, slice_idP))
                  continue;

      if (continue_flag != 1) {
	switch (get_tmode(module_idP, CC_id, UE_id)) {
	case 1:
	case 2:
	case 7:
	  aggregation =
	    get_aggregation(get_bw_index(module_idP, CC_id),
			    ue_sched_ctl->dl_cqi[CC_id],
			    format1);
	  break;
	case 3:
	  aggregation =
	    get_aggregation(get_bw_index(module_idP, CC_id),
			    ue_sched_ctl->dl_cqi[CC_id],
			    format2A);
	  break;
	default:
	  LOG_W(MAC, "Unsupported transmission mode %d\n",
		get_tmode(module_idP, CC_id, UE_id));
	  aggregation = 2;
	}
      }
      /* if (continue_flag != 1 */
      if ((ue_sched_ctl->pre_nb_available_rbs[CC_id] == 0) ||	// no RBs allocated
	  CCE_allocation_infeasible(module_idP, CC_id, 1, subframeP,
				    aggregation, rnti)) {
	LOG_D(MAC,
	      "[eNB %d] Frame %d : no RB allocated for UE %d on CC_id %d: continue \n",
	      module_idP, frameP, UE_id, CC_id);
	continue_flag = 1;	//to next user (there might be rbs availiable for other UEs in TM5
      }

      if (cc[CC_id].tdd_Config != NULL) {	//TDD
	set_ue_dai(subframeP,
		   UE_id,
		   CC_id,
		   cc[CC_id].tdd_Config->subframeAssignment,
		   UE_list);
	// update UL DAI after DLSCH scheduling
	set_ul_DAI(module_idP, UE_id, CC_id, frameP, subframeP);
      }

      if (continue_flag == 1) {
	add_ue_dlsch_info(module_idP,
			  CC_id, UE_id, subframeP, S_DL_NONE);
	continue;
      }

      nb_available_rb = ue_sched_ctl->pre_nb_available_rbs[CC_id];

      harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config,frameP ,subframeP);

      round = ue_sched_ctl->round[CC_id][harq_pid];

      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(module_idP, 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;


      if (UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status < RRC_CONNECTED) continue;

      header_length_total = 0;
      sdu_length_total = 0;
      num_sdus = 0;

      /*
	DevCheck(((eNB_UE_stats->dl_cqi < MIN_CQI_VALUE) || (eNB_UE_stats->dl_cqi > MAX_CQI_VALUE)),
	eNB_UE_stats->dl_cqi, MIN_CQI_VALUE, MAX_CQI_VALUE);
      */
      if (nfapi_mode) {
		  eNB_UE_stats->dlsch_mcs1 = 10;//cqi_to_mcs[ue_sched_ctl->dl_cqi[CC_id]];
      }
      else { // this operation is also done in the preprocessor
		  eNB_UE_stats->dlsch_mcs1 = cmin(eNB_UE_stats->dlsch_mcs1, slice_maxmcs[slice_idP]);  //cmin(eNB_UE_stats->dlsch_mcs1, openair_daq_vars.target_ue_dl_mcs);
      }



      // store stats
      //UE_list->eNB_UE_stats[CC_id][UE_id].dl_cqi= eNB_UE_stats->dl_cqi;

      // initializing the rb allocation indicator for each UE
      for (j = 0; j < N_RBG[CC_id]; j++) {
	UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = 0;
      }

      LOG_D(MAC,
	    "[eNB %d] Frame %d: Scheduling UE %d on CC_id %d (rnti %x, harq_pid %d, round %d, rb %d, cqi %d, mcs %d, rrc %d)\n",
	    module_idP, frameP, UE_id, CC_id, rnti, harq_pid, round,
	    nb_available_rb, ue_sched_ctl->dl_cqi[CC_id],
	    eNB_UE_stats->dlsch_mcs1,
	    UE_list->eNB_UE_stats[CC_id][UE_id].rrc_status);



      /* process retransmission  */

      if (round != 8) {

	// get freq_allocation
	nb_rb = UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid];
	TBS = get_TBS_DL(UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid],
			 nb_rb);

	if (nb_rb <= nb_available_rb) {
	  if (cc[CC_id].tdd_Config != NULL) {
	    UE_list->UE_template[CC_id][UE_id].DAI++;
	    update_ul_dci(module_idP, CC_id, rnti,
			  UE_list->UE_template[CC_id][UE_id].DAI, subframeP);
	    LOG_D(MAC,
		  "DAI update: CC_id %d subframeP %d: UE %d, DAI %d\n",
		  CC_id, subframeP, UE_id,
		  UE_list->UE_template[CC_id][UE_id].DAI);
	  }

	  if (nb_rb == ue_sched_ctl->pre_nb_available_rbs[CC_id]) {
	    for (j = 0; j < N_RBG[CC_id]; j++) {	// for indicating the rballoc for each sub-band
	      UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = ue_sched_ctl->rballoc_sub_UE[CC_id][j];
	    }
	  } else {
	    nb_rb_temp = nb_rb;
	    j = 0;

	    while ((nb_rb_temp > 0) && (j < N_RBG[CC_id])) {
	      if (ue_sched_ctl->rballoc_sub_UE[CC_id][j] == 1) {
		if (UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j])
		  printf("WARN: rballoc_subband not free for retrans?\n");
		UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = ue_sched_ctl->rballoc_sub_UE[CC_id][j];

		if ((j == N_RBG[CC_id] - 1) &&
		    ((N_RB_DL[CC_id] == 25) ||
		     (N_RB_DL[CC_id] == 50))) {
		  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id] + 1;
		} else {
		  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id];
		}
	      }

	      j = j + 1;
	    }
	  }

	  nb_available_rb -= nb_rb;
	  /*
	    eNB->mu_mimo_mode[UE_id].pre_nb_available_rbs = nb_rb;
	    eNB->mu_mimo_mode[UE_id].dl_pow_off = ue_sched_ctl->dl_pow_off[CC_id];

	    for(j=0; j<N_RBG[CC_id]; j++) {
	    eNB->mu_mimo_mode[UE_id].rballoc_sub[j] = UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j];
	    }
	  */

	  switch (get_tmode(module_idP, CC_id, UE_id)) {
	  case 1:
	  case 2:
	  case 7:
	  default:
	    LOG_D(MAC,"retransmission DL_REQ: rnti:%x\n",rnti);

	    dl_config_pdu = &dl_req->dl_config_pdu_list[dl_req->number_pdu];
	    memset((void *) dl_config_pdu, 0,
		   sizeof(nfapi_dl_config_request_pdu_t));
	    dl_config_pdu->pdu_type = NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE;
	    dl_config_pdu->pdu_size = (uint8_t) (2 + sizeof(nfapi_dl_config_dci_dl_pdu));
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tl.tag = NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG;
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format = NFAPI_DL_DCI_FORMAT_1;
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level = get_aggregation(get_bw_index(module_idP, CC_id),
											  ue_sched_ctl->dl_cqi[CC_id],
											  format1);
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti      = rnti;
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type = 1;	// CRNTI : see Table 4-10 from SCF082 - nFAPI specifications
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.transmission_power = 6000;	// equal to RS power

	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.harq_process = harq_pid;
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tpc = 1;	// dont adjust power when retransmitting
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.new_data_indicator_1 = UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid];
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.mcs_1 = UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid];
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.redundancy_version_1 = round & 3;

	    if (cc[CC_id].tdd_Config != NULL) {	//TDD
	      dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.downlink_assignment_index = (UE_list->UE_template[CC_id][UE_id].DAI - 1) & 3;
	      LOG_D(MAC,
		    "[eNB %d] Retransmission CC_id %d : harq_pid %d, round %d, dai %d, mcs %d\n",
		    module_idP, CC_id, harq_pid, round,
		    (UE_list->UE_template[CC_id][UE_id].DAI - 1),
		    UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid]);
	    } else {
	      LOG_D(MAC,
		    "[eNB %d] Retransmission CC_id %d : harq_pid %d, round %d, mcs %d\n",
		    module_idP, CC_id, harq_pid, round,
		    UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid]);

	    }
	    if (!CCE_allocation_infeasible(module_idP, CC_id, 1, subframeP,
					   dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level, rnti)) {
	      dl_req->number_dci++;
	      dl_req->number_pdu++;
	      dl_req->tl.tag = NFAPI_DL_CONFIG_REQUEST_BODY_TAG;

	      eNB->DL_req[CC_id].sfn_sf = frameP<<4 | subframeP;
	      eNB->DL_req[CC_id].header.message_id = NFAPI_DL_CONFIG_REQUEST;

	      fill_nfapi_dlsch_config(eNB, dl_req, TBS, -1
				      /* retransmission, no pdu_index */
				      , rnti, 0,	// type 0 allocation from 7.1.6 in 36.213
				      0,	// virtual_resource_block_assignment_flag, unused here
				      0,	// resource_block_coding, to be filled in later
				      getQm(UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid]), round & 3,	// redundancy version
				      1,	// transport blocks
				      0,	// transport block to codeword swap flag
				      cc[CC_id].p_eNB == 1 ? 0 : 1,	// transmission_scheme
				      1,	// number of layers
				      1,	// number of subbands
				      //                      uint8_t codebook_index,
				      4,	// UE category capacity
				      UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated->pdsch_ConfigDedicated->p_a, 0,	// delta_power_offset for TM5
				      0,	// ngap
				      0,	// nprb
				      cc[CC_id].p_eNB == 1 ? 1 : 2,	// transmission mode
				      0,	//number of PRBs treated as one subband, not used here
				      0	// number of beamforming vectors, not used here
				      );

	      LOG_D(MAC,
		    "Filled NFAPI configuration for DCI/DLSCH %d, retransmission round %d\n",
		    eNB->pdu_index[CC_id], round);

	      program_dlsch_acknak(module_idP, CC_id, UE_id,
				   frameP, subframeP,
				   dl_config_pdu->
				   dci_dl_pdu.dci_dl_pdu_rel8.
				   cce_idx);
	      // No TX request for retransmission (check if null request for FAPI)
	    } else {
	      LOG_W(MAC,
		    "Frame %d, Subframe %d: Dropping DLSCH allocation for UE %d\%x, infeasible CCE allocation\n",
		    frameP, subframeP, UE_id, rnti);
	    }
	  }


	  add_ue_dlsch_info(module_idP,
			    CC_id, UE_id, subframeP,
			    S_DL_SCHEDULED);

	  //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].dlsch_mcs1 = eNB_UE_stats->dlsch_mcs1;
	  UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_mcs2 = eNB_UE_stats->dlsch_mcs1;
	} else {
	  LOG_D(MAC,
		"[eNB %d] Frame %d CC_id %d : don't schedule UE %d, its retransmission takes more resources than we have\n",
		module_idP, frameP, CC_id, UE_id);
	}
      } else {		/* This is a potentially new SDU opportunity */
	rlc_status.bytes_in_buffer = 0;

	// Now check RLC information to compute number of required RBs
	// get maximum TBS size for RLC request
	TBS = get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_available_rb);

	// add the length for  all the control elements (timing adv, drx, etc) : header + payload

	if (ue_sched_ctl->ta_timer == 0) {
	  ta_update = ue_sched_ctl->ta_update;
	  /* if we send TA then set timer to not send it for a while */
	  if (ta_update != 31) ue_sched_ctl->ta_timer = 20;
	  /* reset ta_update */
	  ue_sched_ctl->ta_update = 31;
	} else {
	  ta_update = 31;
	}

	ta_len = (ta_update != 31) ? 2 : 0;

	// RLC data on DCCH
	if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) {
	  rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH,
                                          TBS - ta_len - header_length_total - sdu_length_total - 3
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                                                    ,0, 0
#endif
                          );

	  sdu_lengths[0] = 0;

	  if (rlc_status.bytes_in_buffer > 0) {
	    LOG_D(MAC, "[eNB %d] SFN/SF %d.%d, DL-DCCH->DLSCH CC_id %d, Requesting %d bytes from RLC (RRC message)\n",
		  module_idP, frameP, subframeP, CC_id,
		  TBS - ta_len - header_length_total - sdu_length_total - 3);

	    sdu_lengths[0] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH,
                                              TBS, //not used
					      (char *)&dlsch_buffer[0]
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                          ,0, 0
#endif
                          );

            pthread_mutex_lock(&rrc_release_freelist);
            if((rrc_release_info.num_UEs > 0) && (rlc_am_mui.rrc_mui_num > 0)){
              uint16_t release_total = 0;
              for(uint16_t release_num = 0;release_num < NUMBER_OF_UE_MAX;release_num++){
                if(rrc_release_info.RRC_release_ctrl[release_num].flag > 0){
                  release_total++;
                }else{
                  continue;
                }

                if(rrc_release_info.RRC_release_ctrl[release_num].flag == 1){
                  if(rrc_release_info.RRC_release_ctrl[release_num].rnti == rnti){
                    for(uint16_t mui_num = 0;mui_num < rlc_am_mui.rrc_mui_num;mui_num++){
                      if(rrc_release_info.RRC_release_ctrl[release_num].rrc_eNB_mui == rlc_am_mui.rrc_mui[mui_num]){
                        rrc_release_info.RRC_release_ctrl[release_num].flag = 3;
                        LOG_D(MAC,"DLSCH Release send:index %d rnti %x mui %d mui_num %d flag 1->3\n",release_num,rnti,rlc_am_mui.rrc_mui[mui_num],mui_num);
                        break;
                      }
                    }
                  }
                }
                if(rrc_release_info.RRC_release_ctrl[release_num].flag == 2){
                  if(rrc_release_info.RRC_release_ctrl[release_num].rnti == rnti){
                    for(uint16_t mui_num = 0;mui_num < rlc_am_mui.rrc_mui_num;mui_num++){
                      if(rrc_release_info.RRC_release_ctrl[release_num].rrc_eNB_mui == rlc_am_mui.rrc_mui[mui_num]){
                        rrc_release_info.RRC_release_ctrl[release_num].flag = 4;
                        LOG_D(MAC,"DLSCH Release send:index %d rnti %x mui %d mui_num %d flag 2->4\n",release_num,rnti,rlc_am_mui.rrc_mui[mui_num],mui_num);
                        break;
                      }
                    }
                  }
                }
                if(release_total >= rrc_release_info.num_UEs)
                  break;
              }
            }
            pthread_mutex_unlock(&rrc_release_freelist);

            RA_t *ra = &eNB->common_channels[CC_id].ra[0];
            for (uint8_t ra_ii = 0; ra_ii < NB_RA_PROC_MAX; ra_ii++) {
              if((ra[ra_ii].rnti == rnti) && (ra[ra_ii].state == MSGCRNTI)){
                for(uint16_t mui_num = 0;mui_num < rlc_am_mui.rrc_mui_num;mui_num++){
                  if(ra[ra_ii].crnti_rrc_mui == rlc_am_mui.rrc_mui[mui_num]){
                    ra[ra_ii].crnti_harq_pid = harq_pid;
                    ra[ra_ii].state = MSGCRNTI_ACK;
                    break;
                  }
                }
              }
            }

	    T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP),
	      T_INT(CC_id), T_INT(rnti), T_INT(frameP),
	      T_INT(subframeP), T_INT(harq_pid), T_INT(DCCH),
	      T_INT(sdu_lengths[0]));

	    LOG_D(MAC, "[eNB %d][DCCH] CC_id %d Got %d bytes from RLC\n",
		  module_idP, CC_id, sdu_lengths[0]);

	    sdu_length_total = sdu_lengths[0];
	    sdu_lcids[0] = DCCH;
            UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[0] = DCCH;
            UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[DCCH] = sdu_lengths[0];
	    UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[DCCH] += 1;
	    UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[DCCH] += sdu_lengths[0];

            header_length_last = 1 + 1 + (sdu_lengths[0] >= 128);
            header_length_total += header_length_last;

	    num_sdus = 1;

#ifdef DEBUG_eNB_SCHEDULER
	    LOG_T(MAC,
		  "[eNB %d][DCCH] CC_id %d Got %d bytes :",
		  module_idP, CC_id, sdu_lengths[0]);

	    for (j = 0; j < sdu_lengths[0]; j++) {
	      LOG_T(MAC, "%x ", dlsch_buffer[j]);
	    }

	    LOG_T(MAC, "\n");
#endif
	  }
	}

	// RLC data on DCCH1
	if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) {
	  rlc_status = mac_rlc_status_ind(module_idP, rnti, module_idP, frameP, subframeP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1,
                                          TBS - ta_len - header_length_total - sdu_length_total - 3
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                                                    ,0, 0
#endif
                                         );

	  // DCCH SDU
	  sdu_lengths[num_sdus] = 0;

	  if (rlc_status.bytes_in_buffer > 0) {
	    LOG_D(MAC, "[eNB %d], Frame %d, DCCH1->DLSCH, CC_id %d, Requesting %d bytes from RLC (RRC message)\n",
		  module_idP, frameP, CC_id,
		  TBS - ta_len - header_length_total - sdu_length_total - 3);

	    sdu_lengths[num_sdus] += mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH + 1,
                                                      TBS, //not used
						      (char *)&dlsch_buffer[sdu_length_total]
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                          ,0, 0
#endif
	    );

	    T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP),
	      T_INT(CC_id), T_INT(rnti), T_INT(frameP),
	      T_INT(subframeP), T_INT(harq_pid),
	      T_INT(DCCH + 1), T_INT(sdu_lengths[num_sdus]));

	    sdu_lcids[num_sdus] = DCCH1;
	    sdu_length_total += sdu_lengths[num_sdus];
            UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[num_sdus] = DCCH1;
            UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[DCCH1] = sdu_lengths[num_sdus];
	    UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[DCCH1] += 1;
	    UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[DCCH1] += sdu_lengths[num_sdus];

            header_length_last = 1 + 1 + (sdu_lengths[num_sdus] >= 128);
            header_length_total += header_length_last;

	    num_sdus++;

#ifdef DEBUG_eNB_SCHEDULER
	    LOG_T(MAC,
		  "[eNB %d][DCCH1] CC_id %d Got %d bytes :",
		  module_idP, CC_id, sdu_lengths[num_sdus]);

	    for (j = 0; j < sdu_lengths[num_sdus]; j++) {
	      LOG_T(MAC, "%x ", dlsch_buffer[j]);
	    }

	    LOG_T(MAC, "\n");
#endif

	  }
	}

	// TODO: lcid has to be sorted before the actual allocation (similar struct as ue_list).
	for (lcid = NB_RB_MAX - 1; lcid >= DTCH; lcid--) {
	  // TODO: check if the lcid is active

	  LOG_D(MAC, "[eNB %d], Frame %d, DTCH%d->DLSCH, Checking RLC status (tbs %d, len %d)\n",
		module_idP, frameP, lcid, TBS,
                TBS - ta_len - header_length_total - sdu_length_total - 3);

	  if (TBS - ta_len - header_length_total - sdu_length_total - 3 > 0) {
	    rlc_status = mac_rlc_status_ind(module_idP,
					    rnti,
					    module_idP,
					    frameP,
					    subframeP,
					    ENB_FLAG_YES,
					    MBMS_FLAG_NO,
					    lcid,
					    TBS - ta_len - header_length_total - sdu_length_total - 3
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                                                    ,0, 0
#endif
                                           );



	    if (rlc_status.bytes_in_buffer > 0) {
	      LOG_D(MAC,
		    "[eNB %d][USER-PLANE DEFAULT DRB] Frame %d : DTCH->DLSCH, Requesting %d bytes from RLC (lcid %d total hdr len %d)\n",
		    module_idP, frameP,
                    TBS - ta_len - header_length_total - sdu_length_total - 3,
		    lcid,
		    header_length_total);

	      sdu_lengths[num_sdus] = mac_rlc_data_req(module_idP, rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, lcid,
                                                       TBS, //not used
						       (char *)&dlsch_buffer[sdu_length_total]
#if (RRC_VERSION >= MAKE_VERSION(14, 0, 0))
                          ,0, 0
#endif
	      );

	      T(T_ENB_MAC_UE_DL_SDU, T_INT(module_idP),
		T_INT(CC_id), T_INT(rnti), T_INT(frameP),
		T_INT(subframeP), T_INT(harq_pid),
		T_INT(lcid), T_INT(sdu_lengths[num_sdus]));

	      LOG_D(MAC,
		    "[eNB %d][USER-PLANE DEFAULT DRB] Got %d bytes for DTCH %d \n",
		    module_idP, sdu_lengths[num_sdus], lcid);

	      sdu_lcids[num_sdus] = lcid;
	      sdu_length_total += sdu_lengths[num_sdus];
	      UE_list->eNB_UE_stats[CC_id][UE_id].num_pdu_tx[lcid]++;
              UE_list->eNB_UE_stats[CC_id][UE_id].lcid_sdu[num_sdus] = lcid;
              UE_list->eNB_UE_stats[CC_id][UE_id].sdu_length_tx[lcid] = sdu_lengths[num_sdus];
	      UE_list->eNB_UE_stats[CC_id][UE_id].num_bytes_tx[lcid] += sdu_lengths[num_sdus];

              header_length_last = 1 + 1 + (sdu_lengths[num_sdus] >= 128);
              header_length_total += header_length_last;

	      num_sdus++;

	      UE_list->UE_sched_ctrl[UE_id].uplane_inactivity_timer = 0;
	    }
	  } else {
            // no TBS left
	    break;
	  }
	}

        /* last header does not have length field */
        if (header_length_total) {
          header_length_total -= header_length_last;
          header_length_total++;
        }

	// there is at least one SDU or TA command
	// if (num_sdus > 0 ){
	if (ta_len + sdu_length_total + header_length_total > 0) {

	  // Now compute number of required RBs for total sdu length
	  // Assume RAH format 2

	  mcs = eNB_UE_stats->dlsch_mcs1;

	  if (mcs == 0) {
	    nb_rb = 4;	// don't let the TBS get too small
	  } else {
	    nb_rb = min_rb_unit[CC_id];
	  }

	  TBS = get_TBS_DL(mcs, nb_rb);

	  while (TBS < sdu_length_total + header_length_total + ta_len) {
	    nb_rb += min_rb_unit[CC_id];	//

	    if (nb_rb > nb_available_rb) {	// if we've gone beyond the maximum number of RBs
	      // (can happen if N_RB_DL is odd)
	      TBS = get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_available_rb);
	      nb_rb = nb_available_rb;
	      break;
	    }

	    TBS = get_TBS_DL(eNB_UE_stats->dlsch_mcs1, nb_rb);
	  }

	  if (nb_rb == ue_sched_ctl->pre_nb_available_rbs[CC_id]) {
	    for (j = 0; j < N_RBG[CC_id]; j++) {	// for indicating the rballoc for each sub-band
	      UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] =	ue_sched_ctl->rballoc_sub_UE[CC_id][j];
	    }
	  } else {
	    nb_rb_temp = nb_rb;
	    j = 0;

	    while ((nb_rb_temp > 0) && (j < N_RBG[CC_id])) {
	      if (ue_sched_ctl->rballoc_sub_UE[CC_id][j] == 1) {
		UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][j] = ue_sched_ctl->rballoc_sub_UE[CC_id][j];

		if ((j == N_RBG[CC_id] - 1) &&
		    ((N_RB_DL[CC_id] == 25) ||
		     (N_RB_DL[CC_id] == 50))) {
		  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id] + 1;
		} else {
		  nb_rb_temp = nb_rb_temp - min_rb_unit[CC_id];
		}
	      }

	      j = j + 1;
	    }
	  }

	  // decrease mcs until TBS falls below required length
	  while ((TBS > sdu_length_total + header_length_total + ta_len) && (mcs > 0)) {
	    mcs--;
	    TBS = get_TBS_DL(mcs, nb_rb);
	  }

	  // if we have decreased too much or we don't have enough RBs, increase MCS
	  while ((TBS < sdu_length_total + header_length_total + ta_len)
		 && (((ue_sched_ctl->dl_pow_off[CC_id] > 0)
		      && (mcs < 28))
		     || ((ue_sched_ctl->dl_pow_off[CC_id] == 0)
			 && (mcs <= 15)))) {
	    mcs++;
	    TBS = get_TBS_DL(mcs, nb_rb);
	  }

	  LOG_D(MAC,
		"dlsch_mcs before and after the rate matching = (%d, %d)\n",
		eNB_UE_stats->dlsch_mcs1, mcs);

#ifdef DEBUG_eNB_SCHEDULER
	  LOG_D(MAC,
		"[eNB %d] CC_id %d Generated DLSCH header (mcs %d, TBS %d, nb_rb %d)\n",
		module_idP, CC_id, mcs, TBS, nb_rb);
	  // msg("[MAC][eNB ] Reminder of DLSCH with random data %d %d %d %d \n",
	  //  TBS, sdu_length_total, offset, TBS-sdu_length_total-offset);
#endif

	  if (TBS - header_length_total - sdu_length_total - ta_len <= 2) {
	    padding = TBS - header_length_total - sdu_length_total - ta_len;
	    post_padding = 0;
	  } else {
	    padding = 0;
	    post_padding = 1;
	  }

	  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
	  if (ta_update != 31) {
	    LOG_D(MAC,
		  "[eNB %d][DLSCH] Frame %d Generate header for UE_id %d on CC_id %d: sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,timing advance value : %d, padding %d,post_padding %d,(mcs %d, TBS %d, nb_rb %d),header_length %d\n",
		  module_idP, frameP, UE_id, CC_id,
		  sdu_length_total, num_sdus, sdu_lengths[0],
		  sdu_lcids[0], offset, ta_update, padding,
		  post_padding, mcs, TBS, nb_rb,
		  header_length_total);
	  }
	  //#endif
#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(RC.mac[0].DLSCH_pdu[0][0].payload[0][offset],dcch_buffer,sdu_lengths[0]);

	  // fill remainder of DLSCH with 0
	  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] = 0;
	  }

	  if (opt_enabled == 1) {
	    trace_pdu(1, (uint8_t *)
		      UE_list->DLSCH_pdu[CC_id][0][UE_id].
		      payload[0], TBS, module_idP, 3,
		      UE_RNTI(module_idP, 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",
		  module_idP, CC_id, frameP,
		  UE_RNTI(module_idP, UE_id), TBS);
	  }

	  T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_idP),
	    T_INT(CC_id), T_INT(rnti), T_INT(frameP),
	    T_INT(subframeP), T_INT(harq_pid),
	    T_BUFFER(UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], TBS));

	  UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid] = nb_rb;

	  add_ue_dlsch_info(module_idP,
			    CC_id, UE_id, subframeP,
			    S_DL_SCHEDULED);
	  // 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].rbs_used = nb_rb;
          UE_list->eNB_UE_stats[CC_id][UE_id].num_mac_sdu_tx = num_sdus;
	  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 = eNB_UE_stats->dlsch_mcs1;
	  UE_list->eNB_UE_stats[CC_id][UE_id].dlsch_mcs2 = mcs;
	  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;

	  if (cc[CC_id].tdd_Config != NULL) {	// TDD
	    UE_list->UE_template[CC_id][UE_id].DAI++;
	    update_ul_dci(module_idP, CC_id, rnti,
			  UE_list->UE_template[CC_id][UE_id].DAI,
                          subframeP);
	  }

	  // do PUCCH power control
	  // this is the normalized RX power
	  eNB_UE_stats = &UE_list->eNB_UE_stats[CC_id][UE_id];

	  /* unit is not dBm, it's special from nfapi */
	  // converting to dBm: ToDo: Noise power hard coded to 30
	  normalized_rx_power = (5*ue_sched_ctl->pucch1_snr[CC_id]-640)/10+30;
	  target_rx_power= eNB->puCch10xSnr/10 + 30;
	  // this assumes accumulated tpc
	  // make sure that we are only sending a tpc update once a frame, otherwise the control loop will freak out
	  int32_t framex10psubframe = UE_list->UE_template[CC_id][UE_id].pucch_tpc_tx_frame * 10 + UE_list->UE_template[CC_id][UE_id].pucch_tpc_tx_subframe;
	  if (((framex10psubframe + 10) <= (frameP * 10 + subframeP)) ||	//normal case
	      ((framex10psubframe > (frameP * 10 + subframeP)) && (((10240 - framex10psubframe + frameP * 10 + subframeP) >= 10))))	//frame wrap-around
	    if (ue_sched_ctl->pucch1_cqi_update[CC_id] == 1) {
	      ue_sched_ctl->pucch1_cqi_update[CC_id] = 0;

	      UE_list->UE_template[CC_id][UE_id].pucch_tpc_tx_frame = frameP;
	      UE_list->UE_template[CC_id][UE_id].pucch_tpc_tx_subframe = subframeP;

	      if (normalized_rx_power > (target_rx_power + 4)) {
		tpc = 0;	//-1
	      } else if (normalized_rx_power < (target_rx_power - 4)) {
		tpc = 2;	//+1
	      } else {
		tpc = 1;	//0
	      }

	      LOG_D(MAC,
		    "[eNB %d] DLSCH scheduler: frame %d, subframe %d, harq_pid %d, tpc %d, normalized/target rx power %d/%d\n",
		    module_idP, frameP, subframeP, harq_pid, tpc,
		    normalized_rx_power, target_rx_power);

	    }	// Po_PUCCH has been updated
	    else {
	      tpc = 1;	//0
	    }	// time to do TPC update
	  else {
	    tpc = 1;	//0
	  }

	  dl_config_pdu = &dl_req->dl_config_pdu_list[dl_req->number_pdu];
	  memset((void *) dl_config_pdu, 0,
		 sizeof(nfapi_dl_config_request_pdu_t));
	  dl_config_pdu->pdu_type = NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE;
	  dl_config_pdu->pdu_size = (uint8_t) (2 + sizeof(nfapi_dl_config_dci_dl_pdu));
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format = NFAPI_DL_DCI_FORMAT_1;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level = get_aggregation(get_bw_index(module_idP, CC_id),
											ue_sched_ctl->dl_cqi[CC_id],
											format1);
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tl.tag = NFAPI_DL_CONFIG_REQUEST_DCI_DL_PDU_REL8_TAG;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti = rnti;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type = 1;	// CRNTI : see Table 4-10 from SCF082 - nFAPI specifications
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.transmission_power = 6000;	// equal to RS power

	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.harq_process = harq_pid;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tpc = tpc;	// dont adjust power when retransmitting
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.new_data_indicator_1 = 1 - UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid];
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.mcs_1 = mcs;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.redundancy_version_1 = 0;
	  //deactivate second codeword
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.mcs_2 = 0;
	  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.redundancy_version_2 = 1;
	  if (cc[CC_id].tdd_Config != NULL) {	//TDD
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.downlink_assignment_index = (UE_list->UE_template[CC_id][UE_id].DAI - 1) & 3;
	    LOG_D(MAC,
		  "[eNB %d] Initial transmission CC_id %d : harq_pid %d, dai %d, mcs %d\n",
		  module_idP, CC_id, harq_pid,
		  (UE_list->UE_template[CC_id][UE_id].DAI - 1),
		  mcs);
	  } else {
	    LOG_D(MAC,
		  "[eNB %d] Initial transmission CC_id %d : harq_pid %d, mcs %d\n",
		  module_idP, CC_id, harq_pid, mcs);

	  }

	  LOG_D(MAC, "Checking feasibility pdu %d (new sdu)\n",
		dl_req->number_pdu);

	  if (!CCE_allocation_infeasible(module_idP, CC_id, 1, subframeP,
					 dl_config_pdu->dci_dl_pdu.
					 dci_dl_pdu_rel8.aggregation_level, rnti)) {
	    ue_sched_ctl->round[CC_id][harq_pid] = 0;
	    dl_req->number_dci++;
	    dl_req->number_pdu++;
	    dl_req->tl.tag = NFAPI_DL_CONFIG_REQUEST_BODY_TAG;

	    eNB->DL_req[CC_id].sfn_sf = frameP<<4 | subframeP;
	    eNB->DL_req[CC_id].header.message_id = NFAPI_DL_CONFIG_REQUEST;

	    // Toggle NDI for next time
	    LOG_D(MAC,
		  "CC_id %d Frame %d, subframeP %d: Toggling Format1 NDI for UE %d (rnti %x/%d) oldNDI %d\n",
		  CC_id, frameP, subframeP, UE_id, rnti,
		  harq_pid,
		  UE_list->
		  UE_template[CC_id][UE_id].oldNDI[harq_pid]);

	    UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid] = 1 - UE_list->UE_template[CC_id][UE_id].oldNDI[harq_pid];
	    UE_list->UE_template[CC_id][UE_id].oldmcs1[harq_pid] = mcs;
	    UE_list->UE_template[CC_id][UE_id].oldmcs2[harq_pid] = 0;
	    AssertFatal(UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated != NULL,
			"physicalConfigDedicated is NULL\n");
	    AssertFatal(UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated->pdsch_ConfigDedicated != NULL,
			"physicalConfigDedicated->pdsch_ConfigDedicated is NULL\n");

	    fill_nfapi_dlsch_config(eNB, dl_req, TBS, eNB->pdu_index[CC_id], rnti, 0,	// type 0 allocation from 7.1.6 in 36.213
				    0,	// virtual_resource_block_assignment_flag, unused here
				    0,	// resource_block_coding, to be filled in later
				    getQm(mcs), 0,	// redundancy version
				    1,	// transport blocks
				    0,	// transport block to codeword swap flag
				    cc[CC_id].p_eNB == 1 ? 0 : 1,	// transmission_scheme
				    1,	// number of layers
				    1,	// number of subbands
				    //                       uint8_t codebook_index,
				    4,	// UE category capacity
				    UE_list->UE_template[CC_id][UE_id].physicalConfigDedicated->pdsch_ConfigDedicated->p_a, 0,	// delta_power_offset for TM5
				    0,	// ngap
				    0,	// nprb
				    cc[CC_id].p_eNB == 1 ? 1 : 2,	// transmission mode
				    0,	//number of PRBs treated as one subband, not used here
				    0	// number of beamforming vectors, not used here
				    );
	    eNB->TX_req[CC_id].sfn_sf = fill_nfapi_tx_req(&eNB->TX_req[CC_id].tx_request_body,
							  (frameP * 10) + subframeP,
							  TBS, eNB->pdu_index[CC_id],
							  eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0]);

	    LOG_D(MAC,
		  "Filled NFAPI configuration for DCI/DLSCH/TXREQ %d, new SDU\n",
		  eNB->pdu_index[CC_id]);

	    eNB->pdu_index[CC_id]++;
	    program_dlsch_acknak(module_idP, CC_id, UE_id,
				 frameP, subframeP,
				 dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.cce_idx);
	  } else {
	    LOG_W(MAC,
		  "Frame %d, Subframe %d: Dropping DLSCH allocation for UE %d/%x, infeasible CCE allocations\n",
		  frameP, subframeP, UE_id, rnti);
	  }
	} else {	// There is no data from RLC or MAC header, so don't schedule

	}
      }

      if (cc[CC_id].tdd_Config != NULL) {	// TDD
	set_ul_DAI(module_idP, UE_id, CC_id, frameP, subframeP);
      }

    }			// UE_id loop
  }				// CC_id loop


  fill_DLSCH_dci(module_idP, frameP, subframeP, mbsfn_flag);

  stop_meas(&eNB->schedule_dlsch);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SCHEDULE_DLSCH, VCD_FUNCTION_OUT);
}

//------------------------------------------------------------------------------
void
fill_DLSCH_dci(module_id_t module_idP,
	       frame_t frameP, sub_frame_t subframeP, int *mbsfn_flagP)
//------------------------------------------------------------------------------
{

  // loop over all allocated UEs and compute frequency allocations for PDSCH
  int UE_id = -1;
  uint8_t /* first_rb, */ nb_rb = 3;
  rnti_t rnti;
  //unsigned char *vrb_map;
  uint8_t rballoc_sub[25];
  //uint8_t number_of_subbands=13;

  //unsigned char round;
  unsigned char harq_pid;
  int i;
  int CC_id;
  eNB_MAC_INST *eNB = RC.mac[module_idP];
  UE_list_t *UE_list = &eNB->UE_list;
  int N_RBG;
  int N_RB_DL;
  COMMON_channels_t *cc;

  start_meas(&eNB->fill_DLSCH_dci);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME
    (VCD_SIGNAL_DUMPER_FUNCTIONS_FILL_DLSCH_DCI, VCD_FUNCTION_IN);

  for (CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
    LOG_D(MAC, "Doing fill DCI for CC_id %d\n", CC_id);

    if (mbsfn_flagP[CC_id] > 0)
      continue;

    cc = &eNB->common_channels[CC_id];
    N_RBG = to_rbg(cc->mib->message.dl_Bandwidth);
    N_RB_DL = to_prb(cc->mib->message.dl_Bandwidth);

    // UE specific DCIs
    for (UE_id = UE_list->head; UE_id >= 0;
	 UE_id = UE_list->next[UE_id]) {
      LOG_T(MAC, "CC_id %d, UE_id: %d => status %d\n", CC_id, UE_id,
	    eNB_dlsch_info[module_idP][CC_id][UE_id].status);

      if (eNB_dlsch_info[module_idP][CC_id][UE_id].status == S_DL_SCHEDULED) {

	// clear scheduling flag
	eNB_dlsch_info[module_idP][CC_id][UE_id].status = S_DL_WAITING;
	rnti = UE_RNTI(module_idP, UE_id);
        harq_pid = frame_subframe2_dl_harq_pid(cc->tdd_Config,frameP ,subframeP);
	nb_rb = UE_list->UE_template[CC_id][UE_id].nb_rb[harq_pid];

	/// Synchronizing rballoc with rballoc_sub
	for (i = 0; i < N_RBG; i++) {
	  rballoc_sub[i] = UE_list->UE_template[CC_id][UE_id].rballoc_subband[harq_pid][i];
	}

	nfapi_dl_config_request_t *DL_req = &RC.mac[module_idP]->DL_req[0];
	nfapi_dl_config_request_pdu_t *dl_config_pdu;

	for (i = 0;
	     i < DL_req[CC_id].dl_config_request_body.number_pdu;
	     i++) {
	  dl_config_pdu = &DL_req[CC_id].dl_config_request_body.dl_config_pdu_list[i];
	  if ((dl_config_pdu->pdu_type == NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE)
	      && (dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti == rnti)
	      && (dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format != 1)) {
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.resource_block_coding = allocate_prbs_sub(nb_rb, N_RB_DL, N_RBG,rballoc_sub);
	    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.resource_allocation_type = 0;
	  } else
	    if ((dl_config_pdu->pdu_type == NFAPI_DL_CONFIG_DLSCH_PDU_TYPE)
		&& (dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.rnti == rnti)
		&& (dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_allocation_type == 0)) {
	      dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_block_coding =allocate_prbs_sub(nb_rb, N_RB_DL, N_RBG,rballoc_sub);
	    }
	}
      }
    }

  }

  stop_meas(&eNB->fill_DLSCH_dci);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_FILL_DLSCH_DCI, VCD_FUNCTION_OUT);
}

//------------------------------------------------------------------------------
unsigned char *get_dlsch_sdu(module_id_t module_idP,
			     int CC_id, frame_t frameP, rnti_t rntiP,
			     uint8_t TBindex)
//------------------------------------------------------------------------------
{

  int UE_id;
  eNB_MAC_INST *eNB = RC.mac[module_idP];

  if (rntiP == SI_RNTI) {
    LOG_D(MAC, "[eNB %d] CC_id %d Frame %d Get DLSCH sdu for BCCH \n",
	  module_idP, CC_id, frameP);

    return ((unsigned char *) &eNB->common_channels[CC_id].BCCH_pdu.payload[0]);
  }

  if (rntiP==P_RNTI) {
    LOG_D(MAC,"[eNB %d] CC_id %d Frame %d Get PCH sdu for PCCH \n", module_idP, CC_id, frameP);

    return((unsigned char *)&eNB->common_channels[CC_id].PCCH_pdu.payload[0]);
  }

  UE_id = find_UE_id(module_idP,rntiP);

  if (UE_id != -1) {
    LOG_D(MAC,
	  "[eNB %d] Frame %d:  CC_id %d Get DLSCH sdu for rnti %x => UE_id %d\n",
	  module_idP, frameP, CC_id, rntiP, UE_id);
    return ((unsigned char *) &eNB->UE_list.DLSCH_pdu[CC_id][TBindex][UE_id].payload[0]);
  } else {
    LOG_E(MAC,
	  "[eNB %d] Frame %d: CC_id %d UE with RNTI %x does not exist\n",
	  module_idP, frameP, CC_id, rntiP);
    return NULL;
  }

}


//------------------------------------------------------------------------------
void
update_ul_dci(module_id_t module_idP,
	      uint8_t CC_idP, rnti_t rntiP, uint8_t daiP, sub_frame_t subframe)
//------------------------------------------------------------------------------
{

  nfapi_hi_dci0_request_t *HI_DCI0_req =
    &RC.mac[module_idP]->HI_DCI0_req[CC_idP][subframe];
  nfapi_hi_dci0_request_pdu_t *hi_dci0_pdu =
    &HI_DCI0_req->hi_dci0_request_body.hi_dci0_pdu_list[0];
  COMMON_channels_t *cc = &RC.mac[module_idP]->common_channels[CC_idP];
  int i;


  if (cc->tdd_Config != NULL) {	// TDD
    for (i = 0;
	 i <HI_DCI0_req->hi_dci0_request_body.number_of_dci + HI_DCI0_req->hi_dci0_request_body.number_of_hi;
	 i++) {

      if ((hi_dci0_pdu[i].pdu_type == NFAPI_HI_DCI0_DCI_PDU_TYPE) &&
	  (hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti == rntiP))
	hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.dl_assignment_index = (daiP - 1) & 3;

    }
  }
}


//------------------------------------------------------------------------------
void
set_ue_dai(sub_frame_t subframeP,
	   int UE_id, uint8_t CC_id, uint8_t tdd_config,
	   UE_list_t * UE_list)
//------------------------------------------------------------------------------
{
  switch (tdd_config) {
  case 0:
    if ((subframeP == 0) || (subframeP == 1) || (subframeP == 3)
	|| (subframeP == 5) || (subframeP == 6) || (subframeP == 8)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 1:
    if ((subframeP == 0) || (subframeP == 4) || (subframeP == 5)
	|| (subframeP == 9)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 2:
    if ((subframeP == 4) || (subframeP == 5)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 3:
    if ((subframeP == 5) || (subframeP == 7) || (subframeP == 9)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 4:
    if ((subframeP == 0) || (subframeP == 6)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 5:
    if (subframeP == 9) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  case 6:
    if ((subframeP == 0) || (subframeP == 1) || (subframeP == 5)
	|| (subframeP == 6) || (subframeP == 9)) {
      UE_list->UE_template[CC_id][UE_id].DAI = 0;
    }

    break;

  default:
    UE_list->UE_template[CC_id][UE_id].DAI = 0;
    LOG_N(MAC, "unknow TDD config %d\n", tdd_config);
    break;
  }
}

void schedule_PCH(module_id_t module_idP,frame_t frameP,sub_frame_t subframeP)
{
  /* DCI:format 1A/1C P-RNTI:0xFFFE */
  /* PDU:eNB_rrc_inst[Mod_idP].common_channels[CC_id].PCCH_pdu.payload */
  uint16_t                       pcch_sdu_length;
  int                            mcs = -1;
  int                            CC_id;
  eNB_MAC_INST                   *eNB      = RC.mac[module_idP];
  COMMON_channels_t              *cc;
  uint8_t                        *vrb_map;
  int                            n_rb_dl;
  int                            first_rb = -1;
  nfapi_dl_config_request_pdu_t  *dl_config_pdu;
  nfapi_tx_request_pdu_t         *TX_req;
  nfapi_dl_config_request_body_t *dl_req;
#ifdef FORMAT1C
  int                            gap_index = 0;      /* indicate which gap(1st or 2nd) is used (0:1st) */
  const int                      GAP_MAP [9][2] = {
    {-1, 0},        /* N_RB_DL [6-10] -1: |N_RB/2| 0: N/A*/
    {4, 0},         /* N_RB_DL [11] */
    {8, 0},         /* N_RB_DL [12-19] */
    {12, 0},        /* N_RB_DL [20-26] */
    {18, 0},        /* N_RB_DL [27-44] */
    {27, 0},        /* N_RB_DL [45-49] */
    {27, 9},        /* N_RB_DL [50-63] */
    {32, 16},       /* N_RB_DL [64-79] */
    {48, 16}        /* N_RB_DL [80-110] */
  };
  uint8_t                        n_rb_step = 0;
  uint8_t                        n_gap = 0;
  uint8_t                        n_vrb_dl = 0;
  uint8_t                        Lcrbs = 0;
  uint16_t                       rb_bit    = 168;    /* RB bit number value is unsure */
#endif

  start_meas(&eNB->schedule_pch);

  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    cc              = &eNB->common_channels[CC_id];
    vrb_map         = (void*)&cc->vrb_map;
    n_rb_dl         = to_prb(cc->mib->message.dl_Bandwidth);
    dl_req          = &eNB->DL_req[CC_id].dl_config_request_body;
    for (uint16_t i = 0; i < MAX_MOBILES_PER_ENB; i++) {
      if (UE_PF_PO[CC_id][i].enable_flag != TRUE) {
        continue;
      }
      if (frameP % UE_PF_PO[CC_id][i].T == UE_PF_PO[CC_id][i].PF_min && subframeP == UE_PF_PO[CC_id][i].PO) {
	pcch_sdu_length = mac_rrc_data_req(module_idP,
                                           CC_id,
                                           frameP,
                                           PCCH,1,
                                           &cc->PCCH_pdu.payload[0],
                                           i); // used for ue index
	if (pcch_sdu_length == 0) {
	  LOG_D(MAC,"[eNB %d] Frame %d subframe %d: PCCH not active(size = 0 byte)\n", module_idP,frameP, subframeP);
	  continue;
	}
	LOG_D(MAC,"[eNB %d] Frame %d subframe %d: PCCH->PCH CC_id %d UE_id %d, Received %d bytes \n", module_idP, frameP, subframeP, CC_id,i, pcch_sdu_length);
#ifdef FORMAT1C
	//NO SIB
	if ((subframeP == 0 || subframeP == 1 || subframeP == 2 || subframeP == 4 || subframeP == 6 || subframeP == 9) ||
	    (subframeP == 5 && ((frameP % 2) != 0 && (frameP % 8) != 1))) {
	  switch (n_rb_dl) {
	  case 25:
	    n_gap = GAP_MAP[3][0];  /* expect: 12 */
	    n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 24 */
	    first_rb = 10;
	    break;
	  case 50:
	    n_gap = GAP_MAP[6][gap_index];  /* expect: 27 or 9 */
	    if (gap_index > 0) {
	      n_vrb_dl = (n_rb_dl / (2*n_gap)) * (2*n_gap);  /* 36 */
	    } else {
	      n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 46 */
	    }
	    first_rb = 24;
	    break;
	  case 100:
	    n_gap = GAP_MAP[8][gap_index];  /* expect: 48 or 16 */
	    if (gap_index > 0) {
	      n_vrb_dl = (n_rb_dl / (2*n_gap)) * (2*n_gap);  /* expect: 96 */
	    } else {
	      n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 96 */
	    }
	    first_rb = 48;
	    break;
	  }
	} else if (subframeP == 5 && ((frameP % 2) == 0 || (frameP % 8) == 1)) {  // SIB + paging
	  switch (n_rb_dl) {
	  case 25:
	    n_gap = GAP_MAP[3][0];  /* expect: 12 */
	    n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 24 */
	    first_rb = 14;
	    break;
	  case 50:
	    n_gap = GAP_MAP[6][gap_index];  /* expect: 27 or 9 */
	    if (gap_index > 0) {
	      n_vrb_dl = (n_rb_dl / (2*n_gap)) * (2*n_gap);  /* 36 */
	    } else {
	      n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 46 */
	    }
	    first_rb = 28;
	    break;
	  case 100:
	    n_gap = GAP_MAP[8][gap_index];  /* expect: 48 or 16 */
	    if (gap_index > 0) {
	      n_vrb_dl = (n_rb_dl / (2*n_gap)) * (2*n_gap);  /* expect: 96 */
	    } else {
	      n_vrb_dl = 2*((n_gap < (n_rb_dl - n_gap)) ? n_gap : (n_rb_dl - n_gap));  /* expect: 96 */
	    }
	    first_rb = 52;
	    break;
	  }
	}
	/* Get MCS for length of PCH */
	if (pcch_sdu_length <= TBStable1C[0]) {
	  mcs=0;
	} else if (pcch_sdu_length <= TBStable1C[1]) {
	  mcs=1;
	} else if (pcch_sdu_length <= TBStable1C[2]) {
	  mcs=2;
	} else if (pcch_sdu_length <= TBStable1C[3]) {
	  mcs=3;
	} else if (pcch_sdu_length <= TBStable1C[4]) {
	  mcs=4;
	} else if (pcch_sdu_length <= TBStable1C[5]) {
	  mcs=5;
	} else if (pcch_sdu_length <= TBStable1C[6]) {
	  mcs=6;
	} else if (pcch_sdu_length <= TBStable1C[7]) {
	  mcs=7;
	} else if (pcch_sdu_length <= TBStable1C[8]) {
	  mcs=8;
	} else if (pcch_sdu_length <= TBStable1C[9]) {
	  mcs=9;
	} else {
	  /* unexpected: pcch sdb size is over max value*/
	  LOG_E(MAC,"[eNB %d] Frame %d : PCCH->PCH CC_id %d, Received %d bytes is over max length(256) \n",
		module_idP, frameP,CC_id, pcch_sdu_length);
	  return;
	}
	rb_num = TBStable1C[mcs] / rb_bit + ( (TBStable1C[mcs] % rb_bit == 0)? 0: 1) + 1;
	/* calculate N_RB_STEP and Lcrbs */
	if (n_rb_dl < 50) {
	  n_rb_step = 2;
	  Lcrbs = rb_num / 2 + ((rb_num % 2 == 0) ? 0:2);
	} else {
	  n_rb_step = 4;
	  Lcrbs = rb_num / 4 + ((rb_num % 4 == 0) ? 0:4);
	}
	for(i = 0;i < Lcrbs ;i++){
	  vrb_map[first_rb+i] = 1;
	}
#else
	//NO SIB
	if ((subframeP == 0 || subframeP == 1 || subframeP == 2 || subframeP == 4 || subframeP == 6 || subframeP == 9) ||
	    (subframeP == 5 && ((frameP % 2) != 0 && (frameP % 8) != 1))) {
	  switch (n_rb_dl) {
	  case 25:
	    first_rb = 10;
	    break;
	  case 50:
	    first_rb = 24;
	    break;
	  case 100:
	    first_rb = 48;
	    break;
	  }
	} else if (subframeP == 5 && ((frameP % 2) == 0 || (frameP % 8) == 1)) {  // SIB + paging
	  switch (n_rb_dl) {
	  case 25:
	    first_rb = 14;
	    break;
	  case 50:
	    first_rb = 28;
	    break;
	  case 100:
	    first_rb = 52;
	    break;
	  }
	}

	vrb_map[first_rb] = 1;
	vrb_map[first_rb+1] = 1;
	vrb_map[first_rb+2] = 1;
	vrb_map[first_rb+3] = 1;
	/* Get MCS for length of PCH */
	if (pcch_sdu_length <= get_TBS_DL(0,3)) {
	  mcs=0;
	} else if (pcch_sdu_length <= get_TBS_DL(1,3)) {
	  mcs=1;
	} else if (pcch_sdu_length <= get_TBS_DL(2,3)) {
	  mcs=2;
	} else if (pcch_sdu_length <= get_TBS_DL(3,3)) {
	  mcs=3;
	} else if (pcch_sdu_length <= get_TBS_DL(4,3)) {
	  mcs=4;
	} else if (pcch_sdu_length <= get_TBS_DL(5,3)) {
	  mcs=5;
	} else if (pcch_sdu_length <= get_TBS_DL(6,3)) {
	  mcs=6;
	} else if (pcch_sdu_length <= get_TBS_DL(7,3)) {
	  mcs=7;
	} else if (pcch_sdu_length <= get_TBS_DL(8,3)) {
	  mcs=8;
	} else if (pcch_sdu_length <= get_TBS_DL(9,3)) {
	  mcs=9;
	}
#endif
	dl_config_pdu                                                         = &dl_req->dl_config_pdu_list[dl_req->number_pdu];
	memset((void*)dl_config_pdu,0,sizeof(nfapi_dl_config_request_pdu_t));
	dl_config_pdu->pdu_type                                               = NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE;
	dl_config_pdu->pdu_size                                               = (uint8_t)(2+sizeof(nfapi_dl_config_dci_dl_pdu));
#ifdef FORMAT1C
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format                  = NFAPI_DL_DCI_FORMAT_1C;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.resource_block_coding       = getRIV(n_vrb_dl/n_rb_step, first_rb/n_rb_step, Lcrbs/n_rb_step);
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.ngap                        = n_gap;
#else
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format                  = NFAPI_DL_DCI_FORMAT_1A;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.harq_process                = 0;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tpc                         = 1; // no TPC
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.new_data_indicator_1        = 1;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.redundancy_version_1        = 1;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.resource_block_coding       = getRIV(n_rb_dl,first_rb,4);
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.virtual_resource_block_assignment_flag = 0;
#endif
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level           = 4;
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti                        = 0xFFFE; // P-RNTI
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type                   = 2;    // P-RNTI : see Table 4-10 from SCF082 - nFAPI specifications
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.transmission_power          = 6000; // equal to RS power
	dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.mcs_1                       = mcs;

	// Rel10 fields
#if (RRC_VERSION >= MAKE_VERSION(10, 0, 0))
	dl_config_pdu->dlsch_pdu.dlsch_pdu_rel10.pdsch_start                           = 3;
#endif
	// Rel13 fields
#if (RRC_VERSION >= MAKE_VERSION(13, 0, 0))
	dl_config_pdu->dlsch_pdu.dlsch_pdu_rel13.ue_type                               = 0; // regular UE
	dl_config_pdu->dlsch_pdu.dlsch_pdu_rel13.pdsch_payload_type                    = 2; // not BR
	dl_config_pdu->dlsch_pdu.dlsch_pdu_rel13.initial_transmission_sf_io            = 0xFFFF;
#endif

	if (!CCE_allocation_infeasible(module_idP, CC_id, 0, subframeP, dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level, P_RNTI)) {
	  LOG_D(MAC,"Frame %d: Subframe %d : Adding common DCI for P_RNTI\n", frameP,subframeP);
	  dl_req->number_dci++;
	  dl_req->number_pdu++;
          dl_req->tl.tag = NFAPI_DL_CONFIG_REQUEST_BODY_TAG;
          eNB->DL_req[CC_id].sfn_sf = frameP<<4 | subframeP;
          eNB->DL_req[CC_id].header.message_id = NFAPI_DL_CONFIG_REQUEST;

	  dl_config_pdu                                                                  = &dl_req->dl_config_pdu_list[dl_req->number_pdu];
	  memset((void*)dl_config_pdu,0,sizeof(nfapi_dl_config_request_pdu_t));
	  dl_config_pdu->pdu_type                                                        = NFAPI_DL_CONFIG_DLSCH_PDU_TYPE;
	  dl_config_pdu->pdu_size                                                        = (uint8_t)(2+sizeof(nfapi_dl_config_dlsch_pdu));
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index                              = eNB->pdu_index[CC_id];
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.rnti                                   = 0xFFFE;
#ifdef FORMAT1C
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_allocation_type               = 3;   // format 1C
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_block_coding                  = getRIV(n_vrb_dl/n_rb_step, first_rb/n_rb_step, Lcrbs/n_rb_step);
#else
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_allocation_type               = 2;   // format 1A/1B/1D
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_block_coding                  = getRIV(n_rb_dl,first_rb,4);
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.virtual_resource_block_assignment_flag = 0;   // localized
#endif
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.modulation                             = 2; //QPSK
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.redundancy_version                     = 1;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks                       = 1;// first block
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_block_to_codeword_swap_flag  = 0;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transmission_scheme                    = (cc->p_eNB==1 ) ? 0 : 1;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.number_of_layers                       = 1;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.number_of_subbands                     = 1;
	  // dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.codebook_index                         = ;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.ue_category_capacity                   = 1;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pa                                     = 4; // 0 dB
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.delta_power_offset_index               = 0;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.ngap                                   = 0;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.nprb                                   = get_subbandsize(cc->mib->message.dl_Bandwidth); // ignored
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transmission_mode                      = (cc->p_eNB==1 ) ? 1 : 2;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.num_bf_prb_per_subband                 = 1;
	  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.num_bf_vector                          = 1;
	  // dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.bf_vector                    = ;
	  dl_req->number_pdu++;

	  eNB->TX_req[CC_id].sfn_sf                                                     = (frameP<<4)+subframeP;
	  TX_req                                                                         = &eNB->TX_req[CC_id].tx_request_body.tx_pdu_list[eNB->TX_req[CC_id].tx_request_body.number_of_pdus];
	  TX_req->pdu_length                                                             = pcch_sdu_length;
	  TX_req->pdu_index                                                              = eNB->pdu_index[CC_id]++;
	  TX_req->num_segments                                                           = 1;
	  TX_req->segments[0].segment_length                                             = pcch_sdu_length;
	  TX_req->segments[0].segment_data                                               = cc[CC_id].PCCH_pdu.payload;
          eNB->TX_req[CC_id].tx_request_body.tl.tag = NFAPI_TX_REQUEST_BODY_TAG;
	  eNB->TX_req[CC_id].tx_request_body.number_of_pdus++;
	} else {
	  LOG_E(MAC,"[eNB %d] CCid %d Frame %d, subframe %d : Cannot add DCI 1A/1C for Paging\n",module_idP, CC_id, frameP, subframeP);
	  continue;
	}

	if (opt_enabled == 1) {
	  trace_pdu(1,
		    &eNB->common_channels[CC_id].PCCH_pdu.payload[0],
		    pcch_sdu_length,
		    0xffff,
		    PCCH,
		    P_RNTI,
		    eNB->frame,
		    eNB->subframe,
		    0,
		    0);
	  LOG_D(OPT,"[eNB %d][PCH] Frame %d trace pdu for CC_id %d rnti %x with size %d\n",
		module_idP, frameP, CC_id, 0xffff, pcch_sdu_length);
	}
	eNB->eNB_stats[CC_id].total_num_pcch_pdu+=1;
	eNB->eNB_stats[CC_id].pcch_buffer=pcch_sdu_length;
	eNB->eNB_stats[CC_id].total_pcch_buffer+=pcch_sdu_length;
	eNB->eNB_stats[CC_id].pcch_mcs=mcs;
	//paging first_rb log
	LOG_D(MAC,"[eNB %d] Frame %d subframe %d PCH: paging_ue_index %d pcch_sdu_length %d mcs %d first_rb %d\n",
	      module_idP, frameP, subframeP, UE_PF_PO[CC_id][i].ue_index_value, pcch_sdu_length, mcs, first_rb);

	pthread_mutex_lock(&ue_pf_po_mutex);
	memset(&UE_PF_PO[CC_id][i], 0, sizeof(UE_PF_PO_t));
	pthread_mutex_unlock(&ue_pf_po_mutex);
      }
    }
  }
  /* this might be misleading when pcch is inactive */
  stop_meas(&eNB->schedule_pch);
  return;
}
