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

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


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

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

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

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

*******************************************************************************/
/*! \file eNB_scheduler.c
 * \brief eNB scheduler top level function operates on per subframe basis
 * \author  Navid Nikaein and Raymond Knopp
 * \date 2010 - 2014
 * \email: navid.nikaein@eurecom.fr
 * \version 0.5
 * @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
//#define DEBUG_HEADER_PARSING 1
//#define DEBUG_PACKET_TRACE 1

/*
  #ifndef USER_MODE
  #define msg debug_msg
  #endif
 */




#if FAPI

/* global variables to deal with TPC command - we send one only once per frame maximum */
static long global_subframe;                        /* global subframe - incremented at each TTI */
static long ue_last_pucch_tpc[NUMBER_OF_UE_MAX];    /* last global subframe where a PUCCH TPC was sent to the UE */
static long ue_last_pusch_tpc[NUMBER_OF_UE_MAX];    /* last global subframe where a PUSCH TPC was sent to the UE */

static void fapi_dl_tpc(int module_id, int CC_id, struct DlDciListElement_s *dci)
{
  int              UE_id                = find_UE_id(module_id, dci->rnti);
  LTE_eNB_UE_stats *eNB_UE_stats        = NULL;
  int32_t          normalized_rx_power;
  int32_t          target_rx_power;
  static int       tpc_accumulated = 0;

if (UE_id == -1) {
#if MEGALOG
printf("SCHEDULER fapi_dl_tpc rnti %x (%d) not existing in MAC\n", dci->rnti, dci->rnti);
#endif
dci->tpc = 0;
return;
}

  if (UE_id == -1) { printf("%s:%d: rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); }

  // this assumes accumulated tpc
  // make sure that we are only sending a tpc update once a frame, otherwise the control loop will freak out
  if (global_subframe < ue_last_pucch_tpc[UE_id] + 10) {
#if MEGALOG
printf("TPC PUCCH global sf %ld (%ld/%ld) UE %x: no TPC, last one is too recent (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, tpc_accumulated);
#endif
    dci->tpc = 0;
    return;
  }

  eNB_UE_stats        = mac_xface->get_eNB_UE_stats(module_id, CC_id, dci->rnti);
  if (eNB_UE_stats == NULL) { printf("%s:%d: stats for rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); }
  normalized_rx_power = eNB_UE_stats->Po_PUCCH_dBm;
  target_rx_power     = mac_xface->get_target_pucch_rx_power(module_id, CC_id) + 20;

  if (eNB_UE_stats->Po_PUCCH_update == 1) {
    eNB_UE_stats->Po_PUCCH_update = 0;

    ue_last_pucch_tpc[UE_id] = global_subframe;

    if (normalized_rx_power > target_rx_power + 1) {
      dci->tpc = -1;
      tpc_accumulated--;
    } else if (normalized_rx_power < target_rx_power - 1) {
      dci->tpc = 1;
      tpc_accumulated++;
    } else {
      dci->tpc = 0;
    }
  }
#if MEGALOG
printf("TPC PUCCH global sf %ld (%ld/%ld) UE %x: TPC %d (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, dci->tpc, tpc_accumulated);
#endif
}

static void fapi_ul_tpc(int module_id, int CC_id, struct UlDciListElement_s *dci)
{
  int              UE_id                = find_UE_id(module_id, dci->rnti);
  LTE_eNB_UE_stats *eNB_UE_stats        = NULL;
  int32_t          normalized_rx_power;
  int32_t          target_rx_power;
  static int       tpc_accumulated = 0;

if (UE_id == -1) {
#if MEGALOG
printf("SCHEDULER fapi_ul_tpc rnti %x (%d) not existing in MAC\n", dci->rnti, dci->rnti);
#endif
dci->tpc = 0;
return;
}

  if (UE_id == -1) { printf("%s:%d: rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); }

  // this assumes accumulated tpc
  // make sure that we are only sending a tpc update once a frame, otherwise the control loop will freak out
  if (global_subframe < ue_last_pusch_tpc[UE_id] + 10) {
#if MEGALOG
printf("TPC PUSCH global sf %ld (%ld/%ld) UE %x: no TPC, last one is too recent (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, tpc_accumulated);
#endif
    dci->tpc = 0;
    return;
  }

  eNB_UE_stats        = mac_xface->get_eNB_UE_stats(module_id, CC_id, dci->rnti);
  if (eNB_UE_stats == NULL) { printf("%s:%d: stats for rnti %x not found\n", __FILE__, __LINE__, dci->rnti); abort(); }
  normalized_rx_power = eNB_UE_stats->UL_rssi[0];
  target_rx_power     = mac_xface->get_target_pusch_rx_power(module_id, CC_id);

  if (normalized_rx_power > target_rx_power + 1) {
    dci->tpc = -1;
    tpc_accumulated--;
  } else if (normalized_rx_power < target_rx_power - 1) {
    dci->tpc = 1;
    tpc_accumulated++;
  } else {
    dci->tpc = 0;
  }

  if (dci->tpc != 0) {
    ue_last_pusch_tpc[UE_id] = global_subframe;
  }
#if MEGALOG
printf("TPC PUSCH global sf %ld (%ld/%ld) UE %x: TPC %d (accumulated %d)\n", global_subframe, global_subframe/10, global_subframe%10, dci->rnti, dci->tpc, tpc_accumulated);
#endif
}

/* this structure is used to store downlink ack/nack information
 * to be sent to FAPI by SchedDlTriggerReq
 */
/* TODO: lock access to it or not? */
static struct {
  struct {
    int rnti;
    int CC_id;
    int harq_pid;
    int ack[MAX_TB_LIST];
    int ack_count;
  } ack[MAX_DL_INFO_LIST];
  int count;
} fapi_dl_ack_nack_data;

/* this array is used to store uplink ack/nack information
 * to be sent to FAPI by SchedUlTriggerReq
 * one element per TTI
 */
/* TODO: do it per CC */
static struct {
  struct {
    int reception_frame;          /* the frame where the ACK/NACK has been received */
    int reception_subframe;       /* the subframe where the ACK/NACK has been received */
    int rnti;
    int ack;
    int length[MAX_LC_LIST+1];
  } ack[MAX_UL_INFO_LIST];
  int count;
} fapi_ul_ack_nack_data[10];

/* this function is called by the PHY to signal UE's ACK/NACK */
void fapi_dl_ack_nack(int CC_id, int rnti, int harq_pid, int transport_block, int ack)
{
  int pos = fapi_dl_ack_nack_data.count;
#if MEGALOG
printf("GOT DOWNLINK ack %d for rnti %x harq_pid %d cc %d transport_block %d\n", ack, rnti, harq_pid, CC_id, transport_block);
#endif
  /* TODO: handle more than 1 TB */
  if (transport_block) { printf("%s:%d:%s: TODO: tb != 0\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  if (pos == MAX_DL_INFO_LIST) {
    LOG_E(MAC, "fapi_dl_ack_nack: full!\n");
    abort();
  }

  fapi_dl_ack_nack_data.ack[pos].rnti      = rnti;
  fapi_dl_ack_nack_data.ack[pos].CC_id     = CC_id;
  fapi_dl_ack_nack_data.ack[pos].harq_pid  = harq_pid;
  fapi_dl_ack_nack_data.ack[pos].ack[0]    = ack;       /* TODO: use transport_block here */
  fapi_dl_ack_nack_data.ack[pos].ack_count = 1;         /* TODO: take care of transport block */

  fapi_dl_ack_nack_data.count++;
}

/* this array is used to deal with Msg4 ack/nack
 * once we get an ACK, the UE is considered configured
 * if we get a NACK, we have to deal with retransmission of Msg4 (TODO)
 */
unsigned char waiting_for_msg4_ack[65536];

/* this function is called by the PHY to inform about correct or wrong
 * reception by the eNodeB of an uplink UE transmission
 */
/* TODO: do it per CC */
void fapi_ul_ack_nack(int frame, int subframe, int harq_pid, int rnti, int ack)
{
#if MEGALOG
printf("GOT UPLINK ack %d for rnti %x harq_pid %d (f/sf %d/%d)\n", ack, rnti, harq_pid, frame, subframe);
#endif
  int pos = fapi_ul_ack_nack_data[subframe].count;
  if (pos == MAX_UL_INFO_LIST) {
    LOG_E(MAC, "fapi_ul_ack_nack: full! (f/sf %d/%d)\n", frame, subframe);
    abort();
  }
  fapi_ul_ack_nack_data[subframe].ack[pos].reception_frame    = frame;
  fapi_ul_ack_nack_data[subframe].ack[pos].reception_subframe = subframe;
  fapi_ul_ack_nack_data[subframe].ack[pos].rnti               = rnti;
  fapi_ul_ack_nack_data[subframe].ack[pos].ack                = ack;

  if (waiting_for_msg4_ack[rnti] == 1) {
    /* TODO: deal with module_id != 0, CC_id != 0 */
    eNB_MAC_INST  *eNB = &eNB_mac_inst[0 /*module_idP*/];
    RA_TEMPLATE   *RA_template;
    int i, UE_id;
    waiting_for_msg4_ack[rnti] = 0;
    for (i = 0; i < NB_RA_PROC_MAX; i++) {
      RA_template = (RA_TEMPLATE *)&eNB->common_channels[0 /* CC_id */].RA_template[i];
      if (RA_template->RA_active != TRUE) continue;
      if (RA_template->rnti != rnti) continue;
      break;
    }
    if (i == NB_RA_PROC_MAX) { printf("%s:%d:%s: possible?\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
    RA_template->RA_active = FALSE;
    RA_template->wait_ack_Msg4=0;
    fprintf(stderr, "f/sf %d/%d got ack value %d for RA Msg4 for UE %d\n", frame, subframe, ack, rnti);
     UE_id = find_UE_id(0 /*module_idP */,RA_template->rnti);
     if (UE_id == -1) abort();
     eNB_mac_inst[0 /* module_idP */].UE_list.UE_template[UE_PCCID(0 /* module_idP */,UE_id)][UE_id].configured=TRUE;
  }

  /* the values in length are set later in the function fapi_ul_lc_length */
  memset(fapi_ul_ack_nack_data[subframe].ack[pos].length, 0, sizeof(int) * MAX_LC_LIST+1);

  fapi_ul_ack_nack_data[subframe].count++;

  /* TODO: get proper SINR information
   * for the moment: let's report a sinr of 20 (arbitrary)
   * for every RBs
   */
  fapi_interface_t                   *fapi = eNB_mac_inst[0 /* TODO: get correct module_id*/].fapi;
  struct SchedUlCqiInfoReqParameters params;
  struct UlCqi_s                     cqi;
  int                                i;

  cqi.rnti          = rnti;
  for (i = 0; i < 100; i++)
    cqi.sinr[i]     = 20<<3;
  cqi.type          = ff_PUSCH;
  cqi.servCellIndex = 0;         /* TODO: get correct cell index */

  params.sfnSf                 = frame * 16 + subframe;
  params.nr_ulCqiList          = 1;
  params.ulCqiList             = &cqi;
  params.nr_vendorSpecificList = 0;
  params.vendorSpecificList    = NULL;
  SchedUlCqiInfoReq(fapi->sched, &params);
}

/* this function is called by rx_sdu to indicate the number of
 * bytes received by the given Logical Channel
 */
void fapi_ul_lc_length(int frame, int subframe, int lcid, int length, int rnti)
{
#if MEGALOG
printf("GOT lcid %d length %d (f/sf %d/%d)\n", lcid, length, frame, subframe);
#endif
  int pos = fapi_ul_ack_nack_data[subframe].count - 1;
  if (pos < 0) { printf("%s:%d:%s: fatal error\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* TODO: remove this check (and the rnti parameter)? */
  if (rnti != fapi_ul_ack_nack_data[subframe].ack[pos].rnti) {
    printf("%s:%d:%s: fatal error: LCID %d wrong RNTI %x (expected %x)\n",
           __FILE__, __LINE__, __FUNCTION__, lcid,
           rnti, fapi_ul_ack_nack_data[subframe].ack[pos].rnti);
    abort();
  }

  if (lcid < 0 || lcid > 3)
    { printf("%s:%d:%s: fatal error: unhandled LCID %d\n", __FILE__, __LINE__, __FUNCTION__, lcid); abort(); }

  fapi_ul_ack_nack_data[subframe].ack[pos].length[lcid] = length;
}

void fapi_dl_cqi_report(int module_id, int rnti, int frame, int subframe, int cqi_wideband, int *cqi_subband, int rank_indication)
{
  /* TODO: 2 TBs, other reporting modes - we suppose 3-0 (see 36.213 7.2.1) */
  fapi_interface_t                   *fapi;
  struct SchedDlCqiInfoReqParameters params;
  struct CqiListElement_s            cqi;
  int                                i;

  fapi = eNB_mac_inst[module_id].fapi;

  cqi.rnti                          = rnti;
  cqi.csiReport.ri                  = rank_indication;
  cqi.csiReport.mode                = A30;          /* TODO: get real value */
  cqi.csiReport.report.A30Csi.wbCqi = cqi_wideband;
  for (i = 0; i < MAX_HL_SB; i++)
    cqi.csiReport.report.A30Csi.sbCqi[i] = cqi_subband[i];

  /* TODO: remove this oaisim fix
   * value 0 is not allowed in wideband CQI reporting,
   * see 36.213 7.2.3 table 7.2.3-1
   * but we get value 0 in oaisim
   */
  if (cqi.csiReport.report.A30Csi.wbCqi == 0) cqi.csiReport.report.A30Csi.wbCqi++;

  cqi.servCellIndex                 = 0;         /* TODO: get correct value */

  params.sfnSf                 = frame * 16 + subframe;
  params.nrcqiList             = 1;
  params.cqiList               = &cqi;
  params.nr_vendorSpecificList = 0;
  params.vendorSpecificList    = NULL;

//fprintf(stderr, "fsf %d/%d CQI wideband %d rank_indication %d\n", frame, subframe, cqi_wideband, rank_indication);
//for (i = 0; i < MAX_HL_SB; i++)
//fprintf(stderr, "  subband %d: %d\n", i, cqi_subband[i]);
  SchedDlCqiInfoReq(fapi->sched, &params);

/* TODO: get rid of this hack */
cqi.rnti                          = rnti;
cqi.csiReport.ri                  = rank_indication;
cqi.csiReport.mode                = A30;          /* TODO: get real value */
cqi.csiReport.report.A30Csi.wbCqi = cqi_wideband;
for (i = 0; i < MAX_HL_SB; i++)
  cqi.csiReport.report.A30Csi.sbCqi[i] = cqi_subband[i];
if (cqi.csiReport.report.A30Csi.wbCqi == 0) cqi.csiReport.report.A30Csi.wbCqi++;
cqi.servCellIndex                 = 1;         /* TODO: get correct value */
params.sfnSf                 = frame * 16 + subframe;
params.nrcqiList             = 1;
params.cqiList               = &cqi;
params.nr_vendorSpecificList = 0;
params.vendorSpecificList    = NULL;
SchedDlCqiInfoReq(fapi->sched, &params);
}

#define CONVERT_DL_1A_FDD(bandwidth)                                                                    \
static void fapi_convert_dl_1A_ ## bandwidth ## MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) \
{                                                                                                       \
  DCI1A_ ## bandwidth ## MHz_FDD_t *d = (DCI1A_ ## bandwidth ## MHz_FDD_t *)a->dci_pdu;                 \
                                                                                                        \
  if (dci->nr_of_tbs != 1) { printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); exit(1); }                \
                                                                                                        \
  d->type       = 1;    /* type = 0 => DCI Format 0, type = 1 => DCI Format 1A */                       \
  d->vrb_type   = dci->vrbFormat == VRB_LOCALIZED ? 0 :                                                 \
                  dci->vrbFormat == VRB_DISTRIBUTED ? 1 :                                               \
                  (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0);                       \
  d->rballoc    = dci->rbBitmap;                                                                        \
  d->mcs        = dci->mcs[0];        /* TODO: take care of transport block index */                    \
  d->harq_pid   = dci->harqProcess;                                                                     \
  d->ndi        = dci->ndi[0];        /* TODO: take care of transport block index */                    \
  d->rv         = dci->rv[0];         /* TODO: take care of transport block index */                    \
  d->TPC        = dci->tpc == -1 ? 0 :    /* see 36.213 table 5.1.2.1-1 */                              \
                  dci->tpc ==  0 ? 1 :                                                                  \
                  dci->tpc ==  1 ? 2 :                                                                  \
                  dci->tpc ==  3 ? 3 :                                                                  \
                  (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0);                       \
  d->padding    = 0;                                                                                    \
                                                                                                        \
  a->dci_length = sizeof_DCI1A_ ## bandwidth ## MHz_FDD_t;                                              \
  a->format     = format1A;                                                                             \
}

CONVERT_DL_1A_FDD(5);      /* defines fapi_convert_dl_1A_5MHz_FDD */
CONVERT_DL_1A_FDD(10);     /* defines fapi_convert_dl_1A_10MHz_FDD */
CONVERT_DL_1A_FDD(20);     /* defines fapi_convert_dl_1A_20MHz_FDD */

static uint32_t revert(uint32_t x, int len)
{return x;
  int i;
  int ret = 0;
  for (i = 0; i < len; i++) {
    ret <<= 1;
    ret |= x & 1;
    x >>= 1;
  }
  return ret;
}

int cqi_ready[65536];

#define CONVERT_DL_1_FDD(bandwidth)                                                                    \
static void fapi_convert_dl_1_ ## bandwidth ## MHz_FDD(struct DlDciListElement_s *dci, DCI_ALLOC_t *a) \
{                                                                                                      \
  DCI1_ ## bandwidth ## MHz_FDD_t *d = (DCI1_ ## bandwidth ## MHz_FDD_t *)a->dci_pdu;                  \
                                                                                                       \
  if (dci->nr_of_tbs != 1) { printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); exit(1); }               \
                                                                                                       \
  d->rah        = dci->resAlloc == 0 ? 0 :                                                             \
                  dci->resAlloc == 1 ? 1 :                                                             \
                  (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0);                      \
  d->rballoc    = revert(dci->rbBitmap, 13);                                                           \
  d->mcs        = dci->mcs[0];        /* TODO: take care of transport block index */                   \
  d->harq_pid   = dci->harqProcess;                                                                    \
  d->ndi        = dci->ndi[0];        /* TODO: take care of transport block index */                   \
  d->rv         = dci->rv[0];         /* TODO: take care of transport block index */                   \
  d->TPC        = dci->tpc == -1 ? 0 :    /* see 36.213 table 5.1.2.1-1 */                             \
                  dci->tpc ==  0 ? 1 :                                                                 \
                  dci->tpc ==  1 ? 2 :                                                                 \
                  dci->tpc ==  3 ? 3 :                                                                 \
                  (printf("%s:%d: error (dci->tpc = %d)\n", __FUNCTION__, __LINE__, dci->tpc), abort(), 0); \
  d->dummy      = 0;                                                                                   \
                                                                                                       \
  a->dci_length = sizeof_DCI1_ ## bandwidth ## MHz_FDD_t;                                              \
  a->format     = format1;                                                                             \
}

CONVERT_DL_1_FDD(5);     /* defines fapi_convert_dl_1_5MHz_FDD */
CONVERT_DL_1_FDD(10);    /* defines fapi_convert_dl_1_10MHz_FDD */
CONVERT_DL_1_FDD(20);    /* defines fapi_convert_dl_1_20MHz_FDD */

static void fapi_convert_dl_dci(int module_id, int CC_id,
    struct DlDciListElement_s *dci, DCI_ALLOC_t *a)
{
  /* set TPC in the DCI */
  /* TODO: remove it if/when the scheduler does it */
  if (dci->rnti != 0)
    fapi_dl_tpc(module_id, CC_id, dci);

  if (dci->format == ONE && dci->resAlloc != 0) { printf("ERROR: resAlloc must be 0\n"); abort(); }

  /* TODO: handle all bandwidths */
  switch (PHY_vars_eNB_g[module_id][CC_id]->lte_frame_parms.N_RB_DL) {
  case 25:
    switch (dci->format) {
    case ONE_A: fapi_convert_dl_1A_5MHz_FDD(dci, a); break;
    case ONE:   fapi_convert_dl_1_5MHz_FDD(dci, a);  break;
    default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort();
    }
    break;
  case 50:
    switch (dci->format) {
    case ONE_A: fapi_convert_dl_1A_10MHz_FDD(dci, a); break;
    case ONE:   fapi_convert_dl_1_10MHz_FDD(dci, a);  break;
    default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort();
    }
    break;
  case 100:
    switch (dci->format) {
    case ONE_A: fapi_convert_dl_1A_20MHz_FDD(dci, a); break;
    case ONE:   fapi_convert_dl_1_20MHz_FDD(dci, a);  break;
    default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort();
    }
    break;
  default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort();
  }
  a->L = dci->aggrLevel == 1 ? 0 :
         dci->aggrLevel == 2 ? 1 :
         dci->aggrLevel == 4 ? 2 :
         dci->aggrLevel == 8 ? 3 :
         (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0);
  a->firstCCE = dci->cceIndex;
  a->ra_flag  = 0;              /* TODO: set to 1 only by fapi_schedule_RAR, is it ok? */
  a->rnti     = dci->rnti;
}

#define CONVERT_UL_FDD(bandwidth)                                                                       \
static void fapi_convert_ul_ ## bandwidth ## MHz_FDD(module_id_t module_idP, int CC_id,                 \
    struct UlDciListElement_s *dci, DCI_ALLOC_t *a)                                                     \
{                                                                                                       \
  DCI0_ ## bandwidth ## MHz_FDD_t *d = (DCI0_ ## bandwidth ## MHz_FDD_t *)a->dci_pdu;                   \
                                                                                                        \
  d->type       = 0;    /* type = 0 => DCI Format 0, type = 1 => DCI Format 1A */                       \
  d->hopping    = dci->hopping;                                                                         \
  d->rballoc    = mac_xface->computeRIV(PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL, dci->rbStart, dci->rbLen); \
  d->mcs        = dci->mcs;                                                                             \
  d->ndi        = dci->ndi;                                                                             \
  d->TPC        = dci->tpc == -1 ? 0 :    /* see 36.213 table 5.1.1.1-2, accumulated case supposed */   \
                  dci->tpc ==  0 ? 1 :                                                                  \
                  dci->tpc ==  1 ? 2 :                                                                  \
                  dci->tpc ==  3 ? 3 :                                                                  \
                  (printf("%s:%d: error (tpc = %d)\n", __FUNCTION__, __LINE__, dci->tpc), abort(), 0);  \
  d->cshift     = dci->n2Dmrs;    /* TODO: this may be wrong, */                                        \
                                  /* see openair1/PHY/LTE_TRANSPORT/dci_tools.c:generate_eNB_ulsch_params_from_dci */ \
                                  /* there is a translation between those */                            \
  d->cqi_req    = dci->cqiRequest;                                                                      \
if (!cqi_ready[dci->rnti]) d->cqi_req = 0;                                                              \
/* TODO: do not disable CQI request */                                                                  \
d->cqi_req = 0;                                                                                         \
/*if (dci->cqiRequest) fprintf(stderr, "cqi req (ready %d) (d->cqi_req %d)!!\n", cqi_ready[dci->rnti], d->cqi_req);*/ \
  d->padding    = 0;                                                                                    \
                                                                                                        \
  a->dci_length = sizeof_DCI0_ ## bandwidth ## MHz_FDD_t;                                               \
  a->format     = format0;                                                                              \
}

CONVERT_UL_FDD(5);      /* defines fapi_convert_ul_5MHz_FDD */
CONVERT_UL_FDD(10);     /* defines fapi_convert_ul_10MHz_FDD */
CONVERT_UL_FDD(20);     /* defines fapi_convert_ul_20MHz_FDD */

static void fapi_convert_ul_dci(module_id_t module_idP, int CC_id,
    struct UlDciListElement_s *dci, DCI_ALLOC_t *a)
{
  /* set TPC in the DCI */
  /* TODO: remove it if/when the scheduler does it */
  fapi_ul_tpc(module_idP, CC_id, dci);

  if (dci->hopping != 0) { printf("ERROR: hopping must be 0\n"); abort(); }

  /* TODO: handle all bandwidths */
  switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_UL) {
  case 25:  fapi_convert_ul_5MHz_FDD(module_idP, CC_id, dci, a);  break;
  case 50:  fapi_convert_ul_10MHz_FDD(module_idP, CC_id, dci, a); break;
  case 100: fapi_convert_ul_20MHz_FDD(module_idP, CC_id, dci, a); break;
  default: printf("%s:%d: TODO\n", __FUNCTION__, __LINE__); abort();
  }

  a->L = dci->aggrLevel == 1 ? 0 :
         dci->aggrLevel == 2 ? 1 :
         dci->aggrLevel == 4 ? 2 :
         dci->aggrLevel == 8 ? 3 :
         (printf("%s:%d: error\n", __FUNCTION__, __LINE__), abort(), 0);
  a->firstCCE = dci->cceIndex;
  a->ra_flag  = 0;
  a->rnti     = dci->rnti;
#if MEGALOG
printf("FAPI to MAC ul DCI ue %d rbStart %d rbLen %d tbs %d CCE %d mcs %d ndi %d tpc %d cshift %d cqi_req %d aggrLevel %d\n",
    dci->rnti, dci->rbStart, dci->rbLen, dci->tbSize, dci->cceIndex, dci->mcs, dci->ndi, dci->tpc, dci->n2Dmrs, dci->cqiRequest, dci->aggrLevel);
#endif
}

/* index 0 for SIB1, 1 for SIB23 */
static void fapi_schedule_SI(module_id_t module_idP, int CC_id, frame_t frameP,
    sub_frame_t subframeP, uint8_t index, struct DlDciListElement_s *dci)
{
  eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP];
  DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];
  int bcch_sdu_length;

  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  fapi_convert_dl_dci(module_idP, CC_id, dci, a);

  /* bug in libscheduler.a? rnti is 0 */
  a->rnti = SI_RNTI;

  if (index == 0)
    bcch_sdu_length = mac_rrc_get_SIB1(module_idP, CC_id, &eNB->common_channels[CC_id].BCCH_pdu.payload[0]);
  else if (index == 1)
    bcch_sdu_length = mac_rrc_get_SIB23(module_idP, CC_id, &eNB->common_channels[CC_id].BCCH_pdu.payload[0]);
  else { printf("%s:%d: fatal error\n", __FUNCTION__, __LINE__); abort(); }

  eNB->eNB_stats[CC_id].total_num_bcch_pdu+=1;
  eNB->eNB_stats[CC_id].bcch_buffer=bcch_sdu_length;
  eNB->eNB_stats[CC_id].total_bcch_buffer+=bcch_sdu_length;
  eNB->eNB_stats[CC_id].bcch_mcs=dci->mcs[0];         /* TODO: take care of transport block index */
}

static void fapi_schedule_RAR(int module_idP, int CC_id, frame_t frameP,
    sub_frame_t subframeP, uint16_t rnti, uint32_t grant,
    struct DlDciListElement_s *dci)
{
  eNB_MAC_INST *eNB = &eNB_mac_inst[module_idP];
  DCI_PDU *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];
  RA_TEMPLATE *RA_template = NULL;
  int i;

  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  for (i = 0; i < NB_RA_PROC_MAX; i++) {
    RA_template = (RA_TEMPLATE *)&eNB->common_channels[CC_id].RA_template[i];
    if (RA_template->RA_active != TRUE) continue;
    if (RA_template->rnti != rnti) continue;
    break;
  }
  if (i == NB_RA_PROC_MAX) { printf("%s:%d:%s: possible?\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  if (grant & 0x80000) {
    LOG_E(MAC, "uplink grant has hopping flag set (0x%x)\n", grant);
exit(1);
    grant &= ~0x80000;
  }

  RA_template->generate_rar = 1;
  RA_template->UL_grant = grant;

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  fapi_convert_dl_dci(module_idP, CC_id, dci, a);
  a->ra_flag = 1;

  /* bug in libscheduler.a? rnti is 0 */
  a->rnti = RA_template->RA_rnti;
}

static void fapi_schedule_RA_Msg4(int module_idP, int CC_id, int RA,
    int frameP, int subframeP, struct DlDciListElement_s *dci, int pdu_maxsize)
{
  int           UE_id;
  int           rrc_sdu_length;
  eNB_MAC_INST  *eNB = &eNB_mac_inst[module_idP];
  RA_TEMPLATE   *RA_template;
  int           msg4_header_length;
  int           msg4_padding, msg4_post_padding;
  int           TBsize;
  DCI_PDU       *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t   *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];
  int           offset;
  unsigned char lcid;

  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  RA_template = (RA_TEMPLATE *)&eNB->common_channels[CC_id].RA_template[RA];
  UE_id = find_UE_id(module_idP,RA_template->rnti);
  if (UE_id == -1) abort();
  rrc_sdu_length = mac_rrc_data_req(module_idP,
                                    CC_id,
                                    frameP,
                                    CCCH,
                                    1, // 1 transport block
                                    &eNB->common_channels[CC_id].CCCH_pdu.payload[0],
                                    ENB_FLAG_YES,
                                    module_idP,
                                    0); // not used in this case
  if (rrc_sdu_length <= 0) {
    LOG_E(MAC, "fapi_schedule_RA_Msg4: rrc_sdu_length (%d) is not > 0\n", rrc_sdu_length);
    abort();
  }
  if (rrc_sdu_length > pdu_maxsize) {
    LOG_E(MAC, "fapi_schedule_RA_Msg4: rrc_sdu_length (%d) > pdu_maxsize (%d)\n", rrc_sdu_length, pdu_maxsize);
//    abort();
  }

  msg4_header_length = 1+6+1;  // CR header, CR CE, SDU header

  TBsize = dci->tbsSize[CC_id] / 8;

if (TBsize < msg4_header_length+rrc_sdu_length) abort();

  if ((TBsize - rrc_sdu_length - msg4_header_length) <= 2) {
    msg4_padding = TBsize - rrc_sdu_length - msg4_header_length;
    msg4_post_padding = 0;
  } else {
    msg4_padding = 0;
    msg4_post_padding = TBsize - rrc_sdu_length - msg4_header_length -1;
  }

  LOG_I(RRC,"[eNB %d][RAPROC] CC_id %d Frame %d subframeP %d Msg4 : TBS %d, sdu_len %d, msg4_header %d, msg4_padding %d, msg4_post_padding %d\n",
        module_idP, CC_id, frameP, subframeP, TBsize, rrc_sdu_length, msg4_header_length, msg4_padding, msg4_post_padding);

  lcid = 0;
  offset = generate_dlsch_header((unsigned char*)eNB->UE_list.DLSCH_pdu[CC_id][0][(unsigned char)UE_id].payload[0],
                                 1,                           //num_sdus
                                 (unsigned short*)&rrc_sdu_length,             //
                                 &lcid,                       // sdu_lcid
                                 255,                         // no drx
                                 0,                           // no timing advance
                                 RA_template->cont_res_id,  // contention res id
                                 msg4_padding,                // no padding
                                 msg4_post_padding,
                                 /* TODO: scell bitmap command */
                                 0, 0);

  memcpy((void*)&eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0][offset],
         &eNB->common_channels[CC_id].CCCH_pdu.payload[0],
         rrc_sdu_length);

  T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_idP), T_INT(CC_id), T_INT(RA_template->rnti), T_INT(frameP), T_INT(subframeP),
    T_INT(0), T_BUFFER(&eNB->UE_list.DLSCH_pdu[CC_id][0][UE_id].payload[0], TBsize));

  RA_template->generate_Msg4=0;
  RA_template->wait_ack_Msg4=1;
  //RA_template->RA_active = FALSE;

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  waiting_for_msg4_ack[RA_template->rnti] = 1;

  fapi_convert_dl_dci(module_idP, CC_id, dci, a);
}

/* returns 1 if the given LCID has a MAC header of fixed size (for DL-SCH) */
static int fixed_size(int lcid)
{
  /* see 36.321, especially table 6.2.1-1 (rel. 10 is used here) */
  return lcid >= 27;
}

/* TODO: deal with MCS 29-31 in the PHY layer
 * in the meantime, this array stores the latest mcs used for each rnti/servcell/harq_pid
 */
static unsigned char latest_mcs[65536][2][10];
static unsigned int latest_rbbitmap[65536][2][10];

/* TODO: deal with more than one transport block */
static void fapi_schedule_retransmission_ue(int module_id, int CC_id, int frame, int subframe, struct BuildDataListElement_s *d)
{
  int header_size;
  int payload_size;
  int padding_size;
  int tbs;
  int i;
  mac_rlc_status_resp_t rlc_status;
  unsigned char         dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES];
  int                   dlsch_filled = 0;
  int                   output_length;
  int                   UE_id;
  eNB_MAC_INST          *eNB      = &eNB_mac_inst[module_id];
  UE_list_t             *UE_list  = &eNB->UE_list;
  int                   num_sdus;
  unsigned short        sdu_lengths[MAX_RLC_PDU_LIST];
  unsigned char         sdu_lcids[MAX_RLC_PDU_LIST];
  int                   offset;
  DCI_PDU               *dci_pdu = &eNB->common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t           *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];

  /* generate DCI */
  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  /* TODO: handle the case where CC_id != servCellIndex */
  if (CC_id != d->servCellIndex) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

#if MEGALOG
static char *X[] = {"ONE", "ONE_A", "ONE_B", "ONE_C", "ONE_D", "TWO", "TWO_A", "TWO_B"};
static char *Y[] = {"VRB_DISTRIBUTED", "VRB_LOCALIZED"};
printf("RETR fsf %d/%d rnti %d rbBitmap %x rbShift %d rbgSubset %d resAlloc %d nr_of_tbs %d tbsSize[0] %d mcs[0] %d (latest %d) ndi[0] %d rv[0] %d cceIndex %d aggrLevel %d precodingInfo %d format %s tpc %d harqProcess %d vrbFormat %s tbSwap %d spsRelease %d preambleIndex %d prachMaskIndex %d nGap %d dlPowerOffset %d pdcchPowerOffset %d cifPresent %d\n",
  frame,
  subframe,
  d->dci.rnti,
  d->dci.rbBitmap,
  d->dci.rbShift,
  d->dci.rbgSubset,
  d->dci.resAlloc,
  d->dci.nr_of_tbs,
  d->dci.tbsSize[0],
  d->dci.mcs[0],
  latest_mcs[d->rnti][CC_id][d->dci.harqProcess],
  d->dci.ndi[0],
  d->dci.rv[0],
  d->dci.cceIndex,
  d->dci.aggrLevel,
  d->dci.precodingInfo,
  X[d->dci.format],
  d->dci.tpc,
  d->dci.harqProcess,
  Y[d->dci.vrbFormat],
  d->dci.tbSwap,
  d->dci.spsRelease,
  d->dci.preambleIndex,
  d->dci.prachMaskIndex,
  d->dci.nGap+1,
  d->dci.dlPowerOffset,
  d->dci.pdcchPowerOffset,
  d->dci.cifPresent);
#endif
  /* TODO: deal with MCS 29-31 in the PHY layer
   * in the meantime, let's replace with the last used MCS
   */
  d->dci.mcs[0] = latest_mcs[d->rnti][CC_id][d->dci.harqProcess];
  /* TODO: remove this terrible hack! */
  d->dci.rbBitmap = latest_rbbitmap[d->rnti][CC_id][d->dci.harqProcess];

  fapi_convert_dl_dci(module_id, CC_id, &d->dci, a);
#if MEGALOG
printf("RUN fapi_schedule_retransmission_ue\n");
#endif

  if (d->nr_rlcPDU_List[0] != 0) { /*printf("%s:%d:%s: error?\n", __FILE__, __LINE__, __FUNCTION__);*/ /*abort();*/ }
  if (d->nr_rlcPDU_List[1] != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
  if (d->ceBitmap[1])            { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
  if (d->servCellIndex != 0 &&
      d->servCellIndex != 1)     { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* activate/deactive 2nd cell
   * TODO: do it better, only activate at subframe+8 if ACK received at subframe +4
   */
  if (d->ceBitmap[0]) {
    if (d->ceBitmap[0] != ff_AD) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
    LOG_I(MAC, "FAPI: f/sf %d/%d rnti %d ca_activate %2.2x\n", frame, subframe, d->rnti, d->activationDeactivationCE);
    //mac_xface->ca_activate(module_id, d->rnti, d->activationDeactivationCE);
/* TODO: remove this hack */
mac_xface->ca_activate(module_id, d->rnti, 2);
  }

  UE_id = find_UE_id(module_id, d->rnti);
if (UE_id == -1) {
#if MEGALOG
printf("SCHEDULER fapi_schedule_retransmission_ue rnti %x (%d) not existing in MAC\n", d->rnti, d->rnti);
#endif
abort();
}

  add_ue_dlsch_info(module_id,
      CC_id,
      UE_id,
      subframe,
      /* S_DL_SCHEDULED */ S_DL_WAITING);
}

/* TODO: deal with more than one transport block */
static void fapi_schedule_ue(int module_id, int CC_id, int frame, int subframe, struct BuildDataListElement_s *d)
{
  int header_size;
  int payload_size;
  int padding_size;
  int tbs;
  int i;
  mac_rlc_status_resp_t rlc_status;
  unsigned char         dlsch_buffer[MAX_DLSCH_PAYLOAD_BYTES];
  int                   dlsch_filled = 0;
  int                   output_length;
  int                   UE_id;
  eNB_MAC_INST          *eNB      = &eNB_mac_inst[module_id];
  UE_list_t             *UE_list  = &eNB->UE_list;
  int                   num_sdus;
  unsigned short        sdu_lengths[MAX_RLC_PDU_LIST];
  unsigned char         sdu_lcids[MAX_RLC_PDU_LIST];
  int                   offset;
  DCI_PDU               *dci_pdu = &eNB->common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t           *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];

  /* retransmission? */
  if (d->dci.mcs[0] >= 29 && d->dci.mcs[0] <= 31) {
    fapi_schedule_retransmission_ue(module_id, CC_id, frame, subframe, d);
    return;
  }

if (d->nr_rlcPDU_List[0] > 1) { printf("%s:%d:%s: TODO (d->nr_rlcPDU_List[0] %d)\n", __FILE__, __LINE__, __FUNCTION__, d->nr_rlcPDU_List[0]); return; }

#if MEGALOG
static char *X[] = {"ONE", "ONE_A", "ONE_B", "ONE_C", "ONE_D", "TWO", "TWO_A", "TWO_B"};
static char *Y[] = {"VRB_DISTRIBUTED", "VRB_LOCALIZED"};
printf("fsf %d/%d rnti %d rbBitmap %x rbShift %d rbgSubset %d resAlloc %d nr_of_tbs %d tbsSize[0] %d mcs[0] %d ndi[0] %d rv[0] %d cceIndex %d aggrLevel %d precodingInfo %d format %s tpc %d harqProcess %d vrbFormat %s tbSwap %d spsRelease %d preambleIndex %d prachMaskIndex %d nGap %d dlPowerOffset %d pdcchPowerOffset %d cifPresent %d\n",
  frame,
  subframe,
  d->dci.rnti,
  d->dci.rbBitmap,
  d->dci.rbShift,
  d->dci.rbgSubset,
  d->dci.resAlloc,
  d->dci.nr_of_tbs,
  d->dci.tbsSize[0],
  d->dci.mcs[0],
  d->dci.ndi[0],
  d->dci.rv[0],
  d->dci.cceIndex,
  d->dci.aggrLevel,
  d->dci.precodingInfo,
  X[d->dci.format],
  d->dci.tpc,
  d->dci.harqProcess,
  Y[d->dci.vrbFormat],
  d->dci.tbSwap,
  d->dci.spsRelease,
  d->dci.preambleIndex,
  d->dci.prachMaskIndex,
  d->dci.nGap+1,
  d->dci.dlPowerOffset,
  d->dci.pdcchPowerOffset,
  d->dci.cifPresent);
#endif

  /* generate DCI */
  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  fapi_convert_dl_dci(module_id, CC_id, &d->dci, a);
#if MEGALOG
printf("RUN fapi_schedule_ue\n");
#endif

  /* TODO: support more than one RLC PDU */
  if (d->nr_rlcPDU_List[0] > 1)  { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
  if (d->nr_rlcPDU_List[1] != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
  if (d->ceBitmap[1])            { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
  if (d->servCellIndex != 0 &&
      d->servCellIndex != 1)     { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* TODO: handle the case where CC_id != servCellIndex */
  if (CC_id != d->servCellIndex) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* activate/deactive 2nd cell
   * TODO: do it better, only activate at subframe+8 if ACK received at subframe +4
   */
  if (d->ceBitmap[0]) {
    if (d->ceBitmap[0] != ff_AD) { printf("%s:%d:%s: TODO (bitmap %d)\n", __FILE__, __LINE__, __FUNCTION__, d->ceBitmap[0]); abort(); }
    LOG_I(MAC, "FAPI: f/sf %d/%d rnti %d ca_activate %2.2x\n", frame, subframe, d->rnti, d->activationDeactivationCE);
    //mac_xface->ca_activate(module_id, d->rnti, d->activationDeactivationCE);
/* TODO: remove this hack */
mac_xface->ca_activate(module_id, d->rnti, 2);
  }

  /* TODO: deal with MCS 29-31
   * in the meantime, we store the latest used mcs for each rnti/harq_pid
   */
  latest_mcs[d->rnti][CC_id][d->dci.harqProcess] = d->dci.mcs[0];
  latest_rbbitmap[d->rnti][CC_id][d->dci.harqProcess] = d->dci.rbBitmap;

  tbs = d->dci.tbsSize[0] / 8;

  num_sdus = 0;

  /* get DLSCH buffer and adjust size according to what RLC says */
  for (i = 0; i < d->nr_rlcPDU_List[0]; i++) {
    if (d->rlcPduList[0][i].logicalChannelIdentity > 3) { printf("%s:%d:%s: bad lcid %d\n", __FILE__, __LINE__, __FUNCTION__, d->rlcPduList[0][i].logicalChannelIdentity); abort(); }
    rlc_status = mac_rlc_status_ind(
        module_id,
        d->rnti,
        module_id,
        frame,
        ENB_FLAG_YES,
        MBMS_FLAG_NO,
        d->rlcPduList[0][i].logicalChannelIdentity,
        d->rlcPduList[0][i].size);
#if MEGALOG
printf("RLC_SIZE in fapi_schedule_ue %d (asked %d) lcid %d rnti %x f/sf %d/%d\n", rlc_status.bytes_in_buffer, d->rlcPduList[0][i].size, d->rlcPduList[0][i].logicalChannelIdentity, d->rnti, frame, subframe);
#endif
    /* TODO: what if we get 0? */
    if (rlc_status.bytes_in_buffer < 0) { printf("%s:%d:%s: error\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
//    if (rlc_status.bytes_in_buffer > d->rlcPduList[0][i].size) abort(); /* that can't happen */
    if (dlsch_filled + d->rlcPduList[0][i].size > MAX_DLSCH_PAYLOAD_BYTES) {
      printf("dlsch buffer filled too much\n");
      abort();
    }
//if (d->rlcPduList[0][i].size == 2) {
//  printf("yo\n");
//}
    output_length = mac_rlc_data_req(
        module_id,
        d->rnti,
        module_id,
        frame,
        ENB_FLAG_YES,
        MBMS_FLAG_NO,
        d->rlcPduList[0][i].logicalChannelIdentity,
        (char *)&dlsch_buffer[dlsch_filled]);
    if (output_length == 0)
      LOG_W(MAC, "FAPI f/sf %d/%d: RLC returned length 0 while asked %d for channel ID %d\n",
            frame, subframe,
            d->rlcPduList[0][i].size, d->rlcPduList[0][i].logicalChannelIdentity);
    if (output_length < 0 || output_length > d->rlcPduList[0][i].size) abort();
    d->rlcPduList[0][i].size = output_length;
    dlsch_filled += output_length;
    sdu_lengths[num_sdus] = output_length;
    sdu_lcids[num_sdus]   = d->rlcPduList[0][i].logicalChannelIdentity;
    num_sdus++;
#if MEGALOG
printf("FILLED %d bytes\n", output_length);
#endif
  }

  /* get size of header and payload */
  header_size = 0;
  payload_size = 0;

  /* TODO: 2nd TB */
  if (d->ceBitmap[0] & ff_AD) {
    header_size++;
    payload_size++;
  }

  if (d->nr_rlcPDU_List[0]) {
    header_size++;
    payload_size += d->rlcPduList[0][0].size;
  }
  for (i = 1; i < d->nr_rlcPDU_List[0]; i++) {
    if (!fixed_size(d->rlcPduList[0][i-1].logicalChannelIdentity)) {
      header_size++;
      if (d->rlcPduList[0][i-1].size >= 128)
        header_size++;
    }
    header_size++;
    payload_size += d->rlcPduList[0][i].size;
  }

  padding_size = tbs - header_size - payload_size;
#if MEGALOG
printf("PADDING_SIZE %d\n", padding_size);
#endif
  //if (padding_size == 3) { printf("padding size 3\n"); abort(); }

  UE_id = find_UE_id(module_id, d->rnti);
if (UE_id == -1) {
#if MEGALOG
printf("SCHEDULER fapi_schedule_ue rnti %x (%d) not existing in MAC\n", d->rnti, d->rnti);
#endif
abort();
}

  /* generate dlsch header */
  offset = generate_dlsch_header((unsigned char*)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0],
      num_sdus,
      sdu_lengths,
      sdu_lcids,
      255,                                   // no drx
      0,                                     /* TODO: timing advance */
      NULL,                                  // contention res id
      padding_size <= 2 ? padding_size : 0,
      padding_size > 2 ? padding_size : 0,
      /* TODO: remove this hack */
      2 /*d->activationDeactivationCE*/, (d->ceBitmap[0] & ff_AD) != 0);

  /* fill payload */
  memcpy(&UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][offset], dlsch_buffer, dlsch_filled);

  T(T_ENB_MAC_UE_DL_PDU_WITH_DATA, T_INT(module_id), T_INT(CC_id), T_INT(d->rnti), T_INT(frame), T_INT(subframe),
    T_INT(d->dci.harqProcess), T_BUFFER(UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0], tbs));

#if MEGALOG
printf("BLOCK (%d)", tbs);
for (i = 0; i < tbs; i++) printf(" %2.2x", (unsigned char)UE_list->DLSCH_pdu[CC_id][0][UE_id].payload[0][i]);
printf("\n");
#endif
  add_ue_dlsch_info(module_id,
      CC_id,
      UE_id,
      subframe,
      /* S_DL_SCHEDULED */ S_DL_WAITING);
}

static void fapi_schedule_uplink(int module_idP, int CC_id, struct UlDciListElement_s *dci)
{
  DCI_PDU       *dci_pdu = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
  DCI_ALLOC_t   *a = &dci_pdu->dci_alloc[dci_pdu->Num_common_dci];

  if (dci_pdu->Num_common_dci >= NUM_DCI_MAX) { printf("%s:%d:%s: too much DCIs\n", __FILE__, __LINE__, __FUNCTION__); abort(); }

  /* we can increment Num_common_dci or Num_ue_spec_dci, there is no difference */
  dci_pdu->Num_common_dci++;

  fapi_convert_ul_dci(module_idP, CC_id, dci, a);
}

char *binary(unsigned x)
{
  static char r[33];
  char *s = r+31;
  int i;
  r[32] = 0;
  for (i = 0; i < 32; i++) {
    *s = '0' + (x&1);
    s--;
    x >>= 1;
  }
  return r;
}

static char *dci_format_to_string(DCI_format_t f)
{
  switch (f) {
    case format0: return "format 0";
    case format1: return "format 1";
    case format1A: return "format 1A";
    case format1B: return "format 1B";
    case format1C: return "format 1C";
    case format1D: return "format 1D";
    case format1E_2A_M10PRB: return "format 1E_2A_M10PRB";
    case format2: return "format 2";
    case format2A: return "format 2A";
    case format2B: return "format 2B";
    case format2C: return "format 2C";
    case format2D: return "format 2D";
    case format3: return "format 3";
  }
  printf("%s:%d: unhandled DCI format\n", __FILE__, __LINE__);
  abort();
}

static int next_frame = 0;
static int next_subframe = 0;
static pthread_mutex_t fmut = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t fcond = PTHREAD_COND_INITIALIZER;

void eNB_dlsch_ulsch_scheduler(module_id_t module_idP,uint8_t cooperation_flag, frame_t frameP, sub_frame_t subframeP)  //, int calibration_flag) {
{

if (pthread_mutex_lock(&fmut)) abort();

  int                                 CC_id;
  int                                 i, j;
  DCI_PDU                             *DCI_pdu[MAX_NUM_CCs];
  int                                 mbsfn_status[MAX_NUM_CCs];
  UE_list_t                           *UE_list = &eNB_mac_inst[module_idP].UE_list;
  fapi_interface_t                    *fapi;
  struct SchedDlTriggerReqParameters  dlreq;
  struct SchedDlConfigIndParameters   dlind;
  struct DlInfoListElement_s          dlinfo[MAX_DL_INFO_LIST];
  protocol_ctxt_t                     ctxt;
  int                                 UE_id;
  eNB_MAC_INST                        *eNB = &eNB_mac_inst[module_idP];
  struct SchedUlTriggerReqParameters  ulreq;
  struct SchedUlConfigIndParameters   ulind;
  struct UlInfoListElement_s          ulinfo[MAX_UL_INFO_LIST];
  int                                 ulsf;

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_IN);

#if MEGALOG
printf("SCHEDULER called for f/sf %d/%d\n", frameP, subframeP);
#endif
#if defined(ENABLE_ITTI)
  while (1) {
    // Checks if a message has been sent to MAC sub-task
    MessageDef   *msg_p;
    int           result;
    itti_poll_msg (TASK_MAC_ENB, &msg_p);
    if (msg_p == NULL) break;
    result = itti_free (ITTI_MSG_ORIGIN_ID(msg_p), msg_p);
    AssertFatal (result == EXIT_SUCCESS, "Failed to free memory (%d)!\n", result);
  }
#endif

  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    DCI_pdu[CC_id] = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
    //DCI_pdu[CC_id]->nCCE=0;
    DCI_pdu[CC_id]->num_pdcch_symbols=1;
    mbsfn_status[CC_id]=0;
    // clear vrb_map
    memset(eNB_mac_inst[module_idP].common_channels[CC_id].vrb_map,0,100);
  }

  // refresh UE list based on UEs dropped by PHY in previous subframe
  i = UE_list->head;
  while (i>=0) {
    int rnti, next_i;
    rnti = UE_RNTI(module_idP, i);
    CC_id = UE_PCCID(module_idP, i);
    next_i= UE_list->next[i];
    if (mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)==NULL) {
      /* TODO: try to sync this part with develop */
      //mac_remove_ue(module_idP, i, frameP, subframeP);
      printf("%s:%d: TODO: sync with develop\n", __FILE__, __LINE__);
      abort();
    }
    i = next_i;
  }

  // clear DCI and BCCH contents before scheduling
  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    DCI_pdu[CC_id]->Num_common_dci  = 0;
    DCI_pdu[CC_id]->Num_ue_spec_dci = 0;
#ifdef Rel10
    eNB_mac_inst[module_idP].common_channels[CC_id].mcch_active = 0;
#endif
    eNB_mac_inst[module_idP].frame    = frameP;
    eNB_mac_inst[module_idP].subframe = subframeP;
  }

  /* run PDCP */
  PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frameP, 0, module_idP);
  pdcp_run(&ctxt);

  // check HO
  rrc_rx_tx(&ctxt,
            0, // eNB index, unused in eNB
            CC_id);

  /* MBMS thing not done */
  mbsfn_status[0]++; /* avoid gcc warning */

  /* let's FAPI schedule! */
  fapi = eNB_mac_inst[module_idP].fapi;

  /*************************************************/
  /*           downlink scheduling                 */
  /*************************************************/

  /* check RA - is there one in generate_Msg4 mode with some CCCH data ready in the RRC Srb0? */
  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    for (i=0; i < NB_RA_PROC_MAX; i++) {
      if (eNB->common_channels[CC_id].RA_template[i].RA_active == TRUE &&
          eNB->common_channels[CC_id].RA_template[i].generate_Msg4 == 1 &&
          mac_rrc_get_ccch_size(module_idP, CC_id) > 0) {
        struct SchedDlRlcBufferReqParameters rlc;
        memset(&rlc, 0, sizeof(rlc));
        rlc.rnti = eNB->common_channels[CC_id].RA_template[i].rnti;
        rlc.logicalChannelIdentity = CCCH;
        rlc.rlcTransmissionQueueSize = mac_rrc_get_ccch_size(module_idP, CC_id) + 1;
        LOG_D(MAC, "calling SchedDlRlcBufferReq on CCCH rnti %x queue_size %d\n", rlc.rnti, rlc.rlcTransmissionQueueSize);
#if MEGALOG
printf("MAC to FAPI downlink BUF CCCH %d\n", rlc.rlcTransmissionQueueSize);
#endif
        SchedDlRlcBufferReq(fapi->sched, &rlc);
        /* let's report only once, so put generate_Msg4 to 2
         * (we will generate later on, when FAPI tells us to do so)
         */
        eNB->common_channels[CC_id].RA_template[i].generate_Msg4 = 2;
      }
    }
  }

  /* update RLC buffers status in the scheduler */
  for (UE_id = UE_list->head; UE_id >= 0; UE_id = UE_list->next[UE_id]) {
    struct SchedDlRlcBufferReqParameters rlc;
    mac_rlc_status_resp_t                rlc_status;

    memset(&rlc, 0, sizeof(rlc));
    rlc.rnti = UE_RNTI(module_idP, UE_id);

    /* this code is wrong: we should loop over all configured RaB */

    /* DCCH   (srb 1, lcid 1) */
    rlc_status = mac_rlc_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH, 0);
    rlc.logicalChannelIdentity = DCCH;
    rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer;
if (rlc.rlcTransmissionQueueSize == 2) rlc.rlcTransmissionQueueSize = 10;
    LOG_D(MAC, "calling SchedDlRlcBufferReq on DCCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer);
    SchedDlRlcBufferReq(fapi->sched, &rlc);
#if MEGALOG
printf("MAC to FAPI downlink BUF DCCH %d\n", rlc_status.bytes_in_buffer);
#endif

    /* DCCH+1 (srb 2, lcid 2) */
    rlc_status = mac_rlc_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DCCH+1, 0);
    rlc.logicalChannelIdentity = DCCH+1;
    rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer;
    LOG_D(MAC, "calling SchedDlRlcBufferReq on DCCH+1 rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer);
    SchedDlRlcBufferReq(fapi->sched, &rlc);
#if MEGALOG
printf("MAC to FAPI downlink BUF DCCH+1 %d\n", rlc_status.bytes_in_buffer);
#endif

    /* DTCH   (drb 1, lcid 3) */
    rlc_status = mac_rlc_status_ind(module_idP, rlc.rnti, module_idP, frameP, ENB_FLAG_YES, MBMS_FLAG_NO, DTCH, 0);
    rlc.logicalChannelIdentity = DTCH;
    rlc.rlcTransmissionQueueSize = rlc_status.bytes_in_buffer;
    LOG_D(MAC, "calling SchedDlRlcBufferReq on DTCH rnti %x queue_size %d\n", rlc.rnti, rlc_status.bytes_in_buffer);
    SchedDlRlcBufferReq(fapi->sched, &rlc);
#if MEGALOG
printf("MAC to FAPI downlink BUF DTCH %d\n", rlc_status.bytes_in_buffer);
#endif
  }

  dlreq.sfnSf                 = frameP * 16 + subframeP;
  dlreq.nr_dlInfoList         = 0;
  dlreq.dlInfoList            = NULL;
  dlreq.nr_vendorSpecificList = 0;
  dlreq.vendorSpecificList    = NULL;

  /* fill harq feedback data */
  /* TODO: deal with more than one TB */
  for (i = 0; i < fapi_dl_ack_nack_data.count; i++) {
    dlinfo[i].rnti          = fapi_dl_ack_nack_data.ack[i].rnti;
    dlinfo[i].harqProcessId = fapi_dl_ack_nack_data.ack[i].harq_pid;
    dlinfo[i].nr_harqStatus = 1;                                                         /* TODO: deal with more than 1 TB */
    dlinfo[i].harqStatus[0] = fapi_dl_ack_nack_data.ack[i].ack[0] ? ff_ACK : ff_NACK;    /* TODO: more than 1 TB */
    dlinfo[i].servCellIndex = fapi_dl_ack_nack_data.ack[i].CC_id;                        /* TODO: get real value for the servCellIndex */
#if MEGALOG
printf("MAC to FAPI downlink ack/nack from PHY CC %d f/sf %d/%d rnti %x harq %d ack %d\n", dlinfo[i].servCellIndex, frameP, subframeP, dlinfo[i].rnti, dlinfo[i].harqProcessId, fapi_dl_ack_nack_data.ack[i].ack[0]);
#endif
  }
  if (fapi_dl_ack_nack_data.count) {
    dlreq.nr_dlInfoList = fapi_dl_ack_nack_data.count;
    dlreq.dlInfoList    = dlinfo;
  }
  fapi_dl_ack_nack_data.count = 0;

  LOG_D(MAC, "calling SchedDlTriggerReq\n");
  SchedDlTriggerReq(fapi->sched, &dlreq);

  LOG_D(MAC, "calling SchedDlConfigInd\n");
  SchedDlConfigInd(fapi, &dlind);

  LOG_D(MAC, "SchedDlConfigInd returns dlind.nr_buildDataList %d f/sf %d/%d\n", dlind.nr_buildDataList, frameP, subframeP);
  LOG_D(MAC, "SchedDlConfigInd returns dlind.nr_buildRARList %d f/sf %d/%d\n", dlind.nr_buildRARList, frameP, subframeP);
  LOG_D(MAC, "SchedDlConfigInd returns dlind.nr_buildBroadcastList %d f/sf %d/%d\n", dlind.nr_buildBroadcastList, frameP, subframeP);

  /* TODO: rewrite. All should go into fapi_schedule_ue where special cases should be handled */
  for (i = 0; i < dlind.nr_buildDataList; i++) {
    if (dlind.buildDataList[i].ceBitmap[1] != 0 || dlind.buildDataList[i].nr_rlcPDU_List[1] != 0) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
#if MEGALOG
printf("FAPI to MAC downlink schedule ue %x channel %d f/sf %d/%d\n", dlind.buildDataList[i].rnti, dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity, frameP, subframeP);
#endif
    switch (dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity) {
    case 0: /* CCCH */
      if (dlind.buildDataList[i].nr_rlcPDU_List[0] != 1) { printf("%s:%d:%s: TODO\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
      /* TODO: get the right CC_id from servCellIndex, depending on the UE rnti/pcell/scell settings */
      CC_id = dlind.buildDataList[i].servCellIndex;
      /* look for an active RA with generate_Msg4 == 2 for this rnti */
      for (j=0; j < NB_RA_PROC_MAX; j++) {
        if (eNB->common_channels[CC_id].RA_template[j].RA_active == TRUE &&
            eNB->common_channels[CC_id].RA_template[j].generate_Msg4 == 2 &&
            eNB->common_channels[CC_id].RA_template[j].rnti == dlind.buildDataList[i].rnti) {
          if (dlind.buildDataList[i].ceBitmap[CC_id] != ff_CR) { printf("%s:%d:%s: TODO)\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
          fapi_schedule_RA_Msg4(module_idP, CC_id, j, frameP, subframeP,
              &dlind.buildDataList[i].dci, dlind.buildDataList[i].rlcPduList[0][0].size);
          break;
        }
      }
      if (j == NB_RA_PROC_MAX) { printf("%s:%d:%s: possible?\n", __FILE__, __LINE__, __FUNCTION__); /*abort();*/ }
      break;
    case 1: /* DCCH   (SRB1) */
    case 2: /* DCCH+1 (SRB2) */
    case 3: /* DTCH   (DRB1) */
      /* TODO: get the correct CC_id from servCellIndex */
      fapi_schedule_ue(module_idP, dlind.buildDataList[i].servCellIndex /* CC_id */, frameP, subframeP, &dlind.buildDataList[i]);
      break;
    default: printf("%s:%d:%s: bad channel ID (%d)\n", __FILE__, __LINE__, __FUNCTION__, dlind.buildDataList[i].rlcPduList[0][0].logicalChannelIdentity); abort();
    }
  }

  if (dlind.nr_buildRARList) {
    if (dlind.nr_buildRARList != 1) { printf("%s:%d: more than 1 RAR, todo\n", __FUNCTION__, __LINE__); exit(0); }
    if (dlind.buildRarList[0].carrierIndex != 0) { printf("%s:%d: 2nd CC: todo properly\n", __FUNCTION__, __LINE__); exit(0); }
#if MEGALOG
printf("FAPI to MAC downlink schedule RAR ue %x f/sf %d/%d\n", dlind.buildRarList[0].rnti, frameP, subframeP);
#endif
dlind.buildRarList[0].grant &= ~1;
    fapi_schedule_RAR(module_idP, dlind.buildRarList[0].carrierIndex, frameP, subframeP,
        dlind.buildRarList[0].rnti, dlind.buildRarList[0].grant, &dlind.buildRarList[0].dci);
  }

  for (i = 0; i < dlind.nr_buildBroadcastList; i++) {
    if (dlind.buildBroadcastList[i].type == ff_PCCH) { printf("%s:%d: PCCH: todo\n", __FUNCTION__, __LINE__); exit(0); }
#if MEGALOG
printf("FAPI to MAC downlink schedule SI %d CC %d f/sf %d/%d\n", dlind.buildBroadcastList[i].index, dlind.buildBroadcastList[i].carrierIndex, frameP, subframeP);
#endif
    fapi_schedule_SI(module_idP, dlind.buildBroadcastList[i].carrierIndex, frameP, subframeP,
        dlind.buildBroadcastList[i].index,
        &dlind.buildBroadcastList[i].dci);
  }

  /* TODO: do it correctly */
  if (dlind.nr_ofdmSymbolsCount != 0) {
    if (dlind.nr_ofdmSymbolsCount != MAX_NUM_CCs) { printf("%s:%d:%s: what to do?\n", __FILE__, __LINE__, __FUNCTION__); abort(); }
    for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
      if (dlind.nrOfPdcchOfdmSymbols[CC_id] != NULL) {
        int cc = dlind.nrOfPdcchOfdmSymbols[CC_id]->carrierIndex;
        DCI_pdu[cc]->num_pdcch_symbols = dlind.nrOfPdcchOfdmSymbols[CC_id]->pdcchOfdmSymbolCount;
#if MEGALOG
printf("FAPI to MAC downlink DCI_pdu[%d]->num_pdcch_symbols %d f/sf %d/%d\n", cc, DCI_pdu[cc]->num_pdcch_symbols, frameP, subframeP);
#endif
      }
    }
  }

#if 0
  // Allocate CCEs for good after scheduling is done
  for (CC_id=0;CC_id<MAX_NUM_CCs;CC_id++)
    allocate_CCEs(module_idP,CC_id,subframeP,0);
#endif

  /*************************************************/
  /*            uplink scheduling                  */
  /*************************************************/

  ulreq.sfnSf         = frameP * 16 + subframeP;
  ulreq.nr_ulInfoList = 0;
  ulreq.ulInfoList    = NULL;
  ulreq.nr_vendorSpecificList = 0;
  ulreq.vendorSpecificList    = NULL;

  /* fill ulInfoList */
  ulsf = (subframeP + 10 - 3) % 10;
  if (fapi_ul_ack_nack_data[ulsf].count) {
    ulreq.nr_ulInfoList = fapi_ul_ack_nack_data[ulsf].count;
    ulreq.ulInfoList    = ulinfo;
    for (i = 0; i < ulreq.nr_ulInfoList; i++) {
#if MEGALOG
printf("MAC to FAPI uplink acknack ue %x f/sf %d/%d ulsf %d [reception frame/subframe %d/%d] ack %d\n", fapi_ul_ack_nack_data[ulsf].ack[i].rnti, frameP, subframeP, ulsf, fapi_ul_ack_nack_data[ulsf].ack[i].reception_frame, fapi_ul_ack_nack_data[ulsf].ack[i].reception_subframe, fapi_ul_ack_nack_data[ulsf].ack[i].ack);
#endif
      ulinfo[i].puschTransmissionTimestamp = fapi_ul_ack_nack_data[ulsf].ack[i].reception_frame * 16
                                             + fapi_ul_ack_nack_data[ulsf].ack[i].reception_subframe;
      ulinfo[i].rnti                       = fapi_ul_ack_nack_data[ulsf].ack[i].rnti;
      ulinfo[i].receptionStatus            = fapi_ul_ack_nack_data[ulsf].ack[i].ack == 1 ? Ok : NotOk;
      ulinfo[i].tpc                        = 0;           /* TODO */
      ulinfo[i].servCellIndex              = 0;           /* TODO: get correct value */
      for (j = 0; j < MAX_LC_LIST+1; j++) {
        ulinfo[i].ulReception[j] = fapi_ul_ack_nack_data[ulsf].ack[i].length[j];
#if MEGALOG
printf("MAC to FAPI uplink ue %x f/sf %d/%d lcid %d size acked %d\n", fapi_ul_ack_nack_data[ulsf].ack[i].rnti, frameP, subframeP, j, fapi_ul_ack_nack_data[ulsf].ack[i].length[j]);
#endif
}
    }
    fapi_ul_ack_nack_data[ulsf].count = 0;
  }

  LOG_D(MAC, "calling SchedUlTriggerReq\n");
  SchedUlTriggerReq(fapi->sched, &ulreq);

  LOG_D(MAC, "calling SchedUlConfigInd\n");
  SchedUlConfigInd(fapi, &ulind);

  /* a hack to report nice CQI all the time it's asked */
  for (i = 0; i < ulind.nr_dciList; i++) {
    struct UlDciListElement_s *dci = &ulind.dciList[i];
    if (dci->cqiRequest) {
      int cqi_subband[13];
      int k;
      for (k = 0; k < 13; k++)
        cqi_subband[k] = 14;
      fapi_dl_cqi_report(0, dci->rnti, frameP + (subframeP > 6 ? 1 : 0), (subframeP + 4) % 10, 14, cqi_subband, 0);
    }
  }

#if MEGALOG
printf("FAPI to MAC uplink nr_dclList %d nr_phichList %d\n", ulind.nr_dciList, ulind.nr_phichList);
#endif
  for (i = 0; i < ulind.nr_dciList; i++) {
    /* TODO: get the right CC_id from servCellIndex, depending on the UE rnti/pcell/scell settings */
    CC_id = ulind.dciList[i].servCellIndex;
#if MEGALOG
printf("FAPI to MAC uplink schedule ue %x ndi %d (fsf %d %d) rbstart %d rblen %d tbsize %d mcs %d\n", ulind.dciList[i].rnti, ulind.dciList[i].ndi, frameP, subframeP, ulind.dciList[i].rbStart, ulind.dciList[i].rbLen, ulind.dciList[i].tbSize, ulind.dciList[i].mcs);
#endif
    fapi_schedule_uplink(module_idP, CC_id, &ulind.dciList[i]);
  }

#if MEGALOG
int cc;
for (cc = 0; cc < 2; cc++) {
  printf("RECAP dci pdu count %d cc %d\n", eNB_mac_inst[0].common_channels[cc].DCI_pdu.Num_common_dci, cc);
  for (i = 0; i < eNB_mac_inst[0].common_channels[cc].DCI_pdu.Num_common_dci; i++) {
  printf("    RECAP %i rnti %x %s dci pdu %s\n", i,
    eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].rnti,
    dci_format_to_string(eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].format),
    binary(*(uint32_t *)eNB_mac_inst[0].common_channels[cc].DCI_pdu.dci_alloc[i].dci_pdu)
    );
  }
}
printf("RECAP phich count %d\n", ulind.nr_phichList);
for (i = 0; i < ulind.nr_phichList; i++) {
printf("    RECAP %i rnti %x %s\n", i,
  ulind.phichList[i].rnti,
  ulind.phichList[i].phich == ACK ? "ACK" : "NACK");
}
#endif

  global_subframe++;

if (pthread_mutex_unlock(&fmut)) abort();

  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_OUT);

}

#else /* FAPI */

void eNB_dlsch_ulsch_scheduler(module_id_t module_idP,uint8_t cooperation_flag, frame_t frameP, sub_frame_t subframeP)  //, int calibration_flag) {
{

  int mbsfn_status[MAX_NUM_CCs];
  protocol_ctxt_t   ctxt;
#ifdef EXMIMO
  //int ret;
#endif
#if defined(ENABLE_ITTI)
  MessageDef   *msg_p;
  const char         *msg_name;
  instance_t    instance;
  int           result;
#endif
  DCI_PDU *DCI_pdu[MAX_NUM_CCs];
  int CC_id,i,next_i;
  UE_list_t *UE_list=&eNB_mac_inst[module_idP].UE_list;
  rnti_t rnti;
  void         *DLSCH_dci=NULL;
  int size_bits=0,size_bytes=0;

  LOG_D(MAC,"[eNB %d] Frame %d, Subframe %d, entering MAC scheduler (UE_list->head %d)\n",module_idP, frameP, subframeP,UE_list->head);

  start_meas(&eNB_mac_inst[module_idP].eNB_scheduler);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_IN);

  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    DCI_pdu[CC_id] = &eNB_mac_inst[module_idP].common_channels[CC_id].DCI_pdu;
    mbsfn_status[CC_id]=0;
    // clear vrb_map
    memset(eNB_mac_inst[module_idP].common_channels[CC_id].vrb_map,0,100);
  }

  // refresh UE list based on UEs dropped by PHY in previous subframe
  i = UE_list->head;

  while (i>=0) {
    rnti = UE_RNTI(module_idP, i);
    CC_id = UE_PCCID(module_idP, i);
    LOG_D(MAC,"UE %d: rnti %x (%p)\n", i, rnti,
          mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti));
    next_i= UE_list->next[i];

    PHY_vars_eNB_g[module_idP][CC_id]->pusch_stats_bsr[i][(frameP*10)+subframeP]=-63;
    if (i==UE_list->head)
      VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_UE0_BSR,PHY_vars_eNB_g[module_idP][CC_id]->pusch_stats_bsr[i][(frameP*10)+subframeP]); 
    // increment this, it is cleared when we receive an sdu
    eNB_mac_inst[module_idP].UE_list.UE_sched_ctrl[i].ul_inactivity_timer++;

    eNB_mac_inst[module_idP].UE_list.UE_sched_ctrl[i].cqi_req_timer++;
    
    if (mac_xface->get_eNB_UE_stats(module_idP, CC_id, rnti)==NULL) {
      //      mac_remove_ue(module_idP, i, frameP, subframeP);
    }
    else {
      // check uplink failure
      if ((UE_list->UE_sched_ctrl[i].ul_failure_timer>0)&&
	  (UE_list->UE_sched_ctrl[i].ul_out_of_sync==0)) {
	LOG_D(MAC,"UE %d rnti %x: UL Failure timer %d \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer);
	if (UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent==0) {
	  UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent=1;
	  
	  // add a format 1A dci for this UE to request an RA procedure (only one UE per subframe)
	  LOG_D(MAC,"UE %d rnti %x: sending PDCCH order for RAPROC (failure timer %d) \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer);	    
	  DLSCH_dci = (void *)UE_list->UE_template[CC_id][i].DLSCH_DCI[0];
	  *(uint32_t*)DLSCH_dci = 0;
	  if (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.frame_type == TDD) {
	    switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL) {
	    case 6:
	      ((DCI1A_1_5MHz_TDD_1_6_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_1_5MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 31;
	      size_bytes = sizeof(DCI1A_1_5MHz_TDD_1_6_t);
	      size_bits  = sizeof_DCI1A_1_5MHz_TDD_1_6_t;
	      break;
	    case 25:
	      ((DCI1A_5MHz_TDD_1_6_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_5MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 511;
	      size_bytes = sizeof(DCI1A_5MHz_TDD_1_6_t);
	      size_bits  = sizeof_DCI1A_5MHz_TDD_1_6_t;
	      break;
	    case 50:
	      ((DCI1A_10MHz_TDD_1_6_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_10MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 2047;
	      size_bytes = sizeof(DCI1A_10MHz_TDD_1_6_t);
	      size_bits  = sizeof_DCI1A_10MHz_TDD_1_6_t;
	      break;
	    case 100:
	      ((DCI1A_20MHz_TDD_1_6_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_20MHz_TDD_1_6_t*)DLSCH_dci)->rballoc = 8191;
	      size_bytes = sizeof(DCI1A_20MHz_TDD_1_6_t);
	      size_bits  = sizeof_DCI1A_20MHz_TDD_1_6_t;
	      break;
	    }
	  }
	  else { // FDD
	    switch (PHY_vars_eNB_g[module_idP][CC_id]->lte_frame_parms.N_RB_DL) {
	    case 6:
	      ((DCI1A_1_5MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_1_5MHz_FDD_t*)DLSCH_dci)->rballoc = 31;
	      size_bytes = sizeof(DCI1A_1_5MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_1_5MHz_FDD_t;
	      break;
	    case 15:/*
	      ((DCI1A_2_5MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_2_5MHz_FDD_t*)DLSCH_dci)->rballoc = 31;
	      size_bytes = sizeof(DCI1A_1_5MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_1_5MHz_FDD_t;*/
	      break;
	    case 25:
	      ((DCI1A_5MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_5MHz_FDD_t*)DLSCH_dci)->rballoc = 511;
	      size_bytes = sizeof(DCI1A_5MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_5MHz_FDD_t;
	      break;
	    case 50:
	      ((DCI1A_10MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_10MHz_FDD_t*)DLSCH_dci)->rballoc = 2047;
	      size_bytes = sizeof(DCI1A_10MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_10MHz_FDD_t;
		break;
	    case 75:
	      /*	      ((DCI1A_15MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_15MHz_FDD_t*)DLSCH_dci)->rballoc = 2047;
	      size_bytes = sizeof(DCI1A_10MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_10MHz_FDD_t;*/
		break;
	    case 100:
	      ((DCI1A_20MHz_FDD_t*)DLSCH_dci)->type = 1;
	      ((DCI1A_20MHz_FDD_t*)DLSCH_dci)->rballoc = 8191;
	      size_bytes = sizeof(DCI1A_20MHz_FDD_t);
	      size_bits  = sizeof_DCI1A_20MHz_FDD_t;
	      break;
	    }
	  }
	  
	  add_ue_spec_dci(DCI_pdu[CC_id],
			  DLSCH_dci,
			  rnti,
			    size_bytes,
			  process_ue_cqi (module_idP,i),//aggregation,
			  size_bits,
			  format1A,
			  0);
	}
	else { // ra_pdcch_sent==1
	  LOG_D(MAC,"UE %d rnti %x: sent PDCCH order for RAPROC waiting (failure timer %d) \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer);	    	    
	  if ((UE_list->UE_sched_ctrl[i].ul_failure_timer % 40) == 0)
	    UE_list->UE_sched_ctrl[i].ra_pdcch_order_sent=0; // resend every 4 frames	      
	}
      
	UE_list->UE_sched_ctrl[i].ul_failure_timer++;
	// check threshold
	if (UE_list->UE_sched_ctrl[i].ul_failure_timer > 200) {
	  // inform RRC of failure and clear timer
	  LOG_I(MAC,"UE %d rnti %x: UL Failure after repeated PDCCH orders: Triggering RRC \n",i,rnti,UE_list->UE_sched_ctrl[i].ul_failure_timer);
	  mac_eNB_rrc_ul_failure(module_idP,CC_id,frameP,subframeP,rnti);
	  UE_list->UE_sched_ctrl[i].ul_failure_timer=0;
	  UE_list->UE_sched_ctrl[i].ul_out_of_sync=1;
	}
      }
    } // ul_failure_timer>0
    
    i = next_i;
  }

#if defined(ENABLE_ITTI)

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

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

      switch (ITTI_MSG_ID(msg_p)) {
      case MESSAGE_TEST:
        LOG_D(MAC, "Received %s\n", ITTI_MSG_NAME(msg_p));
        break;

      case RRC_MAC_BCCH_DATA_REQ:
        LOG_D(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d\n",
              msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance,
              RRC_MAC_BCCH_DATA_REQ (msg_p).frame, RRC_MAC_BCCH_DATA_REQ (msg_p).enb_index);

        // TODO process BCCH data req.
        break;

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

        // TODO process CCCH data req.
        break;

#ifdef Rel10

      case RRC_MAC_MCCH_DATA_REQ:
        LOG_D(MAC, "Received %s from %s: instance %d, frameP %d, eNB_index %d, mbsfn_sync_area %d\n",
              msg_name, ITTI_MSG_ORIGIN_NAME(msg_p), instance,
              RRC_MAC_MCCH_DATA_REQ (msg_p).frame, RRC_MAC_MCCH_DATA_REQ (msg_p).enb_index, RRC_MAC_MCCH_DATA_REQ (msg_p).mbsfn_sync_area);

        // TODO process MCCH data req.
        break;
#endif

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

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

#endif

  // clear DCI and BCCH contents before scheduling
  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    DCI_pdu[CC_id]->Num_common_dci  = 0;
    DCI_pdu[CC_id]->Num_ue_spec_dci = 0;


#ifdef Rel10
    eNB_mac_inst[module_idP].common_channels[CC_id].mcch_active =0;
#endif

    eNB_mac_inst[module_idP].frame    = frameP;
    eNB_mac_inst[module_idP].subframe = subframeP;


  }

  //if (subframeP%5 == 0)
  //#ifdef EXMIMO
  PROTOCOL_CTXT_SET_BY_MODULE_ID(&ctxt, module_idP, ENB_FLAG_YES, NOT_A_RNTI, frameP, 0,module_idP);
  pdcp_run(&ctxt);
  //#endif

  // check HO
  rrc_rx_tx(&ctxt,
            0, // eNB index, unused in eNB
            CC_id);

#ifdef Rel10

  for (CC_id=0; CC_id<MAX_NUM_CCs; CC_id++) {
    if (eNB_mac_inst[module_idP].common_channels[CC_id].MBMS_flag >0) {
      start_meas(&eNB_mac_inst[module_idP].schedule_mch);
      mbsfn_status[CC_id] = schedule_MBMS(module_idP,CC_id,frameP,subframeP);
      stop_meas(&eNB_mac_inst[module_idP].schedule_mch);
    }
  }

#endif
  // refresh UE list based on UEs dropped by PHY in previous subframe
  /*
  i=UE_list->head;
  while (i>=0) {
    next_i = UE_list->next[i];
    LOG_T(MAC,"UE %d : rnti %x, stats %p\n",i,UE_RNTI(module_idP,i),mac_xface->get_eNB_UE_stats(module_idP,0,UE_RNTI(module_idP,i)));
    if (mac_xface->get_eNB_UE_stats(module_idP,0,UE_RNTI(module_idP,i))==NULL) {
      mac_remove_ue(module_idP,i,frameP);
    }
    i=next_i;
  }
  */

  switch (subframeP) {
  case 0:

    // FDD/TDD Schedule Downlink RA transmissions (RA response, Msg4 Contention resolution)
    // Schedule ULSCH for FDD or subframeP 4 (TDD config 0,3,6)
    // Schedule Normal DLSCH


    schedule_RA(module_idP,frameP,subframeP,2);


    if (mac_xface->lte_frame_parms->frame_type == FDD) {  //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,0,4);//,calibration_flag);
    } else if  ((mac_xface->lte_frame_parms->tdd_config == TDD) || //TDD
                (mac_xface->lte_frame_parms->tdd_config == 3) ||
                (mac_xface->lte_frame_parms->tdd_config == 6)) {
      //schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,4);//,calibration_flag);
    }

    schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
    fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);

    break;

  case 1:

    // TDD, schedule UL for subframeP 7 (TDD config 0,1) / subframeP 8 (TDD Config 6)
    // FDD, schedule normal UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 0:
      case 1:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,7);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 6:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,8);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,1,5);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 2:

    // TDD, nothing
    // FDD, normal UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == FDD) {  //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,2,6);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 3:

    // TDD Config 2, ULSCH for subframeP 7
    // TDD Config 2/5 normal DLSCH
    // FDD, normal UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == TDD) {
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 2:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,7);

        // no break here!
      case 5:
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD

      schedule_ulsch(module_idP,frameP,cooperation_flag,3,7);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 4:

    // TDD Config 1, ULSCH for subframeP 8
    // TDD Config 1/2/4/5 DLSCH
    // FDD UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == 1) { // TDD
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 1:
        //        schedule_RA(module_idP,frameP,subframeP);
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,8);

        // no break here!
      case 2:

        // no break here!
      case 4:

        // no break here!
      case 5:

        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else {
      if (mac_xface->lte_frame_parms->frame_type == FDD) {  //FDD

	schedule_ulsch(module_idP, frameP, cooperation_flag, 4, 8);
	schedule_ue_spec(module_idP, frameP, subframeP,  mbsfn_status);
        fill_DLSCH_dci(module_idP, frameP, subframeP,   mbsfn_status);
      }
    }

    break;

  case 5:
    // TDD/FDD Schedule SI
    // TDD Config 0,6 ULSCH for subframes 9,3 resp.
    // TDD normal DLSCH
    // FDD normal UL/DLSCH
    schedule_SI(module_idP,frameP,subframeP);

    //schedule_RA(module_idP,frameP,subframeP,5);
    if (mac_xface->lte_frame_parms->frame_type == FDD) {
      schedule_RA(module_idP,frameP,subframeP,1);
      schedule_ulsch(module_idP,frameP,cooperation_flag,5,9);
      schedule_ue_spec(module_idP, frameP, subframeP,  mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    } else if ((mac_xface->lte_frame_parms->tdd_config == 0) || // TDD Config 0
               (mac_xface->lte_frame_parms->tdd_config == 6)) { // TDD Config 6
      //schedule_ulsch(module_idP,cooperation_flag,subframeP);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    } else {
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 6:

    // TDD Config 0,1,6 ULSCH for subframes 2,3
    // TDD Config 3,4,5 Normal DLSCH
    // FDD normal ULSCH/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 0:
        break;

      case 1:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,2);
        //  schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 6:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3);
        //  schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 5:
        schedule_RA(module_idP,frameP,subframeP,2);
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 3:
      case 4:
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,6,0);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 7:

    // TDD Config 3,4,5 Normal DLSCH
    // FDD Normal UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 3:
      case 4:
        schedule_RA(module_idP,frameP,subframeP,3);  // 3 = Msg3 subframeP, not
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 5:
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,7,1);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 8:

    // TDD Config 2,3,4,5 ULSCH for subframeP 2
    //
    // FDD Normal UL/DLSCH
    if (mac_xface->lte_frame_parms->frame_type == TDD) { // TDD
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 2:
      case 3:
      case 4:
      case 5:

        //  schedule_RA(module_idP,subframeP);
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,2);
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,8,2);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  case 9:

    // TDD Config 1,3,4,6 ULSCH for subframes 3,3,3,4
    if (mac_xface->lte_frame_parms->frame_type == TDD) {
      switch (mac_xface->lte_frame_parms->tdd_config) {
      case 1:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3);
        schedule_RA(module_idP,frameP,subframeP,7);  // 7 = Msg3 subframeP, not
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 3:
      case 4:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,3);
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 6:
        schedule_ulsch(module_idP,frameP,cooperation_flag,subframeP,4);
        //schedule_RA(module_idP,frameP,subframeP);
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      case 2:
      case 5:
        //schedule_RA(module_idP,frameP,subframeP);
        schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
        fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
        break;

      default:
        break;
      }
    } else { //FDD
      schedule_ulsch(module_idP,frameP,cooperation_flag,9,3);
      schedule_ue_spec(module_idP,frameP,subframeP,mbsfn_status);
      fill_DLSCH_dci(module_idP,frameP,subframeP,mbsfn_status);
    }

    break;

  }

  LOG_D(MAC,"FrameP %d, subframeP %d : Scheduling CCEs\n",frameP,subframeP);

  // Allocate CCEs for good after scheduling is done
  for (CC_id=0;CC_id<MAX_NUM_CCs;CC_id++)
    allocate_CCEs(module_idP,CC_id,subframeP,0);

  /*
  int dummy=0;
  for (i=0;
       i<DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci;
       i++)
    if (DCI_pdu[CC_id]->dci_alloc[i].rnti==2)
      dummy=1;
	
  if (dummy==1)
    for (i=0;
	 i<DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci;
	 i++)
      LOG_I(MAC,"Frame %d, subframe %d: DCI %d/%d, format %d, rnti %x, NCCE %d(num_pdcch_symb %d)\n",
	    frameP,subframeP,i,DCI_pdu[CC_id]->Num_common_dci+DCI_pdu[CC_id]->Num_ue_spec_dci,
	    DCI_pdu[CC_id]->dci_alloc[i].format,
	    DCI_pdu[CC_id]->dci_alloc[i].rnti,
	    DCI_pdu[CC_id]->dci_alloc[i].firstCCE,
	    DCI_pdu[CC_id]->num_pdcch_symbols);


  LOG_D(MAC,"frameP %d, subframeP %d\n",frameP,subframeP);
  */

  stop_meas(&eNB_mac_inst[module_idP].eNB_scheduler);
  VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_ENB_DLSCH_ULSCH_SCHEDULER,VCD_FUNCTION_OUT);

}

#endif /* FAPI */