diff --git a/nfapi/debug.h b/nfapi/debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..05e987054f8d137a0b98915fdb84ed1e24204216
--- /dev/null
+++ b/nfapi/debug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2017 Cisco Systems, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+/*! The trace levels used by the nfapi libraries */
+typedef enum nfapi_trace_level
+{
+	NFAPI_TRACE_ERROR = 1,
+	NFAPI_TRACE_WARN,
+	NFAPI_TRACE_NOTE,
+	NFAPI_TRACE_INFO,
+
+	NFAPI_TRACE_LEVEL_MAX
+} nfapi_trace_level_t;
+
+/*! The trace function pointer */
+typedef void (*nfapi_trace_fn_t)(nfapi_trace_level_t level, const char* format, ...);
+
+/*! Global trace function */
+extern nfapi_trace_fn_t nfapi_trace_g;
+
+/*! Global trace level */
+extern nfapi_trace_level_t nfapi_trace_level_g;
+
+/*! NFAPI trace macro */
+#define NFAPI_TRACE(level, format, ...) { if(nfapi_trace_g && ((nfapi_trace_level_t)level <= nfapi_trace_level_g)) (*nfapi_trace_g)(level, format, ##__VA_ARGS__); }
+
+/*! Function to change the trace level 
+ * \param new_level The modified trace level
+ */
+
+void nfapi_set_trace_level(nfapi_trace_level_t new_level);
+
+#endif /* _DEBUG_H_ */
diff --git a/nfapi/nfapi_pnf_interface.h b/nfapi/nfapi_pnf_interface.h
new file mode 100644
index 0000000000000000000000000000000000000000..e48b93250ec051aa8e88d44a5c6af5b167ac61d9
--- /dev/null
+++ b/nfapi/nfapi_pnf_interface.h
@@ -0,0 +1,803 @@
+/*
+ * Copyright 2017 Cisco Systems, Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#ifndef _NFAPI_PNF_INTERFACE_H_
+#define _NFAPI_PNF_INTERFACE_H_
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#include "nfapi_interface.h"
+#include "debug.h"
+
+#include <sys/types.h>
+
+/*! This enum is used to describe the states of the pnf 
+ */
+typedef enum
+{
+	NFAPI_PNF_IDLE = 0,
+	NFAPI_PNF_CONFIGURED,
+	NFAPI_PNF_RUNNING
+} nfapi_pnf_state_e;
+
+/*! This enum is used to describe the states of a phy instance of a pnf
+ */
+typedef enum
+{
+	NFAPI_PNF_PHY_IDLE = 0,
+	NFAPI_PNF_PHY_CONFIGURED = 1,
+	NFAPI_PNF_PHY_RUNNING = 2
+} nfapi_pnf_phy_state_e;
+
+typedef struct nfapi_pnf_phy_config nfapi_pnf_phy_config_t;
+
+/*! Configuration information for a pnf phy instance
+ */
+typedef struct nfapi_pnf_phy_config
+{
+	/*! The PHY id*/
+	uint16_t phy_id;
+
+	/*! The state of the PNF PHY instance*/
+	nfapi_pnf_phy_state_e state;
+
+	/*! Optional user defined data that will be passed back in the callbacks*/
+	void* user_data;
+
+	/*! Pointer for use in a linked list */
+	struct nfapi_pnf_phy_config* next;
+} nfapi_pnf_phy_config_t;
+
+typedef struct nfapi_pnf_config nfapi_pnf_config_t;
+
+/*! Configuration information for the pnf created by calling nfapi_pnf_create
+ */
+typedef struct nfapi_pnf_config
+{
+	/*! A user define callback to override the default memory allocation 
+	 * \param size The size of the data buffer to allocate
+	 * \return A pointer to a data buffer
+	 */
+	void* (*malloc)(size_t size);
+	
+	/*! A user define callback to override the default memory deallocation 
+	 * \param ptr Pointer to a data buffer to be deallocated
+	 */
+	void (*free)(void* ptr);
+
+	/*! A user define callback to handle trace from the pnf 
+	 * \param level The trace level 
+	 * \param message The trace string
+	 * 
+	 * This is a vardic function.
+	 */
+	void (*trace)(nfapi_trace_level_t  level, const char* message, ...);
+
+	/*! The ip address of the VNF 
+	 *
+	 */
+	char* vnf_ip_addr;
+
+	/*! The ip port of the VNF 
+	 */
+	int vnf_p5_port;
+
+	/*! The state of the PNF */
+	nfapi_pnf_state_e state;
+
+	/*! List of PHY instances configured for this PNF */
+	nfapi_pnf_phy_config_t* phys;
+	
+	/*! Configuation option of the p4 p5 encode decode functions */
+	nfapi_p4_p5_codec_config_t codec_config;
+	
+	/*! Optional user defined data that will be passed back in the callbacks*/
+	void* user_data;
+
+
+	/*! A callback for the PNF_PARAM.request 
+	 *  \param config A pointer to the pnf configuration
+	 *  \param req A data structure for the decoded PNF_PARAM.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the PNF_PARAM.response after receiving the
+	 *  PNF_PARAM.request. This can be done in the call back. 
+	 */
+	int (*pnf_param_req)(nfapi_pnf_config_t* config, nfapi_pnf_param_request_t* req);
+	
+	/*! A callback for the PNF_CONFIG.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param req A data structure for the decoded PNF_CONFIG.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the PNF_CONFIG.response after receiving the
+	 *  PNF_CONFIG.request. This can be done in the call back. 
+	 */
+	int (*pnf_config_req)(nfapi_pnf_config_t* config, nfapi_pnf_config_request_t* req);
+	
+	/*! A callback for the PNF_START.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param req A data structure for the decoded PNF_CONFIG.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the PNF_START.response after receiving the
+	 *  PNF_START.request. This can be done in the call back. 
+	 */
+	int (*pnf_start_req)(nfapi_pnf_config_t* config, nfapi_pnf_start_request_t* req);
+	
+	/*! A callback for the PNF_STOP.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param req A data structure for the decoded PNF_STOP.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the PNF_STOP.response after receiving the
+	 *  PNF_STOP.request. This can be done in the call back. 
+	 */
+	int (*pnf_stop_req)(nfapi_pnf_config_t* config, nfapi_pnf_stop_request_t* req);
+
+	/*! A callback for the PARAM.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded PARAM.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the PARAM.response after receiving the
+	 *  PARAM.request. This can be done in the call back. 
+	 */
+	int (*param_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_param_request_t* req);
+	
+	/*! A callback for the CONFIG.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded CONFIG.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the CONFIG.response after receiving the
+	 *  CONFIG.request. This can be done in the call back. 
+	 */
+	int (*config_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_config_request_t* req);
+	
+	/*! A callback for the START.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded START.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the START.response after the client has received the
+	 *  first subframe indication from FAPI.
+	 */
+	int (*start_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_start_request_t* req);
+	
+	/*! A callback for the STOP.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded STOP.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the STOP.response after receiving the
+	 *  STOP.request. This can be done in the call back. 
+	 */
+	int (*stop_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_stop_request_t* req);
+	
+	/*! A callback for the MEASUREMENT.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded MEASUREMENT.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the MEASUREMENT.response after receiving the
+	 *  MEASUREMENT.request. This can be done in the call back. 
+	 */
+	int (*measurement_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_measurement_request_t* req);
+	
+	/*! A callback for the RSSI.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded RSSI.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the RSSI.response after receiving the
+	 *  RSSI.request. This can be done in the call back. 
+	 */
+	int (*rssi_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_rssi_request_t* req);
+	
+	/*! A callback for the CELL_SEARCH.request
+	 *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded CELL_SEARCH.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the CELL_SEARCH.response after receiving the
+	 *  CELL_SEARCH.request. This can be done in the call back. 	
+	 */
+	int (*cell_search_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_cell_search_request_t* req);
+	
+	/*! A callback for the BROADCAST_DETECT.request
+     *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded BROADCAST_DETECT.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the BROADCAST_DETECT.response after receiving the
+	 *  BROADCAST_DETECT.request. This can be done in the call back. 	
+	 */
+	int (*broadcast_detect_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_broadcast_detect_request_t* req);
+	
+	/*! A callback for the SYSTEM_INFORMATION_SCHEDULE.request
+     *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded SYSTEM_INFORMATION_SCHEDULE.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the SYSTEM_INFORMATION_SCHEDULE.response after receiving the
+	 *  SYSTEM_INFORMATION_SCHEDULE.request. This can be done in the call back. 	
+	 */
+	int (*system_information_schedule_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_system_information_schedule_request_t* req);
+	
+	/*! A callback for the SYSTEM_INFORMATION.request
+     *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded SYSTEM_INFORMATION.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the SYSTEM_INFORMATION.response after receiving the
+	 *  SYSTEM_INFORMATION.request. This can be done in the call back. 	
+	 */
+	int (*system_information_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_system_information_request_t* req);
+	
+	/*! A callback for the NMM_STOP.request
+     *  \param config A pointer to the pnf configuration
+	 *  \param phy A pointer to the pnf phy configuration
+	 *  \param req A data structure for the decoded NMM_STOP.request. This will have
+	 *             been allocated on the stack
+	 *  \return not currently used
+	 * 
+	 * 	The client is expected to send the NMM_STOP.response after receiving the
+	 *  NMM_STOP.request. This can be done in the call back.
+	 */
+	int (*nmm_stop_req)(nfapi_pnf_config_t* config, nfapi_pnf_phy_config_t* phy, nfapi_nmm_stop_request_t* req);
+	
+	/*! A callback for any vendor extension messages recevied
+	 *  \param config A pointer to the pnf configuration
+	 *  \param msg A pointer to the decode P4/P5 message
+	 *  \return not current used
+	 */
+	int (*vendor_ext)(nfapi_pnf_config_t* config, nfapi_p4_p5_message_header_t* msg);
+	
+	/*! A callback to allocate vendor extension message
+	 * \param message_id The message id from the decode P4/P5 message header
+	 * \param msg_size A pointer a the size of the allocated message structure. The callee should set this
+	 * \return A pointer to a allocated P4/P5 message structure
+	 */
+	nfapi_p4_p5_message_header_t* (*allocate_p4_p5_vendor_ext)(uint16_t message_id, uint16_t* msg_size);
+	
+	/*! A callback to deallocate vendor extension message 
+	 * \param header A pointer to an P4/P5 message structure
+	 */
+	void (*deallocate_p4_p5_vendor_ext)(nfapi_p4_p5_message_header_t* header);
+
+
+
+} nfapi_pnf_config_t;
+
+/*! Create a pnf configuration 
+ *  \return A pointer to a pnf configuration struture
+ * 
+ *  This function will create and initialize a pnf instance. It is expected that 
+ *  the client will set the callback and parameters need before calling nfapi_pnf_start
+ *
+ *  0 will be returned if it fails.
+ * 
+ * \code
+ * nfapi_pnf_config_t* config = nfapi_pnf_config_create(void);
+ * \endcode
+ */
+nfapi_pnf_config_t* nfapi_pnf_config_create(void);
+
+/*! Delete a pnf configuration 
+ * \param config A pointer to a pnf configuraiton
+ * \return 0 is success, -1 for failure
+ */
+int nfapi_pnf_config_destroy(nfapi_pnf_config_t* config);
+
+/*! Start the PNF library. 
+ * \param config A pointer to the pnf configuration
+ * \return 0 is success, -1 for failure
+ * 
+ * This function will not return until nfapi_pnf_stop is called
+ *
+ * \code
+ * // Create the pnf config
+ * nfapi_pnf_config_t* config = nfapi_pnf_config_create(void);
+ *
+ * // Assumed that the vnf_address and vnf_port are provided over the P9 interface
+ * config->vnf_ip_addr = vnf_address;
+ * config->vnf_p5_port = vnf_port;
+ *
+ * // Set the required callbacks
+ * config->pnf_param_req = &pnf_param_request;
+ * ...
+ * 
+ * // Call start for the PNF to initiate a connection to the VNF
+ * nfai_pnf_start(config);
+ * 
+ * \endcode
+ */
+int nfapi_pnf_start(nfapi_pnf_config_t* config);
+
+/*! Stop the PNF library. 
+ * \param config A pointer to the pnf configuration
+ * \return 0 is success, -1 for failure
+ * 
+ * This function will cause the nfapi_pnf_start function to return
+ */
+int nfapi_pnf_stop(nfapi_pnf_config_t* config);
+
+/*! Send the PNF_PARAM.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_pnf_param_resp(nfapi_pnf_config_t* config, nfapi_pnf_param_response_t* resp);
+
+/*! Send the PNF_CONFIG.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_pnf_config_resp(nfapi_pnf_config_t* config, nfapi_pnf_config_response_t* resp);
+
+/*! Send the PNF_START.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_pnf_start_resp(nfapi_pnf_config_t* config, nfapi_pnf_start_response_t* resp);
+
+/*! Send the PNF_STOP.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_pnf_stop_resp(nfapi_pnf_config_t* config, nfapi_pnf_stop_response_t* resp);
+
+/*! Send the PARAM.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_param_resp(nfapi_pnf_config_t* config, nfapi_param_response_t* resp);
+
+/*! Send the CONFIG.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_config_resp(nfapi_pnf_config_t* config, nfapi_config_response_t* resp);
+
+/*! Send the START.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_start_resp(nfapi_pnf_config_t* config, nfapi_start_response_t* resp);
+
+/*! Send the STOP.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_stop_resp(nfapi_pnf_config_t* config, nfapi_stop_response_t* resp);
+
+/*! Send the MEASUREMENT.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_measurement_resp(nfapi_pnf_config_t* config, nfapi_measurement_response_t* resp);
+
+/*! Send the RSSI.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_rssi_resp(nfapi_pnf_config_t* config, nfapi_rssi_response_t* resp);
+
+/*! Send the RSSI.indication
+ * \param config A pointer to a pnf configuraiton
+ * \param ind A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_rssi_ind(nfapi_pnf_config_t* config, nfapi_rssi_indication_t* ind);
+
+/*! Send the CELL_SEARCH.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_cell_search_resp(nfapi_pnf_config_t* config, nfapi_cell_search_response_t* resp);
+
+/*! Send the CELL_SEARCH.indication
+ * \param config A pointer to a pnf configuraiton
+ * \param ind A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_cell_search_ind(nfapi_pnf_config_t* config, nfapi_cell_search_indication_t* ind);
+
+/*! Send the BROADCAST_DETECT.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_broadcast_detect_resp(nfapi_pnf_config_t* config, nfapi_broadcast_detect_response_t* resp);
+
+/*! Send the BROADCAST_DETECT.indication
+ * \param config A pointer to a pnf configuraiton
+ * \param ind A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_broadcast_detect_ind(nfapi_pnf_config_t* config, nfapi_broadcast_detect_indication_t* ind);
+
+/*! Send the SYSTEM_INFORMATION_SCHEDULE.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_system_information_schedule_resp(nfapi_pnf_config_t* config, nfapi_system_information_schedule_response_t* resp);
+
+/*! Send the SYSTEM_INFORMATION_SCHEDULE.indication
+ * \param config A pointer to a pnf configuraiton
+ * \param ind A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_system_information_schedule_ind(nfapi_pnf_config_t* config, nfapi_system_information_schedule_indication_t* ind);
+
+/*! Send the SYSTEM_INFORMATION.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_system_information_resp(nfapi_pnf_config_t* config, nfapi_system_information_response_t* resp);
+
+/*! Send the SYSTEM_INFORMATION.indication
+ * \param config A pointer to a pnf configuraiton
+ * \param ind A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_system_information_ind(nfapi_pnf_config_t* config, nfapi_system_information_indication_t* ind);
+
+/*! Send the NMM_STOP.response
+ * \param config A pointer to a pnf configuraiton
+ * \param resp A pointer to the message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_nmm_stop_resp(nfapi_pnf_config_t* config, nfapi_nmm_stop_response_t* resp);
+
+/*! Send a vendor extension message
+ * \param config A pointer to a pnf configuraiton
+ * \param msg A pointer to the vendor extention message structure
+ * \param msg_len The size of the vendor extention message structure
+ * \return 0 for success, -1 for failure
+ * 
+ */
+int nfapi_pnf_vendor_extension(nfapi_pnf_config_t* config, nfapi_p4_p5_message_header_t* msg, uint32_t msg_len);
+
+//--------------------------
+
+/*! A subframe buffer structure which can be used by the client to 
+ *  to configure the dummy information
+ */
+typedef struct 
+{
+	uint16_t sfn_sf;
+	
+	nfapi_dl_config_request_t* dl_config_req;
+	nfapi_ul_config_request_t* ul_config_req;
+	nfapi_hi_dci0_request_t* hi_dci0_req;
+	nfapi_tx_request_t* tx_req;
+	nfapi_lbt_dl_config_request_t* lbt_dl_config_req;
+
+} nfapi_pnf_p7_subframe_buffer_t;
+
+typedef struct nfapi_pnf_p7_config nfapi_pnf_p7_config_t;
+
+/*! The nfapi PNF PHY P7 configuration information created using the nfapi_pnf_p7_create function
+ */
+typedef struct nfapi_pnf_p7_config
+{
+	/*! A user define callback to override the default memory allocation 
+	 * \param size The size of the buffer to allocate
+	 * \return An allocated buffer. 0 in the case of failure
+	 * 
+	 * If not set malloc will be used
+	 */
+	void* (*malloc)(size_t size);
+	
+	/*! A user define callback to override the default memory deallocation 
+	 *  \param ptr Pointer to a buffer to dellocate
+	 *
+	 * If not set free will be used
+	 */
+	void (*free)(void* ptr);
+
+	/*! A user define callback to handle trace from the pnf
+	 * \param level The trace level
+	 * \param message The message string
+	 */
+	void (*trace)(nfapi_trace_level_t  level, const char* message, ...);
+
+	/*! The PHY id*/
+	uint16_t phy_id;
+
+	// remote
+	/*! The VNF P7 UDP port */
+	int remote_p7_port;
+	/*! The VNF P7 UDP address */
+	char* remote_p7_addr;
+
+	// local
+	/*! The PNF P7 UDP port */
+	int local_p7_port;
+	/*! The PNF P7 UDP address */
+	char* local_p7_addr;
+
+	/*! Flag to indicate of the pnf should use the P7 checksum */
+	uint8_t checksum_enabled;
+
+	/*! The maxium size of a P7 segement. If a message is large that this it
+	 * will be segemented */
+	uint16_t segment_size;
+
+	/*! The dummy subframe buffer structure that should be used in case there
+	 * are no 'valid' subframe messages */
+	nfapi_pnf_p7_subframe_buffer_t dummy_subframe;
+	
+	/*! Configuration options for the p7 pack unpack functions*/
+	nfapi_p7_codec_config_t codec_config;
+	
+	/*! Optional userdata that will be passed back in the callbacks*/
+	void* user_data;
+
+	// tdb : if these should be public
+	uint16_t subframe_buffer_size;
+	uint8_t timing_info_mode_periodic; // 0:false 1:true
+	uint8_t timing_info_mode_aperiodic; // 0:false 1:true
+	uint8_t timing_info_period; // 1..225 in subframes
+
+	/*! A callback for the DL_CONFIG.request
+	 * \param config A poiner to the PNF P7 config
+	 * \param req A pointer to the dl config request message structure
+	 * \return not currently used
+	 */
+	int (*dl_config_req)(nfapi_pnf_p7_config_t* config, nfapi_dl_config_request_t* req);
+	
+	/*! A callback for the UL_CONFIG.request
+	 * \param config A poiner to the PNF P7 config
+	 * \param req A pointer to the ul config request message structure
+	 * \return not currently used	
+	 */
+	int (*ul_config_req)(nfapi_pnf_p7_config_t* config, nfapi_ul_config_request_t* req);
+	
+	/*! A callback for the HI_DCI0.request
+	 * \param config A poiner to the PNF P7 config
+	 * \param req A pointer to the hi dci0 request message structure
+	 * \return not currently used
+	 */
+	int (*hi_dci0_req)(nfapi_pnf_p7_config_t* config, nfapi_hi_dci0_request_t* req);
+	
+	/*! A callback for the TX_REQ.request
+	 * \param config A poiner to the PNF P7 config
+	 * \param req A pointer to the tx request message structure
+	 * \return not currently used
+	 * 
+	 * The TX request contains pointers to the downlink PDUs to be sent. In the case that the FAPI interface
+	 * will 'keep' the pointers until they are transmitted the callee should set the pointers in the req to 0
+	 * and then use the p7 codec config free function to release the pdu's when appropriate. 
+	 */
+	int (*tx_req)(nfapi_pnf_p7_config_t* config, nfapi_tx_request_t* req);
+	
+	/*! A callback for the LBT_DL_CONFIG.request
+	 * \param config A poiner to the PNF P7 config
+	 * \param req A pointer to the lbt dl request message structure
+	 * \return not currently used
+	 */
+	int (*lbt_dl_config_req)(nfapi_pnf_p7_config_t* config, nfapi_lbt_dl_config_request_t* req);
+	
+	/*! A callback for vendor extension messages
+	 * \param config A poiner to the PNF P7 config
+	 * \param msg A pointer to a decode vendor extention message
+	 * \return not currently used
+	 */
+	int (*vendor_ext)(nfapi_pnf_p7_config_t* config, nfapi_p7_message_header_t* msg);
+
+	/*! A callback to allocate vendor extension message
+	 * \param message_id The vendor extention message id from the decode message header
+	 * \param msg_size A pointer to size of the allocate vendor extention message. Set by the callee
+	 * \return A pointer to an allocated vendor extention message structure. 0 if failed
+	 * 
+	 * 
+	 */
+	nfapi_p7_message_header_t* (*allocate_p7_vendor_ext)(uint16_t message_id, uint16_t* msg_size);
+	
+	/*! A callback to deallocate vendor extension message
+	 * \param header A pointer to a p7 vendor extention message
+	 */
+	void (*deallocate_p7_vendor_ext)(nfapi_p7_message_header_t* header);
+
+
+
+} nfapi_pnf_p7_config_t;
+
+/*! Create and initialise a nfapi_pnf_p7_config structure
+ * \return A pointer to a PNF P7 config structure
+ */
+nfapi_pnf_p7_config_t* nfapi_pnf_p7_config_create(void);
+
+/*! Delete an nfapi_pnf_p7_config structure
+ * \param config 
+ */
+int nfapi_pnf_p7_config_destroy(nfapi_pnf_p7_config_t* config);
+
+
+/*! Start the PNF P7 library. 
+ * \param config A pointer to a PNF P7 config
+ * \return 0 means success, -1 means failure
+ * 
+ * This function will not return until nfapi_pnf_p7_stop is called.
+ */
+int nfapi_pnf_p7_start(nfapi_pnf_p7_config_t* config);
+
+/*! Stop the PNF P7 library. 
+ * \param config A pointer to a PNF P7 config
+ * \return  0 means success, -1 means failure
+ * 
+ * This function will cause the nfapi_pnf_p7_start to return
+ */
+int nfapi_pnf_p7_stop(nfapi_pnf_p7_config_t* config);
+
+/*! Subframe indication
+ * \param config A pointer to a PNF P7 config
+ * \param phy_id The phy_id for the phy instance
+ * \param sfn_sf The SFN and SF in the format of FAPI
+ * \return 0 means success, -1 means failure
+ * 
+ * The client should call the subframe indication every 1ms. The PNF will
+ * respond by invoking the pnf p7 subframe callbacks with the messages from the subframe buffer
+ *
+ * If messages are not in the subframe buffer, they dummy subframe messages will be sent
+ */
+int nfapi_pnf_p7_subframe_ind(nfapi_pnf_p7_config_t* config, uint16_t phy_id, uint16_t sfn_sf);
+
+/*! Send the HARQ.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the harq indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_harq_ind(nfapi_pnf_p7_config_t* config, nfapi_harq_indication_t* ind);
+
+/*! Send the CRC.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the crc indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_crc_ind(nfapi_pnf_p7_config_t* config, nfapi_crc_indication_t* ind);
+
+/*! Send the RX.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the rx indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_rx_ind(nfapi_pnf_p7_config_t* config, nfapi_rx_indication_t* ind);
+
+/*! Send the RACH.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the rach indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_rach_ind(nfapi_pnf_p7_config_t* config, nfapi_rach_indication_t* ind);
+
+/*! Send the SRS.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the srs indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_srs_ind(nfapi_pnf_p7_config_t* config, nfapi_srs_indication_t* ind);
+
+/*! Send the SR.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the sr indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_sr_ind(nfapi_pnf_p7_config_t* config, nfapi_sr_indication_t* ind);
+
+/*! Send the CQI.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the cqi indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_cqi_ind(nfapi_pnf_p7_config_t* config, nfapi_cqi_indication_t* ind);
+
+/*! Send the LBT_DL.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the lbt dl indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_lbt_dl_ind(nfapi_pnf_p7_config_t* config, nfapi_lbt_dl_indication_t* ind);
+
+/*! Send the NB_HARQ.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the lbt dl indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_nb_harq_ind(nfapi_pnf_p7_config_t* config, nfapi_nb_harq_indication_t* ind);
+
+/*! Send the NRACH.indication
+ * \param config A pointer to a PNF P7 config
+ * \param ind A pointer to the lbt dl indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_nrach_ind(nfapi_pnf_p7_config_t* config, nfapi_nrach_indication_t* ind);
+
+
+/*! Send a vendor exntesion message
+ * \param config A pointer to a PNF P7 config
+ * \param msg A pointer to the lbt dl indication message structure
+ * \return 0 means success, -1 means failure
+ */
+int nfapi_pnf_p7_vendor_extension(nfapi_pnf_p7_config_t* config, nfapi_p7_message_header_t* msg);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif // _NFAPI_PNF_INTERFACE_H_
diff --git a/openair1/SCHED/phy_procedures_lte_ue.c b/openair1/SCHED/phy_procedures_lte_ue.c
index 1f85d226f5dd97638b4742cdeff7098a6b46825d..15f735c5c91a92e455ef3630534663619d8d9b44 100644
--- a/openair1/SCHED/phy_procedures_lte_ue.c
+++ b/openair1/SCHED/phy_procedures_lte_ue.c
@@ -1766,7 +1766,6 @@ void ue_ulsch_uespec_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB
       if (ue->mac_enabled == 1) {
 
 	// signal MAC that Msg3 was sent
-    // Substitute call to this function by call to fill_Tx_indication (UE_MAC_Tx_IND_Msg3_TYPE)
 	Msg3_transmitted(Mod_id,
 			 CC_id,
 			 frame_tx,
@@ -1784,9 +1783,6 @@ void ue_ulsch_uespec_procedures(PHY_VARS_UE *ue,UE_rxtx_proc_t *proc,uint8_t eNB
 	  //if (ue->ulsch[eNB_id]->harq_processes[harq_pid]->calibration_flag == 0) {
 	  access_mode=SCHEDULED_ACCESS;
 
-	  // Panos: Remove the call to ue_get_sdu here and use
-	  // the corresponding Tx.request PDU, instead of ulsch_input_buffer, below.
-
 	  ue_get_sdu(Mod_id,
 		     CC_id,
 		     frame_tx,
diff --git a/openair2/LAYER2/MAC/ue_procedures.c b/openair2/LAYER2/MAC/ue_procedures.c
index 173ef442562cf6a16a82d62099f83caa2d20a13d..f411d3ff8beaddd988899453a7186e6bbb45cd0d 100644
--- a/openair2/LAYER2/MAC/ue_procedures.c
+++ b/openair2/LAYER2/MAC/ue_procedures.c
@@ -52,6 +52,7 @@
 #include "UTIL/OPT/opt.h"
 #include "OCG.h"
 #include "OCG_extern.h"
+#include "openair2/PHY_INTERFACE/phy_stub_UE.h"
 
 #ifdef PHY_EMUL
 # include "SIMULATION/simulation_defs.h"
@@ -71,6 +72,8 @@
 
 extern uint8_t usim_test;
 
+extern UL_IND_t *UL_INFO;
+
 /*
 #ifndef USER_MODE
 #define msg debug_msg
@@ -1586,7 +1589,9 @@ for (lcid=DCCH; (lcid < MAX_NUM_LCID) && (is_all_lcid_processed == FALSE) ; lcid
 
   // build PHR and update the timers
   if (phr_ce_len == sizeof(POWER_HEADROOM_CMD)) {
-    phr_p->PH = get_phr_mapping(module_idP,CC_id,eNB_index);
+	  //Panos: Substitute with a static value for the MAC layer abstraction
+    //phr_p->PH = get_phr_mapping(module_idP,CC_id,eNB_index);
+	  phr_p->PH = 40;
     phr_p->R  = 0;
     LOG_D(MAC,"[UE %d] Frame %d report PHR with mapping (%d->%d) for LCID %d\n",
           module_idP,frameP, get_PHR(module_idP,CC_id,eNB_index), phr_p->PH,POWER_HEADROOM);
@@ -1595,6 +1600,7 @@ for (lcid=DCCH; (lcid < MAX_NUM_LCID) && (is_all_lcid_processed == FALSE) ; lcid
     phr_p=NULL;
   }
 
+
   LOG_T(MAC,"[UE %d] Frame %d: bsr s %p bsr_l %p, phr_p %p\n",  module_idP,frameP,bsr_s, bsr_l, phr_p);
 
 
@@ -1804,7 +1810,6 @@ for (lcid=DCCH; (lcid < MAX_NUM_LCID) && (is_all_lcid_processed == FALSE) ; lcid
   stop_meas(&UE_mac_inst[module_idP].tx_ulsch_sdu);
 #endif
   
-  // Panos: Tx_request should be filled from the ulsch_buffer here.
 
   if (opt_enabled) {
     trace_pdu(0, ulsch_buffer, buflen, module_idP, 3, UE_mac_inst[module_idP].crnti, UE_mac_inst[module_idP].txFrame, UE_mac_inst[module_idP].txSubframe, 0, 0);
@@ -1813,6 +1818,8 @@ for (lcid=DCCH; (lcid < MAX_NUM_LCID) && (is_all_lcid_processed == FALSE) ; lcid
   }
 }
 
+
+
 //------------------------------------------------------------------------------
 // called at each subframe
 // Performs :
diff --git a/openair2/PHY_INTERFACE/phy_stub_UE.c b/openair2/PHY_INTERFACE/phy_stub_UE.c
index 8a5e05c50d9025b7d004d2646f06ab318d56fc0e..7072b5e0386605de91946850efbe1da6c444a072 100644
--- a/openair2/PHY_INTERFACE/phy_stub_UE.c
+++ b/openair2/PHY_INTERFACE/phy_stub_UE.c
@@ -7,11 +7,15 @@
 #include "openair2/LAYER2/MAC/vars.h"
 //#include "common/ran_context.h"
 #include "openair2/PHY_INTERFACE/phy_stub_UE.h"
+//#include "nfapi_pnf_interface.h"
+//#include "nfapi.h"
+//#include "nfapi_pnf.h"
 
 
 
-//extern uint8_t nfapi_pnf;
 
+//extern uint8_t nfapi_pnf;
+//UL_IND_t *UL_INFO;
 
 
 void handle_nfapi_UE_Rx(uint8_t Mod_id, Sched_Rsp_t *Sched_INFO, int eNB_id){
@@ -80,11 +84,11 @@ void handle_nfapi_UE_Rx(uint8_t Mod_id, Sched_Rsp_t *Sched_INFO, int eNB_id){
 
 					// C-RNTI parameter not actually used. Provided only to comply with existing function definition.
 					// Not sure about parameters to fill the preamble index.
-					const rnti_t c_rnti = UE_mac_inst[Mod_id].crnti;
+					rnti_t c_rnti = UE_mac_inst[Mod_id].crnti;
 					ue_process_rar(Mod_id, CC_id, frame,
 							dl_config_pdu_tmp->dlsch_pdu.dlsch_pdu_rel8.rnti, //RA-RNTI
 							Tx_req->tx_request_body.tx_pdu_list[dl_config_pdu_tmp->dlsch_pdu.dlsch_pdu_rel8.pdu_index].segments[0].segment_data,
-							c_rnti,
+							&c_rnti,
 							UE_mac_inst[Mod_id].RA_prach_resources.ra_PreambleIndex,
 							Tx_req->tx_request_body.tx_pdu_list[dl_config_pdu_tmp->dlsch_pdu.dlsch_pdu_rel8.pdu_index].segments[0].segment_data);
 				}
@@ -102,15 +106,18 @@ void handle_nfapi_UE_Rx(uint8_t Mod_id, Sched_Rsp_t *Sched_INFO, int eNB_id){
 }
 
 
-void fill_rx_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint8_t *ulsch_buffer, uint16_t buflen)
+
+void fill_rx_indication_UE_MAC(module_id_t Mod_id,int frame,int subframe, UL_IND_t* UL_INFO, uint8_t *ulsch_buffer, uint16_t buflen, uint16_t rnti)
 {
 	  nfapi_rx_indication_pdu_t *pdu;
 
 	  int timing_advance_update;
 	  //int sync_pos;
 
-	  /*uint32_t harq_pid = subframe2harq_pid(&eNB->frame_parms,
-						frame,subframe);*/
+	  //uint32_t harq_pid = subframe2harq_pid(&eNB->frame_parms,
+	 //					frame,subframe);
+
+	  //UL_IND_t *UL_INFO = (UL_IND_t*)malloc16(sizeof(UL_IND_t));
 
 
 	  pthread_mutex_lock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
@@ -122,7 +129,7 @@ void fill_rx_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_I
 
 	  //  pdu->rx_ue_information.handle          = eNB->ulsch[UE_id]->handle;
 	  pdu->rx_ue_information.tl.tag          = NFAPI_RX_UE_INFORMATION_TAG;
-	  pdu->rx_ue_information.rnti            = UE_mac_inst[Mod_id].crnti;
+	  pdu->rx_ue_information.rnti            = rnti;
 	  pdu->rx_indication_rel8.tl.tag         = NFAPI_RX_INDICATION_REL8_TAG;
 	  //pdu->rx_indication_rel8.length         = eNB->ulsch[UE_id]->harq_processes[harq_pid]->TBS>>3;
 	  pdu->rx_indication_rel8.length         = buflen;
@@ -134,7 +141,7 @@ void fill_rx_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_I
 	  pdu->rx_indication_rel8.timing_advance = timing_advance_update;
 
 	  //  if (timing_advance_update > 10) { dump_ulsch(eNB,frame,subframe,UE_id); exit(-1);}
-	  //  if (timing_advance_update < -10) { dump_ulsch(eNB,frame,subframe,UE_id); exit(-1);}
+	  //  if (timing_advance_update < -10) { dump_ulsch(eNB,frame,subframe,UE_id); exit(-1);}*/
 	  /*switch (eNB->frame_parms.N_RB_DL) {
 	  case 6:
 	    pdu->rx_indication_rel8.timing_advance = timing_advance_update;
@@ -182,7 +189,7 @@ void fill_rx_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_I
 
 }
 
-void fill_sr_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO) {
+void fill_sr_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint16_t rnti) {
 
   pthread_mutex_lock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
   nfapi_sr_indication_pdu_t *pdu =   &UL_INFO->sr_ind.sr_pdu_list[UL_INFO->rx_ind.number_of_pdus];
@@ -190,7 +197,7 @@ void fill_sr_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_I
   pdu->instance_length                                = 0; // don't know what to do with this
   //  pdu->rx_ue_information.handle                       = handle;
   pdu->rx_ue_information.tl.tag                       = NFAPI_RX_UE_INFORMATION_TAG;
-  pdu->rx_ue_information.rnti                         = UE_mac_inst[Mod_id].crnti;; //Panos: Is this the right RNTI?
+  pdu->rx_ue_information.rnti                         = rnti; //UE_mac_inst[Mod_id].crnti;; //Panos: Is this the right RNTI?
 
 
   // Panos dependency from PHY not sure how to substitute this. Should we hardcode it?
@@ -244,6 +251,7 @@ void fill_rach_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL
 	    //Panos: The two following should get extracted from the call to get_prach_resources().
 	    UL_INFO->rach_ind.preamble_list[0].preamble_rel8.preamble = ra_PreambleIndex;
 	    UL_INFO->rach_ind.preamble_list[0].preamble_rel8.rnti 	  = ra_RNTI;
+	    UL_INFO->rach_ind.number_of_preambles++;
 
 
 	    UL_INFO->rach_ind.preamble_list[0].preamble_rel13.rach_resource_type = 0;
@@ -253,10 +261,14 @@ void fill_rach_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL
 	        // If NFAPI PNF then we need to send the message to the VNF
 	        //if (nfapi_pnf == 1)
 	        //{
-	          nfapi_rach_indication_t rach_ind;
-	          rach_ind.header.message_id = NFAPI_RACH_INDICATION;
-	          rach_ind.sfn_sf = frame<<4 | subframe;
-	          rach_ind.rach_indication_body = UL_INFO->rach_ind;
+	        //Panos: Not sure if we need the following. They refer to nfapi_rach_indication_t type
+	        //so we cannot insert it to the UL_INFO which has an nfapi_rach_indication_body_t type.
+	    	//Probably it should be part of UL_indication() function before calling oai_nfapi_rach_ind(&rach_ind).
+
+	    	  /*nfapi_rach_indication_t *rach_ind;
+	          rach_ind->header.message_id = NFAPI_RACH_INDICATION;
+	          rach_ind->sfn_sf = frame<<4 | subframe;
+	          rach_ind->rach_indication_body = UL_INFO->rach_ind;*/
 
 	          LOG_E(PHY,"\n\n\n\nDJP - this needs to be sent to VNF **********************************************\n\n\n\n");
 	          LOG_E(PHY,"UE Filling NFAPI indication for RACH : TA %d, Preamble %d, rnti %x, rach_resource_type %d\n",
@@ -275,12 +287,12 @@ void fill_rach_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL
 
 }
 
-void fill_ulsch_cqi_indication(int Mod_id, uint16_t frame,uint8_t subframe, UL_IND_t *UL_INFO) {
+void fill_ulsch_cqi_indication_UE_MAC(int Mod_id, uint16_t frame,uint8_t subframe, UL_IND_t *UL_INFO, uint16_t rnti) {
 	pthread_mutex_lock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
 	nfapi_cqi_indication_pdu_t *pdu         = &UL_INFO->cqi_ind.cqi_pdu_list[UL_INFO->cqi_ind.number_of_cqis];
 	nfapi_cqi_indication_raw_pdu_t *raw_pdu = &UL_INFO->cqi_ind.cqi_raw_pdu_list[UL_INFO->cqi_ind.number_of_cqis];
 
-	pdu->rx_ue_information.rnti = UE_mac_inst[Mod_id].crnti;;
+	pdu->rx_ue_information.rnti = rnti;
 	//if (ulsch_harq->cqi_crc_status != 1)
 	//Panos: Since we assume that CRC flag is always 0 (ACK) I guess that data_offset should always be 0.
 	pdu->cqi_indication_rel9.data_offset = 0;
@@ -315,6 +327,452 @@ void fill_ulsch_cqi_indication(int Mod_id, uint16_t frame,uint8_t subframe, UL_I
 
 }
 
+void fill_ulsch_harq_indication_UE_MAC(int Mod_id, int frame,int subframe, UL_IND_t *UL_INFO, nfapi_ul_config_ulsch_harq_information *harq_information, uint16_t rnti)
+{
+
+  //int UE_id = find_dlsch(rnti,eNB,SEARCH_EXIST);
+  //AssertFatal(UE_id>=0,"UE_id doesn't exist\n");
+
+  pthread_mutex_lock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
+  nfapi_harq_indication_pdu_t *pdu =   &UL_INFO->harq_ind.harq_pdu_list[UL_INFO->harq_ind.number_of_harqs];
+  int i;
+
+  pdu->instance_length                                = 0; // don't know what to do with this
+  //  pdu->rx_ue_information.handle                       = handle;
+  pdu->rx_ue_information.rnti                         = rnti;
+
+  //Panos: For now we consider only FDD
+  //if (eNB->frame_parms.frame_type == FDD) {
+    pdu->harq_indication_fdd_rel13.mode = 0;
+    pdu->harq_indication_fdd_rel13.number_of_ack_nack = harq_information->harq_information_rel10.harq_size;
+
+    //Panos: Could this be wrong? Is the number_of_ack_nack field equivalent to O_ACK?
+    //pdu->harq_indication_fdd_rel13.number_of_ack_nack = ulsch_harq->O_ACK;
+
+    for (i=0;i<harq_information->harq_information_rel10.harq_size;i++) {
+      //AssertFatal(ulsch_harq->o_ACK[i] == 0 || ulsch_harq->o_ACK[i] == 1, "harq_ack[%d] is %d, should be 1,2 or 4\n",i,ulsch_harq->o_ACK[i]);
+
+      pdu->harq_indication_fdd_rel13.harq_tb_n[i] = 1; //Panos: Assuming always an ACK (No NACK or DTX)
+      // release DLSCH if needed
+      //if (ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
+
+    }
+  //}
+  /*else { // TDD
+    M=ul_ACK_subframe2_M(&eNB->frame_parms,
+			 subframe);
+
+    pdu->harq_indication_fdd_rel13.mode = 1-bundling;
+    pdu->harq_indication_fdd_rel13.number_of_ack_nack = ulsch_harq->O_ACK;
+
+    for (i=0;i<ulsch_harq->O_ACK;i++) {
+      AssertFatal(ulsch_harq->o_ACK[i] == 0 || ulsch_harq->o_ACK[i] == 1, "harq_ack[%d] is %d, should be 1,2 or 4\n",i,ulsch_harq->o_ACK[i]);
+
+      pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = 2-ulsch_harq->o_ACK[i];
+      // release DLSCH if needed
+      if (ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
+      if      (M==1 && ulsch_harq->O_ACK==1 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+      else if (M==1 && ulsch_harq->O_ACK==2 && ulsch_harq->o_ACK[i] == 1) release_harq(eNB,UE_id,i,frame,subframe,0xffff);
+      else if (M>1 && ulsch_harq->o_ACK[i] == 1) {
+	// spatial bundling
+	release_harq(eNB,UE_id,0,frame,subframe,1<<i);
+	release_harq(eNB,UE_id,1,frame,subframe,1<<i);
+      }
+    }
+  }*/
+
+  UL_INFO->harq_ind.number_of_harqs++;
+  pthread_mutex_unlock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
+}
+
+
+void fill_uci_harq_indication_UE_MAC(int Mod_id,
+			      int frame,
+			      int subframe,
+			      UL_IND_t *UL_INFO,
+			      nfapi_ul_config_harq_information *harq_information,
+			      uint16_t rnti
+			      /*uint8_t tdd_mapping_mode,
+			      uint16_t tdd_multiplexing_mask*/) {
+
+  //int UE_id=find_dlsch(uci->rnti,eNB,SEARCH_EXIST);
+  //AssertFatal(UE_id>=0,"UE_id doesn't exist\n");
+
+
+  pthread_mutex_lock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
+  nfapi_harq_indication_pdu_t *pdu =   &UL_INFO->harq_ind.harq_pdu_list[UL_INFO->harq_ind.number_of_harqs];
+
+  pdu->instance_length                                = 0; // don't know what to do with this
+  //  pdu->rx_ue_information.handle                       = handle;
+  pdu->rx_ue_information.rnti                         = rnti;
+
+  // estimate UL_CQI for MAC (from antenna port 0 only)
+
+  // Panos: Set static SNR for now
+  //int SNRtimes10 = dB_fixed_times10(uci->stat) - 200;//(10*eNB->measurements.n0_power_dB[0]);
+  int SNRtimes10 = 640;
+
+  //if (SNRtimes10 < -100) LOG_I(PHY,"uci->stat %d \n",uci->stat);
+
+  if      (SNRtimes10 < -640) pdu->ul_cqi_information.ul_cqi=0;
+  else if (SNRtimes10 >  635) pdu->ul_cqi_information.ul_cqi=255;
+  else                        pdu->ul_cqi_information.ul_cqi=(640+SNRtimes10)/5;
+  pdu->ul_cqi_information.channel = 0;
+
+  //Panos: Considering only FDD for now
+  //if (eNB->frame_parms.frame_type == FDD) {
+
+    //Panos: Condition taken from fapi_l1::handle_uci_harq_information() function
+    if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 0) &&
+          (harq_information->harq_information_rel9_fdd.harq_size == 1)) {
+    //if (uci->pucch_fmt == pucch_format1a) {
+      pdu->harq_indication_fdd_rel13.mode = 0;
+      pdu->harq_indication_fdd_rel13.number_of_ack_nack = 1;
+
+      //AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]);
+      pdu->harq_indication_fdd_rel13.harq_tb_n[0] = 1; //Panos: Assuming always an ACK (No NACK or DTX)
+
+
+    }
+    else if ((harq_information->harq_information_rel9_fdd.ack_nack_mode == 0) &&
+                 (harq_information->harq_information_rel9_fdd.harq_size == 2)) {
+      pdu->harq_indication_fdd_rel13.mode = 0;
+      pdu->harq_indication_fdd_rel13.number_of_ack_nack = 2;
+      //AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]);
+      //AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]);
+      pdu->harq_indication_fdd_rel13.harq_tb_n[0] = 1; //Panos: Assuming always an ACK (No NACK or DTX)
+      pdu->harq_indication_fdd_rel13.harq_tb_n[1] = 1; //Panos: Assuming always an ACK (No NACK or DTX)
+      // release DLSCH if needed
+      //if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+      //if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+    }
+    else AssertFatal(1==0,"only format 1a/b for now, received \n");
+  //}
+  /*else { // TDD
+
+    AssertFatal(tdd_mapping_mode==0 || tdd_mapping_mode==1 || tdd_mapping_mode==2,
+		"Illegal tdd_mapping_mode %d\n",tdd_mapping_mode);
+
+    pdu->harq_indication_tdd_rel13.mode = tdd_mapping_mode;
+
+    switch (tdd_mapping_mode) {
+    case 0: // bundling
+
+      if (uci->pucch_fmt == pucch_format1a) {
+	pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1;
+	AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]);
+	pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = harq_ack[0];
+	// release all bundled DLSCH if needed
+	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+      }
+      else if (uci->pucch_fmt == pucch_format1b) {
+	pdu->harq_indication_tdd_rel13.number_of_ack_nack = 2;
+	AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]);
+	AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]);
+	pdu->harq_indication_tdd_rel13.harq_data[0].bundling.value_0 = harq_ack[0];
+	pdu->harq_indication_tdd_rel13.harq_data[1].bundling.value_0 = harq_ack[1];
+	// release all DLSCH if needed
+	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+	if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+      }
+      break;
+    case 1: // multiplexing
+      AssertFatal(uci->pucch_fmt == pucch_format1b,"uci->pucch_format %d is not format1b\n",uci->pucch_fmt);
+
+      if (uci->num_pucch_resources == 1 && uci->pucch_fmt == pucch_format1a) {
+	pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1;
+	AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[0] == 4, "harq_ack[0] is %d, should be 1,2 or 4\n",harq_ack[0]);
+	pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0];
+	// release all DLSCH if needed
+	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+      }
+      else if (uci->num_pucch_resources == 1 && uci->pucch_fmt == pucch_format1b) {
+	pdu->harq_indication_tdd_rel13.number_of_ack_nack = 2;
+	AssertFatal(harq_ack[0] == 1 || harq_ack[0] == 2 || harq_ack[1] == 4, "harq_ack[0] is %d, should be 0,1 or 4\n",harq_ack[0]);
+	AssertFatal(harq_ack[1] == 1 || harq_ack[1] == 2 || harq_ack[1] == 4, "harq_ack[1] is %d, should be 0,1 or 4\n",harq_ack[1]);
+	pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0];
+	pdu->harq_indication_tdd_rel13.harq_data[1].multiplex.value_0 = harq_ack[1];
+	// release all DLSCH if needed
+	if (harq_ack[0] == 1) release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+	if (harq_ack[1] == 1) release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+      }
+      else { // num_pucch_resources (M) > 1
+	pdu->harq_indication_tdd_rel13.number_of_ack_nack = uci->num_pucch_resources;
+
+	pdu->harq_indication_tdd_rel13.harq_data[0].multiplex.value_0 = harq_ack[0];
+	pdu->harq_indication_tdd_rel13.harq_data[1].multiplex.value_0 = harq_ack[1];
+	if (uci->num_pucch_resources == 3) 	pdu->harq_indication_tdd_rel13.harq_data[2].multiplex.value_0 = harq_ack[2];
+	if (uci->num_pucch_resources == 4) 	pdu->harq_indication_tdd_rel13.harq_data[3].multiplex.value_0 = harq_ack[3];
+	// spatial-bundling in this case so release both HARQ if necessary
+	release_harq(eNB,UE_id,0,frame,subframe,tdd_multiplexing_mask);
+	release_harq(eNB,UE_id,1,frame,subframe,tdd_multiplexing_mask);
+      }
+      break;
+    case 2: // special bundling (SR collision)
+      pdu->harq_indication_tdd_rel13.number_of_ack_nack = 1;
+      int tdd_config5_sf2scheds=0;
+      if (eNB->frame_parms.tdd_config==5) tdd_config5_sf2scheds = getM(eNB,frame,subframe);
+
+      switch (harq_ack[0]) {
+      case 0:
+	break;
+      case 1: // check if M=1,4,7
+	if (uci->num_pucch_resources == 1 || uci->num_pucch_resources == 4 ||
+	    tdd_config5_sf2scheds == 1 || tdd_config5_sf2scheds == 4 || tdd_config5_sf2scheds == 7) {
+	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+	}
+	break;
+      case 2: // check if M=2,5,8
+	if (uci->num_pucch_resources == 2 || tdd_config5_sf2scheds == 2 ||
+	    tdd_config5_sf2scheds == 5 || tdd_config5_sf2scheds == 8) {
+	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+	}
+	break;
+      case 3: // check if M=3,6,9
+	if (uci->num_pucch_resources == 3 || tdd_config5_sf2scheds == 3 ||
+	    tdd_config5_sf2scheds == 6 || tdd_config5_sf2scheds == 9) {
+	  release_harq(eNB,UE_id,0,frame,subframe,0xffff);
+	  release_harq(eNB,UE_id,1,frame,subframe,0xffff);
+	}
+	break;
+      }
+      break;
+
+    }
+  } //TDD*/
+
+
+  UL_INFO->harq_ind.number_of_harqs++;
+  LOG_E(PHY,"Incremented eNB->UL_INFO.harq_ind.number_of_harqs:%d\n", UL_INFO->harq_ind.number_of_harqs);
+  pthread_mutex_unlock(&UE_mac_inst[Mod_id].UL_INFO_mutex);
+
+}
+
+
+void handle_nfapi_ul_pdu_UE_MAC(module_id_t Mod_id,
+                         nfapi_ul_config_request_pdu_t *ul_config_pdu,
+                         uint16_t frame,uint8_t subframe,uint8_t srs_present)
+{
+  nfapi_ul_config_ulsch_pdu_rel8_t *rel8 = &ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8;
+
+  //int8_t UE_id;
+
+  // check if we have received a dci for this ue and ulsch descriptor is configured
+
+  if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_PDU_TYPE) {
+    //AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.rnti,eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No existing UE ULSCH for rnti %x\n",rel8->rnti);
+    LOG_D(PHY,"Applying UL config for UE, rnti %x for frame %d, subframe %d\n",
+         rel8->rnti,frame,subframe);
+    uint8_t ulsch_buffer[5477] __attribute__ ((aligned(32)));
+    uint16_t buflen = ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.size;
+
+    uint16_t rnti = ul_config_pdu->ulsch_pdu.ulsch_pdu_rel8.rnti;
+    uint8_t access_mode=SCHEDULED_ACCESS;
+    if(buflen>0){
+    	ue_get_sdu( Mod_id, 0, frame, subframe, 0, ulsch_buffer, buflen, &access_mode);
+    	fill_rx_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_buffer,buflen, rnti);
+    }
+  }
+
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE) {
+    //AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
+	  uint8_t ulsch_buffer[5477] __attribute__ ((aligned(32)));
+	  uint16_t buflen = ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.size;
+	  nfapi_ul_config_ulsch_harq_information *ulsch_harq_information = &ul_config_pdu->ulsch_harq_pdu.harq_information;
+	  uint16_t rnti = ul_config_pdu->ulsch_harq_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti;
+	  uint8_t access_mode=SCHEDULED_ACCESS;
+	  if(buflen>0){
+		  	ue_get_sdu( Mod_id, 0, frame, subframe, 0, ulsch_buffer, buflen, &access_mode);
+	      	fill_rx_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_buffer,buflen, rnti);
+	  }
+
+	  if(ulsch_harq_information)
+		  fill_ulsch_harq_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_harq_information, rnti);
+
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE) {
+    //AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,
+    //                                eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
+	  uint8_t ulsch_buffer[5477] __attribute__ ((aligned(32)));
+	  uint16_t buflen = ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.size;
+
+	  uint16_t rnti = ul_config_pdu->ulsch_cqi_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti;
+	  uint8_t access_mode=SCHEDULED_ACCESS;
+	  if(buflen>0){
+		  ue_get_sdu( Mod_id, 0, frame, subframe, 0, ulsch_buffer, buflen, &access_mode);
+		  fill_rx_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_buffer,buflen, rnti);
+	  }
+	  fill_ulsch_cqi_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, rnti);
+
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE) {
+    //AssertFatal((UE_id = find_ulsch(ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti,
+    //                                eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No available UE ULSCH for rnti %x\n",ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti);
+
+	  uint8_t ulsch_buffer[5477] __attribute__ ((aligned(32)));
+	  uint16_t buflen = ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.size;
+	  nfapi_ul_config_ulsch_harq_information *ulsch_harq_information = &ul_config_pdu->ulsch_cqi_harq_ri_pdu.harq_information;
+
+	  uint16_t rnti = ul_config_pdu->ulsch_cqi_harq_ri_pdu.ulsch_pdu.ulsch_pdu_rel8.rnti;
+	  uint8_t access_mode=SCHEDULED_ACCESS;
+	  if(buflen>0){
+	      	ue_get_sdu( Mod_id, 0, frame, subframe, 0, ulsch_buffer, buflen, &access_mode);
+	      	fill_rx_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_buffer,buflen, rnti);
+	  }
+
+	  if(ulsch_harq_information)
+		  fill_ulsch_harq_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, ulsch_harq_information, rnti);
+	  fill_ulsch_cqi_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, rnti);
+
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE) {
+  //  AssertFatal((UE_id = find_uci(ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti,
+  //                                proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
+  //              "No available UE UCI for rnti %x\n",ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti);
+
+	  uint16_t rnti = ul_config_pdu->uci_harq_pdu.ue_information.ue_information_rel8.rnti;
+
+	  nfapi_ul_config_harq_information *ulsch_harq_information = &ul_config_pdu->uci_harq_pdu.harq_information;
+
+	  fill_uci_harq_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO,ulsch_harq_information, rnti);
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE) {
+    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_PDU_TYPE not handled yet\n");
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE) {
+    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_HARQ_PDU_TYPE not handled yet\n");
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE) {
+    AssertFatal(1==0,"NFAPI_UL_CONFIG_UCI_CQI_SR_PDU_TYPE not handled yet\n");
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE) {
+    //AssertFatal((UE_id = find_uci(ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti,
+    //                              proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No available UE UCI for rnti %x\n",ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti);
+	  uint16_t rnti = ul_config_pdu->uci_sr_pdu.ue_information.ue_information_rel8.rnti;
+
+	  fill_sr_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO, rnti);
+
+  }
+  else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE) {
+    //AssertFatal((UE_id = find_uci(rel8->rnti,proc->frame_tx,proc->subframe_tx,eNB,SEARCH_EXIST_OR_FREE))>=0,
+    //            "No available UE UCI for rnti %x\n",ul_config_pdu->uci_sr_harq_pdu.ue_information.ue_information_rel8.rnti);
+
+	  uint16_t rnti = ul_config_pdu->uci_sr_harq_pdu.ue_information.ue_information_rel8.rnti;
+
+	  // We fill the sr_indication only if ue_get_sr() would normally instruct PHY to send a SR.
+	  if (ue_get_SR(Mod_id ,0,frame, 0, rnti, subframe))
+		  fill_sr_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO,rnti);
+
+	  nfapi_ul_config_harq_information *ulsch_harq_information = &ul_config_pdu->uci_sr_harq_pdu.harq_information;
+	  fill_uci_harq_indication_UE_MAC(Mod_id, frame, subframe, UL_INFO,ulsch_harq_information, rnti);
+
+  }
+  /*else if (ul_config_pdu->pdu_type == NFAPI_UL_CONFIG_SRS_PDU_TYPE) {
+    handle_srs_pdu(eNB,ul_config_pdu,frame,subframe);
+  }*/
+}
+
+
+
+
+
+
+int ul_config_req_UE_MAC(nfapi_pnf_p7_config_t* pnf_p7, nfapi_ul_config_request_t* req)
+{
+  LOG_D(PHY,"[PNF] UL_CONFIG_REQ %s() sfn_sf:%d pdu:%d rach_prach_frequency_resources:%d srs_present:%u\n",
+      __FUNCTION__,
+      NFAPI_SFNSF2DEC(req->sfn_sf),
+      req->ul_config_request_body.number_of_pdus,
+      req->ul_config_request_body.rach_prach_frequency_resources,
+      req->ul_config_request_body.srs_present
+      );
+
+  /*if (RC.ru == 0)
+  {
+    return -1;
+  }
+
+  if (RC.eNB == 0)
+  {
+    return -2;
+  }
+
+  if (RC.eNB[0][0] == 0)
+  {
+    return -3;
+  }
+
+  if (sync_var != 0)
+  {
+    NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() Main system not up - is this a dummy subframe?\n", __FUNCTION__);
+    return -4;
+  }*/
+
+  int sfn = NFAPI_SFNSF2SFN(req->sfn_sf);
+  int sf = NFAPI_SFNSF2SF(req->sfn_sf);
+
+  //struct PHY_VARS_eNB_s *eNB = RC.eNB[0][0];
+  //eNB_rxtx_proc_t *proc = &eNB->proc.proc_rxtx[0];
+
+  module_id_t Mod_id = 0; //Panos: Currently static (only for one UE) but this should change.
+  nfapi_ul_config_request_pdu_t* ul_config_pdu_list = req->ul_config_request_body.ul_config_pdu_list;
+
+  //Panos: Not sure whether we should put the memory allocation here.
+  //*** Note we should find the right place to call free(UL_INFO).
+  UL_INFO = (UL_IND_t*)malloc(sizeof(UL_IND_t));
+
+  uint8_t is_rach = req->ul_config_request_body.rach_prach_frequency_resources;
+  if(is_rach) {
+	  PRACH_RESOURCES_t *prach_resources = ue_get_rach(Mod_id, 0, sfn, 0, sf);
+	  fill_rach_indication_UE_MAC(Mod_id, sfn ,sf, UL_INFO, prach_resources->ra_PreambleIndex, prach_resources->ra_RNTI);
+  }
+
+  // subframe works off TX SFN/SF which is 4 ahead, need to put it back to RX SFN/SF
+  // probably could just use proc->frame_rx
+
+  // PanosQ: This an eNB MAC function. Are we allowed to call it from here?
+  // Also, it is only in the nfapi-RU-RAU-split
+  //subtract_subframe(&sfn, &sf, 4);
+
+
+  for (int i=0;i<req->ul_config_request_body.number_of_pdus;i++)
+  {
+    //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() sfn/sf:%d PDU[%d] size:%d\n", __FUNCTION__, NFAPI_SFNSF2DEC(req->sfn_sf), i, ul_config_pdu_list[i].pdu_size);
+
+    if (
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_HARQ_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_RI_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_ULSCH_CQI_HARQ_RI_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_UCI_HARQ_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_UCI_SR_PDU_TYPE ||
+        ul_config_pdu_list[i].pdu_type == NFAPI_UL_CONFIG_UCI_SR_HARQ_PDU_TYPE
+       )
+    {
+      //NFAPI_TRACE(NFAPI_TRACE_INFO, "%s() handle_nfapi_ul_pdu() for PDU:%d\n", __FUNCTION__, i);
+
+
+      handle_nfapi_ul_pdu_UE_MAC(Mod_id,&ul_config_pdu_list[i],sfn,sf,req->ul_config_request_body.srs_present);
+    }
+    else
+    {
+      //NFAPI_TRACE(NFAPI_TRACE_ERROR, "%s() PDU:%i UNKNOWN type :%d\n", __FUNCTION__, i, ul_config_pdu_list[i].pdu_type);
+    }
+  }
+
+  return 0;
+}
+
+
+
+
 
 
 
diff --git a/openair2/PHY_INTERFACE/phy_stub_UE.h b/openair2/PHY_INTERFACE/phy_stub_UE.h
index 58ce7346f76de813535999c6b7fbd2aa34457740..b3b07b60b32e98f29ee5bdcf8569c2a6a3a41f10 100644
--- a/openair2/PHY_INTERFACE/phy_stub_UE.h
+++ b/openair2/PHY_INTERFACE/phy_stub_UE.h
@@ -11,10 +11,15 @@
 
 #include <stdint.h>
 #include "openair2/PHY_INTERFACE/IF_Module.h"
+#include "nfapi_interface.h"
+#include "nfapi_pnf_interface.h"
 //#include "openair1/PHY/LTE_TRANSPORT/defs.h"
 //#include "openair1/PHY/defs.h"
 //#include "openair1/PHY/LTE_TRANSPORT/defs.h"
 
+UL_IND_t *UL_INFO;
+
+
 
 // Panos: This function should return all the sched_response config messages which concern a specific UE. Inside this
 // function we should somehow make the translation of config message's rnti to Mod_ID.
@@ -24,6 +29,8 @@ Sched_Rsp_t get_nfapi_sched_response(uint8_t Mod_id);
 // namely:ue_send_sdu(), or ue_decode_si(), or ue_decode_p(), or ue_process_rar() based on the rnti type.
 void handle_nfapi_UE_Rx(uint8_t Mod_id, Sched_Rsp_t *Sched_INFO, int eNB_id);
 
+int pnf_ul_config_req_UE_MAC(nfapi_pnf_p7_config_t* pnf_p7, nfapi_ul_config_request_t* req);
+
 // This function will be processing UL and HI_DCI0 config requests to trigger all the MAC Tx related calls
 // at the UE side, namely: ue_get_SR(), ue_get_rach(), ue_get_sdu() based on the pdu configuration type.
 // The output of these calls will be put to an UL_IND_t structure which will then be the input to
@@ -35,12 +42,15 @@ void send_nfapi_UL_indications(UL_IND_t UL_INFO);
 
 // This function should be filling the nfapi ULSCH indications at the MAC level of the UE in a similar manner
 // as fill_rx_indication() does. It should get called from ue_get_SDU()
-void fill_rx_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint8_t *ulsch_buffer, uint16_t buflen);
+
+//void fill_rx_indication_UE_MAC(module_id_t Mod_id,int frame,int subframe);
+
+void fill_rx_indication_UE_MAC(module_id_t Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint8_t *ulsch_buffer, uint16_t buflen, uint16_t rnti);
 
 
 // This function should be indicating directly to the eNB when there is a planned scheduling request at the MAC layer
 // of the UE. It should get called from ue_get_SR()
-void fill_sr_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO);
+void fill_sr_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint16_t rnti);
 
 // In our case the this function will be always indicating ACK to the MAC of the eNB (i.e. always assuming)
 // successful decoding.
@@ -50,7 +60,21 @@ void fill_crc_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_
 void fill_rach_indication_UE_MAC(int Mod_id,int frame,int subframe, UL_IND_t *UL_INFO, uint8_t ra_PreambleIndex, uint16_t ra_RNTI);
 
 
-void fill_ulsch_cqi_indication(int Mod_id, uint16_t frame,uint8_t subframe, UL_IND_t *UL_INFO);
+void fill_ulsch_cqi_indication_UE_MAC(int Mod_id, uint16_t frame,uint8_t subframe, UL_IND_t *UL_INFO, uint16_t rnti);
+
+
+void fill_ulsch_harq_indication_UE_MAC(int Mod_id, int frame,int subframe, UL_IND_t *UL_INFO, nfapi_ul_config_ulsch_harq_information *harq_information, uint16_t rnti);
+
+void fill_uci_harq_indication_UE_MAC(int Mod_id, int frame, int subframe, UL_IND_t *UL_INFO,nfapi_ul_config_harq_information *harq_information, uint16_t rnti
+			      /*uint8_t tdd_mapping_mode,
+			      uint16_t tdd_multiplexing_mask*/);
+
+int ul_config_req_UE_MAC(nfapi_pnf_p7_config_t* pnf_p7, nfapi_ul_config_request_t* req);
+
+void handle_nfapi_ul_pdu_UE_MAC(module_id_t Mod_id,
+                         nfapi_ul_config_request_pdu_t *ul_config_pdu,
+                         uint16_t frame,uint8_t subframe,uint8_t srs_present);
+
 
 
 #endif /* PHY_STUB_UE_H_ */