/*
 * 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.0  (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_primitives.c
 * \brief primitives used by eNB for BCH, RACH, ULSCH, DLSCH scheduling
 * \author  Navid Nikaein and Raymond Knopp
 * \date 2010 - 2014
 * \email: navid.nikaein@eurecom.fr
 * \version 1.0
 * @ingroup _mac

 */

#include "assertions.h"
#include "PHY/defs.h"
#include "PHY/extern.h"

#include "SCHED/defs.h"
#include "SCHED/extern.h"

#include "LAYER2/MAC/defs.h"
#include "LAYER2/MAC/extern.h"

#include "LAYER2/MAC/proto.h"
#include "UTIL/LOG/log.h"
#include "UTIL/LOG/vcd_signal_dumper.h"
#include "UTIL/OPT/opt.h"
#include "OCG.h"
#include "OCG_extern.h"

#include "RRC/LITE/extern.h"
#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"

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

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

#define ENABLE_MAC_PAYLOAD_DEBUG
#define DEBUG_eNB_SCHEDULER 1

int to_prb(int dl_Bandwidth) {
  int prbmap[6] = {6,15,25,50,75,100};

  AssertFatal(dl_Bandwidth < 6,"dl_Bandwidth is 0..5\n");
  return(prbmap[dl_Bandwidth]);
}

int to_rbg(int dl_Bandwidth) {
  int rbgmap[6] = {6,8,13,17,19,25};

  AssertFatal(dl_Bandwidth < 6,"dl_Bandwidth is 0..5\n");
  return(rbgmap[dl_Bandwidth]);
}


int get_phich_resource_times6(COMMON_channels_t *cc) {
  int phichmap[4] = {1,3,6,12};
  AssertFatal(cc!=NULL,"cc is null\n");
  AssertFatal(cc->mib!=NULL,"cc->mib is null\n");
  AssertFatal((cc->mib->message.phich_Config.phich_Resource>=0) && 
	      (cc->mib->message.phich_Config.phich_Resource<4),
	      "phich_Resource %d not in 0..3\n",(int)cc->mib->message.phich_Config.phich_Resource);

  return(phichmap[cc->mib->message.phich_Config.phich_Resource]);
}

uint16_t mac_computeRIV(uint16_t N_RB_DL,uint16_t RBstart,uint16_t Lcrbs) {

  uint16_t RIV;

  if (Lcrbs<=(1+(N_RB_DL>>1)))
    RIV = (N_RB_DL*(Lcrbs-1)) + RBstart;
  else
    RIV = (N_RB_DL*(N_RB_DL+1-Lcrbs)) + (N_RB_DL-1-RBstart);

  return(RIV);
}

//------------------------------------------------------------------------------
void init_ue_sched_info(void)
//------------------------------------------------------------------------------
{
  module_id_t i,j,k;

  for (i=0; i<NUMBER_OF_eNB_MAX; i++) {
    for (k=0; k<MAX_NUM_CCs; k++) {
      for (j=0; j<NUMBER_OF_UE_MAX; j++) {
        // init DL
        eNB_dlsch_info[i][k][j].weight           = 0;
        eNB_dlsch_info[i][k][j].subframe         = 0;
        eNB_dlsch_info[i][k][j].serving_num      = 0;
        eNB_dlsch_info[i][k][j].status           = S_DL_NONE;
        // init UL
        eNB_ulsch_info[i][k][j].subframe         = 0;
        eNB_ulsch_info[i][k][j].serving_num      = 0;
        eNB_ulsch_info[i][k][j].status           = S_UL_NONE;
      }
    }
  }
}



//------------------------------------------------------------------------------
unsigned char get_ue_weight(module_id_t module_idP, int CC_idP, int ue_idP)
//------------------------------------------------------------------------------
{

  return(eNB_dlsch_info[module_idP][CC_idP][ue_idP].weight);

}

//------------------------------------------------------------------------------
int find_UE_id(module_id_t mod_idP, rnti_t rntiP)
//------------------------------------------------------------------------------
{
  int UE_id;
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;

  for (UE_id = 0; UE_id < NUMBER_OF_UE_MAX; UE_id++) {
    if (UE_list->active[UE_id] != TRUE) continue;
    if (UE_list->UE_template[UE_PCCID(mod_idP,UE_id)][UE_id].rnti==rntiP) {
      return(UE_id);
    }
  }

  return(-1);
}

//------------------------------------------------------------------------------
int UE_num_active_CC(UE_list_t *listP,int ue_idP)
//------------------------------------------------------------------------------
{
  return(listP->numactiveCCs[ue_idP]);
}

//------------------------------------------------------------------------------
int UE_PCCID(module_id_t mod_idP,int ue_idP)
//------------------------------------------------------------------------------
{
  return(RC.mac[mod_idP]->UE_list.pCC_id[ue_idP]);
}

//------------------------------------------------------------------------------
rnti_t UE_RNTI(module_id_t mod_idP, int ue_idP)
//------------------------------------------------------------------------------
{

  rnti_t rnti = RC.mac[mod_idP]->UE_list.UE_template[UE_PCCID(mod_idP,ue_idP)][ue_idP].rnti;

  if (rnti>0) {
    return (rnti);
  }

  LOG_D(MAC,"[eNB %d] Couldn't find RNTI for UE %d\n",mod_idP,ue_idP);
  //display_backtrace();
  return(NOT_A_RNTI);
}

//------------------------------------------------------------------------------
boolean_t is_UE_active(module_id_t mod_idP, int ue_idP)
//------------------------------------------------------------------------------
{
  return(RC.mac[mod_idP]->UE_list.active[ue_idP]);
}

/*
uint8_t find_active_UEs(module_id_t module_idP,int CC_id){

  module_id_t        ue_mod_id      = 0;
  rnti_t        rnti         = 0;
  uint8_t            nb_active_ue = 0;

  for (ue_mod_id=0;ue_mod_id<NUMBER_OF_UE_MAX;ue_mod_id++) {

      if (((rnti=eNB_mac_inst[module_idP][CC_id].UE_template[ue_mod_id].rnti) !=0)&&(eNB_mac_inst[module_idP][CC_id].UE_template[ue_mod_id].ul_active==TRUE)){

          if (mac_xface->get_eNB_UE_stats(module_idP,rnti) != NULL){ // check at the phy enb_ue state for this rnti
      nb_active_ue++;
          }
          else { // this ue is removed at the phy => remove it at the mac as well
      mac_remove_ue(module_idP, CC_id, ue_mod_id);
          }
      }
  }
  return(nb_active_ue);
}
*/


// get aggregation (L) form phy for a give UE
unsigned char get_aggregation (uint8_t bw_index, uint8_t cqi, uint8_t dci_fmt)
{
  unsigned char aggregation=3;
  
  switch (dci_fmt){
    
  case format0:
    aggregation = cqi2fmt0_agg[bw_index][cqi];
    break;
  case format1:
  case format1A:
  case format1B:
  case format1D:
    aggregation = cqi2fmt1x_agg[bw_index][cqi]; 
    break;
  case format2:
  case format2A:
  case format2B:
  case format2C:
  case format2D:
    aggregation = cqi2fmt2x_agg[bw_index][cqi]; 
    break;
  case format1C:
  case format1E_2A_M10PRB:
  case format3:
  case format3A:
  case format4:
  default:
    LOG_W(MAC,"unsupported DCI format %d\n",dci_fmt);
  }

   LOG_D(MAC,"Aggregation level %d (cqi %d, bw_index %d, format %d)\n", 
   	1<<aggregation, cqi,bw_index,dci_fmt);
   
  return 1<<aggregation;
}

void dump_ue_list(UE_list_t *listP, int ul_flag)
{
  int j;

  if ( ul_flag == 0 ) {
    for (j=listP->head; j>=0; j=listP->next[j]) {
      LOG_T(MAC,"node %d => %d\n",j,listP->next[j]);
    }
  } else {
    for (j=listP->head_ul; j>=0; j=listP->next_ul[j]) {
      LOG_T(MAC,"node %d => %d\n",j,listP->next_ul[j]);
    }
  }
}

int add_new_ue(module_id_t mod_idP, int cc_idP, rnti_t rntiP,int harq_pidP)
{
  int UE_id;
  int i, j;

  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;

  LOG_D(MAC,"[eNB %d, CC_id %d] Adding UE with rnti %x (next avail %d, num_UEs %d)\n",mod_idP,cc_idP,rntiP,UE_list->avail,UE_list->num_UEs);
  dump_ue_list(UE_list,0);

  for (i = 0; i < NUMBER_OF_UE_MAX; i++) {
    if (UE_list->active[i] == TRUE) continue;
printf("MAC: new UE id %d rnti %x\n", i, rntiP);
    UE_id = i;
    UE_list->UE_template[cc_idP][UE_id].rnti       = rntiP;
    UE_list->UE_template[cc_idP][UE_id].configured = FALSE;
    UE_list->numactiveCCs[UE_id]                   = 1;
    UE_list->numactiveULCCs[UE_id]                 = 1;
    UE_list->pCC_id[UE_id]                         = cc_idP;
    UE_list->ordered_CCids[0][UE_id]               = cc_idP;
    UE_list->ordered_ULCCids[0][UE_id]             = cc_idP;
    UE_list->num_UEs++;
    UE_list->active[UE_id]                         = TRUE;
    memset((void*)&UE_list->UE_sched_ctrl[UE_id],0,sizeof(UE_sched_ctrl));

    for (j=0; j<8; j++) {
      UE_list->UE_template[cc_idP][UE_id].oldNDI[j]    = (j==0)?1:0;   // 1 because first transmission is with format1A (Msg4) for harq_pid 0
      UE_list->UE_template[cc_idP][UE_id].oldNDI_UL[j] = (j==harq_pidP)?0:1; // 1st transmission is with Msg3;
    }

    eNB_ulsch_info[mod_idP][cc_idP][UE_id].status = S_UL_WAITING;
    eNB_dlsch_info[mod_idP][cc_idP][UE_id].status = S_DL_WAITING;
    LOG_D(MAC,"[eNB %d] Add UE_id %d on Primary CC_id %d: rnti %x\n",mod_idP,UE_id,cc_idP,rntiP);
    dump_ue_list(UE_list,0);
    return(UE_id);
  }

printf("MAC: cannot add new UE for rnti %x\n", rntiP);
  LOG_E(MAC,"error in add_new_ue(), could not find space in UE_list, Dumping UE list\n");
  dump_ue_list(UE_list,0);
  return(-1);
}

//------------------------------------------------------------------------------
int rrc_mac_remove_ue(module_id_t mod_idP,rnti_t rntiP) 
//------------------------------------------------------------------------------
{
  int i;
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;
  int UE_id = find_UE_id(mod_idP,rntiP);
  int pCC_id;

  if (UE_id == -1) {
printf("MAC: cannot remove UE rnti %x\n", rntiP);
    LOG_W(MAC,"rrc_mac_remove_ue: UE %x not found\n", rntiP);
    mac_phy_remove_ue(mod_idP, rntiP);
    return 0;
  }

  pCC_id = UE_PCCID(mod_idP,UE_id);

printf("MAC: remove UE %d rnti %x\n", UE_id, rntiP);
  LOG_I(MAC,"Removing UE %d from Primary CC_id %d (rnti %x)\n",UE_id,pCC_id, rntiP);
  dump_ue_list(UE_list,0);

  UE_list->active[UE_id] = FALSE;
  UE_list->num_UEs--;

  // clear all remaining pending transmissions
  UE_list->UE_template[pCC_id][UE_id].bsr_info[LCGID0]  = 0;
  UE_list->UE_template[pCC_id][UE_id].bsr_info[LCGID1]  = 0;
  UE_list->UE_template[pCC_id][UE_id].bsr_info[LCGID2]  = 0;
  UE_list->UE_template[pCC_id][UE_id].bsr_info[LCGID3]  = 0;

  UE_list->UE_template[pCC_id][UE_id].ul_SR             = 0;
  UE_list->UE_template[pCC_id][UE_id].rnti              = NOT_A_RNTI;
  UE_list->UE_template[pCC_id][UE_id].ul_active         = FALSE;
  eNB_ulsch_info[mod_idP][pCC_id][UE_id].rnti                        = NOT_A_RNTI;
  eNB_ulsch_info[mod_idP][pCC_id][UE_id].status                      = S_UL_NONE;
  eNB_dlsch_info[mod_idP][pCC_id][UE_id].rnti                        = NOT_A_RNTI;
  eNB_dlsch_info[mod_idP][pCC_id][UE_id].status                      = S_DL_NONE;

  mac_phy_remove_ue(mod_idP,rntiP);

  // check if this has an RA process active
  RA_TEMPLATE *RA_template;
  for (i=0;i<NB_RA_PROC_MAX;i++) {
    RA_template = (RA_TEMPLATE *)&RC.mac[mod_idP]->common_channels[pCC_id].RA_template[i];
    if (RA_template->rnti == rntiP){
      RA_template->RA_active=FALSE;
      RA_template->generate_rar=0;
      RA_template->generate_Msg4=0;
      RA_template->wait_ack_Msg4=0;
      RA_template->timing_offset=0;
      RA_template->RRC_timer=20;
      RA_template->rnti = 0;
      //break;
    }
  }

  return 0;
}



int prev(UE_list_t *listP, int nodeP, int ul_flag)
{
  int j;

  if (ul_flag == 0 ) {
    if (nodeP==listP->head) {
      return(nodeP);
    }

    for (j=listP->head; j>=0; j=listP->next[j]) {
      if (listP->next[j]==nodeP) {
        return(j);
    }
    }
  } else {
    if (nodeP==listP->head_ul) {
      return(nodeP);
    }

    for (j=listP->head_ul; j>=0; j=listP->next_ul[j]) {
      if (listP->next_ul[j]==nodeP) {
        return(j);
      }
    }
  }

  LOG_E(MAC,"error in prev(), could not find previous to %d in UE_list %s, should never happen, Dumping UE list\n",
        nodeP, (ul_flag == 0)? "DL" : "UL");
  dump_ue_list(listP, ul_flag);


  return(-1);
}

void swap_UEs(UE_list_t *listP,int nodeiP, int nodejP, int ul_flag)
{

  int prev_i,prev_j,next_i,next_j;

  LOG_T(MAC,"Swapping UE %d,%d\n",nodeiP,nodejP);
  dump_ue_list(listP,ul_flag);

  prev_i = prev(listP,nodeiP,ul_flag);
  prev_j = prev(listP,nodejP,ul_flag);

  AssertFatal((prev_i>=0) && (prev_j>=0),
	      "swap_UEs: problem");

  if (ul_flag == 0) {
    next_i = listP->next[nodeiP];
    next_j = listP->next[nodejP];
  } else {
    next_i = listP->next_ul[nodeiP];
    next_j = listP->next_ul[nodejP];
  }

  LOG_T(MAC,"[%s] next_i %d, next_i, next_j %d, head %d \n",
        (ul_flag == 0)? "DL" : "UL",
        next_i,next_j,listP->head);

  if (ul_flag == 0 ) {

    if (next_i == nodejP) {   // case ... p(i) i j n(j) ... => ... p(j) j i n(i) ...
      LOG_T(MAC,"Case ... p(i) i j n(j) ... => ... p(j) j i n(i) ...\n");

      listP->next[nodeiP] = next_j;
      listP->next[nodejP] = nodeiP;

      if (nodeiP==listP->head) { // case i j n(j)
        listP->head = nodejP;
      } else {
        listP->next[prev_i] = nodejP;
      }
    } else if (next_j == nodeiP) {  // case ... p(j) j i n(i) ... => ... p(i) i j n(j) ...
      LOG_T(MAC,"Case ... p(j) j i n(i) ... => ... p(i) i j n(j) ...\n");
      listP->next[nodejP] = next_i;
      listP->next[nodeiP] = nodejP;

      if (nodejP==listP->head) { // case j i n(i)
        listP->head = nodeiP;
      } else {
        listP->next[prev_j] = nodeiP;
      }
    } else {  // case ...  p(i) i n(i) ... p(j) j n(j) ...
      listP->next[nodejP] = next_i;
      listP->next[nodeiP] = next_j;


      if (nodeiP==listP->head) {
        LOG_T(MAC,"changing head to %d\n",nodejP);
        listP->head=nodejP;
        listP->next[prev_j] = nodeiP;
      } else if (nodejP==listP->head) {
        LOG_D(MAC,"changing head to %d\n",nodeiP);
        listP->head=nodeiP;
        listP->next[prev_i] = nodejP;
      } else {
        listP->next[prev_i] = nodejP;
        listP->next[prev_j] = nodeiP;
      }
    }
  } else { // ul_flag

    if (next_i == nodejP) {   // case ... p(i) i j n(j) ... => ... p(j) j i n(i) ...
      LOG_T(MAC,"[UL] Case ... p(i) i j n(j) ... => ... p(j) j i n(i) ...\n");

      listP->next_ul[nodeiP] = next_j;
      listP->next_ul[nodejP] = nodeiP;

      if (nodeiP==listP->head_ul) { // case i j n(j)
        listP->head_ul = nodejP;
      } else {
        listP->next_ul[prev_i] = nodejP;
      }
    } else if (next_j == nodeiP) {  // case ... p(j) j i n(i) ... => ... p(i) i j n(j) ...
      LOG_T(MAC,"[UL]Case ... p(j) j i n(i) ... => ... p(i) i j n(j) ...\n");
      listP->next_ul[nodejP] = next_i;
      listP->next_ul[nodeiP] = nodejP;

      if (nodejP==listP->head_ul) { // case j i n(i)
        listP->head_ul = nodeiP;
      } else {
        listP->next_ul[prev_j] = nodeiP;
      }
    } else {  // case ...  p(i) i n(i) ... p(j) j n(j) ...

      listP->next_ul[nodejP] = next_i;
      listP->next_ul[nodeiP] = next_j;


      if (nodeiP==listP->head_ul) {
        LOG_T(MAC,"[UL]changing head to %d\n",nodejP);
        listP->head_ul=nodejP;
        listP->next_ul[prev_j] = nodeiP;
      } else if (nodejP==listP->head_ul) {
        LOG_T(MAC,"[UL]changing head to %d\n",nodeiP);
        listP->head_ul=nodeiP;
        listP->next_ul[prev_i] = nodejP;
      } else {
        listP->next_ul[prev_i] = nodejP;
        listP->next_ul[prev_j] = nodeiP;
      }
    }
  }

  LOG_T(MAC,"After swap\n");
  dump_ue_list(listP,ul_flag);
}








/*
  #if defined(Rel10) || defined(Rel14)
  unsigned char generate_mch_header( unsigned char *mac_header,
  unsigned char num_sdus,
  unsigned short *sdu_lengths,
  unsigned char *sdu_lcids,
  unsigned char msi,
  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[2*num_sdus],*ce_ptr;

  ce_ptr = &mac_header_control_elements[0];

  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;
  }

  // SUBHEADER for MSI CE
  if (msi != 0) {// there is MSI MAC Control Element
  if (first_element>0) {
  mac_header_ptr->E = 1;
  mac_header_ptr+=last_size;
  }
  else {
  first_element = 1;
  }
  if (num_sdus*2 < 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 = MCH_SCHDL_INFO;
  ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->L    = num_sdus*2;
  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 = MCH_SCHDL_INFO;
  ((SCH_SUBHEADER_LONG *)mac_header_ptr)->L    = (num_sdus*2)&0x7fff;
  last_size=3;
  }
  // Create the MSI MAC Control Element here
  }

  // SUBHEADER for MAC SDU (MCCH+MTCHs)
  for (i=0;i<num_sdus;i++) {
  if (first_element>0) {
  mac_header_ptr->E = 1;
  mac_header_ptr+=last_size;
  }
  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    = (unsigned short) sdu_lengths[i]&0x7fff;
  last_size=3;
  }
  }

  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++;
  }

  // Copy MSI Control Element to the end of the MAC Header if it presents
  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);
  }

  return((unsigned char*)mac_header_ptr - mac_header);
  }
  #endif
 */



// This has to be updated to include BSR information
uint8_t UE_is_to_be_scheduled(module_id_t module_idP,int CC_id,uint8_t UE_id)
{

  UE_TEMPLATE *UE_template    = &RC.mac[module_idP]->UE_list.UE_template[CC_id][UE_id];
  UE_sched_ctrl *UE_sched_ctl = &RC.mac[module_idP]->UE_list.UE_sched_ctrl[UE_id];


  // do not schedule UE if UL is not working
  if (UE_sched_ctl->ul_failure_timer>0)
    return(0);
  if (UE_sched_ctl->ul_out_of_sync>0)
    return(0);

  LOG_D(MAC,"[eNB %d][PUSCH] Checking UL requirements UE %d/%x\n",module_idP,UE_id,UE_RNTI(module_idP,UE_id));

  if ((UE_template->bsr_info[LCGID0]>0) ||
      (UE_template->bsr_info[LCGID1]>0) ||
      (UE_template->bsr_info[LCGID2]>0) ||
      (UE_template->bsr_info[LCGID3]>0) ||
      (UE_template->ul_SR>0) || // uplink scheduling request
      ((UE_sched_ctl->ul_inactivity_timer>20)&&
       (UE_sched_ctl->ul_scheduled==0))||  // every 2 frames when RRC_CONNECTED
      ((UE_sched_ctl->ul_inactivity_timer>10)&&
       (UE_sched_ctl->ul_scheduled==0)&&
       (mac_eNB_get_rrc_status(module_idP,UE_RNTI(module_idP,UE_id)) < RRC_CONNECTED))) // every Frame when not RRC_CONNECTED
    { 

    LOG_D(MAC,"[eNB %d][PUSCH] UE %d/%x should be scheduled\n",module_idP,UE_id,UE_RNTI(module_idP,UE_id));
    return(1);
  } else {
    return(0);
  }
}


uint8_t get_tmode(module_id_t module_idP,int CC_idP,int UE_idP) {

  eNB_MAC_INST         *eNB                                = RC.mac[module_idP];
  COMMON_channels_t    *cc                                 = &eNB->common_channels[CC_idP];

  struct PhysicalConfigDedicated  *physicalConfigDedicated = eNB->UE_list.physicalConfigDedicated[CC_idP][UE_idP];

  AssertFatal(physicalConfigDedicated->antennaInfo!=NULL,
	      "antennaInfo is null for CCId %d, UEid %d\n",CC_idP,UE_idP);

  AssertFatal(physicalConfigDedicated->antennaInfo->present != PhysicalConfigDedicated__antennaInfo_PR_NOTHING, 
	      "antennaInfo (mod_id %d, CC_id %d) is set to NOTHING\n",module_idP,CC_idP);

  if (physicalConfigDedicated->antennaInfo->present == PhysicalConfigDedicated__antennaInfo_PR_explicitValue) {
    return(physicalConfigDedicated->antennaInfo->choice.explicitValue.transmissionMode);
  }
  else if (physicalConfigDedicated->antennaInfo->present == PhysicalConfigDedicated__antennaInfo_PR_defaultValue) {
    AssertFatal(cc->p_eNB<=2,"p_eNB is %d, should be <2\n",cc->p_eNB);
    return(cc->p_eNB);
  }
  else AssertFatal(1==0,"Shouldn't be here\n");

}

int8_t get_UL_harq(module_id_t module_idP,int CC_idP,uint16_t frameP,uint8_t subframeP) {

  uint8_t           ret       = -1;
  eNB_MAC_INST      *eNB      = RC.mac[module_idP];
  COMMON_channels_t *cc       = &eNB->common_channels[CC_idP];

  if (cc->tdd_Config==NULL) { // FDD
    ret = (((frameP<<1)+subframeP)&7);
  } else {

    switch (cc->tdd_Config->subframeAssignment) {

    case 1:
      if ((subframeP==2) ||
          (subframeP==3) ||
          (subframeP==7) ||
          (subframeP==8))
        switch (subframeP) {
        case 2:
        case 3:
          ret = (subframeP-2);
          break;

        case 7:
        case 8:
          ret = (subframeP-5);
          break;

        default:
          LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,cc->tdd_Config->subframeAssignment);
          ret = -1;
          break;
        }

      break;

    case 2:
      if ((subframeP!=2) && (subframeP!=7)) {
	LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,cc->tdd_Config->subframeAssignment);
	ret=-1;
      }
      else ret = (subframeP/7);
      break;

    case 3:
      if ((subframeP<2) || (subframeP>4)) {
        LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,cc->tdd_Config->subframeAssignment);
        ret = -1;
      }
      else ret = (subframeP-2);
      break;

    case 4:
      if ((subframeP<2) || (subframeP>3)) {
        LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,cc->tdd_Config->subframeAssignment);
        ret = -1;
      }
      else ret = (subframeP-2);
      break;

    case 5:
      if (subframeP!=2) {
        LOG_E(PHY,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,cc->tdd_Config->subframeAssignment);
        ret = -1;
      }
      else ret = (subframeP-2);
      break;

    default:
      LOG_E(PHY,"subframe2_harq_pid, Unsupported TDD mode %d\n",cc->tdd_Config->subframeAssignment);
      ret = -1;
    }
  }

  AssertFatal(ret!=-1,
	      "invalid harq_pid(%d) at SFN/SF = %d/%d\n", (int8_t)ret, frameP, subframeP);
  return ret;
}


uint16_t getRIV(uint16_t N_RB_DL,uint16_t RBstart,uint16_t Lcrbs)
{

  uint16_t RIV;

  if (Lcrbs<=(1+(N_RB_DL>>1)))
    RIV = (N_RB_DL*(Lcrbs-1)) + RBstart;
  else
    RIV = (N_RB_DL*(N_RB_DL+1-Lcrbs)) + (N_RB_DL-1-RBstart);

  return(RIV);
}

uint32_t allocate_prbs(int UE_id,unsigned char nb_rb, int N_RB_DL, uint32_t *rballoc)
{

  int i;
  uint32_t rballoc_dci=0;
  unsigned char nb_rb_alloc=0;

  for (i=0; i<(N_RB_DL-2); i+=2) {
    if (((*rballoc>>i)&3)==0) {
      *rballoc |= (3<<i);
      rballoc_dci |= (1<<((12-i)>>1));
      nb_rb_alloc+=2;
    }

    if (nb_rb_alloc==nb_rb) {
      return(rballoc_dci);
    }
  }

  if ((N_RB_DL&1)==1) {
    if ((*rballoc>>(N_RB_DL-1)&1)==0) {
      *rballoc |= (1<<(N_RB_DL-1));
      rballoc_dci |= 1;
    }
  }

  return(rballoc_dci);
}

int get_bw_index(module_id_t module_id, uint8_t CC_id)
{

  int bw_index=0;
 
  int N_RB_DL = to_prb(RC.mac[module_id]->common_channels[CC_id].mib->message.dl_Bandwidth);

  switch (N_RB_DL) {
  case 6: // 1.4 MHz
    bw_index=0;
    break;

  case 25: // 5HMz
    bw_index=1;
    break;

  case 50: // 10HMz
    bw_index=2;
    break;

  case 100: // 20HMz
    bw_index=3;
    break;

  default:
    bw_index=1;
    LOG_W(MAC,"[eNB %d] N_RB_DL %d unknown for CC_id %d, setting bw_index to 1\n", module_id, N_RB_DL,CC_id);
    break;
  }

  return bw_index;
}

int get_min_rb_unit(module_id_t module_id, uint8_t CC_id)
{

  int min_rb_unit=0;
  int N_RB_DL = to_prb(RC.mac[module_id]->common_channels[CC_id].mib->message.dl_Bandwidth);

  switch (N_RB_DL) {
  case 6: // 1.4 MHz
    min_rb_unit=1;
    break;

  case 25: // 5HMz
    min_rb_unit=2;
    break;

  case 50: // 10HMz
    min_rb_unit=3;
    break;

  case 100: // 20HMz
    min_rb_unit=4;
    break;

  default:
    min_rb_unit=2;
    LOG_W(MAC,"[eNB %d] N_DL_RB %d unknown for CC_id %d, setting min_rb_unit to 2\n",
          module_id, N_RB_DL, CC_id);
    break;
  }

  return min_rb_unit;
}

uint32_t allocate_prbs_sub(int nb_rb, int N_RB_DL, int N_RBG, uint8_t *rballoc)
{

  int check=0;//check1=0,check2=0;
  uint32_t rballoc_dci=0;
  //uint8_t number_of_subbands=13;

  LOG_T(MAC,"*****Check1RBALLOC****: %d%d%d%d (nb_rb %d,N_RBG %d)\n",
        rballoc[3],rballoc[2],rballoc[1],rballoc[0],nb_rb,N_RBG);

  while((nb_rb >0) && (check < N_RBG)) {
    //printf("rballoc[%d] %d\n",check,rballoc[check]);
    if(rballoc[check] == 1) {
      rballoc_dci |= (1<<((N_RBG-1)-check));

      switch (N_RB_DL) {
      case 6:
        nb_rb--;
        break;

      case 25:
        if ((check == N_RBG-1)) {
          nb_rb--;
        } else {
          nb_rb-=2;
        }

        break;

      case 50:
        if ((check == N_RBG-1)) {
          nb_rb-=2;
        } else {
          nb_rb-=3;
        }

        break;

      case 100:
        nb_rb-=4;
        break;
      }
    }

    //      printf("rb_alloc %x\n",rballoc_dci);
    check = check+1;
    //    check1 = check1+2;
  }

  // rballoc_dci = (rballoc_dci)&(0x1fff);
  LOG_T(MAC,"*********RBALLOC : %x\n",rballoc_dci);
  // exit(-1);
  return (rballoc_dci);
}

int get_subbandsize(uint8_t dl_Bandwidth) {
  uint8_t ss[6] = {6,4,4,6,8,8};

  AssertFatal(dl_Bandwidth<6,"dl_Bandwidth %d is out of bounds\n",dl_Bandwidth);

  return(ss[dl_Bandwidth]);
}
int get_nb_subband(int N_RB_DL)
{

  int nb_sb=0;

  switch (N_RB_DL) {
  case 6:
    nb_sb=0;
    break;

  case 15:
    nb_sb = 4;  // sb_size =4

  case 25:
    nb_sb = 7; // sb_size =4, 1 sb with 1PRB, 6 with 2 RBG, each has 2 PRBs
    break;

  case 50:    // sb_size =6
    nb_sb = 9;
    break;

  case 75:  // sb_size =8
    nb_sb = 10;
    break;

  case 100: // sb_size =8 , 1 sb with 1 RBG + 12 sb with 2RBG, each RBG has 4 PRBs
    nb_sb = 13;
    break;

  default:
    nb_sb=0;
    break;
  }

  return nb_sb;

}

void init_CCE_table(int module_idP,int CC_idP)
{
  memset(RC.mac[module_idP]->CCE_table[CC_idP],0,800*sizeof(int));
} 


int get_nCCE_offset(int *CCE_table,
		    const unsigned char L, 
		    const int nCCE, 
		    const int common_dci, 
		    const unsigned short rnti, 
		    const unsigned char subframe)
{

  int search_space_free,m,nb_candidates = 0,l,i;
  unsigned int Yk;
   /*
    printf("CCE Allocation: ");
    for (i=0;i<nCCE;i++)
    printf("%d.",CCE_table[i]);
    printf("\n");
  */
  if (common_dci == 1) {
    // check CCE(0 ... L-1)
    nb_candidates = (L==4) ? 4 : 2;
    nb_candidates = min(nb_candidates,nCCE/L);

    //    printf("Common DCI nb_candidates %d, L %d\n",nb_candidates,L);

    for (m = nb_candidates-1 ; m >=0 ; m--) {

      search_space_free = 1;
      for (l=0; l<L; l++) {

	//	printf("CCE_table[%d] %d\n",(m*L)+l,CCE_table[(m*L)+l]);
        if (CCE_table[(m*L) + l] == 1) {
          search_space_free = 0;
          break;
        }
      }
     
      if (search_space_free == 1) {

	//	printf("returning %d\n",m*L);

        for (l=0; l<L; l++)
          CCE_table[(m*L)+l]=1;
        return(m*L);
      }
    }

    return(-1);

  } else { // Find first available in ue specific search space
    // according to procedure in Section 9.1.1 of 36.213 (v. 8.6)
    // compute Yk
    Yk = (unsigned int)rnti;

    for (i=0; i<=subframe; i++)
      Yk = (Yk*39827)%65537;

    Yk = Yk % (nCCE/L);


    switch (L) {
    case 1:
    case 2:
      nb_candidates = 6;
      break;

    case 4:
    case 8:
      nb_candidates = 2;
      break;

    default:
      DevParam(L, nCCE, rnti);
      break;
    }


    LOG_D(MAC,"rnti %x, Yk = %d, nCCE %d (nCCE/L %d),nb_cand %d\n",rnti,Yk,nCCE,nCCE/L,nb_candidates);

    for (m = 0 ; m < nb_candidates ; m++) {
      search_space_free = 1;

      for (l=0; l<L; l++) {
        if (CCE_table[(((Yk+m)%(nCCE/L))*L) + l] == 1) {
          search_space_free = 0;
          break;
        }
      }

      if (search_space_free == 1) {
        for (l=0; l<L; l++)
          CCE_table[(((Yk+m)%(nCCE/L))*L)+l]=1;

        return(((Yk+m)%(nCCE/L))*L);
      }
    }

    return(-1);
  }
}

void dump_CCE_table(int *CCE_table,const int nCCE,const unsigned short rnti,const int subframe,int L) {

  int nb_candidates = 0,i;
  unsigned int Yk;
  
  printf("CCE 0: ");
  for (i=0;i<nCCE;i++) {
    printf("%1d.",CCE_table[i]);
    if ((i&7) == 7)
      printf("\n CCE %d: ",i);
  }

  Yk = (unsigned int)rnti;
  
  for (i=0; i<=subframe; i++)
    Yk = (Yk*39827)%65537;
  
  Yk = Yk % (nCCE/L);
  
  
  switch (L) {
  case 1:
  case 2:
    nb_candidates = 6;
    break;
    
  case 4:
  case 8:
    nb_candidates = 2;
    break;
    
  default:
    DevParam(L, nCCE, rnti);
    break;
  }
  
  
  printf("rnti %x, Yk*L = %d, nCCE %d (nCCE/L %d),nb_cand*L %d\n",rnti,Yk*L,nCCE,nCCE/L,nb_candidates*L);

}



uint16_t getnquad(COMMON_channels_t *cc, uint8_t num_pdcch_symbols,uint8_t mi)
{

  uint16_t Nreg=0;

  AssertFatal(cc!=NULL,"cc is null\n");
  AssertFatal(cc->mib!=NULL,"cc->mib is null\n");

  int N_RB_DL        = to_prb(cc->mib->message.dl_Bandwidth);
  int phich_resource = get_phich_resource_times6(cc); 

  uint8_t Ngroup_PHICH = (phich_resource*N_RB_DL)/48;

  if (((phich_resource*N_RB_DL)%48) > 0)
    Ngroup_PHICH++;

  if (cc->Ncp == 1) {
    Ngroup_PHICH<<=1;
  }

  Ngroup_PHICH*=mi;

  if ((num_pdcch_symbols>0) && (num_pdcch_symbols<4))
    switch (N_RB_DL) {
    case 6:
      Nreg=12+(num_pdcch_symbols-1)*18;
      break;

    case 25:
      Nreg=50+(num_pdcch_symbols-1)*75;
      break;

    case 50:
      Nreg=100+(num_pdcch_symbols-1)*150;
      break;

    case 100:
      Nreg=200+(num_pdcch_symbols-1)*300;
      break;

    default:
      return(0);
    }

  //   printf("Nreg %d (%d)\n",Nreg,Nreg - 4 - (3*Ngroup_PHICH));
  return(Nreg - 4 - (3*Ngroup_PHICH));
}

uint16_t getnCCE(COMMON_channels_t *cc, uint8_t num_pdcch_symbols, uint8_t mi)
{
  AssertFatal(cc!=NULL,"cc is null\n");
  return(getnquad(cc,num_pdcch_symbols,mi)/9);
}

uint8_t getmi(COMMON_channels_t *cc,int subframe) {

  AssertFatal(cc!=NULL,"cc is null\n");

  // for FDD
  if (cc->tdd_Config==NULL) // FDD
    return 1;

  // for TDD
  switch (cc->tdd_Config->subframeAssignment) {

  case 0:
    if ((subframe==0) || (subframe==5))
      return(2);
    else return(1);

    break;

  case 1:
    if ((subframe==0) || (subframe==5))
      return(0);
    else return(1);

    break;

  case 2:
    if ((subframe==3) || (subframe==8))
      return(1);
    else return(0);

    break;

  case 3:
    if ((subframe==0) || (subframe==8) || (subframe==9))
      return(1);
    else return(0);

    break;

  case 4:
    if ((subframe==8) || (subframe==9))
      return(1);
    else return(0);

    break;

  case 5:
    if (subframe==8)
      return(1);
    else return(0);

    break;

  case 6:
    return(1);
    break;

  default:
    return(0);
  }
}

uint16_t get_nCCE_max(COMMON_channels_t *cc, int num_pdcch_symbols,int subframe)
{
  AssertFatal(cc!=NULL,"cc is null\n");
  return(getnCCE(cc,num_pdcch_symbols,
		 getmi(cc,subframe))); 
}
 
// Allocate the CCEs
int allocate_CCEs(int module_idP,
		  int CC_idP,
		  int subframeP,
		  int test_onlyP) {


  int                                 *CCE_table           = RC.mac[module_idP]->CCE_table[CC_idP];
  nfapi_dl_config_request_body_t      *DL_req              = &RC.mac[module_idP]->DL_req[CC_idP];
  nfapi_hi_dci0_request_body_t        *HI_DCI0_req         = &RC.mac[module_idP]->HI_DCI0_req[CC_idP];
  nfapi_dl_config_request_pdu_t       *dl_config_pdu       = &DL_req->dl_config_pdu_list[0];
  nfapi_hi_dci0_request_pdu_t         *hi_dci0_pdu         = &HI_DCI0_req->hi_dci0_pdu_list[0];
  int                                 nCCE_max             = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],1,subframeP);
  int fCCE;
  int i,j,idci;
  int nCCE=0;

  LOG_I(MAC,"Allocate CCEs subframe %d, test %d : (DL %d,UL %d)\n",subframeP,test_onlyP,DL_req->number_dci,HI_DCI0_req->number_of_dci);
  DL_req->number_pdcch_ofdm_symbols=1;

try_again:
  init_CCE_table(module_idP,CC_idP);
  nCCE=0;

  for (i=0,idci=0;i<DL_req->number_pdu;i++) {
    // allocate DL common DCIs first
    if ((dl_config_pdu[i].pdu_type == NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE)&&
	(dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti_type==2)) {
      LOG_I(MAC,"Trying to allocate COMMON DCI %d/%d (%d,%d) : rnti %x, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
	    idci,DL_req->number_dci+HI_DCI0_req->number_of_dci,
	    DL_req->number_dci,HI_DCI0_req->number_of_dci,
	    dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti,
	    dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
	    nCCE,nCCE_max, DL_req->number_pdcch_ofdm_symbols);
    
      if (nCCE + (dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level) > nCCE_max) {
	if (DL_req->number_pdcch_ofdm_symbols == 3)
	  goto failed;
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      }
      
      // number of CCEs left can potentially hold this allocation
      fCCE = get_nCCE_offset(CCE_table,
			     dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
			     nCCE_max,
			     1,
			     dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti,
			     subframeP);
      if (fCCE == -1) {
	if (DL_req->number_pdcch_ofdm_symbols == 3) {
	  LOG_I(MAC,"subframe %d: Dropping Allocation for RNTI %x\n",
		subframeP,dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti);
	  for (j=0;j<=i;j++){
	    if (dl_config_pdu[j].pdu_type == NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE)
	      LOG_I(MAC,"DCI %d/%d (%d,%d) : rnti %x dci format %d, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
		    j,DL_req->number_dci+HI_DCI0_req->number_of_dci,
		    DL_req->number_dci,HI_DCI0_req->number_of_dci,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.rnti,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.dci_format,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
		    nCCE,nCCE_max,DL_req->number_pdcch_ofdm_symbols);
	  }
	  //dump_CCE_table(CCE_table,nCCE_max,subframeP,dci_alloc->rnti,1<<dci_alloc->L);
	  goto failed;
	}
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      } // fCCE==-1
      
      // the allocation is feasible, rnti rule passes
      nCCE += dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level;
      LOG_D(MAC,"Allocating at nCCE %d\n",fCCE);
      if (test_onlyP == 0) {
	dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.cce_idx=fCCE;
	LOG_I(MAC,"Allocate COMMON DCI CCEs subframe %d, test %d => L %d fCCE %d\n",subframeP,test_onlyP,dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,fCCE);
      }
      idci++;
    }
  } // for i = 0 ... num_DL_DCIs

  // no try to allocate UL DCIs
  for (i=0;i<HI_DCI0_req->number_of_dci+HI_DCI0_req->number_of_hi;i++) {

    // allocate UL DCIs 
    if (hi_dci0_pdu[i].pdu_type == NFAPI_HI_DCI0_DCI_PDU_TYPE) {

      LOG_D(MAC,"Trying to allocate format 0 DCI %d/%d (%d,%d) : rnti %x, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
	    idci,DL_req->number_dci+HI_DCI0_req->number_of_dci,
	    DL_req->number_dci,HI_DCI0_req->number_of_dci,
	    hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti,hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.aggregation_level,
	    nCCE,nCCE_max, DL_req->number_pdcch_ofdm_symbols);
   
      if (nCCE + (hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.aggregation_level) > nCCE_max) {
	if (DL_req->number_pdcch_ofdm_symbols == 3)
	  goto failed;
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      }
      
      // number of CCEs left can potentially hold this allocation
      fCCE = get_nCCE_offset(CCE_table,
			     hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.aggregation_level,
			     nCCE_max,
			     1,
			     hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti,
			     subframeP);
      if (fCCE == -1) {
	if (DL_req->number_pdcch_ofdm_symbols == 3) {
	  LOG_I(MAC,"subframe %d: Dropping Allocation for RNTI %x\n",
		subframeP,hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti);
	  for (j=0;j<=i;j++){
	    if (hi_dci0_pdu[j].pdu_type == NFAPI_HI_DCI0_DCI_PDU_TYPE)
	      LOG_I(MAC,"DCI %d/%d (%d,%d) : rnti %x dci format %d, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
		    j,DL_req->number_dci+HI_DCI0_req->number_of_dci,
		    DL_req->number_dci,HI_DCI0_req->number_of_dci,
		    hi_dci0_pdu[j].dci_pdu.dci_pdu_rel8.rnti,
		    hi_dci0_pdu[j].dci_pdu.dci_pdu_rel8.dci_format,
		    hi_dci0_pdu[j].dci_pdu.dci_pdu_rel8.aggregation_level,
		    nCCE,nCCE_max,DL_req->number_pdcch_ofdm_symbols);
	  }
	  //dump_CCE_table(CCE_table,nCCE_max,subframeP,dci_alloc->rnti,1<<dci_alloc->L);
	  goto failed;
	}
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      } // fCCE==-1
      
      // the allocation is feasible, rnti rule passes
      nCCE += hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.aggregation_level;
      LOG_D(MAC,"Allocating at nCCE %d\n",fCCE);
      if (test_onlyP == 0) {
	hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.cce_index=fCCE;
	LOG_D(MAC,"Allocate CCEs subframe %d, test %d\n",subframeP,test_onlyP);
      }
      idci++;
    }
  } // for i = 0 ... num_UL_DCIs

  for (i=0;i<DL_req->number_pdu;i++) {
    // allocate DL common DCIs first
    if ((dl_config_pdu[i].pdu_type == NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE)&&
	(dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti_type==1)) {
      LOG_D(MAC,"Trying to allocate DL UE-SPECIFIC DCI %d/%d (%d,%d) : rnti %x, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
	    idci,DL_req->number_dci+HI_DCI0_req->number_of_dci,
	    DL_req->number_dci,HI_DCI0_req->number_of_dci,
	    dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti,dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
	    nCCE,nCCE_max, DL_req->number_pdcch_ofdm_symbols);
   
      if (nCCE + (dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level) > nCCE_max) {
	if (DL_req->number_pdcch_ofdm_symbols == 3)
	  goto failed;
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      }
      
      // number of CCEs left can potentially hold this allocation
      fCCE = get_nCCE_offset(CCE_table,
			     dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
			     nCCE_max,
			     1,
			     dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti,
			     subframeP);
      if (fCCE == -1) {
	if (DL_req->number_pdcch_ofdm_symbols == 3) {
	  LOG_I(MAC,"subframe %d: Dropping Allocation for RNTI %x\n",
		subframeP,dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.rnti);
	  for (j=0;j<=i;j++){
	    if (dl_config_pdu[j].pdu_type == NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE)
	      LOG_I(MAC,"DCI %d/%d (%d,%d) : rnti %x dci format %d, aggreg %d nCCE %d / %d (num_pdcch_symbols %d)\n",
		    j,DL_req->number_dci+HI_DCI0_req->number_of_dci,
		    DL_req->number_dci,HI_DCI0_req->number_of_dci,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.rnti,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.dci_format,
		    dl_config_pdu[j].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
		    nCCE,nCCE_max,DL_req->number_pdcch_ofdm_symbols);
	  }
	  //dump_CCE_table(CCE_table,nCCE_max,subframeP,dci_alloc->rnti,1<<dci_alloc->L);
	  goto failed;
	}
	DL_req->number_pdcch_ofdm_symbols++;
	nCCE_max = get_nCCE_max(&RC.mac[module_idP]->common_channels[CC_idP],DL_req->number_pdcch_ofdm_symbols,subframeP);
	goto try_again;
      } // fCCE==-1
      
      // the allocation is feasible, rnti rule passes
      nCCE += dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level;
      LOG_D(MAC,"Allocating at nCCE %d\n",fCCE);
      if (test_onlyP == 0) {
	dl_config_pdu[i].dci_dl_pdu.dci_dl_pdu_rel8.cce_idx=fCCE;
	LOG_D(MAC,"Allocate CCEs subframe %d, test %d\n",subframeP,test_onlyP);
      }
      idci++;
    }
  } // for i = 0 ... num_DL_DCIs

  return 0;

failed:
  return -1;
}

boolean_t CCE_allocation_infeasible(int module_idP,
				    int CC_idP,
				    int format_flag,
				    int subframe,
				    int aggregation,
				    int rnti) {

  nfapi_dl_config_request_body_t *DL_req       = &RC.mac[module_idP]->DL_req[CC_idP];
  nfapi_dl_config_request_pdu_t* dl_config_pdu = &DL_req->dl_config_pdu_list[DL_req->number_pdu];
  nfapi_hi_dci0_request_body_t *HI_DCI0_req    = &RC.mac[module_idP]->HI_DCI0_req[CC_idP];
  nfapi_hi_dci0_request_pdu_t *hi_dci0_pdu     = &HI_DCI0_req->hi_dci0_pdu_list[HI_DCI0_req->number_of_dci+HI_DCI0_req->number_of_hi]; 
  //DCI_ALLOC_t *dci_alloc;
  int ret;
  boolean_t res=FALSE;

  if (format_flag!=2) { // DL DCI
    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti              = rnti;
    dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level = aggregation;
    DL_req->number_pdu++;
    ret = allocate_CCEs(module_idP,CC_idP,subframe,1);
    if (ret==-1)
      res = TRUE;
    DL_req->number_pdu--;
  }
  else if (format_flag == 2) { // ue-specific UL DCI
    hi_dci0_pdu->dci_pdu.dci_pdu_rel8.rnti             = rnti;
    hi_dci0_pdu->dci_pdu.dci_pdu_rel8.aggregation_level = aggregation;
    HI_DCI0_req->number_of_dci++;
    ret = allocate_CCEs(module_idP,CC_idP,subframe,1);
    if (ret==-1)
      res = TRUE;
    HI_DCI0_req->number_of_dci--;
  }
  return(res);
}

void SR_indication(module_id_t mod_idP, int cc_idP, frame_t frameP, rnti_t rntiP, sub_frame_t subframeP)
{
 
  int UE_id = find_UE_id(mod_idP, rntiP);
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;
 
  if (UE_id  != -1) {
    if (mac_eNB_get_rrc_status(mod_idP,UE_RNTI(mod_idP,UE_id)) < RRC_CONNECTED)
      LOG_I(MAC,"[eNB %d][SR %x] Frame %d subframeP %d Signaling SR for UE %d on CC_id %d\n",mod_idP,rntiP,frameP,subframeP, UE_id,cc_idP);
    UE_list->UE_template[cc_idP][UE_id].ul_SR = 1;
    UE_list->UE_template[cc_idP][UE_id].ul_active = TRUE;
    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SR_INDICATION,1);
    VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_SR_INDICATION,0);
  } else {
    //     AssertFatal(0, "find_UE_id(%u,rnti %d) not found", enb_mod_idP, rntiP);
    //    AssertError(0, 0, "Frame %d: find_UE_id(%u,rnti %d) not found\n", frameP, enb_mod_idP, rntiP);
    LOG_D(MAC,"[eNB %d][SR %x] Frame %d subframeP %d Signaling SR for UE %d (unknown UEid) on CC_id %d\n",mod_idP,rntiP,frameP,subframeP, UE_id,cc_idP);
  }
}

void UL_failure_indication(module_id_t mod_idP, int cc_idP, frame_t frameP, rnti_t rntiP, sub_frame_t subframeP)
{

  int UE_id = find_UE_id(mod_idP, rntiP);
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;

  if (UE_id  != -1) {
    LOG_I(MAC,"[eNB %d][UE %d/%x] Frame %d subframeP %d Signaling UL Failure for UE %d on CC_id %d (timer %d)\n",
	  mod_idP,UE_id,rntiP,frameP,subframeP, UE_id,cc_idP,
	  UE_list->UE_sched_ctrl[UE_id].ul_failure_timer);
    if (UE_list->UE_sched_ctrl[UE_id].ul_failure_timer == 0)
      UE_list->UE_sched_ctrl[UE_id].ul_failure_timer=1;
  } else {
    //     AssertFatal(0, "find_UE_id(%u,rnti %d) not found", enb_mod_idP, rntiP);
    //    AssertError(0, 0, "Frame %d: find_UE_id(%u,rnti %d) not found\n", frameP, enb_mod_idP, rntiP);
    LOG_W(MAC,"[eNB %d][SR %x] Frame %d subframeP %d Signaling UL Failure for UE %d (unknown UEid) on CC_id %d\n",mod_idP,rntiP,frameP,subframeP, UE_id,cc_idP);
  }
}