/*******************************************************************************
    OpenAirInterface
    Copyright(c) 1999 - 2014 Eurecom

    OpenAirInterface is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.


    OpenAirInterface is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with OpenAirInterface.The full GNU General Public License is
    included in this distribution in the file called "COPYING". If not,
    see <http://www.gnu.org/licenses/>.

  Contact Information
  OpenAirInterface Admin: openair_admin@eurecom.fr
  OpenAirInterface Tech : openair_tech@eurecom.fr
  OpenAirInterface Dev  : openair4g-devel@eurecom.fr

  Address      : Eurecom, Campus SophiaTech, 450 Route des Chappes, CS 50193 - 06904 Biot Sophia Antipolis cedex, FRANCE

*******************************************************************************/

/*! \file ue_procedures.c
 * \brief procedures related to UE
 * \author  Navid Nikaein and Raymond Knopp
 * \date 2010 - 2014
 * \version 1
 * \email: navid.nikaein@eurecom.fr
 * @ingroup _mac

 */

#ifdef EXMIMO
#include <pthread.h>
#endif

#include "extern.h"
#include "defs.h"
#include "proto.h"
#ifdef PHY_EMUL
# include "SIMULATION/PHY_EMULATION/impl_defs.h"
#else
# include "SCHED/defs.h"
# include "PHY/impl_defs_top.h"
#endif
#include "PHY_INTERFACE/defs.h"
#include "PHY_INTERFACE/extern.h"
#include "COMMON/mac_rrc_primitives.h"

#include "RRC/L2_INTERFACE/openair_rrc_L2_interface.h"
#include "RRC/LITE/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"

#ifdef PHY_EMUL
# include "SIMULATION/simulation_defs.h"
#endif
#include "pdcp.h"

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

#include "assertions.h"

#define DEBUG_HEADER_PARSING 1
#define ENABLE_MAC_PAYLOAD_DEBUG

/*
#ifndef USER_MODE
#define msg debug_msg
#endif
 */
mapping BSR_names[] = {
    {"NONE", 0},
    {"SHORT BSR", 1},
    {"TRUNCATED BSR", 2},
    {"LONG BSR", 3},
    {"PADDING BSR", 4},
    {NULL, -1}
};

extern inline unsigned int taus(void);


void ue_init_mac(module_id_t module_idP){
  int i;
  // default values as deined in 36.331 sec 9.2.2
  LOG_I(MAC,"[UE%d] Applying default macMainConfig\n",module_idP);
  //UE_mac_inst[module_idP].scheduling_info.macConfig=NULL;
  UE_mac_inst[module_idP].scheduling_info.retxBSR_Timer= MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf2560;
  UE_mac_inst[module_idP].scheduling_info.periodicBSR_Timer=MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_infinity;
  UE_mac_inst[module_idP].scheduling_info.periodicPHR_Timer = MAC_MainConfig__phr_Config__setup__periodicPHR_Timer_sf20;
  UE_mac_inst[module_idP].scheduling_info.prohibitPHR_Timer = MAC_MainConfig__phr_Config__setup__prohibitPHR_Timer_sf20;
  UE_mac_inst[module_idP].scheduling_info.PathlossChange_db = MAC_MainConfig__phr_Config__setup__dl_PathlossChange_dB1;
  UE_mac_inst[module_idP].PHR_state = MAC_MainConfig__phr_Config_PR_setup;
  UE_mac_inst[module_idP].scheduling_info.SR_COUNTER=0;
  UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer=0;
  UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer_Running=0;
  UE_mac_inst[module_idP].scheduling_info.maxHARQ_Tx=MAC_MainConfig__ul_SCH_Config__maxHARQ_Tx_n5;
  UE_mac_inst[module_idP].scheduling_info.ttiBundling=0;
  UE_mac_inst[module_idP].scheduling_info.drx_config=NULL;
  UE_mac_inst[module_idP].scheduling_info.phr_config=NULL;
  UE_mac_inst[module_idP].scheduling_info.periodicBSR_SF  =  get_sf_periodicBSRTimer(UE_mac_inst[module_idP].scheduling_info.periodicBSR_Timer);
  UE_mac_inst[module_idP].scheduling_info.retxBSR_SF     =  get_sf_retxBSRTimer(UE_mac_inst[module_idP].scheduling_info.retxBSR_Timer);
  UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF =  get_sf_perioidicPHR_Timer(UE_mac_inst[module_idP].scheduling_info.periodicPHR_Timer);
  UE_mac_inst[module_idP].scheduling_info.prohibitPHR_SF =  get_sf_prohibitPHR_Timer(UE_mac_inst[module_idP].scheduling_info.prohibitPHR_Timer);
  UE_mac_inst[module_idP].scheduling_info.PathlossChange_db =  get_db_dl_PathlossChange(UE_mac_inst[module_idP].scheduling_info.PathlossChange);
  
  for (i=0; i < MAX_NUM_LCID; i++){
    LOG_D(MAC,"[UE%d] Applying default logical channel config for LCGID %d\n",module_idP,i);
    UE_mac_inst[module_idP].scheduling_info.Bj[i]=-1;
      UE_mac_inst[module_idP].scheduling_info.bucket_size[i]=-1;
      if (i < DTCH) // initilize all control channels lcgid to 0
        UE_mac_inst[module_idP].scheduling_info.LCGID[i]=0;
      else // initialize all the data channels lcgid to 1
        UE_mac_inst[module_idP].scheduling_info.LCGID[i]=1;
      UE_mac_inst[module_idP].scheduling_info.LCID_status[i]=0;
  }
#ifdef CBA
  for (i=0; i <NUM_MAX_CBA_GROUP;i++) 
    UE_mac_inst[module_idP].cba_last_access[i]= round(uniform_rngen(1,30));
#endif 
}


unsigned char *parse_header(unsigned char *mac_header,
			    unsigned char *num_ce,
			    unsigned char *num_sdu,
			    unsigned char *rx_ces,
			    unsigned char *rx_lcids,
			    unsigned short *rx_lengths,
			    unsigned short tb_length) {

  unsigned char not_done=1,num_ces=0,num_sdus=0,lcid, num_sdu_cnt;
  unsigned char *mac_header_ptr = mac_header;
  unsigned short length,ce_len=0;

  while (not_done==1) {

      if (((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E == 0) {
          //      printf("E=0\n");
          not_done = 0;
      }
      lcid = ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID;
      if (lcid < UE_CONT_RES) {
          //printf("[MAC][UE] header %x.%x.%x\n",mac_header_ptr[0],mac_header_ptr[1],mac_header_ptr[2]);
          if (not_done==0) {// last MAC SDU, length is implicit
              mac_header_ptr++;
              length = tb_length-(mac_header_ptr-mac_header)-ce_len;
              for (num_sdu_cnt=0; num_sdu_cnt < num_sdus ; num_sdu_cnt++)
                length -= rx_lengths[num_sdu_cnt];
          }
          else {
              if (((SCH_SUBHEADER_LONG *)mac_header_ptr)->F == 1) {
                  length = ((((SCH_SUBHEADER_LONG *)mac_header_ptr)->L_MSB & 0x7f ) << 8 ) | (((SCH_SUBHEADER_LONG *)mac_header_ptr)->L_LSB & 0xff);
                  mac_header_ptr += 3;
#ifdef DEBUG_HEADER_PARSING
                  LOG_D(MAC,"[UE] parse long sdu, size %x \n",length);
#endif

              }  else {	//if (((SCH_SUBHEADER_SHORT *)mac_header_ptr)->F == 0) {
                  length = ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->L;
                  mac_header_ptr += 2;
              }
          }
#ifdef DEBUG_HEADER_PARSING
          LOG_D(MAC,"[UE] sdu %d lcid %d length %d (offset now %d)\n",
              num_sdus,lcid,length,mac_header_ptr-mac_header);
#endif
          rx_lcids[num_sdus] = lcid;
          rx_lengths[num_sdus] = length;
          num_sdus++;
      }
      else {  // This is a control element subheader
          if (lcid == SHORT_PADDING) {
              mac_header_ptr++;
          }
          else {
              rx_ces[num_ces] = lcid;
              num_ces++;
              mac_header_ptr ++;
              if (lcid==TIMING_ADV_CMD)
                ce_len++;
              else if (lcid==UE_CONT_RES)
                ce_len+=6;
          }
#ifdef DEBUG_HEADER_PARSING
          LOG_D(MAC,"[UE] ce %d lcid %d (offset now %d)\n",num_ces,lcid,mac_header_ptr-mac_header);
#endif
      }
  }
  *num_ce = num_ces;
  *num_sdu = num_sdus;

  return(mac_header_ptr);
}

uint32_t ue_get_SR(module_id_t module_idP,int CC_id,frame_t frameP,uint8_t eNB_id,uint16_t rnti, sub_frame_t subframe) {

  // no UL-SCH resources available for this tti && UE has a valid PUCCH resources for SR configuration for this tti
  //  int MGL=6;// measurement gap length in ms
  int MGRP       = 0; // measurement gap repetition period in ms
  int gapOffset  = -1;
  int T          = 0;

  DevCheck(module_idP < (int)NB_UE_INST, module_idP, NB_UE_INST, 0);

  if (CC_id>0) {
    LOG_E(MAC,"Transmission on secondary CCs is not supported yet\n");
    mac_xface->macphy_exit("MAC FATAL  CC_id>0");
    return 0;
  }

  // determin the measurement gap
  if (UE_mac_inst[module_idP].measGapConfig !=NULL){
      if (UE_mac_inst[module_idP].measGapConfig->choice.setup.gapOffset.present == MeasGapConfig__setup__gapOffset_PR_gp0){
          MGRP= 40;
          gapOffset= UE_mac_inst[module_idP].measGapConfig->choice.setup.gapOffset.choice.gp0;
      }else if (UE_mac_inst[module_idP].measGapConfig->choice.setup.gapOffset.present == MeasGapConfig__setup__gapOffset_PR_gp1){
          MGRP= 80;
          gapOffset= UE_mac_inst[module_idP].measGapConfig->choice.setup.gapOffset.choice.gp1;
      }else{
          LOG_W(MAC, "Measurement GAP offset is unknown\n");
      }
      T=MGRP/10;
      DevAssert( T != 0 );
      //check the measurement gap and sr prohibit timer
      if ((subframe ==  gapOffset %10) && ((frameP %T) == (floor(gapOffset/10)))
          && (UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer_Running ==0)){
          UE_mac_inst[module_idP].scheduling_info.SR_pending=1;
          return(0);
      }
  }
  
  if ((UE_mac_inst[module_idP].physicalConfigDedicated != NULL) && 
      (UE_mac_inst[module_idP].scheduling_info.SR_pending==1) &&
      (UE_mac_inst[module_idP].scheduling_info.SR_COUNTER <
          (1<<(2+UE_mac_inst[module_idP].physicalConfigDedicated->schedulingRequestConfig->choice.setup.dsr_TransMax)))
  ){
    LOG_D(MAC,"[UE %d][SR %x] Frame %d subframe %d PHY asks for SR (SR_COUNTER/dsr_TransMax %d/%d), SR_pending %d\n",
	  module_idP,rnti,frameP,subframe,
	  UE_mac_inst[module_idP].scheduling_info.SR_COUNTER,
	  (1<<(2+UE_mac_inst[module_idP].physicalConfigDedicated->schedulingRequestConfig->choice.setup.dsr_TransMax)),
	  UE_mac_inst[module_idP].scheduling_info.SR_pending);
    
      UE_mac_inst[module_idP].scheduling_info.SR_COUNTER++;
      // start the sr-prohibittimer : rel 9 and above
      if (UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer > 0) { // timer configured
          UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer--;
          UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer_Running=1;
      } else
        UE_mac_inst[module_idP].scheduling_info.sr_ProhibitTimer_Running=0;
      LOG_D(MAC,"[UE %d][SR %x] Frame %d subframe %d send SR indication (SR_COUNTER/dsr_TransMax %d/%d), SR_pending %d\n",
          module_idP,rnti,frameP,subframe,
          UE_mac_inst[module_idP].scheduling_info.SR_COUNTER,
          (1<<(2+UE_mac_inst[module_idP].physicalConfigDedicated->schedulingRequestConfig->choice.setup.dsr_TransMax)),
          UE_mac_inst[module_idP].scheduling_info.SR_pending);

      //UE_mac_inst[module_idP].ul_active =1;

      return(1); //instruct phy to signal SR
  }
  else{
      // notify RRC to relase PUCCH/SRS
      // clear any configured dl/ul
      // initiate RA
      UE_mac_inst[module_idP].scheduling_info.SR_pending=0;
      UE_mac_inst[module_idP].scheduling_info.SR_COUNTER=0;
      return(0);
  }
}

void ue_send_sdu(module_id_t module_idP, uint8_t CC_id,frame_t frameP,uint8_t *sdu,uint16_t sdu_len,uint8_t eNB_index) {

  unsigned char rx_ces[MAX_NUM_CE],num_ce,num_sdu,i,*payload_ptr;
  unsigned char rx_lcids[NB_RB_MAX];
  unsigned short rx_lengths[NB_RB_MAX];
  unsigned char *tx_sdu;

  start_meas(&UE_mac_inst[module_idP].rx_dlsch_sdu);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_IN);

  LOG_T(MAC,"sdu: %x.%x.%x\n",sdu[0],sdu[1],sdu[2]);

  if (opt_enabled == 1) {
    trace_pdu(1, sdu, sdu_len, module_idP, 3, UE_mac_inst[module_idP].crnti,
	      UE_mac_inst[module_idP].subframe, 0, 0);
  }

  payload_ptr = parse_header(sdu,&num_ce,&num_sdu,rx_ces,rx_lcids,rx_lengths,sdu_len);

#ifdef DEBUG_HEADER_PARSING
  LOG_D(MAC,"[UE %d] ue_send_sdu : Frame %d eNB_index %d : num_ce %d num_sdu %d\n",module_idP,
      frameP,eNB_index,num_ce,num_sdu);
#endif

#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
  LOG_T(MAC,"[eNB %d] First 32 bytes of DLSCH : \n");
  for (i=0;i<32;i++)
    LOG_T(MAC,"%x.",sdu[i]);
  LOG_T(MAC,"\n");
#endif

  for (i=0;i<num_ce;i++) {
      //    printf("ce %d : %d\n",i,rx_ces[i]);
      switch (rx_ces[i]) {
      case UE_CONT_RES:

        LOG_I(MAC,"[UE %d][RAPROC] Frame %d : received contention resolution msg: %x.%x.%x.%x.%x.%x, Terminating RA procedure\n",
            module_idP,frameP,payload_ptr[0],payload_ptr[1],payload_ptr[2],payload_ptr[3],payload_ptr[4],payload_ptr[5]);
        if (UE_mac_inst[module_idP].RA_active == 1) {
            LOG_I(MAC,"[UE %d][RAPROC] Frame %d : Clearing RA_active flag\n");
            UE_mac_inst[module_idP].RA_active=0;
            // check if RA procedure has finished completely (no contention)
            tx_sdu = &UE_mac_inst[module_idP].CCCH_pdu.payload[3];
            //Note: 3 assumes sizeof(SCH_SUBHEADER_SHORT) + PADDING CE, which is when UL-Grant has TBS >= 9 (64 bits)
            // (other possibility is 1 for TBS=7 (SCH_SUBHEADER_FIXED), or 2 for TBS=8 (SCH_SUBHEADER_FIXED+PADDING or SCH_SUBHEADER_SHORT)
            for (i=0;i<6;i++)
              if (tx_sdu[i] != payload_ptr[i]) {
                  LOG_E(MAC,"[UE %d][RAPROC] Contention detected, RA failed\n",module_idP);
                  mac_xface->ra_failed(module_idP,CC_id,eNB_index);
                  UE_mac_inst[module_idP].RA_contention_resolution_timer_active = 0;
                  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT);
                  return;
              }
            LOG_I(MAC,"[UE %d][RAPROC] Frame %d : Clearing contention resolution timer\n");
            UE_mac_inst[module_idP].RA_contention_resolution_timer_active = 0;
            mac_xface->ra_succeeded(module_idP,CC_id,eNB_index);
        }
        payload_ptr+=6;
        break;
      case TIMING_ADV_CMD:
#ifdef DEBUG_HEADER_PARSING
        LOG_D(MAC,"[UE] CE %d : UE Timing Advance : %d\n",i,payload_ptr[0]);
#endif
        mac_xface->process_timing_advance(module_idP,CC_id,payload_ptr[0]);
        payload_ptr++;
        break;
      case DRX_CMD:
#ifdef DEBUG_HEADER_PARSING
        LOG_D(MAC,"[UE] CE %d : UE DRX :",i);
#endif
        payload_ptr++;
        break;
      }
  }
  for (i=0;i<num_sdu;i++) {
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,"[UE] SDU %d : LCID %d, length %d\n",i,rx_lcids[i],rx_lengths[i]);
#endif
      if (rx_lcids[i] == CCCH) {

          LOG_D(MAC,"[UE %d] Frame %d : DLSCH -> DL-CCCH, RRC message (eNB %d, %d bytes)\n",module_idP,frameP, eNB_index, rx_lengths[i]);

#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
          int j;
          for (j=0;j<rx_lengths[i];j++)
            LOG_T(MAC,"%x.",(uint8_t)payload_ptr[j]);
          LOG_T(MAC,"\n");
#endif
          mac_rrc_data_ind(module_idP,
              frameP,
              CCCH,
              (uint8_t *)payload_ptr,rx_lengths[i],0,eNB_index,0);

      }
      else if (rx_lcids[i] == DCCH) {
          LOG_D(MAC,"[UE %d] Frame %d : DLSCH -> DL-DCCH%d, RRC message (eNB %d, %d bytes)\n", module_idP, frameP, rx_lcids[i],eNB_index,rx_lengths[i]);
          mac_rlc_data_ind(eNB_index,
              module_idP,
              frameP,
              ENB_FLAG_NO,
              MBMS_FLAG_NO,
              DCCH,
              (char *)payload_ptr,
              rx_lengths[i],
              1,
              NULL);
      }
      else if (rx_lcids[i] == DCCH1) {
          LOG_D(MAC,"[UE %d] Frame %d : DLSCH -> DL-DCCH%d, RRC message (eNB %d, %d bytes)\n", module_idP, frameP, rx_lcids[i], eNB_index,rx_lengths[i]);
          mac_rlc_data_ind(eNB_index,
              module_idP,
              frameP,
              ENB_FLAG_NO,
              MBMS_FLAG_NO,
              DCCH1,
              (char *)payload_ptr,
              rx_lengths[i],
              1,
              NULL);
      }
      else if (rx_lcids[i] == DTCH) {
          LOG_D(MAC,"[UE %d] Frame %d : DLSCH -> DL-DTCH%d (eNB %d, %d bytes)\n", module_idP, frameP,rx_lcids[i], eNB_index,rx_lengths[i]);

#if defined(ENABLE_MAC_PAYLOAD_DEBUG)
          int j;
          for (j=0;j<rx_lengths[i];j++)
            LOG_T(MAC,"%x.",(unsigned char)payload_ptr[j]);
          LOG_T(MAC,"\n");
#endif

          mac_rlc_data_ind(eNB_index,
              module_idP,
              frameP,
              ENB_FLAG_NO,
              MBMS_FLAG_NO,
              DTCH,
              (char *)payload_ptr,
              rx_lengths[i],
              1,
              NULL);
      }
      payload_ptr+= rx_lengths[i];
  }
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_SDU, VCD_FUNCTION_OUT);
  stop_meas(&UE_mac_inst[module_idP].rx_dlsch_sdu);
}

void ue_decode_si(module_id_t module_idP,int CC_id,frame_t frameP, uint8_t eNB_index, void *pdu,uint16_t len) {

  start_meas(&UE_mac_inst[module_idP].rx_si);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_SI, VCD_FUNCTION_IN);

  LOG_D(MAC,"[UE %d] Frame %d Sending SI to RRC (LCID Id %d,len %d)\n",module_idP,frameP,BCCH,len);

  mac_rrc_data_ind(module_idP,
      frameP,
      BCCH,
      (uint8_t *)pdu,
      len,
      0,
      eNB_index,
      0);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_DECODE_SI, VCD_FUNCTION_OUT);
  stop_meas(&UE_mac_inst[module_idP].rx_si);
}

#ifdef Rel10
unsigned char *parse_mch_header(unsigned char *mac_header,
				unsigned char *num_sdu,
				unsigned char *rx_lcids,
				unsigned short *rx_lengths,
				unsigned short tb_length) {
  unsigned char not_done=1, num_sdus=0, lcid, i;
  unsigned char *mac_header_ptr = mac_header;
  unsigned short length;
  
  while (not_done == 1) {
      if (((SCH_SUBHEADER_FIXED *)mac_header_ptr)->E == 0) {
          not_done = 0;
      }
      lcid = ((SCH_SUBHEADER_FIXED *)mac_header_ptr)->LCID;
      if (lcid < SHORT_PADDING) {// subheader for MSI, MCCH or MTCH
          if (not_done == 0) { // last MAC SDU, length is implicit
              mac_header_ptr++;
              length = tb_length- (mac_header_ptr - mac_header);
              for (i=0; i<num_sdus; i++)
                length -= rx_lengths[i];
          }
          else { // not the last MAC SDU
              if ( ((SCH_SUBHEADER_LONG *)mac_header_ptr)->F == 1) {// subheader has length of 3octets
                  //	  length = ((SCH_SUBHEADER_LONG *)mac_header_ptr)->L;
                  length = ((((SCH_SUBHEADER_LONG *)mac_header_ptr)->L_MSB & 0x7f ) << 8 ) | (((SCH_SUBHEADER_LONG *)mac_header_ptr)->L_LSB & 0xff);
                  mac_header_ptr += 3;
              }
              else {// subheader has length of 2octets
                  length = ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->L;
                  mac_header_ptr += 2;
              }
          }
          rx_lcids[num_sdus] = lcid;
          rx_lengths[num_sdus] = length;
          num_sdus++;
      }
      else {// subheader for padding
          //     if (lcid == SHORT_PADDING)
          mac_header_ptr++;
      }
  }
  *num_sdu = num_sdus;
  return(mac_header_ptr);
}

// this function is for sending mch_sdu from phy to mac
void ue_send_mch_sdu(module_id_t module_idP, uint8_t CC_id, frame_t frameP, uint8_t *sdu, uint16_t sdu_len, uint8_t eNB_index, uint8_t sync_area) {

  unsigned char num_sdu, i, *payload_ptr;
  unsigned char rx_lcids[NB_RB_MAX];
  unsigned short rx_lengths[NB_RB_MAX];

  start_meas(&UE_mac_inst[module_idP].rx_mch_sdu);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_MCH_SDU, VCD_FUNCTION_IN);

  LOG_D(MAC,"[UE %d] Frame %d : process the mch PDU for sync area %d \n",module_idP,frameP, sync_area);
  LOG_D(MAC,"[UE %d] sdu: %x.%x\n", module_idP,sdu[0], sdu[1]);
  LOG_D(MAC,"[UE %d] parse_mch_header, demultiplex\n",module_idP);

  payload_ptr = parse_mch_header(sdu, &num_sdu, rx_lcids, rx_lengths, sdu_len);
  LOG_D(MAC,"[UE %d] parse_mch_header, found %d sdus\n",module_idP,num_sdu);

  for (i=0; i<num_sdu; i++) {
      if (rx_lcids[i] == MCH_SCHDL_INFO) {
          if (UE_mac_inst[module_idP].mcch_status==1) {
              LOG_I(MAC,"[UE %d] Frame %d : MCH->MSI for sync area %d (eNB %d, %d bytes)\n",module_idP,frameP, sync_area, eNB_index, rx_lengths[i]);
              // ??store necessary scheduling info to ue_mac_inst in order to
              // calculate exact position of interested service (for the complex case has >1 mtch)
              // set msi_status to 1
              UE_mac_inst[module_idP].msi_status = 1;
          }
      }
      else if (rx_lcids[i] == MCCH_LCHANID) {
          LOG_I(MAC,"[UE %d] Frame %d : SDU %d MCH->MCCH for sync area %d (eNB %d, %d bytes)\n",module_idP,frameP, i, sync_area, eNB_index, rx_lengths[i]);
          mac_rrc_data_ind(module_idP,
              frameP,
              MCCH,
              payload_ptr, rx_lengths[i], 0, eNB_index, sync_area);
      }
      else if (rx_lcids[i] == MTCH) {
          if (UE_mac_inst[module_idP].msi_status==1) {
              LOG_I(MAC,"[UE %d] Frame %d : MCH->MTCH for sync area %d (eNB %d, %d bytes)\n",module_idP,frameP, sync_area, eNB_index, rx_lengths[i]);

              mac_rlc_data_ind(
                  0,
                  module_idP,
                  frameP,
                  ENB_FLAG_NO,
                  MBMS_FLAG_YES,
                  MTCH, /*+ (maxDRB + 3),*/
                  (char *)payload_ptr,
                  rx_lengths[i],
                  1,
                  NULL);

          }
      } else {
          LOG_W(MAC,"[UE %d] Frame %d : unknown sdu %d rx_lcids[%d]=%d mcch status %d eNB %d \n",
              module_idP,
              frameP,
              rx_lengths[i],
              i,
              rx_lcids[i],
              UE_mac_inst[module_idP].mcch_status, eNB_index);
      }

      payload_ptr += rx_lengths[i];
  }

  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SEND_MCH_SDU, VCD_FUNCTION_OUT);
  stop_meas(&UE_mac_inst[module_idP].rx_mch_sdu);
}

int8_t ue_get_mbsfn_sf_alloction (module_id_t module_idP, uint8_t mbsfn_sync_area, unsigned char eNB_index){
  // currently there is one-to-one mapping between sf allocation pattern and sync area
  if (mbsfn_sync_area > MAX_MBSFN_AREA){
      LOG_W(MAC,"[UE %d] MBSFN synchronization area %d out of range for eNB %d\n ", module_idP, mbsfn_sync_area, eNB_index);
      return -1;
  }
  else if (UE_mac_inst[module_idP].mbsfn_SubframeConfig[mbsfn_sync_area] != NULL)
    return mbsfn_sync_area;
  else {
      LOG_W(MAC,"[UE %d] MBSFN Subframe Config pattern %d not found \n ", module_idP, mbsfn_sync_area);
      return -1;
  }
}

int ue_query_mch(module_id_t module_idP, uint8_t CC_id, uint32_t frameP, uint32_t subframe, uint8_t eNB_index,uint8_t *sync_area, uint8_t *mcch_active) {

  int i=0, j=0, ii=0, msi_pos=0, mcch_mcs = - 1;
  int mcch_flag=0, mtch_flag=0, msi_flag=0;
  int mbsfn_period = 0;// 1<<(UE_mac_inst[module_idP].mbsfn_SubframeConfig[0]->radioframeAllocationPeriod);
  int mcch_period = 0;// 32<<(UE_mac_inst[module_idP].mbsfn_AreaInfo[0]->mcch_Config_r9.mcch_RepetitionPeriod_r9);
  int mch_scheduling_period = -1;

  start_meas(&UE_mac_inst[module_idP].ue_query_mch);

  if (UE_mac_inst[module_idP].pmch_Config[0])
    mch_scheduling_period = 8<<(UE_mac_inst[module_idP].pmch_Config[0]->mch_SchedulingPeriod_r9);

  for (i=0;
      i< UE_mac_inst[module_idP].num_active_mbsfn_area;
      i++ ){
      // assume, that there is always a mapping
      if ((j=ue_get_mbsfn_sf_alloction(module_idP,i,eNB_index)) == -1)
        return -1; // continue;
      ii=0;
      msi_pos=0;
      mbsfn_period = 1<<(UE_mac_inst[module_idP].mbsfn_SubframeConfig[0]->radioframeAllocationPeriod);
      mcch_period = 32<<(UE_mac_inst[module_idP].mbsfn_AreaInfo[0]->mcch_Config_r9.mcch_RepetitionPeriod_r9);

      LOG_D(MAC,"[UE %d] Frame %d subframe %d: Checking MBSFN Sync Area %d/%d with SF allocation %d/%d for MCCH and MTCH (mbsfn period %d, mcch period %d,mac sched period (%d,%d))\n",
          module_idP,frameP, subframe,i,UE_mac_inst[module_idP].num_active_mbsfn_area,
          j,UE_mac_inst[module_idP].num_sf_allocation_pattern,mbsfn_period,mcch_period,
          mch_scheduling_period,UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->radioframeAllocationOffset);

      // get the real MCS value
      switch (UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.signallingMCS_r9) {
      case 0:
        mcch_mcs = 2;
        break;
      case 1:
        mcch_mcs = 7;
        break;
      case 2:
        mcch_mcs = 13;
        break;
      case 3:
        mcch_mcs = 19;
        break;
      }

      if (frameP % mbsfn_period == UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->radioframeAllocationOffset){ // MBSFN frameP
          if (UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.present == MBSFN_SubframeConfig__subframeAllocation_PR_oneFrame){// one-frameP format

              if (UE_mac_inst[module_idP].pmch_Config[0]) {
                  //  Find the first subframe in this MCH to transmit MSI
                  if (frameP % mch_scheduling_period == UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->radioframeAllocationOffset ) {
                      while (ii == 0) {
                          ii = UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & (0x80>>msi_pos);
                          msi_pos++;
                      }
                  }
              }

              // Check if the subframe is for MSI, MCCH or MTCHs and Set the correspoding flag to 1
              switch (subframe) {
              case 1:
                if (mac_xface->lte_frame_parms->frame_type == FDD) {
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF1) == MBSFN_FDD_SF1) {
                        if (msi_pos == 1)
                          msi_flag = 1;
                        if ( (frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF1) == MBSFN_FDD_SF1) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 2:
                if (mac_xface->lte_frame_parms->frame_type == FDD){
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF2) == MBSFN_FDD_SF2) {
                        if (msi_pos == 2)
                          msi_flag = 1;
                        if ( (frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF2) == MBSFN_FDD_SF2) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 3:
                if (mac_xface->lte_frame_parms->frame_type == TDD){// TDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_TDD_SF3) == MBSFN_TDD_SF3) {
                        if (msi_pos == 1)
                          msi_flag = 1;
                        if ( (frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_TDD_SF3) == MBSFN_TDD_SF3) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                else {// FDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF3) == MBSFN_FDD_SF3) {
                        if (msi_pos == 3)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF3) == MBSFN_FDD_SF3) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 4:
                if (mac_xface->lte_frame_parms->frame_type == TDD){
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_TDD_SF4) == MBSFN_TDD_SF4) {
                        if (msi_pos == 2)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_TDD_SF4) == MBSFN_TDD_SF4) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 6:
                if (mac_xface->lte_frame_parms->frame_type == FDD){
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF6) == MBSFN_FDD_SF6) {
                        if (msi_pos == 4)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF6) == MBSFN_FDD_SF6) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 7:
                if (mac_xface->lte_frame_parms->frame_type == TDD){ // TDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_TDD_SF7) == MBSFN_TDD_SF7) {
                        if (msi_pos == 3)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_TDD_SF7) == MBSFN_TDD_SF7) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                else {// FDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF7) == MBSFN_FDD_SF7) {
                        if (msi_pos == 5)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF7) == MBSFN_FDD_SF7) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 8:
                if (mac_xface->lte_frame_parms->frame_type == TDD){ //TDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_TDD_SF8) == MBSFN_TDD_SF8) {
                        if (msi_pos == 4)
                          msi_flag = 1;
                        if ( (frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_TDD_SF8) == MBSFN_TDD_SF8) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                else{ // FDD
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_FDD_SF8) == MBSFN_FDD_SF8) {
                        if (msi_pos == 6)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_FDD_SF8) == MBSFN_FDD_SF8) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;

              case 9:
                if (mac_xface->lte_frame_parms->frame_type == TDD){
                    if ((UE_mac_inst[module_idP].mbsfn_SubframeConfig[j]->subframeAllocation.choice.oneFrame.buf[0] & MBSFN_TDD_SF9) == MBSFN_TDD_SF9) {
                        if (msi_pos == 5)
                          msi_flag = 1;
                        if ((frameP % mcch_period == UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.mcch_Offset_r9) &&
                            ((UE_mac_inst[module_idP].mbsfn_AreaInfo[i]->mcch_Config_r9.sf_AllocInfo_r9.buf[0] & MBSFN_TDD_SF9) == MBSFN_TDD_SF9) )
                          mcch_flag = 1;
                        mtch_flag = 1;
                    }
                }
                break;
              }// end switch
              // sf allocation is non-overlapping
              if ((msi_flag==1) || (mcch_flag==1) || (mtch_flag==1)){
                  LOG_D(MAC,"[UE %d] Frame %d Subframe %d: sync area %d SF alloc %d: msi flag %d, mcch flag %d, mtch flag %d\n",
                      module_idP, frameP, subframe,i,j,msi_flag,mcch_flag,mtch_flag);

                  *sync_area=i;
                  break;
              }
          }
          else {// four-frameP format
          }
      }
  } // end of for

  stop_meas(&UE_mac_inst[module_idP].ue_query_mch);

  if ( (mcch_flag==1))// || (msi_flag==1))
    *mcch_active=1;

  if ( (mcch_flag==1) || ((msi_flag==1) && (UE_mac_inst[module_idP].mcch_status==1)) ){
      return mcch_mcs;
  } else if ((mtch_flag==1) && (UE_mac_inst[module_idP].msi_status==1))
    return UE_mac_inst[module_idP].pmch_Config[0]->dataMCS_r9;
  else
    return -1;
}

#endif

unsigned char generate_ulsch_header(uint8_t *mac_header,
				    uint8_t num_sdus,
				    uint8_t short_padding,
				    uint16_t *sdu_lengths,
				    uint8_t *sdu_lcids,
				    POWER_HEADROOM_CMD *power_headroom,
				    uint16_t *crnti,
				    BSR_SHORT *truncated_bsr,
				    BSR_SHORT *short_bsr,
				    BSR_LONG *long_bsr,
				    unsigned short post_padding) {

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

  LOG_D(MAC,"[UE] Generate ULSCH : num_sdus %d\n",num_sdus);
#ifdef DEBUG_HEADER_PARSING
  for (i=0;i<num_sdus;i++)
    LOG_T(MAC,"[UE] sdu %d : lcid %d length %d",i,sdu_lcids[i],sdu_lengths[i]);
  LOG_T(MAC,"\n");
#endif
  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;
  }

  if (power_headroom) {
      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 = POWER_HEADROOM;
      last_size=1;
      *((POWER_HEADROOM_CMD *)ce_ptr)=(*power_headroom);
      ce_ptr+=sizeof(POWER_HEADROOM_CMD);
      LOG_D(MAC, "phr header size %d\n",sizeof(POWER_HEADROOM_CMD));
  }

  if (crnti) {
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,"[UE] CRNTI : %x (first_element %d)\n",*crnti,first_element);
#endif
      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 = CRNTI;
      last_size=1;
      *((uint16_t *)ce_ptr)=(*crnti);
      ce_ptr+=sizeof(uint16_t);
      //    printf("offset %d\n",ce_ptr-mac_header_control_elements);
  }

  if (truncated_bsr) {
      if (first_element>0) {
          mac_header_ptr->E = 1;
          /*
      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);
           */
          mac_header_ptr++;
      }
      else {
          first_element=1;
      }
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,"[UE] Scheduler Truncated BSR Header\n");
#endif
      mac_header_ptr->R = 0;
      mac_header_ptr->E    = 0;
      mac_header_ptr->LCID = TRUNCATED_BSR;
      last_size=1;
      *((BSR_TRUNCATED *)ce_ptr)=(*truncated_bsr);
      ce_ptr+=sizeof(BSR_TRUNCATED);

      //    printf("(cont_res) : offset %d\n",ce_ptr-mac_header_control_elements);
  }
  else if (short_bsr) {
      if (first_element>0) {
          mac_header_ptr->E = 1;
          /*
      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);
           */
          mac_header_ptr++;
      }
      else {
          first_element=1;
      }
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,"[UE] Scheduler SHORT BSR Header\n");
#endif
      mac_header_ptr->R = 0;
      mac_header_ptr->E    = 0;
      mac_header_ptr->LCID = SHORT_BSR;
      last_size=1;
      *((BSR_SHORT *)ce_ptr)=(*short_bsr);
      ce_ptr+=sizeof(BSR_SHORT);

      //    printf("(cont_res) : offset %d\n",ce_ptr-mac_header_control_elements);
  }
  else if (long_bsr) {
      if (first_element>0) {
          mac_header_ptr->E = 1;
          /*
      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);
           */
          mac_header_ptr++;
      }
      else {
          first_element=1;
      }
#ifdef DEBUG_HEADER_PARSING
      LOG_D(MAC,"[UE] Scheduler Long BSR Header\n");
#endif
      mac_header_ptr->R = 0;
      mac_header_ptr->E    = 0;
      mac_header_ptr->LCID = LONG_BSR;
      last_size=1;

      *(ce_ptr)     = (long_bsr->Buffer_size0 << 2) | ((long_bsr->Buffer_size1 & 0x30) >> 4);
      *(ce_ptr + 1) = ((long_bsr->Buffer_size1 & 0x0F) << 4) | ((long_bsr->Buffer_size2 & 0x3C) >> 2);
      *(ce_ptr + 2) = ((long_bsr->Buffer_size2 & 0x03) << 2) | (long_bsr->Buffer_size3 & 0x3F);
      /* Padding */
      *(ce_ptr + 3) = 0;
      ce_ptr += BSR_LONG_SIZE;

      //    printf("(cont_res) : offset %d\n",ce_ptr-mac_header_control_elements);
  }
  //  printf("last_size %d,mac_header_ptr %p\n",last_size,mac_header_ptr);

  for (i=0;i<num_sdus;i++) {
#ifdef DEBUG_HEADER_PARSING
      LOG_T(MAC,"[UE] sdu subheader %d (lcid %d, %d bytes)\n",i,sdu_lcids[i],sdu_lengths[i]);
#endif
      if ((first_element>0)) {
          mac_header_ptr->E = 1;
#ifdef DEBUG_HEADER_PARSING
          LOG_D(MAC,"[UE] 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);
#endif
          mac_header_ptr+=last_size;
          //      printf("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; // 3
          ((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;
#ifdef DEBUG_HEADER_PARSING
          LOG_D(MAC,"[UE] short sdu\n");
          LOG_T(MAC,"[UE] last subheader : %x (R%d,E%d,LCID%d,F%d,L%d)\n",
              ((uint16_t*)mac_header_ptr)[0],
              ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->R,
              ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->E,
              ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->LCID,
              ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->F,
              ((SCH_SUBHEADER_SHORT *)mac_header_ptr)->L);
#endif
      }
      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,"[UE] long sdu\n");
#endif
      }
  }
  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++;
      //mac_header_ptr=last_size; // FIXME: should be ++
  }

  if ((ce_ptr-mac_header_control_elements) > 0) {
      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);
  }
#ifdef DEBUG_HEADER_PARSING
  LOG_T(MAC," [UE %d] header : ", crnti);
  for (i=0;i<((unsigned char*)mac_header_ptr - mac_header);i++)
    LOG_T(MAC,"%2x.",mac_header[i]);
  LOG_T(MAC,"\n");
#endif
  return((unsigned char*)mac_header_ptr - mac_header);

}

void ue_get_sdu(module_id_t module_idP,int CC_id,frame_t frameP,sub_frame_t subframe, uint8_t eNB_index,uint8_t *ulsch_buffer,uint16_t buflen, uint8_t *access_mode) {

  mac_rlc_status_resp_t rlc_status;
  uint8_t dcch_header_len=0,dcch1_header_len=0,dtch_header_len=0;
  uint8_t dcch_header_len_tmp=0, dtch_header_len_tmp=0;
  uint8_t bsr_header_len=0, bsr_ce_len=0, bsr_len=0;
  uint8_t phr_header_len=0, phr_ce_len=0,phr_len=0;
  uint16_t sdu_lengths[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
  uint8_t sdu_lcids[8]    = { 0, 0, 0, 0, 0, 0, 0, 0 };
  uint8_t payload_offset=0,num_sdus=0;
  uint8_t ulsch_buff[MAX_ULSCH_PAYLOAD_BYTES];
  uint16_t sdu_length_total=0;
  BSR_SHORT bsr_short;
  BSR_LONG bsr_long;
  BSR_SHORT *bsr_s=&bsr_short;
  BSR_LONG  *bsr_l=&bsr_long;
  POWER_HEADROOM_CMD phr;
  POWER_HEADROOM_CMD *phr_p=&phr;
  unsigned short short_padding=0, post_padding=0;
  int lcgid;
  int j; // used for padding
  // Compute header length

  if (CC_id>0) {
    LOG_E(MAC,"Transmission on secondary CCs is not supported yet\n");
    mac_xface->macphy_exit("MAC FATAL  CC_id>0");
    return;
  }

  start_meas(&UE_mac_inst[module_idP].tx_ulsch_sdu);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GET_SDU, VCD_FUNCTION_IN);

#ifdef CBA
  if (*access_mode==CBA_ACCESS){
      LOG_D(MAC,"[UE %d] frameP %d subframe %d try CBA transmission\n",
          module_idP, frameP, subframe);
      //if (UE_mac_inst[module_idP].scheduling_info.LCID_status[DTCH] == LCID_EMPTY)
      if (cba_access(module_idP,frameP,subframe,eNB_index,buflen)==0){
          *access_mode=POSTPONED_ACCESS;
          vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GET_SDU, VCD_FUNCTION_OUT);
          return;
      }
      LOG_I(MAC,"[UE %d] frameP %d subframe %d CBA transmission oppurtunity, tbs %d\n",
          module_idP, frameP, subframe,buflen);
  }
#endif
  dcch_header_len=2;//sizeof(SCH_SUBHEADER_SHORT);
  dcch1_header_len=2;//sizeof(SCH_SUBHEADER_SHORT);
  // hypo length,in case of long header skip the padding byte
  dtch_header_len=(buflen > 128 ) ? 3 : 2 ; //sizeof(SCH_SUBHEADER_LONG)-1 : sizeof(SCH_SUBHEADER_SHORT);
  bsr_header_len = 1;//sizeof(SCH_SUBHEADER_FIXED);
  phr_header_len = 1;//sizeof(SCH_SUBHEADER_FIXED);
  phr_ce_len = (UE_mac_inst[module_idP].PHR_reporting_active == 1) ? 1 /* sizeof(POWER_HEADROOM_CMD)*/: 0;
  if (phr_ce_len > 0){
      phr_len = phr_ce_len + phr_header_len;
      LOG_D(MAC,"[UE %d] header size info: PHR len %d (ce%d,hdr%d) buff_len %d\n",
          module_idP, phr_len, phr_ce_len, phr_header_len, buflen);
  }else
    phr_len=0;

  bsr_ce_len = get_bsr_len (module_idP, buflen-phr_len);
  if (bsr_ce_len > 0 ){
      bsr_len = bsr_ce_len + bsr_header_len;
      LOG_D(MAC,"[UE %d] header size info: dcch %d, dcch1 %d, dtch %d, bsr (ce%d,hdr%d) buff_len %d\n",
          module_idP, dcch_header_len,dcch1_header_len,dtch_header_len, bsr_ce_len, bsr_header_len, buflen);
  } else {
    bsr_len=0;
    //LOG_D(MAC,"[UE %d] Empty buffers, send a long BSR to reset the bsr at eNB \n ",Mod_id);
    //    bsr_ce_len = sizeof(BSR_LONG);
    //bsr_len = bsr_ce_len + bsr_header_len;
  }
    // check for UL bandwidth requests and add SR control element

  // check for UL bandwidth requests and add SR control element

  // Check for DCCH first
  sdu_lengths[0]=0;

  if (UE_mac_inst[module_idP].scheduling_info.LCID_status[DCCH] == LCID_NOT_EMPTY) {

    rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO, // eNB_index
          DCCH,
          (buflen-dcch_header_len-bsr_len-phr_len));
      LOG_D(MAC, "[UE %d] Frame %d : UL-DCCH -> ULSCH, RRC message has %d bytes to "
          "send (Transport Block size %d, mac header len %d)\n",
          module_idP,frameP, rlc_status.bytes_in_buffer,buflen,dcch_header_len);

      sdu_lengths[0] += mac_rlc_data_req(0, module_idP,frameP,ENB_FLAG_NO, MBMS_FLAG_NO,
          DCCH,
          (char *)&ulsch_buff[sdu_lengths[0]]);

      sdu_length_total += sdu_lengths[0];
      sdu_lcids[0] = DCCH;
      LOG_D(MAC,"[UE %d] TX Got %d bytes for DCCH\n",module_idP,sdu_lengths[0]);
      num_sdus = 1;
      update_bsr(module_idP, frameP, DCCH, UE_mac_inst[module_idP].scheduling_info.LCGID[DCCH]);
      //header_len +=2;
  }
  else {
      dcch_header_len=0;
      num_sdus = 0;
  }

  // if the RLC AM is used, then RLC will only provide 2 bytes for ACK
  // in this case, we sould add bsr


  // DCCH1
  if (UE_mac_inst[module_idP].scheduling_info.LCID_status[DCCH1] == LCID_NOT_EMPTY) {

    rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO, // eNB_index
          DCCH1,
          (buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-sdu_length_total));

      LOG_D(MAC,"[UE %d] Frame %d : UL-DCCH1 -> ULSCH, RRC message has %d bytes to"
          " send (Transport Block size %d, mac header len %d)\n",
          module_idP,frameP, rlc_status.bytes_in_buffer,buflen,dcch1_header_len);

      sdu_lengths[num_sdus] = mac_rlc_data_req(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO,
          DCCH1,
          (char *)&ulsch_buff[sdu_lengths[0]]);
      sdu_length_total += sdu_lengths[num_sdus];
      sdu_lcids[num_sdus] = DCCH1;
      LOG_D(MAC,"[UE %d] TX Got %d bytes for DCCH1\n",module_idP,sdu_lengths[num_sdus]);
      num_sdus++;
      //update_bsr(module_idP, frameP, DCCH1);
      //dcch_header_len +=2; // include dcch1
  }
  else {
      dcch1_header_len =0;
  }

  if ((UE_mac_inst[module_idP].scheduling_info.LCID_status[DTCH] == LCID_NOT_EMPTY) &&
      ((bsr_len+phr_len+dcch_header_len+dcch1_header_len+dtch_header_len+sdu_length_total) <= buflen)){

      // optimize the dtch header lenght
      //if ((UE_mac_inst[module_idP].scheduling_info.BSR_bytes[DTCH] > 128) &&
      /*   if (((UE_mac_inst[module_idP].scheduling_info.BSR_bytes[DTCH] >= 128) &&
	((UE_mac_inst[module_idP].scheduling_info.BSR_bytes[DTCH]+bsr_len+phr_len+dcch_header_len+dcch1_header_len+dtch_header_len) > buflen)&&
	 buflen >=128 ))
      dtch_header_len = 3;//sizeof(SCH_SUBHEADER_LONG);
    else
      dtch_header_len = 2;//sizeof(SCH_SUBHEADER_SHORT);
       */
    rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO, // eNB_index
          DTCH,
          buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-dtch_header_len-sdu_length_total);

      LOG_D(MAC,"[UE %d] Frame %d : UL-DTCH -> ULSCH, %d bytes to send (Transport Block size %d, mac header len %d, BSR byte[DTCH] %d)\n",
          module_idP,frameP, rlc_status.bytes_in_buffer,buflen,dtch_header_len,
          UE_mac_inst[module_idP].scheduling_info.BSR_bytes[DTCH]);

      sdu_lengths[num_sdus] = mac_rlc_data_req(0, module_idP,frameP, ENB_FLAG_NO, MBMS_FLAG_NO, // eNB_index
          DTCH,
          (char *)&ulsch_buff[sdu_length_total]);

      //adjust dtch header
      dtch_header_len = (sdu_lengths[num_sdus] >= 128) ? 3 : 2;
      LOG_D(MAC,"[UE %d] TX Got %d bytes for DTCH\n",module_idP,sdu_lengths[num_sdus]);
      sdu_lcids[num_sdus] = DTCH;
      sdu_length_total += sdu_lengths[num_sdus];
      num_sdus++;
      UE_mac_inst[module_idP].ul_active = update_bsr(module_idP, frameP, DTCH, UE_mac_inst[module_idP].scheduling_info.LCGID[DTCH]);
  }
  else { // no rlc pdu : generate the dummy header
      dtch_header_len = 0;
  }

  lcgid= get_bsr_lcgid(module_idP);

  if (lcgid < 0 ) {
      bsr_s = NULL;
      bsr_l = NULL ;
  } else if ((lcgid ==MAX_NUM_LCGID) && (bsr_ce_len == sizeof(BSR_LONG))) {
      bsr_s = NULL;
      bsr_l->Buffer_size0 = UE_mac_inst[module_idP].scheduling_info.BSR[LCGID0];
      bsr_l->Buffer_size1 = UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1];
      bsr_l->Buffer_size2 = UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2];
      bsr_l->Buffer_size3 = UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3];
      LOG_D(MAC, "[UE %d] Frame %d report long BSR (level LCGID0 %d,level LCGID1 %d,level LCGID2 %d,level LCGID3 %d)\n", module_idP,frameP,
          UE_mac_inst[module_idP].scheduling_info.BSR[LCGID0],
          UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1],
          UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2],
          UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]);
  } else if (bsr_ce_len == sizeof(BSR_SHORT)) {
      bsr_l = NULL;
      bsr_s->LCGID = lcgid;
      bsr_s->Buffer_size = UE_mac_inst[module_idP].scheduling_info.BSR[lcgid];
      LOG_D(MAC,"[UE %d] Frame %d report SHORT BSR with level %d for LCGID %d\n",
          module_idP, frameP, UE_mac_inst[module_idP].scheduling_info.BSR[lcgid],lcgid);
  } else {
      bsr_s = NULL;
      bsr_l = NULL;
  }

  // build PHR and update the timers
  if (phr_ce_len == sizeof(POWER_HEADROOM_CMD)){
    phr_p->PH = get_phr_mapping(module_idP,CC_id,eNB_index);
    phr_p->R  = 0;
    LOG_D(MAC,"[UE %d] Frame %d report PHR with mapping (%d->%d) for LCID %d\n",
          module_idP,frameP, mac_xface->get_PHR(module_idP,CC_id,eNB_index), phr_p->PH,POWER_HEADROOM);
    update_phr(module_idP,CC_id);
  }else
    phr_p=NULL;

  LOG_T(MAC,"[UE %d] Frame %d: bsr s %p bsr_l %p, phr_p %p\n",  module_idP,frameP,bsr_s, bsr_l, phr_p);

  // adjust the header length
  dcch_header_len_tmp = dcch_header_len;
  dtch_header_len_tmp = dtch_header_len;
  if (dtch_header_len==0)
    dcch_header_len = (dcch_header_len>0)? 1: dcch_header_len;
  else
    dtch_header_len= (dtch_header_len >0)? 1: dtch_header_len;   // for short and long, cut the length+F fields

  if ((buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-dtch_header_len-sdu_length_total) <= 2) {
      short_padding = buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-dtch_header_len-sdu_length_total;
      post_padding = 0;
  }
  else {
      if ((buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-dtch_header_len-sdu_length_total) == buflen) {
            *access_mode=CANCELED_ACCESS;
      }
      short_padding = 0;
      if (dtch_header_len==0)
        dcch_header_len = dcch_header_len_tmp;
      else
        dtch_header_len= dtch_header_len_tmp;

      post_padding = buflen-bsr_len-phr_len-dcch_header_len-dcch1_header_len-dtch_header_len-sdu_length_total -1 ;
  }

  // Generate header
  // if (num_sdus>0) {

  payload_offset = generate_ulsch_header(ulsch_buffer,  // mac header
      num_sdus,      // num sdus
      short_padding,            // short pading
      sdu_lengths,  // sdu length
      sdu_lcids,    // sdu lcid
      phr_p,  // power headroom
      NULL,  // crnti
      NULL,  // truncated bsr
      bsr_s, // short bsr
      bsr_l,
      post_padding); // long_bsr

  LOG_I(MAC,"[UE %d] Generate header :bufflen %d  sdu_length_total %d, num_sdus %d, sdu_lengths[0] %d, sdu_lcids[0] %d => payload offset %d,  dcch_header_len %d, dtch_header_len %d, padding %d,post_padding %d, bsr len %d, phr len %d, reminder %d \n",
      module_idP,buflen, sdu_length_total,num_sdus,sdu_lengths[0],sdu_lcids[0],payload_offset, dcch_header_len,  dtch_header_len,
      short_padding,post_padding, bsr_len, phr_len,buflen-sdu_length_total-payload_offset);
  // cycle through SDUs and place in ulsch_buffer
  memcpy(&ulsch_buffer[payload_offset],ulsch_buff,sdu_length_total);
  // fill remainder of DLSCH with random data
  for (j=0;j<(buflen-sdu_length_total-payload_offset);j++)
    ulsch_buffer[payload_offset+sdu_length_total+j] = (char)(taus()&0xff);

  if (opt_enabled==1){
    trace_pdu(0, ulsch_buffer, buflen, module_idP, 3, UE_mac_inst[module_idP].crnti, subframe, 0, 0);
    
    LOG_D(OPT,"[UE %d][ULSCH] Frame %d trace pdu for rnti %x  with size %d\n",
      module_idP, frameP, UE_mac_inst[module_idP].crnti, buflen);
  }

  LOG_D(MAC,"[UE %d][SR] Gave SDU to PHY, clearing any scheduling request\n",
      module_idP,payload_offset, sdu_length_total);
  UE_mac_inst[module_idP].scheduling_info.SR_pending=0;
  UE_mac_inst[module_idP].scheduling_info.SR_COUNTER=0;
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_GET_SDU, VCD_FUNCTION_OUT);
    stop_meas(&UE_mac_inst[module_idP].tx_ulsch_sdu);
}

// called at each subframe
// Performs :
// 1. Trigger PDCP every 5ms
// 2. Call RRC for link status return to PHY
// 3. Perform SR/BSR procedures for scheduling feedback
// 4. Perform PHR procedures

UE_L2_STATE_t ue_scheduler(module_id_t module_idP,frame_t frameP, sub_frame_t subframeP, lte_subframe_t directionP,uint8_t eNB_indexP,int CC_id) {

  int lcid; // lcid index
  int TTI= 1;
  int bucketsizeduration = -1;
  int bucketsizeduration_max = -1;
  // mac_rlc_status_resp_t rlc_status[MAX_NUM_LCGID]; // 4
  // int8_t lcg_id;
  struct RACH_ConfigCommon *rach_ConfigCommon = (struct RACH_ConfigCommon *)NULL;
#ifdef EXMIMO
  protocol_ctxt_t   ctxt;
  int ret;
#endif
#if defined(ENABLE_ITTI)
  MessageDef   *msg_p;
  const char   *msg_name;
  instance_t    instance;
  int           result;
#endif
  start_meas(&UE_mac_inst[module_idP].ue_scheduler);
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_IN);

#if defined(ENABLE_ITTI)
  do {
      // Checks if a message has been sent to MAC sub-task
      itti_poll_msg (TASK_MAC_UE, &msg_p);

      if (msg_p != NULL) {
          msg_name = ITTI_MSG_NAME (msg_p);
          instance = ITTI_MSG_INSTANCE (msg_p);

          switch (ITTI_MSG_ID(msg_p)) {
          case RRC_MAC_CCCH_DATA_REQ:
            LOG_I(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d\n",
                msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance,
                RRC_MAC_CCCH_DATA_REQ (msg_p).frame, RRC_MAC_CCCH_DATA_REQ (msg_p).enb_index);

            // TODO process CCCH data req.
            break;


          default:
            LOG_E(MAC, "Received unexpected message %s\n", msg_name);
            break;
          }

          result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
          AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
      }
  } while(msg_p != NULL);
#endif

  //Mac_rlc_xface->frameP=frameP;
  //Rrc_xface->Frame_index=Mac_rlc_xface->frameP;
  //if (subframe%5 == 0)
#ifdef EXMIMO
  ctxt.enb_module_id = eNB_indexP;
  ctxt.ue_module_id  = module_idP;
  ctxt.frame         = frameP;
  ctxt.enb_flag      = ENB_FLAG_NO;
  pdcp_run(&ctxt);
#endif
  UE_mac_inst[module_idP].frame = frameP;
  UE_mac_inst[module_idP].subframe = subframeP;

#ifdef CELLULAR
  rrc_rx_tx(module_idP, frameP, 0, eNB_indexP);
#else
  switch (rrc_rx_tx(module_idP,
      frameP,
      0,
      eNB_indexP,
      CC_id)) {
  case RRC_OK:
    break;
  case RRC_ConnSetup_failed:
    LOG_E(MAC,"RRCConnectionSetup failed, returning to IDLE state\n");
    vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
    stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
    return(CONNECTION_LOST);
    break;
  case RRC_PHY_RESYNCH:
    LOG_E(MAC,"RRC Loss of synch, returning PHY_RESYNCH\n");
    vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
    stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
    return(PHY_RESYNCH);
  case RRC_Handover_failed:
    LOG_N(MAC,"Handover failure for UE %d eNB_index %d\n",module_idP,eNB_indexP);
    //Invalid...need to add another MAC UE state for re-connection procedure
    mac_xface->phy_config_afterHO_ue(module_idP,0,eNB_indexP,(MobilityControlInfo_t *)NULL,1);
    //return(3);
    break;
  case RRC_HO_STARTED:
    LOG_I(MAC,"RRC handover, Instruct PHY to start the contention-free PRACH and synchronization\n");
    vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
    stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
    return(PHY_HO_PRACH);
  default:
    break;
  }
#endif

  // Check Contention resolution timer (put in a function later)
  if (UE_mac_inst[module_idP].RA_contention_resolution_timer_active == 1) {

      if (UE_mac_inst[module_idP].radioResourceConfigCommon)
        rach_ConfigCommon = &UE_mac_inst[module_idP].radioResourceConfigCommon->rach_ConfigCommon;
      else {
          LOG_E(MAC,"FATAL: radioResourceConfigCommon is NULL!!!\n");
	  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
	  stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
	  mac_xface->macphy_exit("");
	  //return(RRC_OK);
      }

      LOG_I(MAC,"Frame %d: Contention resolution timer %d/%d\n",frameP,UE_mac_inst[module_idP].RA_contention_resolution_cnt,
          ((1+rach_ConfigCommon->ra_SupervisionInfo.mac_ContentionResolutionTimer)<<3));

      UE_mac_inst[module_idP].RA_contention_resolution_cnt++;
      if (UE_mac_inst[module_idP].RA_contention_resolution_cnt ==
          ((1+rach_ConfigCommon->ra_SupervisionInfo.mac_ContentionResolutionTimer)<<3)) {
          UE_mac_inst[module_idP].RA_active = 0;
	  UE_mac_inst[module_idP].RA_contention_resolution_timer_active = 0;
          // Signal PHY to quit RA procedure
          LOG_E(MAC,"Module id %u Contention resolution timer expired, RA failed\n", module_idP);
          mac_xface->ra_failed(module_idP,0,eNB_indexP);
      }
  }


  // call SR procedure to generate pending SR and BSR for next PUCCH/PUSCH TxOp.  This should implement the procedures
  // outlined in Sections 5.4.4 an 5.4.5 of 36.321
  // Put this in another function

  // Get RLC status info and update Bj for all lcids that are active
  for (lcid=DCCH; lcid <= DTCH; lcid++ ) {
      if ((lcid == 0) ||(UE_mac_inst[module_idP].logicalChannelConfig[lcid])) {
          // meausre the Bj
          if ((directionP == SF_UL)&& (UE_mac_inst[module_idP].scheduling_info.Bj[lcid] >= 0)){
              if (UE_mac_inst[module_idP].logicalChannelConfig[lcid]->ul_SpecificParameters) {
                  bucketsizeduration = UE_mac_inst[module_idP].logicalChannelConfig[lcid]->ul_SpecificParameters->prioritisedBitRate * TTI;
                  bucketsizeduration_max = get_ms_bucketsizeduration(UE_mac_inst[module_idP].logicalChannelConfig[lcid]->ul_SpecificParameters->bucketSizeDuration);
              }
              else {
                  LOG_E(MAC,"[UE %d] lcid %d, NULL ul_SpecificParameters\n",module_idP,lcid);
                  mac_xface->macphy_exit("");
              }
              if ( UE_mac_inst[module_idP].scheduling_info.Bj[lcid] > bucketsizeduration_max )
                UE_mac_inst[module_idP].scheduling_info.Bj[lcid] = bucketsizeduration_max;
              else
                UE_mac_inst[module_idP].scheduling_info.Bj[lcid] = bucketsizeduration;
          }
          if (update_bsr(module_idP,frameP, lcid, UE_mac_inst[module_idP].scheduling_info.LCGID[lcid])) {
              UE_mac_inst[module_idP].scheduling_info.SR_pending= 1;
              LOG_D(MAC,"[UE %d][SR] Frame %d subframe %d SR for PUSCH is pending for LCGID %d with BSR level %d (%d bytes in RLC)\n",
                  module_idP, frameP,subframeP,UE_mac_inst[module_idP].scheduling_info.LCGID[lcid],
                  UE_mac_inst[module_idP].scheduling_info.BSR[UE_mac_inst[module_idP].scheduling_info.LCGID[lcid]],
                  UE_mac_inst[module_idP].scheduling_info.BSR_bytes[UE_mac_inst[module_idP].scheduling_info.LCGID[lcid]]);
          }
      }
  }
  // UE has no valid phy config dedicated ||  no valid/released  SR
  if ((UE_mac_inst[module_idP].physicalConfigDedicated == NULL)) {
      // cancel all pending SRs
      UE_mac_inst[module_idP].scheduling_info.SR_pending=0;
      UE_mac_inst[module_idP].ul_active=0;
      LOG_T(MAC,"[UE %d] Release all SRs \n", module_idP);
      vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
      stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
      return(CONNECTION_OK);
  }

  if ((UE_mac_inst[module_idP].physicalConfigDedicated->schedulingRequestConfig == NULL) ||
      (UE_mac_inst[module_idP].physicalConfigDedicated->schedulingRequestConfig->present == SchedulingRequestConfig_PR_release)){

      // initiate RA with CRNTI included in msg3 (no contention) as descibed in 36.321 sec 5.1.5

      // cancel all pending SRs
      UE_mac_inst[module_idP].scheduling_info.SR_pending=0;
      UE_mac_inst[module_idP].ul_active=0;
      LOG_T(MAC,"[UE %d] Release all SRs \n", module_idP);
  }

  // Put this in a function
  // Call PHR procedure as described in Section 5.4.6 in 36.321
  if (UE_mac_inst[module_idP].PHR_state == MAC_MainConfig__phr_Config_PR_setup){ // normal operation
      
    if (UE_mac_inst[module_idP].PHR_reconfigured == 1) { // upon (re)configuration of the power headroom reporting functionality by upper layers
      UE_mac_inst[module_idP].PHR_reporting_active = 1;
      UE_mac_inst[module_idP].PHR_reconfigured = 0;
    } else {
      //LOG_D(MAC,"PHR normal operation %d active %d \n", UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF, UE_mac_inst[module_idP].PHR_reporting_active);
      if ((UE_mac_inst[module_idP].scheduling_info.prohibitPHR_SF <= 0) &&
	  ((mac_xface->get_PL(module_idP,0,eNB_indexP) <  UE_mac_inst[module_idP].scheduling_info.PathlossChange_db) ||
	   (UE_mac_inst[module_idP].power_backoff_db[eNB_indexP] > UE_mac_inst[module_idP].scheduling_info.PathlossChange_db)))
	// trigger PHR and reset the timer later when the PHR report is sent
	UE_mac_inst[module_idP].PHR_reporting_active = 1;
      else if (UE_mac_inst[module_idP].PHR_reporting_active ==0 )
	UE_mac_inst[module_idP].scheduling_info.prohibitPHR_SF--;
      if (UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF <= 0 )
	// trigger PHR and reset the timer later when the PHR report is sent
	UE_mac_inst[module_idP].PHR_reporting_active = 1;
      else if (UE_mac_inst[module_idP].PHR_reporting_active == 0 )
	UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF--;
    }
  } else {    // release / nothing
    UE_mac_inst[module_idP].PHR_reporting_active = 0; // release PHR
  }
  //If the UE has UL resources allocated for new transmission for this TTI here:
  vcd_signal_dumper_dump_function_by_name(VCD_SIGNAL_DUMPER_FUNCTIONS_UE_SCHEDULER, VCD_FUNCTION_OUT);
  stop_meas(&UE_mac_inst[module_idP].ue_scheduler);
  return(CONNECTION_OK);
}

// to be improved
#ifdef CBA
extern int cba_backoff;
double uniform_rngen(int min, int max) {
  double random = (double)taus()/((double)0xffffffff);
  return (max - min) * random + min;
}

int cba_access(module_id_t module_idP,frame_t frameP,sub_frame_t subframe, uint8_t eNB_index,uint16_t buflen){

  mac_rlc_status_resp_t rlc_status;
  int header_offset=4;
  int rv =0;

  /*  
  if (( ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]<64))   ||
      ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]<64))   ||
      ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]<64)) )
      //  && (UE_mac_inst[module_idP].ul_active == 0) // check if the ul is acrtive
      && (UE_mac_inst[module_idP].cba_last_access[0] <= 0) ) { // backoff
      //  LOG_D(MAC,"[UE %d] Frame %d Subframe %d: the current CBA backoff is %d \n", module_idP, frameP, subframe,
      //  UE_mac_inst[module_idP].cba_last_access[0] );

      UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,40));
      LOG_D(MAC,"[UE %d] Frame %d Subframe %d: start a new CBA backoff  %d UL active state %d \n", module_idP, frameP, subframe,
          UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);

      rv=1;
  } else if (( ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]> 0 ))   ||
      ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]> 0 ))   ||
      ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]> 0 )) )
      // && (UE_mac_inst[module_idP].ul_active == 0) // check if the ul is acrtive
      && (UE_mac_inst[module_idP].cba_last_access[0]> 0) ){

      UE_mac_inst[module_idP].cba_last_access[0]-=1;
      LOG_D(MAC,"[UE %d] Frame %d Subframe %d: CBA backoff is decreased by one to %d UL active state %d \n",
          module_idP, frameP, subframe,
          UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);

  } else if (( ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1] == 0 ))   &&
               ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2] == 0 ))   &&
               ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3] ==  0 )) )
             && (UE_mac_inst[module_idP].cba_last_access[0]> 0) ){
    UE_mac_inst[module_idP].cba_last_access[0]-=1;
    }*/

  if ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID0]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID0]<64) )
    return 0;
 
  if ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1] <= 0 ) &&
      (UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2] <= 0 ) &&
      (UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3] <= 0 ) )
    return 0;

  if (cba_backoff == 0 ){ // apply probablisitc method
    UE_mac_inst[module_idP].cba_last_access[0]= uniform_rngen(0,1);
    if (uniform_rngen(0,1) > 0.6 ){
      LOG_I(MAC,"[UE %d] Frame %d Subframe %d: CBA probability-based backoff (%d), UL active state %d \n", module_idP, frameP, subframe,
	    cba_backoff,UE_mac_inst[module_idP].ul_active);
   
      rv=1;
    }
  }
  else {
    
    if (UE_mac_inst[module_idP].cba_last_access[0] <= 0) {
      UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,cba_backoff));
      
      LOG_I(MAC,"[UE %d] Frame %d Subframe %d: start a new CBA backoff  %d/%d UL active state %d \n", module_idP, frameP, subframe,
	    UE_mac_inst[module_idP].cba_last_access[0], cba_backoff,UE_mac_inst[module_idP].ul_active);
      
      rv = 1; 
      /*    
	    rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO, // eNB_index
	    DTCH,
	    0);
	    
	    if ((
	    //	(rlc_status.pdus_in_buffer > 0 )           && 
	    // (UE_mac_inst[module_idP].ul_active == 0)  && // check if the ul is acrtive
	    (rlc_status.head_sdu_is_segmented  == 0 )          &&	 
	    ((rlc_status.head_sdu_remaining_size_to_send + header_offset ) <= buflen )
	    )){
	    rv = 1;
	    
	    UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,30));
	    LOG_D(MAC,"[UE %d] Frame %d Subframe %d: start a new CBA backoff  %d UL active state %d \n", module_idP, frameP, subframe,
	    UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);
      */
    } else {
      UE_mac_inst[module_idP].cba_last_access[0]-=1;
      LOG_D(MAC,"[UE %d] Frame %d Subframe %d: wait for backoff to expire (%d) CBA UL active state %d \n",
	    module_idP, frameP, subframe,
	    UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);
    }	
  }

  return rv;
  /*
    if (( ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]<64))   ||
	  ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]<64))   ||
	  ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]>0)&&(UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]<64)) ) 
	//  && (UE_mac_inst[module_idP].ul_active == 0) // check if the ul is acrtive
	&& (UE_mac_inst[module_idP].cba_last_access[0] <= 0) ) {
  
      UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,cba_backoff));
      
      LOG_I(MAC,"[UE %d] Frame %d Subframe %d: start a new CBA backoff  %d/%d UL active state %d \n", module_idP, frameP, subframe,
	    UE_mac_inst[module_idP].cba_last_access[0], cba_backoff,UE_mac_inst[module_idP].ul_active);
      
      rv = 1; 
  
      rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO, // eNB_index
				    DTCH,
				    0);
    
    if ((
	 //	(rlc_status.pdus_in_buffer > 0 )           && 
	// (UE_mac_inst[module_idP].ul_active == 0)  && // check if the ul is acrtive
	 (rlc_status.head_sdu_is_segmented  == 0 )          &&	 
	 ((rlc_status.head_sdu_remaining_size_to_send + header_offset ) <= buflen )
	 )){
      rv = 1;
      
      UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,30));
      LOG_D(MAC,"[UE %d] Frame %d Subframe %d: start a new CBA backoff  %d UL active state %d \n", module_idP, frameP, subframe,
	    UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);
    } else 
      UE_mac_inst[module_idP].cba_last_access[0]= round(uniform_rngen(1,5));
  

    } else if (( ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID1]> 0 ))   ||
		 ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID2]> 0 ))   ||
		 ((UE_mac_inst[module_idP].scheduling_info.BSR[LCGID3]> 0 )) )
	       // && (UE_mac_inst[module_idP].ul_active == 0) // check if the ul is acrtive
	       && (UE_mac_inst[module_idP].cba_last_access[0]> 0) )
      {
	
	UE_mac_inst[module_idP].cba_last_access[0]-=1;
	LOG_D(MAC,"[UE %d] Frame %d Subframe %d: wait for backoff to expire (%d) CBA UL active state %d \n",
	      module_idP, frameP, subframe,
	      UE_mac_inst[module_idP].cba_last_access[0], UE_mac_inst[module_idP].ul_active);
      }
  }
*/  
  
}
#endif

int get_bsr_lcgid (module_id_t module_idP){
  int lcgid, lcgid_tmp=-1;
  int num_active_lcgid = 0;

  for (lcgid = 0 ; lcgid < MAX_NUM_LCGID; lcgid++){
      if (UE_mac_inst[module_idP].scheduling_info.BSR[lcgid] > 0 ){
          lcgid_tmp = lcgid;
          num_active_lcgid+=1;
      }
  }
  if (num_active_lcgid == 0)
      return -1;
  else if (num_active_lcgid == 1)
      return lcgid_tmp;
  else
      return MAX_NUM_LCGID;
}

uint8_t get_bsr_len (module_id_t module_idP, uint16_t buflen) {

  int lcgid=0;
  uint8_t bsr_len=0,  num_lcgid=0;
  int pdu = 0;


  for (lcgid=0; lcgid < MAX_NUM_LCGID; lcgid++ ) {
      if (UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcgid] > 0 )
        pdu += (UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcgid] +  bsr_len + 2); //2 = sizeof(SCH_SUBHEADER_SHORT)
      if (UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcgid] > 128 ) // long header size: adjust the header size
          pdu += 1;
      // current phy buff can not transport all sdu for this lcgid -> transmit a bsr for this lcgid

      if ( (pdu > buflen) &&  (UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcgid] > 0 ) ){
          num_lcgid +=1;
          bsr_len = (num_lcgid >= 2 ) ? sizeof(BSR_LONG) :  sizeof(BSR_SHORT) ;
      }
      LOG_D(MAC,"BSR Bytes %d for lcgid %d bsr len %d num lcgid %d\n", UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcgid], lcgid, bsr_len, num_lcgid);
  }
  if ( bsr_len > 0 )
    LOG_D(MAC,"[UE %d] Prepare a %s (Transport Block Size %d, MAC pdu Size %d) \n",
        module_idP, map_int_to_str(BSR_names, bsr_len), buflen, pdu);
  return bsr_len;
}


boolean_t  update_bsr(module_id_t module_idP, frame_t frameP, uint8_t lcid, uint8_t lcg_id){

  mac_rlc_status_resp_t rlc_status;
  boolean_t sr_pending = FALSE;


  if ((lcg_id < 0) || (lcg_id >= MAX_NUM_LCGID) )
    return sr_pending;
  // fixme: need a better way to reset
  if ((lcid == DCCH) || (lcid == DTCH)){
      UE_mac_inst[module_idP].scheduling_info.BSR[lcg_id]=0;
      UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcg_id]=0;
  }
  //  for (lcid =0 ; lcid < MAX_NUM_LCID; lcid++) {
  if (UE_mac_inst[module_idP].scheduling_info.LCGID[lcid] == lcg_id) {
      rlc_status = mac_rlc_status_ind(0, module_idP,frameP,ENB_FLAG_NO,MBMS_FLAG_NO,
				      lcid,
				      0);
      if (rlc_status.bytes_in_buffer > 0 ) {
	sr_pending = TRUE;
	UE_mac_inst[module_idP].scheduling_info.LCID_status[lcid] = LCID_NOT_EMPTY;
	UE_mac_inst[module_idP].scheduling_info.BSR[lcg_id] += locate (BSR_TABLE,BSR_TABLE_SIZE, rlc_status.bytes_in_buffer);
	UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcg_id] += rlc_status.bytes_in_buffer;
	// UE_mac_inst[module_idP].scheduling_info.BSR_short_lcid = lcid; // only applicable to short bsr
	LOG_D(MAC,"[UE %d] BSR level %d (LCGID %d, rlc buffer %d byte)\n",
              module_idP, UE_mac_inst[module_idP].scheduling_info.BSR[lcg_id],lcg_id,  UE_mac_inst[module_idP].scheduling_info.BSR_bytes[lcg_id]);
      }
      else
        UE_mac_inst[module_idP].scheduling_info.LCID_status[lcid]=LCID_EMPTY;
  }
  //}
  return sr_pending;
}

uint8_t locate (const uint32_t *table, int size, int value){

  uint8_t ju, jm, jl;
  int ascend;

  DevAssert( size > 0 );
  DevAssert( size <= 256 );

  if (value == 0) return 0; //elseif (value > 150000) return 63;

  jl = 0;        // lower bound
  ju = size - 1; // upper bound
  ascend = (table[ju] >= table[jl]) ? 1 : 0; // determine the order of the the table:  1 if ascending order of table, 0 otherwise

  while (ju-jl > 1) { //If we are not yet done,
      jm = (ju+jl) >> 1; //compute a midpoint,
      if ((value >= table[jm]) == ascend)
        jl=jm; // replace the lower limit
      else
        ju=jm; //replace the upper limit
      LOG_T(MAC,"[UE] searching BSR index %d for (BSR TABLE %d < value %d)\n", jm, table[jm], value);
  }
  if (value == table[jl]) return jl;
  else                    return jl+1; //equally  ju

}

int get_sf_periodicBSRTimer(uint8_t sf_offset){

  switch (sf_offset) {
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf5:
    return 5;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf10:
    return 10;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf16:
    return 16;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf20:
    return 20;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf32:
    return 32;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf40:
    return 40;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf64:
    return 64;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf80:
    return 80;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf128:
    return 128;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf160:
    return 160;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf320:
    return 320;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf640:
    return 640;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf1280:
    return 1280;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_sf2560:
    return 2560;
    break;
  case MAC_MainConfig__ul_SCH_Config__periodicBSR_Timer_infinity:
  default:
    return -1;
    break;
  }
}

int get_sf_retxBSRTimer(uint8_t sf_offset){

  switch (sf_offset) {
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf320:
    return 320;
    break;
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf640:
    return 640;
    break;
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf1280:
    return 1280;
    break;
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf2560:
    return 2560;
    break;
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf5120:
    return 5120;
    break;
  case MAC_MainConfig__ul_SCH_Config__retxBSR_Timer_sf10240:
    return 10240;
    break;
  default:
    return -1;
    break;
  }
}
int get_ms_bucketsizeduration(uint8_t bucketsizeduration){

  switch (bucketsizeduration) {
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms50:
    return 50;
    break;
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms100:
    return 100;
    break;
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms150:
    return 150;
    break;
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms300:
    return 300;
    break;
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms500:
    return 500;
    break;
  case LogicalChannelConfig__ul_SpecificParameters__bucketSizeDuration_ms1000:
    return 1000;
    break;
  default:
    return 0;
    break;
  }
}

void update_phr(module_id_t module_idP,int CC_id){

  if (CC_id>0) {
    LOG_E(MAC,"Transmission on secondary CCs is not supported yet\n");
    mac_xface->macphy_exit("MAC FATAL  CC_id>0");
    return;
  }

  UE_mac_inst[module_idP].PHR_reporting_active =0;
  UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF =  get_sf_perioidicPHR_Timer(UE_mac_inst[module_idP].scheduling_info.periodicPHR_Timer);
  UE_mac_inst[module_idP].scheduling_info.prohibitPHR_SF =  get_sf_prohibitPHR_Timer(UE_mac_inst[module_idP].scheduling_info.prohibitPHR_Timer);
  // LOG_D(MAC,"phr %d %d\n ",UE_mac_inst[module_idP].scheduling_info.periodicPHR_SF, UE_mac_inst[module_idP].scheduling_info.prohibitPHR_SF);
}
uint8_t get_phr_mapping (module_id_t module_idP, int CC_id, uint8_t eNB_index){

  if (CC_id>0) {
    LOG_E(MAC,"Transmission on secondary CCs is not supported yet\n");
    mac_xface->macphy_exit("MAC FATAL  CC_id>0");
    return 0; // not reached
  }
  //power headroom reporting range is from -23 ...+40 dB, as described in 36313
  //note: mac_xface->get_Po_NOMINAL_PUSCH(module_idP) is float
  if (mac_xface->get_PHR(module_idP,CC_id,eNB_index) < -23)
    return 0;
  else if (mac_xface->get_PHR(module_idP,CC_id,eNB_index) >= 40)
    return 63;
  else  // -23 to 40
    return  (uint8_t) mac_xface->get_PHR(module_idP,CC_id,eNB_index) + PHR_MAPPING_OFFSET;

}
int get_sf_perioidicPHR_Timer(uint8_t perioidicPHR_Timer){
  return (perioidicPHR_Timer+1)*10;
}


int get_sf_prohibitPHR_Timer(uint8_t prohibitPHR_Timer){
  return (prohibitPHR_Timer)*10;
}

int get_db_dl_PathlossChange(uint8_t dl_PathlossChange){
  switch (dl_PathlossChange){
  case MAC_MainConfig__phr_Config__setup__dl_PathlossChange_dB1:
    return 1;
    break;
  case MAC_MainConfig__phr_Config__setup__dl_PathlossChange_dB3:
    return 3;
    break;
  case MAC_MainConfig__phr_Config__setup__dl_PathlossChange_dB6:
    return 6;
    break;
  case MAC_MainConfig__phr_Config__setup__dl_PathlossChange_infinity:
  default:
    return -1;
    break;
  }
}