diff --git a/openair2/LAYER2/MAC/defs.h b/openair2/LAYER2/MAC/defs.h
index a0a43b6d7951ea3f4eb9fa1cebb9aa2dad5e34d7..d0b71157af05a33f0b6ec6d13ca02a9214e376d9 100644
--- a/openair2/LAYER2/MAC/defs.h
+++ b/openair2/LAYER2/MAC/defs.h
@@ -71,6 +71,10 @@
 #include "SCellToAddMod-r10.h"
 #endif
 
+#ifdef FAPI
+#include "ff-mac.h"
+#endif
+
 //#ifdef PHY_EMUL
 //#include "SIMULATION/PHY_EMULATION/impl_defs.h"
 //#endif
@@ -910,6 +914,10 @@ typedef struct {
   /// processing time of eNB ULSCH reception
   time_stats_t rx_ulsch_sdu; // include rlc_data_ind
 
+#ifdef FAPI
+  /// fapi interface handler
+  fapi_interface_t *fapi;
+#endif
 } eNB_MAC_INST;
 
 /* 
diff --git a/openair2/LAYER2/MAC/ff-mac.c b/openair2/LAYER2/MAC/ff-mac.c
index 1dedf8cbb4641199a56074feac90f818eda565da..c05fb5e261de8988684542d0df018c7f87c6008c 100644
--- a/openair2/LAYER2/MAC/ff-mac.c
+++ b/openair2/LAYER2/MAC/ff-mac.c
@@ -3,60 +3,209 @@
 #include "ff-mac-csched-sap.h"
 #include "ff-mac-init.h"
 #include "log.h"
+#include "assertions.h"
+
+#undef LOG_D
+#define LOG_D LOG_I
 
 #include <stdlib.h>
+#include <pthread.h>
+
+/* number of callbacks */
+#define N 9
 
 /* this structure stores required data for OAI to work with FAPI */
 /* it is the private version of fapi_interface_t */
 struct fapi {
   fapi_interface_t fi;   /* the start of the structure matches fapi_interface_t */
+  pthread_mutex_t mutex[N];
+  pthread_cond_t cond[N];
+  volatile int req_id[N];
+  volatile int rsp_id[N];
+  struct CschedCellConfigCnfParameters CschedCellConfigCnfParameters;
 };
 
-/* here come the callbacks */
+#define LOCK(fi, fn) do { \
+    LOG_D(MAC, "%s: locking fn %d fi %p mutex %p\n", __FUNCTION__, fn, fi, &fi->mutex[fn]); \
+    if (pthread_mutex_lock(&fi->mutex[fn])) \
+      AssertFatal(0, "%s:%d:%s: mutex error\n", __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define UNLOCK(fi, fn) do { \
+    LOG_D(MAC, "%s: unlocking fn %d fi %p mutex %p\n", __FUNCTION__, fn, fi, &fi->mutex[fn]); \
+    if (pthread_mutex_unlock(&fi->mutex[fn])) \
+      AssertFatal(0, "%s:%d:%s: mutex error\n", __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define CHECK(fi, fn) do { \
+    if (fi->req_id[fn] != fi->rsp_id[fn]) \
+      AssertFatal(0, "%s:%d:%s: check error\n", __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define WAIT(fi, fn) do { \
+    LOG_D(MAC, "%s: WAIT fn %d req %d rsp %d\n", __FUNCTION__, fn, fi->req_id[fn], fi->rsp_id[fn]); \
+    while (fi->req_id[fn] == fi->rsp_id[fn]) \
+      if (pthread_cond_wait(&fi->cond[fn], &fi->mutex[fn])) \
+        AssertFatal(0, "%s:%d:%s: cond error\n", __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define DONE_callback(fi, fn) do { \
+    fi->req_id[fn]++; \
+    if (pthread_cond_signal(&fi->cond[fn])) \
+      AssertFatal(0, "%s:%d:%s: mutex error\n", __FILE__, __LINE__, __FUNCTION__); \
+  } while (0)
+
+#define DONE_wrapper(fi, fn) do { \
+    fi->rsp_id[fn]++; \
+  } while (0)
+
+/* SCHED "wrappers" */
+
+void SchedDlConfigInd(fapi_interface_t *_fi, struct SchedDlConfigIndParameters *params)
+{
+  int fn = 0;
+}
+
+void SchedUlConfigInd(fapi_interface_t *_fi, struct SchedUlConfigIndParameters *params)
+{
+  int fn = 1;
+}
+
+/* CSCHED "wrappers" */
+
+void CschedCellConfigCnf(fapi_interface_t *_fi, struct CschedCellConfigCnfParameters *params)
+{
+  struct fapi *fi = (struct fapi *)_fi;
+  int fn = 2;
+  LOG_D(MAC, "CschedCellConfigCnf enter\n");
+
+  LOCK(fi, fn);
+  WAIT(fi, fn);
+
+  *params = fi->CschedCellConfigCnfParameters;
+
+  DONE_wrapper(fi, fn);
+  UNLOCK(fi, fn);
+
+  LOG_D(MAC, "CschedCellConfigCnf leave\n");
+}
+
+void CschedUeConfigCnf(fapi_interface_t *_fi, struct CschedUeConfigCnfParameters *params)
+{
+  int fn = 3;
+}
+
+void CschedLcConfigCnf(fapi_interface_t *_fi, struct CschedLcConfigCnfParameters *params)
+{
+  int fn = 4;
+}
+
+void CschedLcReleaseCnf(fapi_interface_t *_fi, struct CschedLcReleaseCnfParameters *params)
+{
+  int fn = 5;
+}
+
+void CschedUeReleaseCnf(fapi_interface_t *_fi, struct CschedUeReleaseCnfParameters *params)
+{
+  int fn = 6;
+}
+
+void CschedUeConfigUpdateInd(fapi_interface_t *_fi, struct CschedUeConfigUpdateIndParameters *params)
+{
+  int fn = 7;
+}
+
+void CschedCellConfigUpdateInd(fapi_interface_t *_fi, struct CschedCellConfigUpdateIndParameters *params)
+{
+  int fn = 8;
+}
+
+/* SCHED callbacks */
+
 void SchedDlConfigInd_callback(void *callback_data, const struct SchedDlConfigIndParameters *params)
 {
+  int fn = 0;
 }
 
 void SchedUlConfigInd_callback(void *callback_data, const struct SchedUlConfigIndParameters *params)
 {
+  int fn = 1;
 }
 
+/* CSCHED callbacks */
+
 void CschedCellConfigCnf_callback(void *callback_data, const struct CschedCellConfigCnfParameters *params)
 {
+  struct fapi *fi = callback_data;
+  int fn = 2;
+  LOG_D(MAC, "CschedCellConfigCnf_callback enter\n");
+
+  LOCK(fi, fn);
+  CHECK(fi, fn);
+
+  fi->CschedCellConfigCnfParameters = *params;
+
+  DONE_callback(fi, fn);
+  UNLOCK(fi, fn);
+
+  LOG_D(MAC, "CschedCellConfigCnf_callback leave\n");
 }
 
 void CschedUeConfigCnf_callback(void *callback_data, const struct CschedUeConfigCnfParameters *params)
 {
+  int fn = 3;
 }
 
 void CschedLcConfigCnf_callback(void *callback_data, const struct CschedLcConfigCnfParameters *params)
 {
+  int fn = 4;
 }
 
 void CschedLcReleaseCnf_callback(void *callback_data, const struct CschedLcReleaseCnfParameters *params)
 {
+  int fn = 5;
 }
 
 void CschedUeReleaseCnf_callback(void *callback_data, const struct CschedUeReleaseCnfParameters *params)
 {
+  int fn = 6;
 }
 
 void CschedUeConfigUpdateInd_callback(void *callback_data, const struct CschedUeConfigUpdateIndParameters *params)
 {
+  int fn = 7;
 }
 
 void CschedCellConfigUpdateInd_callback(void *callback_data, const struct CschedCellConfigUpdateIndParameters *params)
 {
+  int fn = 8;
 }
 
 fapi_interface_t *init_fapi(void)
 {
   struct fapi *ret;
+  int i;
 
   LOG_I(MAC, "FAPI initialization\n");
 
   ret = calloc(1, sizeof(struct fapi));
-  if (ret == NULL) LOG_E(MAC, "init_fapi: memory allocation error\n");
+  if (ret == NULL) {
+    LOG_E(MAC, "init_fapi: memory allocation error\n");
+    return NULL;
+  }
+
+  for (i = 0; i < N; i++) {
+    if (pthread_mutex_init(&ret->mutex[i], NULL)) {
+      LOG_E(MAC, "init_fapi: mutex init error\n");
+      exit(1);
+    }
+    if (pthread_cond_init(&ret->cond[i], NULL)) {
+      LOG_E(MAC, "init_fapi: cond init error\n");
+      exit(1);
+    }
+    ret->req_id[i] = 0;
+    ret->rsp_id[i] = 0;
+  }
 
   ret->fi.sched = SchedInit(ret,
                       SchedDlConfigInd_callback,
diff --git a/openair2/LAYER2/MAC/ff-mac.h b/openair2/LAYER2/MAC/ff-mac.h
index d3caabd856d102ddab938a36c84e220e4c6fa3d5..55dac78f23bf64ac446e21f3d5043ce1a6d9ba42 100644
--- a/openair2/LAYER2/MAC/ff-mac.h
+++ b/openair2/LAYER2/MAC/ff-mac.h
@@ -14,4 +14,25 @@ typedef struct {
  */
 fapi_interface_t *init_fapi(void);
 
+/* the following functions are called by OAI
+ * they wait for the corresponding callback
+ * to be called by the FAPI scheduler
+ */
+
+#include "ff-mac-sched-sap.h"
+#include "ff-mac-csched-sap.h"
+
+/* from SCHED */
+void SchedDlConfigInd(fapi_interface_t *, struct SchedDlConfigIndParameters* params);
+void SchedUlConfigInd(fapi_interface_t *, struct SchedUlConfigIndParameters* params);
+
+/* from CSCHED */
+void CschedCellConfigCnf(fapi_interface_t *, struct CschedCellConfigCnfParameters *params);
+void CschedUeConfigCnf(fapi_interface_t *, struct CschedUeConfigCnfParameters *params);
+void CschedLcConfigCnf(fapi_interface_t *, struct CschedLcConfigCnfParameters *params);
+void CschedLcReleaseCnf(fapi_interface_t *, struct CschedLcReleaseCnfParameters *params);
+void CschedUeReleaseCnf(fapi_interface_t *, struct CschedUeReleaseCnfParameters *params);
+void CschedUeConfigUpdateInd(fapi_interface_t *, struct CschedUeConfigUpdateIndParameters *params);
+void CschedCellConfigUpdateInd(fapi_interface_t *, struct CschedCellConfigUpdateIndParameters *params);
+
 #endif /* FF_MAC_H */
diff --git a/openair2/LAYER2/MAC/main.c b/openair2/LAYER2/MAC/main.c
index ef281ee0f68b4e05b7e3415fd9a70c006dbbdc00..df25009529d311899468cbacdfea66c21ddbdb3b 100644
--- a/openair2/LAYER2/MAC/main.c
+++ b/openair2/LAYER2/MAC/main.c
@@ -352,6 +352,22 @@ int mac_top_init(int eMBMS_active, char *uecap_xer, uint8_t cba_group_active, ui
 #endif
   //end ALU's algo
 
+#ifdef FAPI
+  /// setup FAPI interface
+  for (i=0; i<NB_eNB_INST; i++) {
+    eNB_mac_inst[i].fapi = init_fapi();
+    AssertFatal(eNB_mac_inst[i].fapi != NULL, "error calling init_fapi()\n");
+  }
+
+  /* test code, to remove */
+  struct CschedCellConfigReqParameters p;
+  struct CschedCellConfigCnfParameters r;
+  CschedCellConfigReq(eNB_mac_inst[0].fapi->sched, &p);
+  CschedCellConfigCnf(eNB_mac_inst[0].fapi, &r);
+  if (r.result != ff_SUCCESS) abort();
+
+#endif
+
   LOG_I(MAC,"[MAIN][INIT] Init function finished\n");
 
   return(0);
diff --git a/openair2/LAYER2/MAC/minifapi.c b/openair2/LAYER2/MAC/minifapi.c
index 6a9d307d55511e981ff9e9bb71e0ef5c34914453..4fb2aec53e3b68c59dc09bb36b8a42a7accc2c9c 100644
--- a/openair2/LAYER2/MAC/minifapi.c
+++ b/openair2/LAYER2/MAC/minifapi.c
@@ -11,6 +11,8 @@
 #include <stdlib.h>
 #include <stdio.h>
 
+#define LOG(...) do { printf("minifapi:%s:%d: ", __FUNCTION__, __LINE__); printf(__VA_ARGS__); printf("\n"); } while (0)
+
 struct scheduler {
   void                                 *callback_data;
   SchedDlConfigInd_callback_t          *SchedDlConfigInd;
@@ -38,7 +40,7 @@ void *SchedInit(
 {
   struct scheduler *ret;
 
-  printf("minifapi:%s:%d: start\n", __FUNCTION__, __LINE__);
+  LOG("enter");
 
   ret = calloc(1, sizeof(struct scheduler));
   if (ret == NULL) {
@@ -57,7 +59,7 @@ void *SchedInit(
   ret->CschedUeConfigUpdateInd   = CschedUeConfigUpdateInd;
   ret->CschedCellConfigUpdateInd = CschedCellConfigUpdateInd;
 
-  printf("minifapi:%s:%d: done\n", __FUNCTION__, __LINE__);
+  LOG("leave");
 
   return ret;
 }
@@ -112,6 +114,17 @@ void SchedUlCqiInfoReq(void *_sched, const struct SchedUlCqiInfoReqParameters *p
 
 void CschedCellConfigReq(void *_sched, const struct CschedCellConfigReqParameters *params)
 {
+  struct scheduler *sched = _sched;
+  struct CschedCellConfigCnfParameters conf;
+
+  LOG("enter");
+
+  conf.result = ff_SUCCESS;
+  conf.nr_vendorSpecificList = 0;
+
+  sched->CschedCellConfigCnf(sched->callback_data, &conf);
+
+  LOG("leave");
 }
 
 void CschedUeConfigReq(void *_sched, const struct CschedUeConfigReqParameters *params)