/*
 * 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

#include "T.h"

#define ENABLE_MAC_PAYLOAD_DEBUG
#define DEBUG_eNB_SCHEDULER 1

int choose(int n,int k)
{
  int res  = 1;
  int res2 = 1;
  int i;

  if (k>n) return(0);
  if (n==k) return(1);

  for (i=n;i>k;i--) res*=i;
  for (i=2;i<=(n-k);i++) res2*=i;

  return(res/res2);
}

// Patented algorithm from Yang et al, US Patent 2009, "Channel Quality Indexing and Reverse Indexing"
void reverse_index(int N,int M,int r,int *v)
{
  int BaseValue=0;
  int IncreaseValue,ThresholdValue;
  int sumV;
  int i;

  r = choose(N,M) - 1 - r;
  memset((void*)v,0,M*sizeof(int));

  sumV=0;
  i=M;
  while (i>0 && r>0) {
    IncreaseValue = choose(N-M+1-sumV-v[i-1]+i-2,i-1);
    ThresholdValue = BaseValue+IncreaseValue;
    if (r>=ThresholdValue) {
      v[i-1]++;
      BaseValue=ThresholdValue;
    }
    else {
      r=r-BaseValue;
      sumV+=v[i-1];
      i--;
      BaseValue=0;
    }
  }
}

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

uint8_t getQm(uint8_t mcs)
{
  if (mcs<10) return(2);
  else if (mcs<17) return(4);
  else return (6);
}

void get_Msg3alloc(COMMON_channels_t *cc,

                   sub_frame_t current_subframe,
                   frame_t current_frame,
                   frame_t *frame,
                   sub_frame_t *subframe)
{
  // Fill in other TDD Configuration!!!!

  if (cc->tdd_Config==NULL) { // FDD
    *subframe = current_subframe+6;

    if (*subframe>9) {
      *subframe = *subframe-10;
      *frame = (current_frame+1) & 1023;
    } else {
      *frame=current_frame;
    }
  } else { // TDD
    if (cc->tdd_Config->subframeAssignment == 1) {
      switch (current_subframe) {

      case 0:
        *subframe = 7;
        *frame = current_frame;
        break;

      case 4:
        *subframe = 2;
        *frame = (current_frame+1) & 1023;
        break;

      case 5:
        *subframe = 2;
        *frame = (current_frame+1) & 1023;
        break;

      case 9:
        *subframe = 7;
        *frame = (current_frame+1) & 1023;
        break;
      }
    } else if (cc->tdd_Config->subframeAssignment == 3) {
      switch (current_subframe) {

      case 0:
      case 5:
      case 6:
        *subframe = 2;
        *frame = (current_frame+1) & 1023;
        break;

      case 7:
        *subframe = 3;
        *frame = (current_frame+1) & 1023;
        break;

      case 8:
        *subframe = 4;
        *frame = (current_frame+1) & 1023;
        break;

      case 9:
        *subframe = 2;
        *frame = (current_frame+2) & 1023;
        break;
      }
    } else if (cc->tdd_Config->subframeAssignment == 4) {
      switch (current_subframe) {

      case 0:
      case 4:
      case 5:
      case 6:
        *subframe = 2;
        *frame = (current_frame+1) & 1023;
        break;

      case 7:
        *subframe = 3;
        *frame = (current_frame+1) & 1023;
        break;

      case 8:
      case 9:
        *subframe = 2;
        *frame = (current_frame+2) & 1023;
        break;
      }
    } else if (cc->tdd_Config->subframeAssignment == 5) {
      switch (current_subframe) {

      case 0:
      case 4:
      case 5:
      case 6:
        *subframe = 2;
        *frame = (current_frame+1) & 1023;
        break;

      case 7:
      case 8:
      case 9:
        *subframe = 2;
        *frame = (current_frame+2) & 1023;
        break;
      }
    }
  }
}



void get_Msg3allocret(COMMON_channels_t *cc,
                      sub_frame_t current_subframe,
                      frame_t current_frame,
                      frame_t *frame,
                      sub_frame_t *subframe)
{
  if (cc->tdd_Config == NULL) { //FDD
    /* always retransmit in n+8 */
    *subframe = current_subframe + 8;

    if (*subframe > 9) {
      *subframe = *subframe - 10;
      *frame = (current_frame + 1) & 1023;
    } else {
      *frame = current_frame;
    }
  } else {
    if (cc->tdd_Config->subframeAssignment == 1) {
      // original PUSCH in 2, PHICH in 6 (S), ret in 2
      // original PUSCH in 3, PHICH in 9, ret in 3
      // original PUSCH in 7, PHICH in 1 (S), ret in 7
      // original PUSCH in 8, PHICH in 4, ret in 8
      *frame = (current_frame+1) & 1023;
    } else if (cc->tdd_Config->subframeAssignment == 3) {
      // original PUSCH in 2, PHICH in 8, ret in 2 next frame
      // original PUSCH in 3, PHICH in 9, ret in 3 next frame
      // original PUSCH in 4, PHICH in 0, ret in 4 next frame
      *frame=(current_frame+1) & 1023;
    } else if (cc->tdd_Config->subframeAssignment == 4) {
        // original PUSCH in 2, PHICH in 8, ret in 2 next frame
        // original PUSCH in 3, PHICH in 9, ret in 3 next frame
        *frame=(current_frame+1) & 1023;
    } else if (cc->tdd_Config->subframeAssignment == 5) {
        // original PUSCH in 2, PHICH in 8, ret in 2 next frame
        *frame=(current_frame+1) & 1023;
    }
  }
}

uint8_t subframe2harqpid(COMMON_channels_t *cc,frame_t frame,sub_frame_t subframe)
{
  uint8_t ret = 255;

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

  if (cc->tdd_Config == NULL) { // FDD
    ret = (((frame<<1)+subframe)&7);
  } else {
    switch (cc->tdd_Config->subframeAssignment) {
    case 1:
      if ((subframe==2) ||
          (subframe==3) ||
          (subframe==7) ||
          (subframe==8))
        switch (subframe) {
        case 2:
        case 3:
          ret = (subframe-2);
          break;

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

        default:
          AssertFatal(1==0,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,(int)cc->tdd_Config->subframeAssignment);
          break;
        }

      break;

    case 2:
      AssertFatal((subframe==2) || (subframe==7),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,(int)cc->tdd_Config->subframeAssignment);

      ret = (subframe/7);
      break;

    case 3:
      AssertFatal((subframe>1) && (subframe<5),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframe-2);
      break;

    case 4:
      AssertFatal((subframe>1) && (subframe<4),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframe-2);
      break;

    case 5:
      AssertFatal(subframe==2,
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframe,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframe-2);
      break;

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

uint8_t get_Msg3harqpid(COMMON_channels_t *cc,
                        frame_t frame,
                        sub_frame_t current_subframe)
{
  uint8_t ul_subframe=0;
  uint32_t ul_frame=0;

  if (cc->tdd_Config == NULL) { // FDD
    ul_subframe = (current_subframe>3) ? (current_subframe-4) : (current_subframe+6);
    ul_frame    = (current_subframe>3) ? ((frame+1)&1023) : frame;
  } else {
    switch (cc->tdd_Config->subframeAssignment) {
    case 1:
      switch (current_subframe) {
      case 9:
      case 0:
        ul_subframe = 7;
        break;

      case 5:
      case 7:
        ul_subframe = 2;
        break;

      }

      break;

    case 3:
      switch (current_subframe) {
      case 0:
      case 5:
      case 6:
        ul_subframe = 2;
        break;

      case 7:
        ul_subframe = 3;
        break;

      case 8:
        ul_subframe = 4;
        break;

      case 9:
        ul_subframe = 2;
        break;
      }

      break;

    case 4:
      switch (current_subframe) {
      case 0:
      case 5:
      case 6:
      case 8:
      case 9:
        ul_subframe = 2;
        break;

      case 7:
        ul_subframe = 3;
        break;
      }

      break;

    case 5:
      ul_subframe =2;
      break;

    default:
      LOG_E(PHY,"get_Msg3_harq_pid: Unsupported TDD configuration %d\n",(int)cc->tdd_Config->subframeAssignment);
      AssertFatal(1==0,"get_Msg3_harq_pid: Unsupported TDD configuration");
      break;
    }
  }

  return(subframe2harqpid(cc,ul_frame,ul_subframe));
}

uint32_t pdcchalloc2ulframe(COMMON_channels_t *ccP,uint32_t frame, uint8_t n)
{
  uint32_t ul_frame;

  if ((ccP->tdd_Config) &&
      (ccP->tdd_Config->subframeAssignment == 1) &&
      ((n==1)||(n==6))) // tdd_config 0,1 SF 1,5
    ul_frame = (frame + (n==1 ? 0 : 1));
  else if ((ccP->tdd_Config) &&
           (ccP->tdd_Config->subframeAssignment == 6) &&
           ((n==0)||(n==1)||(n==5)||(n==6)))
    ul_frame = (frame + (n>=5 ? 1 : 0));
  else if ((ccP->tdd_Config) &&
           (ccP->tdd_Config->subframeAssignment == 6) &&
           (n==9)) // tdd_config 6 SF 9
    ul_frame = (frame+1);
  else
    ul_frame = (frame+(n>=6 ? 1 : 0));

  LOG_D(PHY, "frame %d subframe %d: PUSCH frame = %d\n", frame, n, ul_frame);
  return ul_frame;
}

uint8_t pdcchalloc2ulsubframe(COMMON_channels_t *ccP,uint8_t n)
{
  uint8_t ul_subframe;

  if ((ccP->tdd_Config) &&
      (ccP->tdd_Config->subframeAssignment == 1) &&
      ((n==1)||(n==6))) // tdd_config 0,1 SF 1,5
    ul_subframe = ((n+6)%10);
  else if ((ccP->tdd_Config) &&
           (ccP->tdd_Config->subframeAssignment == 6) &&
           ((n==0)||(n==1)||(n==5)||(n==6)))
    ul_subframe = ((n+7)%10);
  else if ((ccP->tdd_Config) &&
           (ccP->tdd_Config->subframeAssignment == 6) &&
           (n==9)) // tdd_config 6 SF 9
    ul_subframe = ((n+5)%10);
  else
    ul_subframe = ((n+4)%10);

  LOG_D(PHY, "subframe %d: PUSCH subframe = %d\n", n, ul_subframe);
  return ul_subframe;
}

int is_UL_sf(COMMON_channels_t *ccP,sub_frame_t subframeP)
{
  // if FDD return dummy value
  if (ccP->tdd_Config == NULL)
    return(0);

  switch (ccP->tdd_Config->subframeAssignment) {
  case 1:
    switch (subframeP) {
    case 0:
    case 4:
    case 5:
    case 9:
      return(0);
      break;

    case 2:
    case 3:
    case 7:
    case 8:
      return(1);
      break;

    default:
      return(0);
      break;
    }
    break;

  case 3:
    if  ((subframeP<=1) || (subframeP>=5))
      return(0);
    else if ((subframeP>1) && (subframeP < 5))
      return(1);
    else AssertFatal(1==0,"Unknown subframe number\n");
    break;

  case 4:
    if  ((subframeP<=1) || (subframeP>=4))
      return(0);
    else if ((subframeP>1) && (subframeP < 4))
      return(1);
    else AssertFatal(1==0,"Unknown subframe number\n");
    break;

  case 5:
    if  ((subframeP<=1) || (subframeP>=3))
      return(0);
    else if ((subframeP>1) && (subframeP < 3))
      return(1);
    else AssertFatal(1==0,"Unknown subframe number\n");
    break;

  default:
    AssertFatal(1==0,"subframe %d Unsupported TDD configuration %d\n",
                subframeP,(int)ccP->tdd_Config->subframeAssignment);
    break;
  }
}

uint16_t get_pucch1_absSF(COMMON_channels_t *cc,uint16_t dlsch_absSF)
{
  uint16_t sf,f,nextf;

  if (cc->tdd_Config==NULL) { //FDD n+4
    return((dlsch_absSF + 4)%10240);
  }
  else {
    sf    = dlsch_absSF%10;
    f     = dlsch_absSF/10;
    nextf = (f+1)&1023;

    switch (cc->tdd_Config->subframeAssignment) {
    case 0:
      AssertFatal(1==0,"SFA 0 to be filled in now, :-)\n");
      break;
    case 1:
      if      ((sf==5) || (sf==6)) return((10*nextf) + 2);                                        // ACK/NAK in SF 2 next frame
      else if (sf==9)              return((10*nextf) + 3);                                        // ACK/NAK in SF 3 next frame
      else if ((sf==0) || (sf==1)) return((10*f) + 2);                                            // ACK/NAK in SF 7 same frame
      else AssertFatal(1==0,"Impossible dlsch subframe %d for TDD configuration 3\n",sf);
      break;
    case 2:
      if      ((sf==4) || (sf==5) || (sf==6) || (sf==8)) return((10*nextf) + 2);                  // ACK/NAK in SF 2 next frame
      else if (sf==9)                                    return((10*nextf) + 7);                  // ACK/NAK in SF 7 next frame
      else if ((sf==0) || (sf==1) || (sf==3))            return((10*f) + 7);                      // ACK/NAK in SF 7 same frame
      else AssertFatal(1==0,"Impossible dlsch subframe %d for TDD configuration 3\n",sf);
      break;
    case 3:
      if      ((sf==5) || (sf==6) || (sf==7) || (sf==8) || (sf==9)) return((10*nextf) + (sf>>1)); // ACK/NAK in 2,3,4 resp. next frame
      else if (sf==1)                                               return((10*nextf) + 2);       // ACK/NAK in 2 next frame
      else if (sf==0)                                               return((10*f) + 4);           // ACK/NAK in 4 same frame
      else AssertFatal(1==0,"Impossible dlsch subframe %d for TDD configuration 3\n",sf);
      break;
    case 4:
      if      ((sf==6) || (sf==7) || (sf==8) || (sf==9)) return(((10*nextf) + 3)%10240);          // ACK/NAK in SF 3 next frame
      else if ((sf==0) || (sf==1) || (sf==4) || (sf==5)) return(((10*nextf) + 2)%10240);          // ACK/NAK in SF 2 next frame
      else AssertFatal(1==0,"Impossible dlsch subframe %d for TDD configuration 4\n",sf);
      break;
    case 5:
      if      ((sf==0) || (sf==1) || (sf==3) || (sf==4) || (sf==5) || (sf==6) || (sf==7) || (sf==8)) return(((10*nextf) + 2)%10240);     // ACK/NAK in SF 3 next frame
      else if (sf==9)                                                                                return(((10*(1+nextf)) + 2)%10240); // ACK/NAK in SF 2 next frame
      else AssertFatal(1==0,"Impossible dlsch subframe %d for TDD configuration 5\n",sf);
      break;
    case 6:
      AssertFatal(1==0,"SFA 6 To be filled in now, :-)\n");
      break;
    default:
      AssertFatal(1==0,"Illegal TDD subframe Assigment %d\n",(int)cc->tdd_Config->subframeAssignment);
      break;
    }
  }
  AssertFatal(1==0,"Shouldn't get here\n");
}

void get_srs_pos(COMMON_channels_t *cc,uint16_t isrs,uint16_t *psrsPeriodicity,uint16_t *psrsOffset)
{
  if(cc->tdd_Config) { // TDD
    AssertFatal(isrs>=10,"2 ms SRS periodicity not supported");

    if ((isrs>9)&&(isrs<15)) {
      *psrsPeriodicity=5;
      *psrsOffset=isrs-10;
    }
    if ((isrs>14)&&(isrs<25)) {
      *psrsPeriodicity=10;
      *psrsOffset=isrs-15;
    }
    if ((isrs>24)&&(isrs<45)) {
      *psrsPeriodicity=20;
      *psrsOffset=isrs-25;
    }
    if ((isrs>44)&&(isrs<85)) {
      *psrsPeriodicity=40;
      *psrsOffset=isrs-45;
    }
    if ((isrs>84)&&(isrs<165)) {
      *psrsPeriodicity=80;
      *psrsOffset=isrs-85;
    }
    if ((isrs>164)&&(isrs<325)) {
      *psrsPeriodicity=160;
      *psrsOffset=isrs-165;
    }
    if ((isrs>324)&&(isrs<645)) {
      *psrsPeriodicity=320;
      *psrsOffset=isrs-325;
    }

    AssertFatal(isrs<=644,"Isrs out of range %d>644\n",isrs);
  } // TDD
  else { // FDD
    if (isrs<2) {
      *psrsPeriodicity=2;
      *psrsOffset=isrs;
    }
    if ((isrs>1)&&(isrs<7)) {
      *psrsPeriodicity=5;
      *psrsOffset=isrs-2;
    }
    if ((isrs>6)&&(isrs<17)) {
      *psrsPeriodicity=10;
      *psrsOffset=isrs-7;
    }
    if ((isrs>16)&&(isrs<37)) {
      *psrsPeriodicity=20;
      *psrsOffset=isrs-17;
    }
    if ((isrs>36)&&(isrs<77)) {
      *psrsPeriodicity=40;
      *psrsOffset=isrs-37;
    }
    if ((isrs>76)&&(isrs<157)) {
      *psrsPeriodicity=80;
      *psrsOffset=isrs-77;
    }
    if ((isrs>156)&&(isrs<317)) {
      *psrsPeriodicity=160;
      *psrsOffset=isrs-157;
    }
    if ((isrs>316)&&(isrs<637)) {
      *psrsPeriodicity=320;
      *psrsOffset=isrs-317;
    }
    AssertFatal(isrs<=636,"Isrs out of range %d>636\n",isrs);
  }
}

void get_csi_params(COMMON_channels_t *cc,struct CQI_ReportPeriodic *cqi_ReportPeriodic,uint16_t *Npd,uint16_t *N_OFFSET_CQI,int *H)
{
  uint16_t cqi_PMI_ConfigIndex = cqi_ReportPeriodic->choice.setup.cqi_pmi_ConfigIndex;
  uint8_t Jtab[6] = {0,2,2,3,4,4};

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

  if (cc->tdd_Config==NULL) { //FDD
    if (cqi_PMI_ConfigIndex <= 1) {        // 2 ms CQI_PMI period
      *Npd = 2;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex;
    } else if (cqi_PMI_ConfigIndex <= 6) { // 5 ms CQI_PMI period
      *Npd = 5;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-2;
    } else if (cqi_PMI_ConfigIndex <=16) { // 10ms CQI_PMI period
      *Npd = 10;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-7;
    } else if (cqi_PMI_ConfigIndex <= 36) { // 20 ms CQI_PMI period
      *Npd = 20;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-17;
    } else if (cqi_PMI_ConfigIndex <= 76) { // 40 ms CQI_PMI period
      *Npd = 40;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-37;
    } else if (cqi_PMI_ConfigIndex <= 156) { // 80 ms CQI_PMI period
      *Npd = 80;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-77;
    } else if (cqi_PMI_ConfigIndex <= 316) { // 160 ms CQI_PMI period
      *Npd = 160;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-157;
    }
    else if (cqi_PMI_ConfigIndex > 317) {

      if (cqi_PMI_ConfigIndex <= 349) { // 32 ms CQI_PMI period
        *Npd = 32;
      *N_OFFSET_CQI = cqi_PMI_ConfigIndex-318;
      }
      else if (cqi_PMI_ConfigIndex <= 413) { // 64 ms CQI_PMI period
        *Npd = 64;
        *N_OFFSET_CQI = cqi_PMI_ConfigIndex-350;
      }
      else if (cqi_PMI_ConfigIndex <= 541) { // 128 ms CQI_PMI period
        *Npd = 128;
        *N_OFFSET_CQI = cqi_PMI_ConfigIndex-414;
      }
    }
  }
  else { // TDD
   if (cqi_PMI_ConfigIndex == 0) {        // all UL subframes
     *Npd = 1;
     *N_OFFSET_CQI = 0;
   } else if (cqi_PMI_ConfigIndex <= 6) { // 5 ms CQI_PMI period
     *Npd = 5;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-1;
   } else if (cqi_PMI_ConfigIndex <=16) { // 10ms CQI_PMI period
     *Npd = 10;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-6;
   } else if (cqi_PMI_ConfigIndex <= 36) { // 20 ms CQI_PMI period
     *Npd = 20;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-16;
   } else if (cqi_PMI_ConfigIndex <= 76) { // 40 ms CQI_PMI period
     *Npd = 40;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-36;
   } else if (cqi_PMI_ConfigIndex <= 156) { // 80 ms CQI_PMI period
     *Npd = 80;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-76;
   } else if (cqi_PMI_ConfigIndex <= 316) { // 160 ms CQI_PMI period
     *Npd = 160;
     *N_OFFSET_CQI = cqi_PMI_ConfigIndex-156;
   }
  }

  // get H
  if (cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_subbandCQI)
    *H = 1+(Jtab[cc->mib->message.dl_Bandwidth]*cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.choice.subbandCQI.k);
  else
    *H=1;
}

uint8_t get_dl_cqi_pmi_size_pusch(COMMON_channels_t *cc,uint8_t tmode,uint8_t ri, CQI_ReportModeAperiodic_t *cqi_ReportModeAperiodic)
{
  int Ntab[6]       = {0,4,7,9,10,13};
  int N             = Ntab[cc->mib->message.dl_Bandwidth];
  int Ltab_uesel[6] = {0,6,9,13,15,18};
  int L             = Ltab_uesel[cc->mib->message.dl_Bandwidth];

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

  switch (*cqi_ReportModeAperiodic) {
  case CQI_ReportModeAperiodic_rm12:
    AssertFatal(tmode==4 || tmode==6 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm12\n",tmode);
    AssertFatal(cc->p_eNB<=4,"only up to 4 antenna ports supported here\n");
    if (ri==1 && cc->p_eNB==2) return(4+(N<<1));
    else if (ri==2 && cc->p_eNB==2) return(8+N);
    else if (ri==1 && cc->p_eNB==4) return(4+(N<<2));
    else if (ri>1  && cc->p_eNB==4) return(8+(N<<2));
    break;
  case CQI_ReportModeAperiodic_rm20:
    // Table 5.2.2.6.3-1 (36.212)
    AssertFatal(tmode==1 || tmode==2 || tmode==3 || tmode==7 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm20\n",tmode);
    AssertFatal(tmode!=9 && tmode!=10,"TM9/10 will be handled later for CQI_ReportModeAperiodic_rm20\n");
    return(4+2+L);
    break;
  case CQI_ReportModeAperiodic_rm22:
    // Table 5.2.2.6.3-2 (36.212)
    AssertFatal(tmode==4 || tmode==6 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm22\n",tmode);
    AssertFatal(tmode!=9 && tmode!=10,"TM9/10 will be handled later for CQI_ReportModeAperiodic_rm22\n");
    if (ri==1 && cc->p_eNB==2) return(4+2+0+0+L+4);
    if (ri==2 && cc->p_eNB==2) return(4+2+4+2+L+2);
    if (ri==1 && cc->p_eNB==4) return(4+2+0+0+L+8);
    if (ri>=2 && cc->p_eNB==4) return(4+2+4+2+L+8);
    break;
  case CQI_ReportModeAperiodic_rm30:
    // Table 5.2.2.6.2-1 (36.212)
    AssertFatal(tmode==1 || tmode==2 || tmode==3 || tmode==7 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm30\n",tmode);
    AssertFatal(tmode!=8 && tmode!=9 && tmode!=10,"TM8/9/10 will be handled later for CQI_ReportModeAperiodic_rm30\n");
    return(4+(N<<1));
    break;
  case CQI_ReportModeAperiodic_rm31:
    // Table 5.2.2.6.2-2 (36.212)
    AssertFatal(tmode==4 || tmode==6 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm31\n",tmode);
    AssertFatal(tmode!=8 && tmode!=9 && tmode!=10,"TM8/9/10 will be handled later for CQI_ReportModeAperiodic_rm31\n");
    if (ri==1 && cc->p_eNB==2) return(4+(N<<1)+0+0+2);
    else if (ri==2 && cc->p_eNB==2) return(4+(N<<1)+4+(N<<1)+1);
    else if (ri==1 && cc->p_eNB==4) return(4+(N<<1)+0+0+4);
    else if (ri>=2 && cc->p_eNB==4) return(4+(N<<1)+4+(N<<1)+4);
    break;
  case CQI_ReportModeAperiodic_rm32_v1250:
    AssertFatal(tmode==4 || tmode==6 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm32\n",tmode);
    AssertFatal(1==0,"CQI_ReportModeAperiodic_rm32_v1250 not supported yet\n");
    break;
  case CQI_ReportModeAperiodic_rm10_v1310:
    // Table 5.2.2.6.1-1F/G (36.212)
    if (ri==1) return(4); // F
    else return(7); // G
    break;
  case CQI_ReportModeAperiodic_rm11_v1310:
    // Table 5.2.2.6.1-1H (36.212)
    AssertFatal(tmode==4 || tmode==6 || tmode==8 || tmode==9 || tmode==10,"Illegal TM (%d) for CQI_ReportModeAperiodic_rm11\n",tmode);
    AssertFatal(cc->p_eNB<=4,"only up to 4 antenna ports supported here\n");
    if (ri==1 && cc->p_eNB==2) return(4+0+2);
    else if (ri==2 && cc->p_eNB==2) return(4+4+1);
    else if (ri==1 && cc->p_eNB==4) return(4+0+4);
    else if (ri>1  && cc->p_eNB==4) return(4+4+4);

    break;
  }
  AssertFatal(1==0,"Shouldn't get here\n");
  return(0);
}

uint8_t get_rel8_dl_cqi_pmi_size(UE_sched_ctrl *sched_ctl,int CC_idP,COMMON_channels_t *cc,uint8_t tmode, struct CQI_ReportPeriodic *cqi_ReportPeriodic)
{
  int no_pmi=0;
  //    Ltab[6] = {0,log2(15/4/2),log2(25/4/2),log2(50/6/3),log2(75/8/4),log2(100/8/4)};

  uint8_t Ltab[6] = {0,1,2,2,2,2};
  uint8_t ri = sched_ctl->periodic_ri_received[CC_idP];

  AssertFatal(cqi_ReportPeriodic != NULL,"cqi_ReportPeriodic is null!\n");
  AssertFatal(cqi_ReportPeriodic->present != CQI_ReportPeriodic_PR_NOTHING,
              "cqi_ReportPeriodic->present == CQI_ReportPeriodic_PR_NOTHING!\n");
  AssertFatal(cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present != CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_NOTHING,
              "cqi_ReportPeriodic->cqi_FormatIndicatorPeriodic.choice.setup.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_NOTHING!\n");

  switch(tmode) {
  case 1:
  case 2:
  case 5:
  case 6:
  case 7:
    no_pmi=1;
    break;
  default:
    no_pmi=0;
  }

  if ((cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_widebandCQI) ||
      (sched_ctl->feedback_cnt[CC_idP] == 0)) {
    // send wideband report every opportunity if wideband reporting mode is selected, else every H opportunities
    if (no_pmi == 1)
      return(4);
    else if ((cc->p_eNB==2) && (ri==1)) return(6);
    else if ((cc->p_eNB==2) && (ri==2)) return(8);
    else if ((cc->p_eNB==4) && (ri==1)) return(8);
    else if ((cc->p_eNB==4) && (ri==2)) return(11);
    else AssertFatal(1==0,"illegal combination p %d, ri %d, no_pmi %d\n",cc->p_eNB,ri,no_pmi);
  }
  else if (cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_subbandCQI) {
    if ((no_pmi == 1)||ri==1) return(4+Ltab[cc->mib->message.dl_Bandwidth]);
    else
      return(7+Ltab[cc->mib->message.dl_Bandwidth]);
  }
  AssertFatal(1==0,"Shouldn't get here : cqi_ReportPeriodic->present %d\n",cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present);
}

void fill_nfapi_dl_dci_1A(nfapi_dl_config_request_pdu_t   *dl_config_pdu,
                          uint8_t aggregation_level,
                          uint16_t rnti,
                          uint8_t rnti_type,
                          uint8_t harq_process,
                          uint8_t tpc,
                          uint16_t resource_block_coding,
                          uint8_t mcs,
                          uint8_t ndi,
                          uint8_t rv,
                          uint8_t vrb_flag)
{
  memset((void*)dl_config_pdu,0,sizeof(nfapi_dl_config_request_pdu_t));
  dl_config_pdu->pdu_type                                                          = NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE;
  dl_config_pdu->pdu_size                                                          = (uint8_t)(2+sizeof(nfapi_dl_config_dci_dl_pdu));
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.dci_format                             = NFAPI_DL_DCI_FORMAT_1A;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level                      = aggregation_level;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti                                   = rnti;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type                              = rnti_type;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.transmission_power                     = 6000; // equal to RS power
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.harq_process                           = harq_process;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.tpc                                    = tpc; // no TPC
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.resource_block_coding                  = resource_block_coding;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.mcs_1                                  = mcs;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.new_data_indicator_1                   = ndi;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.redundancy_version_1                   = rv;
  dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.virtual_resource_block_assignment_flag = vrb_flag;
}

void program_dlsch_acknak(module_id_t module_idP, int CC_idP,int UE_idP, frame_t frameP, sub_frame_t subframeP,uint8_t cce_idx)
{
  eNB_MAC_INST                   *eNB      = RC.mac[module_idP];
  COMMON_channels_t              *cc       = eNB->common_channels;
  UE_list_t                      *UE_list  = &eNB->UE_list;
  rnti_t                         rnti      = UE_RNTI(module_idP,UE_idP);
  nfapi_ul_config_request_body_t *ul_req;
  nfapi_ul_config_request_pdu_t  *ul_config_pdu;

  int use_simultaneous_pucch_pusch=0;
  nfapi_ul_config_ulsch_harq_information *ulsch_harq_information = NULL;
  nfapi_ul_config_harq_information *harq_information = NULL;

#if defined(Rel10) || defined(Rel14)

  if ((UE_list->UE_template[CC_idP][UE_idP].physicalConfigDedicated->ext2) &&
      (UE_list->UE_template[CC_idP][UE_idP].physicalConfigDedicated->ext2->pucch_ConfigDedicated_v1020) &&
      (UE_list->UE_template[CC_idP][UE_idP].physicalConfigDedicated->ext2->pucch_ConfigDedicated_v1020->simultaneousPUCCH_PUSCH_r10) &&
      (*UE_list->UE_template[CC_idP][UE_idP].physicalConfigDedicated->ext2->pucch_ConfigDedicated_v1020->simultaneousPUCCH_PUSCH_r10 ==
          PUCCH_ConfigDedicated_v1020__simultaneousPUCCH_PUSCH_r10_true))
    use_simultaneous_pucch_pusch=1;
#endif

  // pucch1 and pusch feedback is similar, namely in n+k subframes from now
  // This is used in the following "if/else" condition to check if there isn't or is already an UL grant in n+k
  int16_t ul_absSF = get_pucch1_absSF(&cc[CC_idP],subframeP+(10*frameP));

  if ((ul_config_pdu = has_ul_grant(module_idP,CC_idP,
                                    ul_absSF,
                                    rnti)) == NULL) {
    // no UL grant so
    // Program ACK/NAK alone Format 1a/b or 3

    ul_req        = &RC.mac[module_idP]->UL_req_tmp[CC_idP][ul_absSF%10].ul_config_request_body;
    ul_config_pdu = &ul_req->ul_config_pdu_list[ul_req->number_of_pdus];
    // Do PUCCH
    fill_nfapi_uci_acknak(module_idP,
                          CC_idP,
                          rnti,
                          (frameP*10)+subframeP,
                          cce_idx);
  }
  else {
    /* there is already an existing UL grant so update it if needed
     * on top of some other UL resource (PUSCH,combined SR/CQI/HARQ on PUCCH, etc)
     */
    switch(ul_config_pdu->pdu_type) {

    /* [ulsch] to [ulsch + harq] or [ulsch + harq on pucch] */

    case NFAPI_UL_CONFIG_ULSCH_PDU_TYPE:
      if (use_simultaneous_pucch_pusch==1) {
        // Convert it to an NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE
        harq_information = &ul_config_pdu->ulsch_uci_harq_pdu.harq_information;
        ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE;
        LOG_D(MAC,"Frame %d, Subframe %d: Switched UCI HARQ to ULSCH UCI HARQ\n",frameP,subframeP);
      }
      else {
        // Convert it to an NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE
        ulsch_harq_information = &ul_config_pdu->ulsch_harq_pdu.harq_information;
        ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE;
        ul_config_pdu->ulsch_harq_pdu.initial_transmission_parameters.initial_transmission_parameters_rel8.n_srs_initial=0; // last symbol not punctured
        ul_config_pdu->ulsch_harq_pdu.initial_transmission_parameters.initial_transmission_parameters_rel8.initial_number_of_resource_blocks=
          ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.number_of_resource_blocks; // we don't change the number of resource blocks across retransmissions yet
        LOG_D(MAC,"Frame %d, Subframe %d: Switched UCI HARQ to ULSCH HARQ\n",frameP,subframeP);
      }
      break;
    case NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE:
      AssertFatal(use_simultaneous_pucch_pusch == 1,
                  "Cannot be NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE, simultaneous_pucch_pusch is active");
      break;
    case NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE:
      AssertFatal(use_simultaneous_pucch_pusch == 0,
                  "Cannot be NFAPI_UL_CONFIG_ULSCH_UCI_PDU_TYPE, simultaneous_pucch_pusch is inactive\n");
      break;

    /* [ulsch + cqi] to [ulsch + cqi + harq] */

    case NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE:
      // Convert it to an NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE
      ulsch_harq_information = &ul_config_pdu->ulsch_cqi_harq_ri_pdu.harq_information;
      ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE;
      /* TODO: check this - when converting from nfapi_ul_config_ulsch_cqi_ri_pdu to
       * nfapi_ul_config_ulsch_cqi_harq_ri_pdu, shouldn't we copy initial_transmission_parameters
       * from the one to the other?
       * Those two types are not compatible. 'initial_transmission_parameters' is not at the
       * place in both.
       */
      ul_config_pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters.initial_transmission_parameters_rel8.n_srs_initial=0; // last symbol not punctured
      ul_config_pdu->ulsch_cqi_harq_ri_pdu.initial_transmission_parameters.initial_transmission_parameters_rel8.initial_number_of_resource_blocks=
        ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.number_of_resource_blocks; // we don't change the number of resource blocks across retransmissions yet
      break;
    case NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE:
      AssertFatal(use_simultaneous_pucch_pusch == 0,
                  "Cannot be NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE, simultaneous_pucch_pusch is active\n");
      break;

    /* [ulsch + cqi on pucch] to [ulsch + cqi on pucch + harq on pucch] */

    case NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE:
      // convert it to an NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE
      harq_information = &ul_config_pdu->ulsch_csi_uci_harq_pdu.harq_information;
      ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE;
      break;
    case NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE:
      AssertFatal(use_simultaneous_pucch_pusch == 1,
                  "Cannot be NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE, simultaneous_pucch_pusch is inactive\n");
      break;

    /* [sr] to [sr + harq] */

    case NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE:
      // convert to NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE
      ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE;
      harq_information = &ul_config_pdu->uci_sr_harq_pdu.harq_information;
      break;
    case NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE:
      /* nothing to do */
      break;

    /* [cqi] to [cqi + harq] */

    case NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE:
      // convert to NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE
      ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE;
      harq_information = &ul_config_pdu->uci_cqi_harq_pdu.harq_information;
      break;
    case NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE:
      /* nothing to do */
      break;

    /* [cqi + sr] to [cqr + sr + harq] */

    case NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE:
      // convert to NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE
      ul_config_pdu->pdu_type = NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE;
      harq_information = &ul_config_pdu->uci_cqi_sr_harq_pdu.harq_information;
      break;
    case NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE:
      /* nothing to do */
      break;
    }
  }

  if (ulsch_harq_information) fill_nfapi_ulsch_harq_information(module_idP,CC_idP,
                                                                rnti,
                                                                ulsch_harq_information);

  if (harq_information) fill_nfapi_harq_information(module_idP,CC_idP,
                                                    rnti,
                                                    (frameP*10)+subframeP,
                                                    harq_information,
                                                    cce_idx);
}

uint8_t get_V_UL_DAI(module_id_t module_idP,int CC_idP,uint16_t rntiP)
{
  nfapi_hi_dci0_request_body_t        *HI_DCI0_req         = &RC.mac[module_idP]->HI_DCI0_req[CC_idP].hi_dci0_request_body;
  nfapi_hi_dci0_request_pdu_t         *hi_dci0_pdu         = &HI_DCI0_req->hi_dci0_pdu_list[0];

  for (int i=0;i<HI_DCI0_req->number_of_dci;i++) {
    if ((hi_dci0_pdu[i].pdu_type == NFAPI_HI_DCI0_DCI_PDU_TYPE)&&
        (hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti == rntiP))
        return(hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.dl_assignment_index);
  }
  return(4); // this is rule from Section 7.3 in 36.213
}

void fill_nfapi_ulsch_harq_information(module_id_t module_idP,
                                       int CC_idP,
                                       uint16_t rntiP,
                                       nfapi_ul_config_ulsch_harq_information *harq_information)
{
  eNB_MAC_INST                   *eNB      = RC.mac[module_idP];
  COMMON_channels_t              *cc       = &eNB->common_channels[CC_idP];
  UE_list_t                      *UE_list  = &eNB->UE_list;

  int UE_id                                = find_UE_id(module_idP,rntiP);

  PUSCH_ConfigDedicated_t              *puschConfigDedicated;
  //  PUSCH_ConfigDedicated_v1020_t        *puschConfigDedicated_v1020;
  //  PUSCH_ConfigDedicated_v1130_t        *puschConfigDedicated_v1130;
  //  PUSCH_ConfigDedicated_v1250_t        *puschConfigDedicated_v1250;

  AssertFatal(UE_id>=0,"UE_id cannot be found, impossible\n");
  AssertFatal(UE_list!=NULL,"UE_list is null\n");
  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated!=NULL,"physicalConfigDedicated for rnti %x is null\n",rntiP);
  AssertFatal((puschConfigDedicated = (PUSCH_ConfigDedicated_t *)UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pusch_ConfigDedicated)!=NULL,"physicalConfigDedicated->puschConfigDedicated for rnti %x is null\n",rntiP);
#if defined(Rel14) || defined(Rel14)
  /*  if (UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext2) puschConfigDedicated_v1020 =  UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext2->pusch_ConfigDedicated_v1020;
#endif
#ifdef Rel14
  if (UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext4) puschConfigDedicated_v1130 =  UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext4->pusch_ConfigDedicated_v1130;
  if (UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext5) puschConfigDedicated_v1250 =  UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->ext5->pusch_ConfigDedicated_v1250;
  */
#endif
  harq_information->harq_information_rel10.delta_offset_harq = puschConfigDedicated->betaOffset_ACK_Index;
  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated!=NULL,"pucch_ConfigDedicated is null!\n");
  if ((UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode!=NULL)&&
      (*UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode==PUCCH_ConfigDedicated__tdd_AckNackFeedbackMode_multiplexing))
      harq_information->harq_information_rel10.ack_nack_mode = 1;  // multiplexing
  else
      harq_information->harq_information_rel10.ack_nack_mode = 0;  // bundling

  switch(get_tmode(module_idP,CC_idP,UE_id)) {
  case 1:
  case 2:
  case 5:
  case 6:
  case 7:
    if (cc->tdd_Config==NULL) // FDD
      harq_information->harq_information_rel10.harq_size = 1;
    else {
      if (harq_information->harq_information_rel10.ack_nack_mode == 1)
        harq_information->harq_information_rel10.harq_size = get_V_UL_DAI(module_idP,CC_idP,rntiP);
      else
        harq_information->harq_information_rel10.harq_size = 1;
    }
    break;
  default: // for any other TM we need 2 bits harq
    if (cc->tdd_Config==NULL) {
        harq_information->harq_information_rel10.harq_size     = 2;
    }
    else {
      if (harq_information->harq_information_rel10.ack_nack_mode == 1)
        harq_information->harq_information_rel10.harq_size     = get_V_UL_DAI(module_idP,CC_idP,rntiP);
      else
        harq_information->harq_information_rel10.harq_size     = 2;
    }
    break;
  } // get Tmode
}

void fill_nfapi_harq_information(module_id_t module_idP,
                                 int CC_idP,
                                 uint16_t rntiP,
                                 uint16_t absSFP,
                                 nfapi_ul_config_harq_information *harq_information,
                                 uint8_t cce_idxP)
{
  eNB_MAC_INST                   *eNB      = RC.mac[module_idP];
  COMMON_channels_t              *cc       = &eNB->common_channels[CC_idP];
  UE_list_t                      *UE_list  = &eNB->UE_list;

  int UE_id                                = find_UE_id(module_idP,rntiP);

  AssertFatal(UE_id>=0,"UE_id cannot be found, impossible\n");
  AssertFatal(UE_list!=NULL,"UE_list is null\n");
  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated!=NULL,"physicalConfigDedicated for rnti %x is null\n",rntiP);

  harq_information->harq_information_rel11.num_ant_ports = 1;

  switch(get_tmode(module_idP,CC_idP,UE_id)) {
  case 1:
  case 2:
  case 5:
  case 6:
  case 7:
    if (cc->tdd_Config!=NULL) {
      AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated!=NULL,
                  "pucch_ConfigDedicated is null for TDD!\n");
      if ((UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode!=NULL)&&
          (*UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode==PUCCH_ConfigDedicated__tdd_AckNackFeedbackMode_multiplexing)) {
        harq_information->harq_information_rel10_tdd.harq_size     = 2;  // 2-bit ACK/NAK
        harq_information->harq_information_rel10_tdd.ack_nack_mode = 1;  // multiplexing
      }
      else {
        harq_information->harq_information_rel10_tdd.harq_size     = 1;  // 1-bit ACK/NAK
        harq_information->harq_information_rel10_tdd.ack_nack_mode = 0;  // bundling
      }
      harq_information->harq_information_rel10_tdd.n_pucch_1_0   = cc->radioResourceConfigCommon->pucch_ConfigCommon.n1PUCCH_AN + cce_idxP;
      harq_information->harq_information_rel10_tdd.number_of_pucch_resources = 1;
    } else {
      harq_information->harq_information_rel9_fdd.number_of_pucch_resources = 1;
      harq_information->harq_information_rel9_fdd.harq_size                 = 1;  // 1-bit ACK/NAK
      harq_information->harq_information_rel9_fdd.n_pucch_1_0               = cc->radioResourceConfigCommon->pucch_ConfigCommon.n1PUCCH_AN + cce_idxP;
    }
    break;
  default: // for any other TM we need 2 bits harq
    if (cc->tdd_Config!=NULL) {
      AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated!=NULL,
                  "pucch_ConfigDedicated is null for TDD!\n");
      if ((UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode!=NULL)&&
          (*UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->pucch_ConfigDedicated->tdd_AckNackFeedbackMode==PUCCH_ConfigDedicated__tdd_AckNackFeedbackMode_multiplexing)) {
        harq_information->harq_information_rel10_tdd.ack_nack_mode = 1;  // multiplexing
      }
      else {
        harq_information->harq_information_rel10_tdd.ack_nack_mode = 0;  // bundling
      }
      harq_information->harq_information_rel10_tdd.harq_size       = 2;
      harq_information->harq_information_rel10_tdd.n_pucch_1_0   = cc->radioResourceConfigCommon->pucch_ConfigCommon.n1PUCCH_AN + cce_idxP;
      harq_information->harq_information_rel10_tdd.number_of_pucch_resources = 1;
    }
    else {
      harq_information->harq_information_rel9_fdd.number_of_pucch_resources = 1;
      harq_information->harq_information_rel9_fdd.ack_nack_mode    = 0;  // 1a/b
      harq_information->harq_information_rel9_fdd.harq_size        = 2;
      harq_information->harq_information_rel9_fdd.n_pucch_1_0      = cc->radioResourceConfigCommon->pucch_ConfigCommon.n1PUCCH_AN + cce_idxP;
    }
    break;
  } // get Tmode
}

uint16_t fill_nfapi_uci_acknak(module_id_t module_idP,
                               int CC_idP,
                               uint16_t rntiP,
                               uint16_t absSFP,
                               uint8_t cce_idxP)
{
  eNB_MAC_INST                   *eNB          = RC.mac[module_idP];
  COMMON_channels_t              *cc           = &eNB->common_channels[CC_idP];

  int ackNAK_absSF                             = get_pucch1_absSF(cc,absSFP);
  nfapi_ul_config_request_body_t *ul_req       = &eNB->UL_req_tmp[CC_idP][ackNAK_absSF%10].ul_config_request_body;
  nfapi_ul_config_request_pdu_t *ul_config_pdu = &ul_req->ul_config_pdu_list[ul_req->number_of_pdus];

  memset((void*)ul_config_pdu,0,sizeof(nfapi_ul_config_request_pdu_t));
  ul_config_pdu->pdu_type                                                              = NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE;
  ul_config_pdu->pdu_size                                                              = (uint8_t)(2+sizeof(nfapi_ul_config_uci_harq_pdu));
  ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.handle                = 0; // don't know how to use this
  ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti                  = rntiP;

  fill_nfapi_harq_information(module_idP,CC_idP,
                              rntiP,
                              absSFP,
                              &ul_config_pdu->uci_harq_pdu.harq_information,
                              cce_idxP);
  LOG_D(MAC,"Filled in UCI HARQ request for rnti %x SF %d.%d acknakSF %d.%d, cce_idxP %d-> n1_pucch %d\n",rntiP,
        absSFP/10,absSFP%10,ackNAK_absSF/10,ackNAK_absSF%10,cce_idxP,ul_config_pdu->uci_harq_pdu.harq_information.harq_information_rel9_fdd.n_pucch_1_0);

  ul_req->number_of_pdus++;

  return(((ackNAK_absSF/10)<<4) + (ackNAK_absSF%10));
}

void fill_nfapi_dlsch_config(eNB_MAC_INST *eNB,
                             nfapi_dl_config_request_body_t *dl_req,
                             uint16_t length,
                             uint16_t pdu_index,
                             uint16_t rnti,
                             uint8_t resource_allocation_type,
                             uint8_t virtual_resource_block_assignment_flag,
                             uint16_t resource_block_coding,
                             uint8_t modulation,
                             uint8_t redundancy_version,
                             uint8_t transport_blocks,
                             uint8_t transport_block_to_codeword_swap_flag,
                             uint8_t transmission_scheme,
                             uint8_t number_of_layers,
                             uint8_t number_of_subbands,
                             //                             uint8_t codebook_index,
                             uint8_t ue_category_capacity,
                             uint8_t pa,
                             uint8_t delta_power_offset_index,
                             uint8_t ngap,
                             uint8_t nprb,
                             uint8_t transmission_mode,
                             uint8_t num_bf_prb_per_subband,
                             uint8_t num_bf_vector
                             )
{
  nfapi_dl_config_request_pdu_t   *dl_config_pdu = &dl_req->dl_config_pdu_list[dl_req->number_pdu];
  memset((void*)dl_config_pdu,0,sizeof(nfapi_dl_config_request_pdu_t));

  dl_config_pdu->pdu_type                                                        = NFAPI_DL_CONFIG_DLSCH_PDU_TYPE;
  dl_config_pdu->pdu_size                                                        = (uint8_t)(2+sizeof(nfapi_dl_config_dlsch_pdu));
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.length                                 = length;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pdu_index                              = pdu_index;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.rnti                                   = rnti;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_allocation_type               = resource_allocation_type;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.virtual_resource_block_assignment_flag = virtual_resource_block_assignment_flag;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.resource_block_coding                  = resource_block_coding;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.modulation                             = modulation;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.redundancy_version                     = redundancy_version;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_blocks                       = transport_blocks;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transport_block_to_codeword_swap_flag  = transport_block_to_codeword_swap_flag;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transmission_scheme                    = transmission_scheme;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.number_of_layers                       = number_of_layers;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.number_of_subbands                     = number_of_subbands;
  //  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.codebook_index                         = codebook_index;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.ue_category_capacity                   = ue_category_capacity;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.pa                                     = pa;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.delta_power_offset_index               = delta_power_offset_index;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.ngap                                   = ngap;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.nprb                                   = nprb;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.transmission_mode                      = transmission_mode;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.num_bf_prb_per_subband                 = num_bf_prb_per_subband;
  dl_config_pdu->dlsch_pdu.dlsch_pdu_rel8.num_bf_vector                          = num_bf_vector;
  dl_req->number_pdu++;
}

uint16_t fill_nfapi_tx_req(nfapi_tx_request_body_t *tx_req_body,uint16_t absSF,uint16_t pdu_length, uint16_t pdu_index, uint8_t *pdu)
{
  nfapi_tx_request_pdu_t *TX_req        = &tx_req_body->tx_pdu_list[tx_req_body->number_of_pdus];
  LOG_D(MAC,"Filling TX_req %d for pdu length %d\n",tx_req_body->number_of_pdus,pdu_length);
  TX_req->pdu_length                    = pdu_length;
  TX_req->pdu_index                     = pdu_index;
  TX_req->num_segments                  = 1;
  TX_req->segments[0].segment_length    = pdu_length;
  TX_req->segments[0].segment_data      = pdu;
  tx_req_body->number_of_pdus++;

  return(((absSF/10)<<4) + (absSF%10));
}

void fill_nfapi_ulsch_config_request_rel8(nfapi_ul_config_request_pdu_t  *ul_config_pdu,
                                          uint8_t                        cqi_req,
                                          COMMON_channels_t              *cc,
                                          struct PhysicalConfigDedicated  *physicalConfigDedicated,
                                          uint8_t                        tmode,
                                          uint32_t                       handle,
                                          uint16_t                       rnti,
                                          uint8_t                        resource_block_start,
                                          uint8_t                        number_of_resource_blocks,
                                          uint8_t                        mcs,
                                          uint8_t                        cyclic_shift_2_for_drms,
                                          uint8_t                        frequency_hopping_enabled_flag,
                                          uint8_t                        frequency_hopping_bits,
                                          uint8_t                        new_data_indication,
                                          uint8_t                        redundancy_version,
                                          uint8_t                        harq_process_number,
                                          uint8_t                        ul_tx_mode,
                                          uint8_t                        current_tx_nb,
                                          uint8_t                        n_srs,
                                          uint16_t                       size
                                          )
{
  memset((void*)ul_config_pdu,0,sizeof(nfapi_ul_config_request_pdu_t));

  ul_config_pdu->pdu_type                                                        = NFAPI_UL_CONFIG_ULSCH_PDU_TYPE;
  ul_config_pdu->pdu_size                                                        = (uint8_t)(2+sizeof(nfapi_ul_config_ulsch_pdu));
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.handle                                 = handle;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.rnti                                   = rnti;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.resource_block_start                   = resource_block_start;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.number_of_resource_blocks              = number_of_resource_blocks;
  if      (mcs<11) ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.modulation_type       = 2;
  else if (mcs<21) ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.modulation_type       = 4;
  else             ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.modulation_type       = 6;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.cyclic_shift_2_for_drms                = cyclic_shift_2_for_drms;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.frequency_hopping_enabled_flag         = frequency_hopping_enabled_flag;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.frequency_hopping_bits                 = frequency_hopping_bits;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.new_data_indication                    = new_data_indication;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.redundancy_version                     = redundancy_version;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.harq_process_number                    = harq_process_number;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.ul_tx_mode                             = ul_tx_mode;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.current_tx_nb                          = current_tx_nb;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.n_srs                                  = n_srs;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.size                                   = size;

  if (cqi_req == 1) {
    // Add CQI portion
    ul_config_pdu->pdu_type                                                           = NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE;
    ul_config_pdu->pdu_size                                                           = (uint8_t)(2+sizeof(nfapi_ul_config_ulsch_cqi_ri_pdu));
    ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.report_type             = 1;
    ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.number_of_cc = 1;
    LOG_D(MAC,"report_type %d\n",ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.report_type);

    if (cc->p_eNB<=2 && (tmode==3||tmode==4||tmode==8||tmode==9||tmode==10))
      ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.cc[0].ri_size = 1;
    else if (cc->p_eNB<=2)
      ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.cc[0].ri_size = 0;
    else if (cc->p_eNB==4)
      ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.cc[0].ri_size = 2;

    AssertFatal(physicalConfigDedicated->cqi_ReportConfig!=NULL,"physicalConfigDedicated->cqi_ReportConfig is null!\n");
    AssertFatal(physicalConfigDedicated->cqi_ReportConfig->cqi_ReportModeAperiodic!=NULL,"physicalConfigDedicated->cqi_ReportModeAperiodic is null!\n");
    AssertFatal(physicalConfigDedicated->pusch_ConfigDedicated!=NULL,"physicalConfigDedicated->puschConfigDedicated is null!\n");

    for (int ri=0;
         ri<(1<<ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.cc[0].ri_size);
         ri++)
      ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.aperiodic_cqi_pmi_ri_report.cc[0].dl_cqi_pmi_size[ri] =
        get_dl_cqi_pmi_size_pusch(cc,
                                  tmode,
                                  1+ri,
                                  physicalConfigDedicated->cqi_ReportConfig->cqi_ReportModeAperiodic);

    ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.delta_offset_cqi        = physicalConfigDedicated->pusch_ConfigDedicated->betaOffset_CQI_Index;
    ul_config_pdu->ulsch_cqi_ri_pdu.cqi_ri_information.cqi_ri_information_rel9.delta_offset_ri         = physicalConfigDedicated->pusch_ConfigDedicated->betaOffset_RI_Index;
  }
}

#ifdef Rel14
void fill_nfapi_ulsch_config_request_emtc(nfapi_ul_config_request_pdu_t  *ul_config_pdu,
                                          uint8_t ue_type,
                                          uint16_t total_number_of_repetitions,
                                          uint16_t repetition_number,
                                          uint16_t initial_transmission_sf_io)
{
  // Re13 fields

  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel13.ue_type                               = ue_type;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel13.total_number_of_repetitions           = total_number_of_repetitions;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel13.repetition_number                     = repetition_number;
  ul_config_pdu->ulsch_pdu.ulsch_pdu_rel13.initial_transmission_sf_io            = initial_transmission_sf_io;
}

int get_numnarrowbands(long dl_Bandwidth)
{
  int nb_tab[6] = {1,2,4,8,12,16};

  AssertFatal(dl_Bandwidth<7 || dl_Bandwidth>=0,"dl_Bandwidth not in [0..6]\n");
  return(nb_tab[dl_Bandwidth]);
}

int get_numnarrowbandbits(long dl_Bandwidth)
{
  int nbbits_tab[6] = {0,1,2,3,4,4};

  AssertFatal(dl_Bandwidth<7 || dl_Bandwidth>=0,"dl_Bandwidth not in [0..6]\n");
  return(nbbits_tab[dl_Bandwidth]);
}

//This implements the frame/subframe condition for first subframe of MPDCCH transmission (Section 9.1.5 36.213, Rel 13/14)
int startSF_fdd_RA_times2[8] = {2,3,4,5,8,10,16,20};
int startSF_tdd_RA[7]        = {1,2,4,5,8,10,20};

int mpdcch_sf_condition(eNB_MAC_INST *eNB,int CC_id, frame_t frameP,sub_frame_t subframeP,int rmax,MPDCCH_TYPES_t mpdcch_type,int UE_id)
{
  struct PRACH_ConfigSIB_v1310 *ext4_prach = eNB->common_channels[CC_id].radioResourceConfigCommon_BR->ext4->prach_ConfigCommon_v1310;

  int T;
  EPDCCH_SetConfig_r11_t *epdcch_setconfig_r11;

  switch (mpdcch_type) {
  case TYPE0:
    AssertFatal(1==0,"MPDCCH Type 0 not handled yet\n");
    break;
  case TYPE1:
    AssertFatal(1==0,"MPDCCH Type 1 not handled yet\n");
    break;
  case TYPE1A:
    AssertFatal(1==0,"MPDCCH Type 1A not handled yet\n");
    break;
  case TYPE2: // RAR
    AssertFatal(ext4_prach->mpdcch_startSF_CSS_RA_r13!=NULL,
                "mpdcch_startSF_CSS_RA_r13 is null\n");
    AssertFatal(rmax>0,"rmax is 0!\b");
    if (eNB->common_channels[CC_id].tdd_Config==NULL) //FDD
      T = rmax*startSF_fdd_RA_times2[ext4_prach->mpdcch_startSF_CSS_RA_r13->choice.fdd_r13]>>1;
    else //TDD
      T = rmax*startSF_tdd_RA[ext4_prach->mpdcch_startSF_CSS_RA_r13->choice.tdd_r13];
    break;
  case TYPE2A:
    AssertFatal(1==0,"MPDCCH Type 2A not handled yet\n");
    break;
  case TYPEUESPEC:
    epdcch_setconfig_r11= eNB->UE_list.UE_template[CC_id][UE_id].physicalConfigDedicated->ext4->epdcch_Config_r11->config_r11.choice.setup.setConfigToAddModList_r11->list.array[0] ;

    AssertFatal(epdcch_setconfig_r11 != NULL," epdcch_setconfig_r11 is null for UE specific \n");
    AssertFatal(epdcch_setconfig_r11->ext2 != NULL," ext2 doesn't exist in epdcch config ' \n");

    if (eNB->common_channels[CC_id].tdd_Config==NULL) //FDD
      T = rmax*startSF_fdd_RA_times2[epdcch_setconfig_r11->ext2->mpdcch_config_r13->choice.setup.mpdcch_StartSF_UESS_r13.choice.fdd_r13]>>1;
    else //TDD
      T = rmax*startSF_tdd_RA[epdcch_setconfig_r11->ext2->mpdcch_config_r13->choice.setup.mpdcch_StartSF_UESS_r13.choice.tdd_r13];

    break;
  default:
    return(0);
  }

  AssertFatal(T>0,"T is 0!\n");
  if (((10*frameP) + subframeP)%T == 0) return(1);
  else return(0);
}

int narrowband_to_first_rb(COMMON_channels_t *cc, int nb_index)
{
  switch (cc->mib->message.dl_Bandwidth) {
  case 0: // 6 PRBs, N_NB=1, i_0=0
    return(0);
    break;
  case 3: // 50 PRBs, N_NB=8, i_0=1
    return((int)(1+(6*nb_index)));
    break;
  case 5: // 100 PRBs, N_NB=16, i_0=2
    return((int)(2+(6*nb_index)));
    break;
  case 1: // 15 PRBs  N_NB=2, i_0=1
    if (nb_index>0) return(1);
    else            return(0);
    break;
  case 2: // 25 PRBs, N_NB=4, i_0=0
    if (nb_index>1) return(1+(6*nb_index));
    else            return((6*nb_index));
    break;
  case 4: // 75 PRBs, N_NB=12, i_0=1
    if (nb_index>5) return(2+(6*nb_index));
    else            return(1+(6*nb_index));
    break;
  default:
    AssertFatal(1==0,"Impossible dl_Bandwidth %d\n",(int)cc->mib->message.dl_Bandwidth);
    break;
  }
}
#endif

//------------------------------------------------------------------------------
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 find_RA_id(module_id_t mod_idP, int CC_idP, rnti_t rntiP)
//------------------------------------------------------------------------------
{
  int RA_id;
  AssertFatal(RC.mac[mod_idP],"RC.mac[%d] is null\n",mod_idP);

  RA_TEMPLATE *RA_template = (RA_TEMPLATE *)&RC.mac[mod_idP]->common_channels[CC_idP].RA_template[0];

  for (RA_id = 0; RA_id < NB_RA_PROC_MAX; RA_id++) {
    LOG_D(MAC,"Checking RA_id %d for %x : RA_active %d, wait_ack_Msg4 %d\n",
          RA_id,rntiP,
          RA_template[RA_id].RA_active,
          RA_template[RA_id].wait_ack_Msg4);

    if (RA_template[RA_id].RA_active==TRUE &&
        RA_template[RA_id].wait_ack_Msg4 == 0 &&
        RA_template[RA_id].rnti == rntiP) return(RA_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
               #ifdef Rel14
                 ,uint8_t rach_resource_type
               #endif
               )
{
  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;
    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;

#ifdef Rel14
    UE_list->UE_template[cc_idP][UE_id].rach_resource_type = rach_resource_type ;
#endif

    memset((void*)&UE_list->UE_sched_ctrl[UE_id],0,sizeof(UE_sched_ctrl));
    memset((void*)&UE_list->eNB_UE_stats[cc_idP][UE_id],0,sizeof(eNB_UE_STATS));

    UE_list->UE_sched_ctrl[UE_id].ta_update = 31;

    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;
      UE_list->UE_sched_ctrl[UE_id].round[cc_idP][j]   = 8;
      UE_list->UE_sched_ctrl[UE_id].round_UL[cc_idP][j]   = 0;
    }

    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) {
    LOG_W(MAC,"rrc_mac_remove_ue: UE %x not found\n", rntiP);
    return 0;
  }

  pCC_id = UE_PCCID(mod_idP,UE_id);

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

  if (UE_list->head == UE_id) UE_list->head=UE_list->next[UE_id];
  else UE_list->next[prev(UE_list,UE_id,0)]=UE_list->next[UE_id];
  if (UE_list->head_ul == UE_id) UE_list->head_ul=UE_list->next_ul[UE_id];
  else UE_list->next_ul[prev(UE_list,UE_id,0)]=UE_list->next_ul[UE_id];

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

  // 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 (BSR0 %d,SR %d)\n",module_idP,UE_id,UE_RNTI(module_idP,UE_id),
          UE_template->bsr_info[LCGID0],
          UE_template->ul_SR);
    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];

  if (physicalConfigDedicated == NULL ) { // RRCConnectionSetup not received by UE yet
    AssertFatal(cc->p_eNB<=2,"p_eNB is %d, should be <2\n",cc->p_eNB);
    return(cc->p_eNB);
  }
  else {
    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_ULharq(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:
          AssertFatal(1==0,"subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,(int)cc->tdd_Config->subframeAssignment);
          break;
        }

      break;

    case 2:
      AssertFatal((subframeP==2) || (subframeP==7),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframeP/7);
      break;

    case 3:
      AssertFatal((subframeP>1) && (subframeP<5),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframeP-2);
      break;

    case 4:
      AssertFatal((subframeP>1) && (subframeP<4),
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframeP-2);
      break;

    case 5:
      AssertFatal(subframeP==2,
                  "subframe2_harq_pid, Illegal subframe %d for TDD mode %d\n",subframeP,(int)cc->tdd_Config->subframeAssignment);
      ret = (subframeP-2);
      break;

    default:
      AssertFatal(1==0,"subframe2_harq_pid, Unsupported TDD mode %d\n",(int)cc->tdd_Config->subframeAssignment);
      break;
    }
  }

  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++) {
        int cce = (((Yk+m)%(nCCE/L))*L) + l;
        if (cce >= nCCE || CCE_table[cce] == 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].dl_config_request_body;
  nfapi_hi_dci0_request_body_t        *HI_DCI0_req         = &RC.mac[module_idP]->HI_DCI0_req[CC_idP].hi_dci0_request_body;
  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_D(MAC,"Allocate CCEs subframe %d, test %d : (DL PDU %d, DL DCI %d, UL %d)\n",subframeP,test_onlyP,DL_req->number_pdu,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_D(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;
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols, increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);
        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_D(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_D(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;
        }
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols (rnti condition), increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);

        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 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;
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols, increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);

        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,
                             0,
                             hi_dci0_pdu[i].dci_pdu.dci_pdu_rel8.rnti,
                             subframeP);
      if (fCCE == -1) {
        if (DL_req->number_pdcch_ofdm_symbols == 3) {
          LOG_D(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_D(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;
        }
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols (rnti condition), increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);

        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 UE specific DCIs
    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;
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols, increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);

        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,
                             0,
                             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;
        }
        LOG_D(MAC,"Can't fit DCI allocations with %d PDCCH symbols (rnti condition), increasing by 1\n",DL_req->number_pdcch_ofdm_symbols);

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

/*
uint8_t get_ul_req_index(module_id_t module_idP, int CC_idP, sub_frame_t subframeP)
{
  if (RC.mac[module_idP]->common_channels[CC_idP].tdd_Config == NULL)
    return(0);

  switch (RC.mac[module_idP]->common_channels[CC_idP].tdd_Config->subframeAssignment) {
  case 0:
  case 1:
  case 2:
  case 6:
    return(0);
  case 3:
    // 1,5,6 -> 2, prog. 8, buffer 0
    // 7,8   -> 3, prog. 9, buffer 1
    // 9,0   -> 4, prog. 0, buffer 0
    if ((subframeP == 7) || (subframeP == 8)) return(1);
    else                                      return(0);
  case 4:
    // 0,1,4,5 -> 2, prog. 8, buffer 0
    // 6,7,8,9 -> 3, prog. 9, buffer 1
    if (subframeP<6) return(0);
    else             return(1);
    return(1);
    break;
  case 5:
    // 9(-1),0,1,3,4,5,6,7,8,9 -> 2, prog 8, buffer 0
    return(0);
    break;
  default:
    AssertFatal(1==0,"Should not get here, why is tdd_Config->subframeAssignment = %d\n",(int)RC.mac[module_idP]->common_channels[CC_idP].tdd_Config->subframeAssignment);
    break;
  }
  return(0);
}
*/

nfapi_ul_config_request_pdu_t* has_ul_grant(module_id_t module_idP,int CC_idP,uint16_t absSFP,uint16_t rnti)
{
  nfapi_ul_config_request_body_t *ul_req;
  nfapi_ul_config_request_pdu_t *ul_config_pdu;

  ul_req        = &RC.mac[module_idP]->UL_req_tmp[CC_idP][absSFP%10].ul_config_request_body;
  ul_config_pdu = &ul_req->ul_config_pdu_list[0];
  LOG_D(MAC,"Checking for rnti %x UL grant in subframeP %d (num pdu %d)\n",rnti,absSFP%10,ul_req->number_of_pdus);

  for (int i=0; i<ul_req->number_of_pdus;i++){
    LOG_D(MAC,"PDU %d : type %d,rnti %x\n",i,ul_config_pdu[i].pdu_type,rnti);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);

    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE)&&
        (ul_config_pdu[i].uci_cqi_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE)&&
        (ul_config_pdu[i].uci_sr_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].uci_harq_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].uci_sr_harq_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].uci_cqi_harq_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE)&&
        (ul_config_pdu[i].uci_cqi_sr_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_UCI_CQI_SR_HARQ_PDU_TYPE) &&
        (ul_config_pdu[i].uci_cqi_sr_harq_pdu.ue_information.ue_information_rel8.rnti == rnti)) return(&ul_config_pdu[i]);

    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_UCI_CSI_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_uci_csi_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_UCI_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_uci_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
    if ((ul_config_pdu[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_CSI_UCI_HARQ_PDU_TYPE)&&
        (ul_config_pdu[i].ulsch_csi_uci_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti == rnti)) return(&ul_config_pdu[i]);
  }

  return(NULL); // no ul grant at all for this UE
}

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].dl_config_request_body;
  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].hi_dci0_request_body;
  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];
  int ret;
  boolean_t res = FALSE;

  if (format_flag != 2) { // DL DCI
    if (DL_req->number_pdu == MAX_NUM_DL_PDU) {
      LOG_W(MAC, "Subframe %d: FAPI DL structure is full, skip scheduling UE %d\n",
            subframe, rnti);
    } else {
      dl_config_pdu->pdu_type                                     = NFAPI_DL_CONFIG_DCI_DL_PDU_TYPE;
      dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti              = rnti;
      dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type         = (format_flag == 0)?2:1;
      dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level = aggregation;
      DL_req->number_pdu++;
      LOG_D(MAC,"Subframe %d: Checking CCE feasibility format %d : (%x,%d) (%x,%d,%d)\n",
        subframe,format_flag,rnti,aggregation,
          dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti,
          dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.aggregation_level,
          dl_config_pdu->dci_dl_pdu.dci_dl_pdu_rel8.rnti_type);
      ret = allocate_CCEs(module_idP,CC_idP,subframe,0);
      if (ret==-1)
        res = TRUE;
      DL_req->number_pdu--;
    }
  }
  else { // ue-specific UL DCI
    if (HI_DCI0_req->number_of_dci+HI_DCI0_req->number_of_hi == MAX_NUM_HI_DCI0_PDU) {
      LOG_W(MAC, "Subframe %d: FAPI UL structure is full, skip scheduling UE %d\n",
            subframe, rnti);
    } else {
      hi_dci0_pdu->pdu_type                               = NFAPI_HI_DCI0_DCI_PDU_TYPE;
      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,0);
      if (ret==-1)
        res = TRUE;
      HI_DCI0_req->number_of_dci--;
    }
  }

  return res;
}

void extract_harq(module_id_t mod_idP,int CC_idP,int UE_id,frame_t frameP,sub_frame_t subframeP,void *harq_indication,int format)
{
  UE_list_t *UE_list       = &RC.mac[mod_idP]->UE_list;
  UE_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];
  rnti_t rnti              = UE_RNTI(mod_idP,UE_id);
  COMMON_channels_t *cc    = &RC.mac[mod_idP]->common_channels[CC_idP];
  nfapi_harq_indication_fdd_rel13_t *harq_indication_fdd;
  nfapi_harq_indication_tdd_rel13_t *harq_indication_tdd;
  uint16_t num_ack_nak;
  int numCC = UE_list->numactiveCCs[UE_id];
  int pCCid = UE_list->pCC_id[UE_id];
  int spatial_bundling = 0;
  int tmode[5];
  int i,j;
  uint8_t *pdu;

#ifdef Rel14
  AssertFatal(UE_list->UE_template[pCCid][UE_id].physicalConfigDedicated->pucch_ConfigDedicated!=NULL,"pucch_ConfigDedicated is null!\n");
  if ((UE_list->UE_template[pCCid][UE_id].physicalConfigDedicated->ext7) &&
      (UE_list->UE_template[pCCid][UE_id].physicalConfigDedicated->ext7->pucch_ConfigDedicated_r13) &&
      (((UE_list->UE_template[pCCid][UE_id].physicalConfigDedicated->ext7->pucch_ConfigDedicated_r13->spatialBundlingPUCCH_r13) &&
        (format==0)) ||
       ((UE_list->UE_template[pCCid][UE_id].physicalConfigDedicated->ext7->pucch_ConfigDedicated_r13->spatialBundlingPUSCH_r13) &&
        (format==1))))
    spatial_bundling = 1;
#endif

  for (i=0;i<numCC;i++) tmode[i] = get_tmode(mod_idP,i,UE_id);

  if (cc->tdd_Config) {
    harq_indication_tdd = (nfapi_harq_indication_tdd_rel13_t *)harq_indication;
    //    pdu = &harq_indication_tdd->harq_tb_n[0];

    num_ack_nak = harq_indication_tdd->number_of_ack_nack;

    switch (harq_indication_tdd->mode) {
      case 0: // Format 1a/b
        AssertFatal(numCC==1,"numCC %d > 1, should not be using Format1a/b\n",numCC);
        break;
      case 1: // Channel Selection
        break;
      case 2: // Format 3
        break;
      case 3: // Format 4
        break;
      case 4: // Format 5
        break;
    }
  }
  else {
    harq_indication_fdd = (nfapi_harq_indication_fdd_rel13_t *)harq_indication;
    num_ack_nak = harq_indication_fdd->number_of_ack_nack;
    pdu = &harq_indication_fdd->harq_tb_n[0];

    uint8_t harq_pid = ((10*frameP) + subframeP + 10236)&7;

    switch (harq_indication_fdd->mode) {
      case 0: // Format 1a/b (10.1.2.1)
        AssertFatal(numCC==1,"numCC %d > 1, should not be using Format1a/b\n",numCC);
        if (tmode[0]==1 || tmode[0]==2 || tmode[0]==5 || tmode[0]==6 || tmode[0]==7) { // NOTE: have to handle the case of TM9-10 with 1 antenna port
          // single ACK/NAK bit
          AssertFatal(num_ack_nak==1,"num_ack_nak %d > 1 for 1 CC and single-layer transmission\n",num_ack_nak);
          AssertFatal(sched_ctl->round[CC_idP][harq_pid]<8,"Got ACK/NAK for inactive harq_pid %d for UE %d/%x\n",harq_pid,UE_id,rnti);
          AssertFatal(pdu[0] == 1 || pdu[0] == 2 || pdu[0] == 4,
                      "Received ACK/NAK %d which is not 1 or 2 for harq_pid %d from UE %d/%x\n",pdu[0],harq_pid,UE_id,rnti);
          LOG_D(MAC,"Received %d for harq_pid %d\n",pdu[0],harq_pid);

          if (pdu[0] == 1) { // ACK
            sched_ctl->round[CC_idP][harq_pid]=8; // release HARQ process
            sched_ctl->tbcnt[CC_idP][harq_pid]=0;
          }
          else if (pdu[0] == 2 || pdu[0] == 4) // NAK (treat DTX as NAK)
            sched_ctl->round[CC_idP][harq_pid]++; // increment round
        }
        else {
          // one or two ACK/NAK bits
          AssertFatal(num_ack_nak>2,"num_ack_nak %d > 2 for 1 CC and TM3/4/8/9/10\n",num_ack_nak);
          if ((num_ack_nak==2) && (sched_ctl->round[CC_idP][harq_pid]<8) && (sched_ctl->tbcnt[CC_idP][harq_pid]==1) && (pdu[0] == 1) && (pdu[1] == 1)) {
            sched_ctl->round[CC_idP][harq_pid]=8;
            sched_ctl->tbcnt[CC_idP][harq_pid]=0;
          }
          if ((num_ack_nak==2) && (sched_ctl->round[CC_idP][harq_pid]<8) && (sched_ctl->tbcnt[CC_idP][harq_pid]==1) && (pdu[0] == 2) && (pdu[1] == 2))
            sched_ctl->round[CC_idP][harq_pid]++;
          else if (((num_ack_nak==2) && (sched_ctl->round[CC_idP][harq_pid]<8) && (sched_ctl->tbcnt[0][harq_pid]==2) && (pdu[0] == 1) && (pdu[1] == 2)) ||
                   ((num_ack_nak==2) && (sched_ctl->round[CC_idP][harq_pid]<8) && (sched_ctl->tbcnt[CC_idP][harq_pid]==2) && (pdu[0] == 2) && (pdu[1] == 1))) {
            sched_ctl->round[CC_idP][harq_pid]++;
            sched_ctl->tbcnt[CC_idP][harq_pid]=1;
          }
          else if ((num_ack_nak==2) && (sched_ctl->round[CC_idP][harq_pid]<8) && (sched_ctl->tbcnt[CC_idP][harq_pid]==2) && (pdu[0] == 2) && (pdu[1] == 2))
            sched_ctl->round[CC_idP][harq_pid]++;
          else AssertFatal(1==0,"Illegal ACK/NAK/round combination (%d,%d,%d,%d,%d) for harq_pid %d, UE %d/%x\n",
                           num_ack_nak,sched_ctl->round[CC_idP][harq_pid],sched_ctl->round[CC_idP][harq_pid],pdu[0],pdu[1], harq_pid,UE_id,
                           rnti);
        }
        break;
      case 1: // FDD Channel Selection (10.1.2.2.1), must be received for 2 serving cells
        AssertFatal(numCC==2,"Should not receive harq indication with channel selection with %d active CCs\n",
                    numCC);

        if ((num_ack_nak == 2) && (sched_ctl->round[pCCid][harq_pid]<8) && (sched_ctl->round[1-pCCid][harq_pid]<8) && (sched_ctl->tbcnt[pCCid][harq_pid]==1) && (sched_ctl->tbcnt[1-pCCid][harq_pid]==1)) {
          AssertFatal(pdu[0]<=3,"pdu[0] %d is not ACK/NAK/DTX\n",pdu[0]);
          AssertFatal(pdu[1]<=3,"pdu[1] %d is not ACK/NAK/DTX\n",pdu[1]);
          if (pdu[0] == 1) sched_ctl->round[pCCid][harq_pid]=8;
          else             sched_ctl->round[pCCid][harq_pid]++;
          if (pdu[1] == 1) sched_ctl->round[1-pCCid][harq_pid]=8;
          else             sched_ctl->round[1-pCCid][harq_pid]++;
        } // A=2
        else if ((num_ack_nak == 3) && (sched_ctl->round[pCCid][harq_pid]<8) && (sched_ctl->tbcnt[pCCid][harq_pid]==2) && (sched_ctl->round[1-pCCid][harq_pid]<8) && (sched_ctl->tbcnt[1-pCCid][harq_pid]==1)) {
          AssertFatal(pdu[0]<=3,"pdu[0] %d is not ACK/NAK/DTX\n",pdu[0]);
          AssertFatal(pdu[1]<=3,"pdu[1] %d is not ACK/NAK/DTX\n",pdu[1]);
          AssertFatal(pdu[2]<=3,"pdu[2] %d is not ACK/NAK/DTX\n",pdu[2]);
          AssertFatal(sched_ctl->tbcnt[pCCid][harq_pid] == 2,"sched_ctl->tbcnt[%d][%d] != 2 for UE %d/%x\n",pCCid,harq_pid,UE_id,rnti);
          AssertFatal(sched_ctl->tbcnt[1-pCCid][harq_pid] == 1,"sched_ctl->tbcnt[%d][%d] != 1 for UE %d/%x\n",1-pCCid,harq_pid,UE_id,rnti);
          if ((pdu[0] == 1) && (pdu[1] == 1)) { // both ACK
              sched_ctl->round[pCCid][harq_pid]=8;
              sched_ctl->tbcnt[pCCid][harq_pid]=0;
          }
          else if (((pdu[0] == 2) && (pdu[1] == 1))||
                   ((pdu[0] == 1) && (pdu[1] == 2))){
            sched_ctl->round[pCCid][harq_pid]++;
            sched_ctl->tbcnt[pCCid][harq_pid]=1;
          }
          else
             sched_ctl->round[pCCid][harq_pid]++;

          if (pdu[2] == 1) sched_ctl->round[1-pCCid][harq_pid]=8;
          else             sched_ctl->round[1-pCCid][harq_pid]++;
        } // A=3 primary cell has 2 TBs
        else if ((num_ack_nak == 3) && (sched_ctl->round[1-pCCid][harq_pid]<8) && (sched_ctl->round[pCCid][harq_pid]<8) && (sched_ctl->tbcnt[1-pCCid][harq_pid]==2) && (sched_ctl->tbcnt[pCCid][harq_pid]==1)) {
          AssertFatal(pdu[0]<=3,"pdu[0] %d is not ACK/NAK/DTX\n",pdu[0]);
          AssertFatal(pdu[1]<=3,"pdu[1] %d is not ACK/NAK/DTX\n",pdu[1]);
          AssertFatal(pdu[2]<=3,"pdu[2] %d is not ACK/NAK/DTX\n",pdu[2]);
          AssertFatal(sched_ctl->tbcnt[1-pCCid][harq_pid] == 2,"sched_ctl->tbcnt[%d][%d] != 2 for UE %d/%x\n",1-pCCid,harq_pid,UE_id,rnti);
          AssertFatal(sched_ctl->tbcnt[pCCid][harq_pid] == 1,"sched_ctl->tbcnt[%d][%d] != 1 for UE %d/%x\n",pCCid,harq_pid,UE_id,rnti);
          if ((pdu[0] == 1) && (pdu[1] == 1)) { // both ACK
              sched_ctl->round[1-pCCid][harq_pid]=8;
              sched_ctl->tbcnt[1-pCCid][harq_pid]=0;
          }
          else if (((pdu[0] >= 2) && (pdu[1] == 1))||
                   ((pdu[0] == 1) && (pdu[1] >= 2))){ // one ACK
            sched_ctl->round[1-pCCid][harq_pid]++;
            sched_ctl->tbcnt[1-pCCid][harq_pid]=1;
          }
          else  // both NAK/DTX
             sched_ctl->round[1-pCCid][harq_pid]++;

          if (pdu[2] == 1) sched_ctl->round[pCCid][harq_pid]=8;
          else             sched_ctl->round[pCCid][harq_pid]++;
        } // A=3 secondary cell has 2 TBs
#if MAX_NUM_CCs>1
        else if ((num_ack_nak == 4) && (sched_ctl->round[0][harq_pid]<8) && (sched_ctl->round[1][harq_pid]<8) && (sched_ctl->tbcnt[1-pCCid][harq_pid]==2) && (sched_ctl->tbcnt[pCCid][harq_pid]==2)) {
          AssertFatal(pdu[0]<=3,"pdu[0] %d is not ACK/NAK/DTX\n",pdu[0]);
          AssertFatal(pdu[1]<=3,"pdu[1] %d is not ACK/NAK/DTX\n",pdu[1]);
          AssertFatal(pdu[2]<=3,"pdu[2] %d is not ACK/NAK/DTX\n",pdu[2]);
          AssertFatal(pdu[3]<=3,"pdu[3] %d is not ACK/NAK/DTX\n",pdu[3]);
          AssertFatal(sched_ctl->tbcnt[0][harq_pid] == 2,"sched_ctl->tbcnt[0][%d] != 2 for UE %d/%x\n",harq_pid,UE_id,rnti);
          AssertFatal(sched_ctl->tbcnt[1][harq_pid] == 2,"sched_ctl->tbcnt[1][%d] != 2 for UE %d/%x\n",harq_pid,UE_id,rnti);
          if ((pdu[0] == 1) && (pdu[1] == 1)) { // both ACK
              sched_ctl->round[0][harq_pid]=8;
              sched_ctl->tbcnt[0][harq_pid]=0;
          }
          else if (((pdu[0] >= 2) && (pdu[1] == 1))||
                   ((pdu[0] == 1) && (pdu[1] >= 2))){ // one ACK
            sched_ctl->round[0][harq_pid]++;
            sched_ctl->tbcnt[0][harq_pid]=1;
          }
          else  // both NAK/DTX
             sched_ctl->round[0][harq_pid]++;

          if ((pdu[2] == 1) && (pdu[3] == 1)) { // both ACK
              sched_ctl->round[1][harq_pid]=8;
              sched_ctl->tbcnt[1][harq_pid]=0;
          }
          else if (((pdu[2] >= 2) && (pdu[3] == 1))||
                   ((pdu[2] == 1) && (pdu[3] >= 2))){ // one ACK
            sched_ctl->round[1][harq_pid]++;
            sched_ctl->tbcnt[1][harq_pid]=1;
          }
          else  // both NAK/DTX
             sched_ctl->round[1][harq_pid]++;
        } // A=4 both serving cells have 2 TBs
#endif
        break;
      case 2: // Format 3
        AssertFatal(numCC>2,"Should not receive harq indication with FDD format 3 with %d < 3 active CCs\n",
                    numCC);
        for (i=0,j=0;i<numCC;i++) {
          if ((sched_ctl->round[i][harq_pid]<8)) {
            if (tmode[i]==1 || tmode[i]==2 || tmode[0]==5 || tmode[0]==6 || tmode[0]==7) {
              if (pdu[j] == 1) {
                sched_ctl->round[i][harq_pid]=8;
                sched_ctl->tbcnt[i][harq_pid]=0;
              }
              else if (pdu[j] == 2) sched_ctl->round[i][harq_pid]++;
              else AssertFatal(1==0,"Illegal harq_ack value for CC %d harq_pid %d (%d) UE %d/%x\n",
                               i,harq_pid,pdu[j],UE_id,rnti);
              j++;
            }
            else if (spatial_bundling == 0) {
              if      ((sched_ctl->tbcnt[i][harq_pid]==2) && (pdu[j] == 1) && (pdu[j+1]==1)) {
                sched_ctl->round[i][harq_pid]=8;
                sched_ctl->tbcnt[i][harq_pid]=0;
              }
              else if ((sched_ctl->tbcnt[i][harq_pid]==2) && (pdu[j] == 1) && (pdu[j+1]==2)) {
                sched_ctl->round[i][harq_pid]++;
                sched_ctl->tbcnt[i][harq_pid]=1;
              }
              else if ((sched_ctl->tbcnt[i][harq_pid]==2) && (pdu[j] == 2) && (pdu[j+1]==1)) {
                sched_ctl->round[i][harq_pid]++;
                sched_ctl->tbcnt[i][harq_pid]=1;
              }
              else if ((sched_ctl->tbcnt[i][harq_pid]==2) && (pdu[j] == 2) && (pdu[j+1]==2)) {
                sched_ctl->round[i][harq_pid]++;
              }
              else AssertFatal(1==0,"Illegal combination for CC %d harq_pid %d (%d,%d,%d) UE %d/%x\n",
                               i,harq_pid,sched_ctl->tbcnt[i][harq_pid],pdu[j],pdu[j+1],UE_id,rnti);
              j+=2;
            }
            else if (spatial_bundling == 1) {
              if      (pdu[j] == 1) {
                sched_ctl->round[i][harq_pid]=8;
                sched_ctl->tbcnt[i][harq_pid]=0;
              }
              else if (pdu[j] == 2) {
                sched_ctl->round[i][harq_pid]++;
              }
              else AssertFatal(1==0,"Illegal hack_nak value %d for CC %d harq_pid %d UE %d/%x\n",
                               pdu[j],i,harq_pid,UE_id,rnti);
              j++;
            }
            else AssertFatal(1==0,"Illegal value for spatial_bundling %d\n",spatial_bundling);
          }
        }
        break;
      case 3: // Format 4
        AssertFatal(1==0,"Should not receive harq indication with Format 4\n");
        break;
      case 4: // Format 5
        AssertFatal(1==0,"Should not receive harq indication with Format 5\n");
        break;
    }
  }
}

void extract_pucch_csi(module_id_t mod_idP,int CC_idP,int UE_id, frame_t frameP,sub_frame_t subframeP,uint8_t *pdu, uint8_t length)
{
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;
  UE_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];
  COMMON_channels_t *cc=&RC.mac[mod_idP]->common_channels[CC_idP];
  struct CQI_ReportPeriodic *cqi_ReportPeriodic;
  int no_pmi;
  uint8_t Ltab[6] = {0,2,4,4,4,4};
  uint8_t Jtab[6] = {0,2,2,3,4,4};
  int feedback_cnt;

  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated != NULL, "physicalConfigDedicated is null for UE %d\n",UE_id);
  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->cqi_ReportConfig != NULL,"cqi_ReportConfig is null for UE %d\n",UE_id);
  AssertFatal((cqi_ReportPeriodic = UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->cqi_ReportConfig->cqi_ReportPeriodic)!=NULL,
              "cqi_ReportPeriodic is null for UE %d\n",UE_id);

  // determine feedback mode
  AssertFatal(cqi_ReportPeriodic->present != CQI_ReportPeriodic_PR_NOTHING,
              "cqi_ReportPeriodic->present == CQI_ReportPeriodic_PR_NOTHING!\n");
  AssertFatal(cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present != CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_NOTHING,
              "cqi_ReportPeriodic->cqi_FormatIndicatorPeriodic.choice.setup.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_NOTHING!\n");

  uint16_t Npd,N_OFFSET_CQI;
  int H,K,bandwidth_part,L,Lmask;
  int ri    = sched_ctl->periodic_ri_received[CC_idP];

  get_csi_params(cc,cqi_ReportPeriodic,&Npd,&N_OFFSET_CQI,&H);
  K            =(H-1)/Jtab[cc->mib->message.dl_Bandwidth];
  L            = Ltab[cc->mib->message.dl_Bandwidth];
  Lmask        =L-1;
  feedback_cnt = (((frameP*10)+subframeP)/Npd)%H;

  if (feedback_cnt>0) bandwidth_part = (feedback_cnt-1)%K;
  else                bandwidth_part = 0;

  switch(get_tmode(mod_idP,CC_idP,UE_id)) {
  case 1:
  case 2:
  case 3:
  case 7:
    no_pmi=1;
    break;
  case 4:
  case 5:
  case 6:
    no_pmi=0;
    break;
  default:
    // note: need to check TM8-10 without PMI/RI or with 1 antenna port (see Section 5.2.3.3.1 from 36.213)
    no_pmi=0;
  }

  if ((cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_widebandCQI) ||
      (feedback_cnt==0)){
      // Note: This implements only Tables: 5.3.3.1-1,5.3.3.1-1A and 5.3.3.1-2 from 36.213 (1,2,4 antenna ports Wideband CQI/PMI)

    if (no_pmi == 1) { // get spatial_diffcqi if needed
      sched_ctl->periodic_wideband_cqi[CC_idP]             = pdu[0]&0xF;
      sched_ctl->periodic_wideband_spatial_diffcqi[CC_idP] = (pdu[0]>>4)&7;
    }
    else if ((cc->p_eNB==2) && (ri==1))  {
      // p=2 Rank 1 wideband CQI/PMI 6 bits
      sched_ctl->periodic_wideband_cqi[CC_idP]             = pdu[0]&0xF;
      sched_ctl->periodic_wideband_pmi[CC_idP]             = (pdu[0]>>4)&3;
    }
    else if ((cc->p_eNB==2) && (ri>1)) {
      // p=2 Rank 2 wideband CQI/PMI 8 bits
      sched_ctl->periodic_wideband_cqi[CC_idP]             = pdu[0]&0xF;
      sched_ctl->periodic_wideband_spatial_diffcqi[CC_idP] = (pdu[0]>>4)&7;
      sched_ctl->periodic_wideband_pmi[CC_idP]             = (pdu[0]>>7)&1;
    }
    else if ((cc->p_eNB==4) && (ri==1)) {
      // p=4 Rank 1 wideband CQI/PMI 8 bits
      sched_ctl->periodic_wideband_cqi[CC_idP]             = pdu[0]&0xF;
      sched_ctl->periodic_wideband_pmi[CC_idP]             = (pdu[0]>>4)&0x0F;

    }
    else if ((cc->p_eNB==4) && (ri>1)) {
      // p=4 Rank 2 wideband CQI/PMI 11 bits
      sched_ctl->periodic_wideband_cqi[CC_idP]             = pdu[0]&0xF;
      sched_ctl->periodic_wideband_spatial_diffcqi[CC_idP] = (pdu[0]>>4)&7;
      sched_ctl->periodic_wideband_pmi[CC_idP]             = (pdu[0]>>7)&0xF;
    }
    else AssertFatal(1==0,"illegal combination p %d, ri %d, no_pmi %d\n",cc->p_eNB,ri,no_pmi);
  }
  else if (cqi_ReportPeriodic->choice.setup.cqi_FormatIndicatorPeriodic.present == CQI_ReportPeriodic__setup__cqi_FormatIndicatorPeriodic_PR_subbandCQI) {
    // This is Table 5.2.3.3.2-2 for 36.213
    if (ri==1) {
      //4+Ltab[cc->mib->message.dl_Bandwidth] bits
      sched_ctl->periodic_subband_cqi[CC_idP][(bandwidth_part*L)+((pdu[0]>>4)&Lmask)]              = pdu[0]&0xF;
    }
    else if (ri>1) {
      //7+Ltab[cc->mib->message.dl_Bandwidth] bits;
      sched_ctl->periodic_subband_spatial_diffcqi[CC_idP][(bandwidth_part*L)+((pdu[0]>>7)&Lmask)]  = (pdu[0]>>4)&7;
      sched_ctl->periodic_subband_cqi[CC_idP][(bandwidth_part*L)+((pdu[0]>>7)&Lmask)]              = pdu[0]&0xF;
    }
  }
}

void extract_pusch_csi(module_id_t mod_idP,int CC_idP,int UE_id, frame_t frameP,sub_frame_t subframeP,uint8_t *pdu, uint8_t length)
{
  UE_list_t *UE_list    = &RC.mac[mod_idP]->UE_list;
  COMMON_channels_t *cc = &RC.mac[mod_idP]->common_channels[CC_idP];
  UE_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];
  int Ntab[6]       = {0,4,7,9,10,13};
  int Ntab_uesel[6] = {0,8,13,17,19,25};
  int Ltab_uesel[6] = {0,6,9,13,15,18};
  int Mtab_uesel[6] = {0,1,3,5,6,6};
  int v[6];
  int i;
  uint64_t p = *(uint64_t*)pdu;
  int curbyte, curbit;
  CQI_ReportModeAperiodic_t *cqi_ReportModeAperiodic;

  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated != NULL, "physicalConfigDedicated is null for UE %d\n",UE_id);
  AssertFatal(UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->cqi_ReportConfig != NULL,"cqi_ReportConfig is null for UE %d\n",UE_id);
  AssertFatal((cqi_ReportModeAperiodic = UE_list->UE_template[CC_idP][UE_id].physicalConfigDedicated->cqi_ReportConfig->cqi_ReportModeAperiodic)!=NULL,
              "cqi_ReportModeAperiodic is null for UE %d\n",UE_id);

  int N     = Ntab[cc->mib->message.dl_Bandwidth];
  int tmode = get_tmode(mod_idP,CC_idP,UE_id);
  int ri    = sched_ctl->aperiodic_ri_received[CC_idP];
  int r,diffcqi0=0,diffcqi1=0,pmi_uesel=0;
  int bw = cc->mib->message.dl_Bandwidth;
  int m;

  switch(*cqi_ReportModeAperiodic) {
  case CQI_ReportModeAperiodic_rm12:
    AssertFatal(0==1, "to be fixed, don't use p but pdu directly\n");
    // wideband multiple PMI (TM4/6), Table 5.2.2.6.1-1 (for TM4/6)
    AssertFatal(tmode==4 || tmode==6 || tmode==8|| tmode==9 || tmode==10,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm12\n",tmode);
    if (tmode <= 6) { //Table 5.2.2.6.1-1 36.213
      if ((ri==1) && (cc->p_eNB==2)) {
        sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        for (i=0;i<N;i++) {
          sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x03);
          p>>=2;
        }
      }
      if ((ri==2) && (cc->p_eNB==2)) {
        sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        sched_ctl->aperiodic_wideband_cqi1[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        for (i=0;i<N;i++) {
          sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
          p>>=1;
        }
      }
      if ((ri==1) && (cc->p_eNB==4)) {
        sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        for (i=0;i<N;i++) {
          sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x03);
          p>>=4;
        }
      }
      if ((ri==2) && (cc->p_eNB==4)) {
        sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        sched_ctl->aperiodic_wideband_cqi1[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
        for (i=0;i<N;i++) {
          sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
          p>>=4;
        }
      }
    } // if (tmode <= 6) { //Table 5.2.2.6.1-1 36.213
    else {
      AssertFatal(1==0,"support for TM 8-10 to be done\n");
    }

   break;
  case CQI_ReportModeAperiodic_rm20:
    AssertFatal(0==1, "to be fixed, don't use p but pdu directly\n");
    // UE-selected subband CQI no PMI (TM1/2/3/7) , Table 5.2.2.6.3-1 from 36.213
    AssertFatal(tmode==1 || tmode==2 || tmode==3 || tmode==7,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm20\n",tmode);

    sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
    diffcqi0 = (uint8_t)(p&0x03); p>>=2;
    r = (uint8_t)(p&((1>>Ltab_uesel[bw])-1));
    reverse_index(Ntab_uesel[bw],Mtab_uesel[bw],r,v);
    for (m=0;m<Mtab_uesel[bw];m++) sched_ctl->aperiodic_subband_diffcqi0[CC_idP][v[m]] = diffcqi0;
    break;
  case CQI_ReportModeAperiodic_rm22:
    AssertFatal(0==1, "to be fixed, don't use p but pdu directly\n");
    // UE-selected subband CQI multiple PMI (TM4/6) Table 5.2.2.6.3-2 from 36.213

    AssertFatal(tmode==4 || tmode==6 || tmode==8|| tmode==9 || tmode==10,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm22\n",tmode);

    sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
    diffcqi0 = (uint8_t)(p&0x03); p>>=2;

    if (ri>1) {
      sched_ctl->aperiodic_wideband_cqi1[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      diffcqi1 = (uint8_t)(p&0x03); p>>=2;
    }
    r = (uint8_t)(p&((1>>Ltab_uesel[bw])-1)); p>>=Ltab_uesel[bw];
    reverse_index(Ntab_uesel[bw],Mtab_uesel[bw],r,v);
    if ((ri==1)&&(cc->p_eNB==2)) {
      pmi_uesel                                  = p&0x3; p>>=2;
      sched_ctl->aperiodic_wideband_pmi[CC_idP]  = p&0x3;
    }
    else if ((ri==2)&&(cc->p_eNB==2)) {
      pmi_uesel                                  = p&0x1; p>>=1;
      sched_ctl->aperiodic_wideband_pmi[CC_idP]  = p&0x1;
    }
    else if (cc->p_eNB==4) {
      pmi_uesel                                  = p&0x0F; p>>=4;
      sched_ctl->aperiodic_wideband_pmi[CC_idP]  = p&0x0F;
    }
    for (m=0;m<Mtab_uesel[bw];m++) {
      sched_ctl->aperiodic_subband_diffcqi0[CC_idP][v[m]] = diffcqi0;
      if (ri>1) sched_ctl->aperiodic_subband_diffcqi1[CC_idP][v[m]] = diffcqi1;
      sched_ctl->aperiodic_subband_pmi[CC_idP][v[m]]      = pmi_uesel;
    }
    break;
  case CQI_ReportModeAperiodic_rm30:
    //subband CQI no PMI (TM1/2/3/7)
    AssertFatal(tmode==1 || tmode==2 || tmode==3 || tmode==7,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm30\n",tmode);
    sched_ctl->aperiodic_wideband_cqi0[CC_idP] = pdu[0]>>4;
    curbyte = 0;
    curbit = 3;
    for (i=0;i<N;i++) {
      sched_ctl->aperiodic_subband_diffcqi0[CC_idP][i] = (pdu[curbyte] >> (curbit-1)) & 0x03;
      curbit -= 2;
      if (curbit < 0) {
        curbit = 7;
        curbyte++;
      }
    }
    sched_ctl->dl_cqi[CC_idP] = sched_ctl->aperiodic_wideband_cqi0[CC_idP];
    break;
  case CQI_ReportModeAperiodic_rm31:
    AssertFatal(0==1, "to be fixed, don't use p but pdu directly\n");
    //subband CQI single PMI (TM4/5/6)
    AssertFatal(tmode==4 || tmode==5 || tmode==6 || tmode==8|| tmode==9|| tmode==10,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm31\n",tmode);

    if ((ri==1) && (cc->p_eNB==2)) {
      sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_diffcqi0[CC_idP][i] = (uint8_t)(p&0x03);
        p>>=2;
      }
      sched_ctl->aperiodic_wideband_pmi[CC_idP] = p&0x03;
    }
    if ((ri==2) && (cc->p_eNB==2)) {
      sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
        p>>=1;
      }
      sched_ctl->aperiodic_wideband_cqi1[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
        p>>=1;
      }
      sched_ctl->aperiodic_wideband_pmi[CC_idP] = p&0x01;
    }
    if ((ri==1) && (cc->p_eNB==4)) {
      sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_diffcqi0[CC_idP][i] = (uint8_t)(p&0x03);
        p>>=2;
      }
      sched_ctl->aperiodic_wideband_pmi[CC_idP] = p&0x0F;
    }
    if ((ri>1) && (cc->p_eNB==4)) { // Note : 64 bits for 20 MHz
      sched_ctl->aperiodic_wideband_cqi0[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
        p>>=1;
      }
      sched_ctl->aperiodic_wideband_cqi1[CC_idP] = (uint8_t)(p&0x0F); p>>=4;
      for (i=0;i<N;i++) {
        sched_ctl->aperiodic_subband_pmi[CC_idP][i] = (uint8_t)(p&0x01);
        p>>=2;
      }
      sched_ctl->aperiodic_wideband_pmi[CC_idP] = p&0x0F;
    }

    break;
  case CQI_ReportModeAperiodic_rm32_v1250:
    AssertFatal(tmode==4 || tmode==5 || tmode==6 || tmode==8|| tmode==9|| tmode==10,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm32\n",tmode);
    AssertFatal(1==0,"CQI_ReportModeAperiodic_rm32 to be done\n");
    break;
  case CQI_ReportModeAperiodic_rm10_v1310:
    AssertFatal(tmode==1 || tmode==2 || tmode==3 || tmode==7,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm10\n",tmode);
    AssertFatal(1==0,"CQI_ReportModeAperiodic_rm10 to be done\n");
    break;
  case CQI_ReportModeAperiodic_rm11_v1310:
    AssertFatal(tmode==4 || tmode==5 || tmode==6 || tmode==8|| tmode==9|| tmode==10,"Illegal transmission mode %d for CQI_ReportModeAperiodic_rm11\n",tmode);
    AssertFatal(1==0,"CQI_ReportModeAperiodic_rm11 to be done\n");
    break;
  }
}

void cqi_indication(module_id_t mod_idP, int CC_idP, frame_t frameP, sub_frame_t subframeP, rnti_t rntiP,
                    nfapi_cqi_indication_rel9_t *rel9,uint8_t *pdu,
                    nfapi_ul_cqi_information_t *ul_cqi_information)
{
  int UE_id = find_UE_id(mod_idP, rntiP);
  UE_list_t *UE_list = &RC.mac[mod_idP]->UE_list;
  UE_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];

  if (UE_id  >= 0) {

    if (ul_cqi_information->channel == 0) { // PUCCH

      // extract pucch csi information before changing RI information
      extract_pucch_csi(mod_idP,CC_idP,UE_id,frameP,subframeP,pdu,rel9->length);

      memcpy((void*)sched_ctl->periodic_ri_received,
             (void*)rel9->ri,
             rel9->number_of_cc_reported);

      // SNR for PUCCH2
      sched_ctl->pucch2_snr[CC_idP] = ul_cqi_information->ul_cqi;
    }
    else { //PUSCH
      memcpy((void*)sched_ctl->aperiodic_ri_received,
             (void*)rel9->ri,
             rel9->number_of_cc_reported);

      extract_pusch_csi(mod_idP,CC_idP,UE_id,frameP,subframeP,pdu,rel9->length);

    }

    // timing advance
    sched_ctl->timing_advance    = rel9->timing_advance;
    sched_ctl->timing_advance_r9 = rel9->timing_advance_r9;
  }
}

void SR_indication(module_id_t mod_idP, int cc_idP, frame_t frameP, sub_frame_t subframeP, rnti_t rntiP, uint8_t ul_cqi)
{
  T(T_ENB_MAC_SCHEDULING_REQUEST, T_INT(mod_idP), T_INT(cc_idP), T_INT(frameP), T_INT(subframeP), T_INT(rntiP));

  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_D(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_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];

    sched_ctl->pucch1_snr[cc_idP]        = ul_cqi;
    sched_ctl->pucch1_cqi_update[cc_idP] = 1;

    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_D(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);
  }
}

void harq_indication(module_id_t mod_idP, int CC_idP, frame_t frameP, sub_frame_t subframeP, nfapi_harq_indication_pdu_t *harq_pdu)
{
  rnti_t rnti              = harq_pdu->rx_ue_information.rnti;
  uint8_t ul_cqi           = harq_pdu->ul_cqi_information.ul_cqi;
  uint8_t channel          = harq_pdu->ul_cqi_information.channel;
  int UE_id                = find_UE_id(mod_idP, rnti);
  UE_list_t *UE_list       = &RC.mac[mod_idP]->UE_list;
  UE_sched_ctrl *sched_ctl = &UE_list->UE_sched_ctrl[UE_id];
  COMMON_channels_t *cc    = &RC.mac[mod_idP]->common_channels[CC_idP];
    // extract HARQ Information
  LOG_D(MAC,"Frame %d, subframe %d: Received harq indication (%d) from UE %d/%x, ul_cqi %d\n",frameP,subframeP,channel,UE_id,rnti,ul_cqi);
  if (cc->tdd_Config) extract_harq(mod_idP,CC_idP,UE_id,frameP,subframeP,(void*)&harq_pdu->harq_indication_tdd_rel13,channel);
  else                extract_harq(mod_idP,CC_idP,UE_id,frameP,subframeP,(void*)&harq_pdu->harq_indication_fdd_rel13,channel);
  if (channel == 0) {
    sched_ctl->pucch1_snr[CC_idP]       = ul_cqi;
    sched_ctl->pucch1_cqi_update[CC_idP] = 1;
  }
}