FAPI TM, WLS_LIB and ODULOW documentation

    * Additional ODU-Low components for Bronze Rel
    * 5G Fapi Translator Module
    * Wireless Services library based on DPDK
    * Full ODULOW Documentation set

    Issue-Id: ODULOW-2

Change-Id: I3d0df696286ad50798ccd0765f809fb04629b660
Signed-off-by: Luis Farias <luis.farias@intel.com>
diff --git a/wls_lib/Makefile b/wls_lib/Makefile
new file mode 100644
index 0000000..865c618
--- /dev/null
+++ b/wls_lib/Makefile
@@ -0,0 +1,69 @@
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+ifdef DPDK_WLS
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-icc
+RTE_OUTPUT = ${PWD}
+
+include $(RTE_SDK)/mk/rte.vars.mk
+SRCS-y := wls_lib_dpdk.c syslib.c
+SHARED = libwls.so
+#CFLAGS +=-Wall -wd9 -DDPDK_WLS
+CFLAGS +=-Wall -DDPDK_WLS -fstack-protector
+EXTRA_CFLAGS +=-g
+include $(RTE_SDK)/mk/rte.extshared.mk
+
+else
+
+obj-m := wls.o
+wls-objs :=syslib.o \
+	   wls_drv.o
+
+INSTALL_MOD_DIR ?=/lib/modules/`uname -r`/updates/drivers/intel/wls
+cmd_depmod = /sbin/depmod -a
+EXTRA_CFLAGS +=-g -Wall
+SRC := $(shell pwd)
+
+all:
+	$(MAKE) -C $(KERNEL_SRC) $(EXTRA_CFLAGS) M=$(SRC)
+
+modules_install:
+	$(MAKE) -C $(KERNEL_SRC) $(EXTRA_CFLAGS) M=$(SRC) modules_install
+
+wls_lib.o: wls_lib.c
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -D_GNU_SOURCE -g  -fPIC -o $@ -c $<
+
+syslib_user.o: syslib.c
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -g  -fPIC -o $@ -c $<
+
+libwls.so: wls_lib.o syslib_user.o
+	$(CC) $(LDFLAGS) -g -shared -fPIC -Wl,-soname,libwls.so -o $@ wls_lib.o syslib_user.o
+
+install:
+	[ -d "${INSTALL_MOD_DIR}" ] || mkdir -p ${INSTALL_MOD_DIR}
+	cp ./wls.ko ${INSTALL_MOD_DIR}
+	$(call cmd_depmod)
+clean:
+	rm -f *.o *~ core .depend .*.cmd *.ko *.so *.mod.c
+	rm -f Module.markers Module.symvers modules.order
+	rm -rf .tmp_versions Modules.symvers
+endif
diff --git a/wls_lib/build.sh b/wls_lib/build.sh
new file mode 100644
index 0000000..30a0c2a
--- /dev/null
+++ b/wls_lib/build.sh
@@ -0,0 +1,35 @@
+#!/bin/sh
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+#
+#  File: build.sh
+#        Build script to comapile kernel module, library and test application.
+#
+
+#if [ "$1" = "dpdk_wls" ]; then
+    echo "Building dpdk based wls library" 
+    make DPDK_WLS=1 ${*:2}
+    cd testapp
+    make DPDK_WLS=1 ${*:2}
+#else
+#    echo "Building driver based wls library" 
+#    make -C /lib/modules/`uname -r`/build  M=$PWD $*
+#    make libwls.so $*
+#    cd testapp
+#    make $*
+#fi
diff --git a/wls_lib/syslib.c b/wls_lib/syslib.c
new file mode 100644
index 0000000..3bedeed
--- /dev/null
+++ b/wls_lib/syslib.c
@@ -0,0 +1,408 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+#ifdef __KERNEL__
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#endif
+#include "syslib.h"
+#include "wls.h"
+
+#ifdef __KERNEL__
+#ifdef _DEBUG_
+#define PRINT_DEBUG(format, args...)            \
+do {                                          \
+    printk(KERN_INFO "wls debug: " format,##args); \
+}while(0)
+#else
+#define PRINT_DEBUG(x, args...)  do { } while(0)
+#endif
+#else
+#ifdef _DEBUG_
+#define PRINT_DEBUG(x, args...)  printf("wls_lib debug: "x, ## args);
+#else
+#define PRINT_DEBUG(x, args...)  do { } while(0)
+#endif
+#endif
+
+
+
+#define SFL_memcpy  memcpy
+/******************************************************************************
+*                                                                             *
+*     Generic fast queue that operates with pointers (derived from ICC)       *
+*                                                                             *
+******************************************************************************/
+
+int	SFL_WlsEnqueue(PFASTQUEUE pq, U64 pData, wls_us_addr_conv change_addr, void* hWls)
+{
+	U32 put = pq->put;
+	U32 new_put = put + 1;
+
+    PRINT_DEBUG("off %lx put %d get %d  size %d storage %lx\n",(unsigned long)pq - (unsigned long) hWls,  pq->put, pq->get, pq->size, pq->pStorage);
+
+	if (new_put >= pq->size)
+		new_put = 0;
+
+	if (new_put != pq->get)
+	{ // the queue is not full
+
+        U64*  pLocalStorage = (U64*) pq->pStorage; // kernel VA
+        if (change_addr)
+             pLocalStorage = (U64*)change_addr(hWls, (U64)pq->pStorage); // user VA
+
+        PRINT_DEBUG("pLocalStorage %lx\n", (U64)pLocalStorage);
+
+		pLocalStorage[put] = pData;
+
+		DSB();
+		pq->put = new_put;
+		return TRUE;
+	}
+	return FALSE;
+}
+
+
+U64 SFL_WlsDequeue(PFASTQUEUE pq,
+    wls_us_addr_conv change_addr,
+    void *hWls)
+{
+	U64 p;
+    U32   get = pq->get;
+
+	if ((pq->put - get) != 0)
+	{
+        U64* pLocalStorage = (U64*) pq->pStorage; // kernel VA
+
+        DSB();
+        if (change_addr)
+            pLocalStorage = (U64 *)change_addr(hWls, (U64)pLocalStorage); //convert to user VA
+
+		p =  pLocalStorage[get++];
+		if (get >= pq->size)
+			get = 0;
+
+		pq->get = get;
+		return p;
+	}
+	return 0;
+}
+
+/*
+int	SFL_Enqueue_NoSync(PFASTQUEUE pq, PVOID pData)
+{
+	U32 put = pq->put;
+	U32 new_put = put + 1;
+	if (new_put >= pq->size)
+		new_put = 0;
+
+	if (new_put != pq->get)
+	{ // the queue is not full
+		pq->pStorage[ put ] = pData;
+		pq->put = new_put;
+		return TRUE;
+	}
+	return FALSE;
+}*/
+
+/*
+PVOID SFL_Dequeue_NoSync(PFASTQUEUE pq)
+{
+	PVOID p;
+	U32   get = pq->get;
+
+	if ((pq->put - get) != 0)
+	{
+		p = pq->pStorage[get++];
+		if (get >= pq->size)
+			get = 0;
+		pq->get = get;
+		return p;
+	}
+	return NULL;
+}*/
+
+void SFL_DefQueue(PFASTQUEUE pq, void *pStorage, int StorageSize)
+{
+	memset( (void*) pq, 0x00, sizeof( FASTQUEUE) );
+	// always define storage as U64 []
+	pq->size = StorageSize >> 3;
+	pq->pStorage = (U64)pStorage;
+
+    PRINT_DEBUG("put %d get %d  size %d pq->pStorage %lx\n",pq->put, pq->get, pq->size, pq->pStorage);
+
+}
+
+static U32 sfl_SafeQueueLevel(U32 put, U32 get, U32 size)
+{
+	U32 nItems;
+
+	if (put >= get)
+		nItems = put - get;
+	else
+		nItems = size + put - get;
+
+	return nItems;
+}
+
+U32 WLS_GetNumItemsInTheQueue(PWLS_MSG_QUEUE fpq)
+{
+	return sfl_SafeQueueLevel(fpq->put, fpq->get, fpq->size);
+}
+
+U32 SFL_GetNumItemsInTheQueue(FASTQUEUE *fpq)
+{
+	return sfl_SafeQueueLevel(fpq->put, fpq->get, fpq->size);
+}
+
+/*
+
+U32 SFL_Queue_BatchRead( PFASTQUEUE pq, unsigned long *pDestArr, U32 Count)
+{
+	if (Count)
+	{
+		U32 write_index = 0;
+		U32 nReads = 0;
+		//U32 iMask = SFL_IDisable();
+		U32 put = pq->put; // fetch the put atomicly (as app may change it!)
+		U32 get = pq->get; // cache the volatile "get index"
+
+		//printf("nItems_%d ", SFL_GetNumItemsInTheQueue(pq));
+
+		if ( (nReads = sfl_SafeQueueLevel(put, get, pq->size)) < Count )
+			Count = nReads;
+		else
+			nReads = Count;
+
+		if (Count >= pq->size - get)
+		{
+			U32 n = pq->size - get;
+			SFL_memcpy( pDestArr, &pq->pStorage[get], sizeof(pDestArr[0]) * n);
+			get = 0;
+			Count -= n;
+			write_index += n;
+		}
+
+		if (Count)
+		{
+			SFL_memcpy( &pDestArr[write_index], &pq->pStorage[get], sizeof(pDestArr[0]) * Count);
+			get += Count;
+		}
+
+		DSB();
+		pq->get = get;
+
+		//printf("nItems_%d ", SFL_GetNumItemsInTheQueue(pq));
+
+		//SFL_IControl(iMask);
+
+		return nReads;
+	}
+	return FALSE;
+}
+
+
+// the routine does not keep the fifo order (it is used to take items away from the queue)
+U32 SFL_Queue_BatchUnload(PFASTQUEUE pq, unsigned long* pDestArr, U32 Count)
+{
+	if (Count)
+	{
+		U32 write_index = 0;
+		U32 nReads = 0;
+		//U32 iMask = SFL_IDisable();
+		U32 put = pq->put; // lets cache the volatile "put index"
+		U32 get = pq->get; // fetch the get index atomicly (as app may change it)
+
+		//printf("nItems_%d ", SFL_GetNumItemsInTheQueue(pq));
+
+		nReads = sfl_SafeQueueLevel(put, get, pq->size);
+		if (nReads)
+			nReads -= 1; // decrement is used to cover the case when a reader already started reading from head
+
+		if ( nReads < Count )
+			Count = nReads;
+		else
+			nReads = Count;
+
+		if (!put)
+			put = pq->size;
+
+		if (Count >= put)
+		{
+			U32 n = put;
+			SFL_memcpy( pDestArr, &pq->pStorage[0], sizeof(pDestArr[0]) * n);
+			put = pq->size;
+			Count -= n;
+			write_index += n;
+		}
+
+		if (Count)
+		{
+			put -= Count;
+			SFL_memcpy( &pDestArr[write_index], &pq->pStorage[put], sizeof(pDestArr[0]) * Count);
+		}
+
+		if (put >= pq->size)
+			put = 0;
+
+		DSB();
+		pq->put = put;
+
+		//printf("nItems_%d ", SFL_GetNumItemsInTheQueue(pq));
+
+		//SFL_IControl(iMask);
+
+		return nReads;
+	}
+	return FALSE;
+}
+
+
+U32 SFL_Queue_BatchWrite( PFASTQUEUE pq, unsigned long *pSrcArr, U32 Count)
+{
+
+	U32 nWrites = Count;
+
+	if (Count)
+	{
+		U32 read_index = 0;
+		U32 put = pq->put;
+		//U32 iMask = SFL_IDisable();
+
+		if (pq->size - put <= Count)
+		{
+			U32 n = pq->size - put;
+			SFL_memcpy( &pq->pStorage[put], pSrcArr, sizeof(pSrcArr[0]) * n);
+			put = 0;
+			Count -= n;
+			read_index += n;
+		}
+
+		if (Count)
+		{
+			SFL_memcpy( &pq->pStorage[put], &pSrcArr[read_index], sizeof(pSrcArr[0]) * Count);
+			put += Count;
+		}
+
+		DSB();
+		pq->put = put;
+
+		//SFL_IControl(iMask);
+		return nWrites;
+	}
+	return 0;
+}
+*/
+void WLS_MsgDefineQueue(
+	PWLS_MSG_QUEUE pq,
+	PWLS_MSG_HANDLE pStorage,
+	U32 size,
+	U32 sema)
+{
+	memset( pq, 0x00, sizeof(WLS_MSG_QUEUE));
+	pq->pStorage = (U64) pStorage;
+	pq->get = 0;
+	pq->put = 0;
+	pq->size = size; // number of items
+	pq->sema = sema;
+}
+
+U32 WLS_MsgEnqueue(
+	PWLS_MSG_QUEUE pq,
+    U64   pIaPaMsg,
+    U32   MsgSize,
+    U16   TypeID,
+    U16   flags,
+    wls_us_addr_conv change_addr,
+    void *hWls)
+{
+	U32 rc = 0;
+	// below is protected section.
+	U32 put = pq->put;
+	U32 put_new = put + 1;
+
+	if (put_new >= pq->size)
+		put_new = 0;
+
+	if (put_new != pq->get)
+	{
+        PWLS_MSG_HANDLE pLocalStorage = (PWLS_MSG_HANDLE)pq->pStorage; // kernel VA
+        PWLS_MSG_HANDLE pItem;
+
+        PRINT_DEBUG("Kernel VA pq->pStorage %lx put [%d] %d %d\n", pq->pStorage, put_new, pq->get, pq->size);
+
+        if (change_addr)
+            pLocalStorage = (PWLS_MSG_HANDLE)change_addr(hWls, (U64)pq->pStorage);
+
+        pItem = &pLocalStorage[put];
+
+		pItem->pIaPaMsg = pIaPaMsg;
+		pItem->MsgSize  = MsgSize;
+		pItem->TypeID   = TypeID;
+		pItem->flags    = flags;
+		DSB();
+		pq->put = put_new;
+		rc = 1;
+	}
+
+	return rc;
+}
+
+int WLS_MsgDequeue(
+	PWLS_MSG_QUEUE pq,
+	PWLS_MSG_HANDLE pDestItem,
+	wls_us_addr_conv change_addr,
+	void *hWls)
+{
+	int retval = FALSE;
+	U32 get = pq->get;
+    PWLS_MSG_HANDLE pLocalStorage;
+
+	if (!pDestItem)
+		return retval;
+
+    if (get >= pq->size)
+    {
+
+        PRINT_DEBUG("error WLS_MsgDequeue get %d size %d\n", get, pq->size);
+
+        return retval;
+    }
+
+    pLocalStorage = (PWLS_MSG_HANDLE) pq->pStorage; // kernel VA
+    PRINT_DEBUG("WLS_MsgDequeue with pq->pStorage %lX\n",pq->pStorage);
+    if (pq->put != get)
+	{
+
+        DSB();
+        if (change_addr)
+            pLocalStorage = (PWLS_MSG_HANDLE)change_addr(hWls, (U64) pq->pStorage); //convert to user VA
+
+		*pDestItem = pLocalStorage[get];
+
+		if (++get == pq->size)
+			get = 0;
+
+		pq->get = get;
+		retval = TRUE;
+	}
+
+	return retval;
+}
diff --git a/wls_lib/syslib.h b/wls_lib/syslib.h
new file mode 100644
index 0000000..1b52470
--- /dev/null
+++ b/wls_lib/syslib.h
@@ -0,0 +1,131 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 __SYSLIB_H__
+#define __SYSLIB_H__
+
+typedef unsigned char  U8;      /* unsigned 8-bit  integer */
+typedef unsigned short U16;     /* unsigned 16-bit integer */
+typedef unsigned int   U32;     /* unsigned 32-bit integer */
+#ifdef __x86_64__
+typedef unsigned long  U64;     /* unsigned 64-bit integer */
+#else
+typedef unsigned long long  U64;     /* unsigned 64-bit integer */
+#endif
+
+typedef volatile unsigned char  V8;
+typedef volatile unsigned short V16;
+typedef volatile unsigned int   V32;
+typedef volatile unsigned long  V4;
+
+typedef signed char  S8;         /* 8-bit  signed integer */
+typedef signed short S16;       /* 16-bit signed integer */
+typedef signed int   S32;        /* 32-bit signed integer */
+
+#ifdef __x86_64__
+typedef signed long  S64;          /* unsigned 64-bit integer */
+#else
+typedef signed long long  S64;     /* unsigned 64-bit integer */
+#endif
+
+#ifndef _PVOID_
+#define _PVOID_
+typedef void *PVOID;
+#endif
+
+typedef U64 (*wls_us_addr_conv)(void*, U64);
+
+
+#define K 			1024
+#define M			(K*K)
+#define KHZ         1000
+#define MHZ         (KHZ * KHZ)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#ifndef NULL
+#define NULL ((PVOID)(0))
+#endif
+#define HANDLE	PVOID
+
+#ifdef __KERNEL__
+#define DSB()  smp_mb()
+#define DMB()  smp_wmb()
+#else
+#define	DSB()  __asm__ __volatile__("mfence": : :"memory")
+#define	DMB()  __asm__ __volatile__("sfence": : :"memory")
+#endif
+
+typedef struct tagWLS_MSG_HANDLE
+{
+    U64   pIaPaMsg;
+    U32   pWlsPaMsg;
+    U32   MsgSize;
+    U16   TypeID;         // used to identify destination
+    U16   flags;
+    U32   res1;
+} WLS_MSG_HANDLE, *PWLS_MSG_HANDLE; /* 4 x QW */
+
+typedef struct tagFASTQUEUE {
+    U64 pStorage;
+	U32 BlockSize;
+	U32 sema;
+	V32 get;
+	V32 put;
+	U32 size;
+    U32 res;
+} FASTQUEUE, *PFASTQUEUE;
+
+typedef struct tagWLS_MSG_QUEUE {
+    U64 pStorage;
+	U32 sema;
+	V32 get;
+	V32 put;
+	U32 size;
+} WLS_MSG_QUEUE, *PWLS_MSG_QUEUE;
+
+#define COUNT(some_array) ( sizeof(some_array)/sizeof((some_array)[0]) )
+
+void SFL_DefQueue(PFASTQUEUE pq, void *pStorage, int StorageSize);
+int	SFL_WlsEnqueue(PFASTQUEUE pq, U64 pData, wls_us_addr_conv change_addr, void* hWls);
+int	SFL_Enqueue_NoSync(PFASTQUEUE pq, PVOID pData);
+U64 SFL_WlsDequeue(PFASTQUEUE pq, wls_us_addr_conv change_addr, void *hWls);
+
+PVOID SFL_Dequeue_NoSync(PFASTQUEUE pq);
+U32 SFL_Queue_BatchRead( PFASTQUEUE pq, unsigned long *pDestArr, U32 Count);
+U32 SFL_Queue_BatchWrite( PFASTQUEUE pq, unsigned long *pSrcArr, U32 Count);
+
+void WLS_MsgDefineQueue(PWLS_MSG_QUEUE pq, PWLS_MSG_HANDLE pStorage, U32 size, U32 sema);
+U32 WLS_MsgEnqueue(PWLS_MSG_QUEUE pq, U64  pIaPaMsg, U32 MsgSize, U16 TypeID, U16   flags, wls_us_addr_conv change_addr, void* h);
+int WLS_MsgDequeue(PWLS_MSG_QUEUE pq, PWLS_MSG_HANDLE pDestItem, wls_us_addr_conv change_addr, void *hWls);
+U32 WLS_GetNumItemsInTheQueue(PWLS_MSG_QUEUE fpq);
+U32 SFL_GetNumItemsInTheQueue(FASTQUEUE *fpq);
+
+
+
+
+
+#endif // __SYSLIB_H__
+
+
diff --git a/wls_lib/test/bin/fapi/fapi.sh b/wls_lib/test/bin/fapi/fapi.sh
new file mode 100644
index 0000000..d5a72fa
--- /dev/null
+++ b/wls_lib/test/bin/fapi/fapi.sh
@@ -0,0 +1,30 @@
+#######################################################################
+#
+# <COPYRIGHT_TAG>
+#
+#######################################################################
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$DIR_WIRELESS_WLS
+
+MACHINE_TYPE=`uname -m`
+
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+
+	ulimit -c unlimited
+	echo 1 > /proc/sys/kernel/core_uses_pid
+
+	sysctl -w kernel.sched_rt_runtime_us=-1
+	sysctl -w kernel.shmmax=2147483648
+	sysctl -w kernel.shmall=2147483648
+	chkconfig --level 12345 irqbalance off
+	echo 0 > /proc/sys/kernel/nmi_watchdog
+	echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
+
+fi
+
+echo start WLS PHY Process
+if [ "$1" = "-g" ]; then
+    shift
+    taskset -c 16 gdb-ia --args ./fapi_app $@
+else
+    taskset -c 16 ./fapi_app $@
+fi
diff --git a/wls_lib/test/bin/mac/mac.sh b/wls_lib/test/bin/mac/mac.sh
new file mode 100644
index 0000000..9b0b4e4
--- /dev/null
+++ b/wls_lib/test/bin/mac/mac.sh
@@ -0,0 +1,31 @@
+#######################################################################
+#
+# <COPYRIGHT_TAG>
+#
+#######################################################################
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$DIR_WIRELESS_WLS
+
+MACHINE_TYPE=`uname -m`
+
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+
+	ulimit -c unlimited
+	echo 1 > /proc/sys/kernel/core_uses_pid
+
+	sysctl -w kernel.sched_rt_runtime_us=-1
+	sysctl -w kernel.shmmax=2147483648
+	sysctl -w kernel.shmall=2147483648
+	chkconfig --level 12345 irqbalance off
+	echo 0 > /proc/sys/kernel/nmi_watchdog
+	echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
+
+fi
+
+echo start WLS PHY Process
+if [ "$1" = "-g" ]; then
+    shift
+    taskset -c 17 gdb-ia --args ./mac_app $@
+else
+    taskset -c 17 ./mac_app $@
+fi
+
diff --git a/wls_lib/test/bin/phy/phy.sh b/wls_lib/test/bin/phy/phy.sh
new file mode 100644
index 0000000..41c1ea8
--- /dev/null
+++ b/wls_lib/test/bin/phy/phy.sh
@@ -0,0 +1,30 @@
+#######################################################################
+#
+# <COPYRIGHT_TAG>
+#
+#######################################################################
+export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$DIR_WIRELESS_WLS
+
+MACHINE_TYPE=`uname -m`
+
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+
+	ulimit -c unlimited
+	echo 1 > /proc/sys/kernel/core_uses_pid
+
+	sysctl -w kernel.sched_rt_runtime_us=-1
+	sysctl -w kernel.shmmax=2147483648
+	sysctl -w kernel.shmall=2147483648
+	chkconfig --level 12345 irqbalance off
+	echo 0 > /proc/sys/kernel/nmi_watchdog
+	echo 1 > /sys/module/rcupdate/parameters/rcu_cpu_stall_suppress
+
+fi
+
+echo start WLS PHY Process
+if [ "$1" = "-g" ]; then
+    shift
+    taskset -c 15 gdb-ia --args ./phy_app $@
+else
+    taskset -c 15 ./phy_app $@
+fi
diff --git a/wls_lib/test/build.sh b/wls_lib/test/build.sh
new file mode 100644
index 0000000..2f4e455
--- /dev/null
+++ b/wls_lib/test/build.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+make -C ./mac --no-print-directory -f ./makefile $* PLATFORM=X86_64BIT
+make -C ./fapi --no-print-directory -f ./makefile $* PLATFORM=X86_64BIT
+make -C ./phy --no-print-directory -f ./makefile $* PLATFORM=X86_64BIT
diff --git a/wls_lib/test/build/fapi/dep_file b/wls_lib/test/build/fapi/dep_file
new file mode 100644
index 0000000..d72bb47
--- /dev/null
+++ b/wls_lib/test/build/fapi/dep_file
@@ -0,0 +1,27 @@
+../build/fapi//home/Luis/Integration/oran_wls_lib/test/fapi/fapi_main.o: \
+ /home/Luis/Integration/oran_wls_lib/test/fapi/fapi_main.c \
+ /usr/include/stdc-predef.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_eal.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_config.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_compat.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_common.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_per_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_bus.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_log.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_dev.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_pci_dev_feature_defs.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_cfgfile.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_string_fns.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_launch.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_debug.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_branch_prediction.h \
+ /home/Luis/Integration/oran_wls_lib/wls.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_atomic.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/generic/rte_atomic.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_atomic_64.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_memzone.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_memory.h \
+ /home/Luis/Integration/oran_wls_lib/ttypes.h \
+ /home/Luis/Integration/oran_wls_lib/syslib.h \
+ /home/Luis/Integration/oran_wls_lib/wls_lib.h
diff --git a/wls_lib/test/build/mac/dep_file b/wls_lib/test/build/mac/dep_file
new file mode 100644
index 0000000..31f7627
--- /dev/null
+++ b/wls_lib/test/build/mac/dep_file
@@ -0,0 +1,34 @@
+../build/mac//home/Luis/Integration/oran_wls_lib/test/mac/mac_main.o: \
+ /home/Luis/Integration/oran_wls_lib/test/mac/mac_main.c \
+ /usr/include/stdc-predef.h /home/Luis/Integration/oran_wls_lib/wls_lib.h \
+ /home/Luis/Integration/oran_wls_lib/test/mac/mac_wls.h \
+ /home/Luis/Integration/oran_5g_fapi/include/fapi_interface.h
+../build/mac//home/Luis/Integration/oran_wls_lib/test/mac/mac_wls.o: \
+ /home/Luis/Integration/oran_wls_lib/test/mac/mac_wls.c \
+ /usr/include/stdc-predef.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_eal.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_config.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_compat.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_common.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_per_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_bus.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_log.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_dev.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_pci_dev_feature_defs.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_cfgfile.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_string_fns.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_launch.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_debug.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_branch_prediction.h \
+ /home/Luis/Integration/oran_wls_lib/wls.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_atomic.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/generic/rte_atomic.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_atomic_64.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_memzone.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_memory.h \
+ /home/Luis/Integration/oran_wls_lib/ttypes.h \
+ /home/Luis/Integration/oran_wls_lib/syslib.h \
+ /home/Luis/Integration/oran_wls_lib/wls_lib.h \
+ /home/Luis/Integration/oran_wls_lib/test/mac/mac_wls.h \
+ /home/Luis/Integration/oran_5g_fapi/include/fapi_interface.h
diff --git a/wls_lib/test/build/phy/dep_file b/wls_lib/test/build/phy/dep_file
new file mode 100644
index 0000000..7a11259
--- /dev/null
+++ b/wls_lib/test/build/phy/dep_file
@@ -0,0 +1,19 @@
+../build/phy//home/Luis/Integration/oran_wls_lib/test/phy/phy_main.o: \
+ /home/Luis/Integration/oran_wls_lib/test/phy/phy_main.c \
+ /usr/include/stdc-predef.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_eal.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_config.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_compat.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_common.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_per_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_bus.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_log.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_dev.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_pci_dev_feature_defs.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_cfgfile.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_string_fns.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_lcore.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_launch.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_debug.h \
+ /home/samuelwo/dpdk-18.08/x86_64-native-linuxapp-icc/include/rte_branch_prediction.h \
+ /home/Luis/Integration/oran_wls_lib/wls_lib.h
diff --git a/wls_lib/test/fapi/fapi_main.c b/wls_lib/test/fapi/fapi_main.c
new file mode 100644
index 0000000..7c1a14a
--- /dev/null
+++ b/wls_lib/test/fapi/fapi_main.c
@@ -0,0 +1,649 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * @brief This file is test FAPI wls lib main process
+ * @file fapi_main.c
+ * @ingroup group_testfapiwls
+ * @author Intel Corporation
+ **/
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <rte_eal.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+
+#define DPDK_WLS
+#include "wls.h"
+#include "wls_lib.h"
+
+#define NR5G_FAPI2PHY_WLS_INST 0
+#define NR5G_FAPI2MAC_WLS_INST 1
+#define SUCCESS 0
+#define FAILURE 1
+#define WLS_TEST_DEV_NAME "wls"
+#define WLS_TEST_MSG_ID   1
+#define WLS_TEST_MSG_SIZE 100
+#define WLS_TEST_MEM_SIZE 2126512128 
+#define ALLOC_TRACK_SIZE 16384
+#define MIN_UL_BUF_LOCATIONS 50
+#define MIN_DL_BUF_LOCATIONS 50
+#define MAX_UL_BUF_LOCATIONS 50
+#define MAX_DL_BUF_LOCATIONS 50
+#define NUM_WLS_INSTANCES 2
+#define WLS_HUGE_DEF_PAGE_SIZEA    0x40000000LL
+#define MSG_MAXSIZE1 (16*16384)
+#define NUM_TEST_MSGS   16
+
+
+
+
+typedef void* WLS_HANDLE;
+typedef struct wls_fapi_mem_array
+{
+    void **ppFreeBlock;
+    void *pStorage;
+    void *pEndOfStorage;
+    uint32_t nBlockSize;
+    uint32_t nBlockCount;
+} WLS_FAPI_MEM_STRUCT, *PWLS_FAPI_MEM_STRUCT;
+
+// WLS context structure
+typedef struct _nr5g_fapi_wls_context {
+    void        *shmem;       // shared  memory region.
+    uint64_t    shmem_size;   // shared  memory region size.
+    WLS_FAPI_MEM_STRUCT sWlsStruct;
+    WLS_HANDLE  h_wls[NUM_WLS_INSTANCES]; // WLS context handle
+    void *pWlsMemBase;
+    uint32_t nTotalMemorySize;
+    uint32_t nTotalBlocks;
+    uint32_t nAllocBlocks;
+    uint32_t nTotalAllocCnt;
+    uint32_t nTotalFreeCnt;
+    uint32_t nTotalUlBufAllocCnt;
+    uint32_t nTotalUlBufFreeCnt;
+    uint32_t nTotalDlBufAllocCnt;
+    uint32_t nTotalDlBufFreeCnt;
+    uint32_t nPartitionMemSize;
+    void     *pPartitionMemBase;
+    volatile pthread_mutex_t lock;
+    volatile pthread_mutex_t lock_alloc;
+} nr5g_fapi_wls_context_t, *p_nr5g_fapi_wls_context_t;
+
+nr5g_fapi_wls_context_t  g_wls_ctx;
+typedef void* WLS_HANDLE;
+static uint8_t alloc_track[ALLOC_TRACK_SIZE];
+void *g_shmem;
+uint32_t g_shmem_size=0;
+uint32_t g_nTotalAllocCnt=0;
+uint32_t g_nAllocBlocks=0;
+uint32_t gwls_fapi_ready=0;
+WLS_FAPI_MEM_STRUCT g_MemArray;
+nr5g_fapi_wls_context_t  g_wls_ctx;
+uint32_t g_nTotalDlBufAllocCnt=0;
+uint32_t g_nTotalUlBufAllocCnt=0;
+
+wls_us_ctx_t gphy_wls;
+wls_us_ctx_t gfapi_wls;
+
+WLS_HANDLE   g_phy_wls = &gphy_wls;
+WLS_HANDLE   g_fapi_wls = &gfapi_wls;
+
+uint8_t    fapi_dpdk_init(void);
+uint8_t    fapi_wls_init(const char *dev_name, unsigned long long mem_size);
+uint64_t   fapi_mac_recv();
+uint8_t    fapi_phy_send();
+uint64_t   fapi_phy_recv();
+uint8_t    fapi_mac_send();
+uint8_t    fapi2Phy_wls_init(p_nr5g_fapi_wls_context_t pwls);
+uint8_t wls_fapi_create_partition(p_nr5g_fapi_wls_context_t pWls);
+uint32_t wls_fapi_alloc_mem_array(PWLS_FAPI_MEM_STRUCT pMemArray, void **ppBlock);
+uint32_t wls_fapi_create_mem_array(PWLS_FAPI_MEM_STRUCT pMemArray, void *pMemArrayMemory, uint32_t totalSize, uint32_t nBlockSize);
+
+uint64_t nr5g_fapi_wls_va_to_pa(WLS_HANDLE h_wls, void *ptr)
+{
+    return ((uint64_t)WLS_VA2PA(h_wls, ptr));
+}
+
+inline p_nr5g_fapi_wls_context_t nr5g_fapi_wls_context()
+{
+    return &g_wls_ctx;
+}
+
+static inline WLS_HANDLE nr5g_fapi_fapi2mac_wls_instance()
+{
+    p_nr5g_fapi_wls_context_t p_wls_ctx = nr5g_fapi_wls_context();
+
+    return p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST];
+}
+
+static inline WLS_HANDLE nr5g_fapi_fapi2phy_wls_instance()
+{
+    p_nr5g_fapi_wls_context_t p_wls_ctx = nr5g_fapi_wls_context();
+    return p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST];
+}
+void *wls_fapi_alloc_buffer(uint32_t size, uint32_t loc)
+{
+    void *pBlock = NULL;
+    p_nr5g_fapi_wls_context_t pWls = nr5g_fapi_wls_context();
+    PWLS_FAPI_MEM_STRUCT pMemArray = &pWls->sWlsStruct;
+
+    pthread_mutex_lock((pthread_mutex_t *)&pWls->lock_alloc);
+
+    if (wls_fapi_alloc_mem_array(&pWls->sWlsStruct, &pBlock) != SUCCESS)
+    {
+        printf("wls_fapi_alloc_buffer alloc error size[%d] loc[%d]\n", size, loc);
+
+        exit(-1);
+    }
+    else
+    {
+        pWls->nAllocBlocks++;
+    }
+
+    //printf("----------------wls_fapi_alloc_buffer: size[%d] loc[%d] buf[%p] nAllocBlocks[%d]\n", size, loc, pBlock, pWls->nAllocBlocks);
+
+    //printf("[%p]\n", pBlock);
+
+    pWls->nTotalAllocCnt++;
+    if (loc < MAX_DL_BUF_LOCATIONS)
+        pWls->nTotalDlBufAllocCnt++;
+    else if (loc < MAX_UL_BUF_LOCATIONS)
+        pWls->nTotalUlBufAllocCnt++;
+
+    pthread_mutex_unlock((pthread_mutex_t *)&pWls->lock_alloc);
+    return pBlock;
+}
+
+uint32_t wls_fapi_create_mem_array(PWLS_FAPI_MEM_STRUCT pMemArray, void *pMemArrayMemory, uint32_t totalSize, uint32_t nBlockSize)
+{
+
+    int numBlocks = totalSize / nBlockSize;
+    void **ptr;
+    uint32_t i;
+
+    printf("wls_fapi_create_mem_array: pMemArray[%p] pMemArrayMemory[%p] totalSize[%d] nBlockSize[%d] numBlocks[%d]\n",
+        pMemArray, pMemArrayMemory, totalSize, nBlockSize, numBlocks);
+
+    // Can't be less than pointer size
+    if (nBlockSize < sizeof(void *))
+    {
+        return FAILURE;
+    }
+
+    // Can't be less than one block
+    if (totalSize < sizeof(void *))
+    {
+        return FAILURE;
+    }
+
+    pMemArray->ppFreeBlock = (void **)pMemArrayMemory;
+    pMemArray->pStorage = pMemArrayMemory;
+    pMemArray->pEndOfStorage = ((unsigned long*)pMemArrayMemory) + numBlocks * nBlockSize / sizeof(unsigned long);
+    pMemArray->nBlockSize = nBlockSize;
+    pMemArray->nBlockCount = numBlocks;
+
+    // Initialize single-linked list of free blocks;
+    ptr = (void **)pMemArrayMemory;
+    for (i = 0; i < pMemArray->nBlockCount; i++)
+    {
+#ifdef MEMORY_CORRUPTION_DETECT
+        // Fill with some pattern
+        uint8_t *p = (uint8_t *)ptr;
+        uint32_t j;
+
+        p += (nBlockSize - 16);
+        for (j = 0; j < 16; j++)
+        {
+            p[j] = MEMORY_CORRUPTION_DETECT_FLAG;
+        }
+#endif
+
+        if (i == pMemArray->nBlockCount - 1)
+        {
+            *ptr = NULL;      // End of list
+        }
+        else
+        {
+            // Points to the next block
+            *ptr = (void **)(((uint8_t*)ptr) + nBlockSize);
+            ptr += nBlockSize / sizeof(unsigned long);
+        }
+    }
+
+    memset(alloc_track, 0, sizeof(uint8_t) * ALLOC_TRACK_SIZE);
+
+    return SUCCESS;
+}
+
+uint32_t wls_fapi_alloc_mem_array(PWLS_FAPI_MEM_STRUCT pMemArray, void **ppBlock)
+{
+    int idx;
+
+    if (pMemArray->ppFreeBlock == NULL)
+    {
+        printf("wls_fapi_alloc_mem_array pMemArray->ppFreeBlock = NULL\n");
+        return FAILURE;
+    }
+
+    // FIXME: Remove after debugging
+    if (((void *) pMemArray->ppFreeBlock < pMemArray->pStorage) ||
+        ((void *) pMemArray->ppFreeBlock >= pMemArray->pEndOfStorage))
+    {
+        printf("wls_fapi_alloc_mem_array ERROR: Corrupted MemArray;Arr=%p,Stor=%p,Free=%p\n",
+                pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock);
+        return FAILURE;
+    }
+
+    pMemArray->ppFreeBlock = (void **)((unsigned long)pMemArray->ppFreeBlock & 0xFFFFFFFFFFFFFFF0);
+    *pMemArray->ppFreeBlock = (void **)((unsigned long)*pMemArray->ppFreeBlock & 0xFFFFFFFFFFFFFFF0);
+
+    if ((*pMemArray->ppFreeBlock != NULL) &&
+        (((*pMemArray->ppFreeBlock) < pMemArray->pStorage) ||
+        ((*pMemArray->ppFreeBlock) >= pMemArray->pEndOfStorage)))
+    {
+        fprintf(stderr, "ERROR: Corrupted MemArray;Arr=%p,Stor=%p,Free=%p,Curr=%p\n",
+                pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock,
+                *pMemArray->ppFreeBlock);
+        return FAILURE;
+    }
+
+    *ppBlock = (void *) pMemArray->ppFreeBlock;
+    pMemArray->ppFreeBlock = (void **) (*pMemArray->ppFreeBlock);
+
+    idx = (((uint64_t)*ppBlock - (uint64_t)pMemArray->pStorage)) / pMemArray->nBlockSize;
+    if (alloc_track[idx])
+    {
+        printf("wls_fapi_alloc_mem_array Double alloc Arr=%p,Stor=%p,Free=%p,Curr=%p\n",
+            pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock,
+            *pMemArray->ppFreeBlock);
+    }
+    else
+    {
+#ifdef MEMORY_CORRUPTION_DETECT
+        uint32_t nBlockSize = pMemArray->nBlockSize, i;
+        uint8_t *p = (uint8_t *)*ppBlock;
+
+        p += (nBlockSize - 16);
+        for (i = 0; i < 16; i++)
+        {
+            p[i] = MEMORY_CORRUPTION_DETECT_FLAG;
+        }
+#endif
+        alloc_track[idx] = 1;
+    }
+
+    //printf("Block allocd [%p,%p]\n", pMemArray, *ppBlock);
+
+    return SUCCESS;
+}
+
+
+
+uint8_t wls_fapi_create_partition(p_nr5g_fapi_wls_context_t pWls)
+{
+static long hugePageSize = WLS_HUGE_DEF_PAGE_SIZEA;
+     void *pPartitionMemBase;
+     uint32_t nPartitionMemSize;
+     uint32_t nTotalBlocks;
+     
+    pWls->pPartitionMemBase = pWls->pWlsMemBase + hugePageSize;
+    pWls->nPartitionMemSize = (pWls->nTotalMemorySize - hugePageSize);
+
+    pWls->nTotalBlocks = pWls->nPartitionMemSize / MSG_MAXSIZE1;
+
+    return wls_fapi_create_mem_array(&pWls->sWlsStruct, pWls->pPartitionMemBase, pWls->nPartitionMemSize, MSG_MAXSIZE1);
+}
+
+uint8_t nr5g_fapi2Phy_wls_init(p_nr5g_fapi_wls_context_t pWls)
+{
+    int nBlocks = 0;
+    uint8_t retval = SUCCESS;
+
+
+    WLS_HANDLE h_wls = 	pWls->h_wls[NR5G_FAPI2PHY_WLS_INST];
+    pthread_mutex_init((pthread_mutex_t *)&pWls->lock, NULL);
+    pthread_mutex_init((pthread_mutex_t *)&pWls->lock_alloc, NULL);
+
+    pWls->nTotalAllocCnt = 0;
+    pWls->nTotalFreeCnt = 0;
+    pWls->nTotalUlBufAllocCnt = 0;
+    pWls->nTotalUlBufFreeCnt = 0;
+    pWls->nTotalDlBufAllocCnt = 0;
+    pWls->nTotalDlBufFreeCnt = 0;
+    // Need to add wls_fapi_create_partition
+    retval = wls_fapi_create_partition(pWls);
+    if (retval == SUCCESS)
+    {	
+    	gwls_fapi_ready = 1;
+    	nBlocks = WLS_EnqueueBlock(h_wls, nr5g_fapi_wls_va_to_pa( h_wls, wls_fapi_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+2)));
+    	printf("WLS_EnqueueBlock [%d]\n", nBlocks);
+    	// Allocate Blocks for UL Transmission
+    	while(WLS_EnqueueBlock(h_wls, nr5g_fapi_wls_va_to_pa(h_wls,wls_fapi_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+3))))
+    	{
+         nBlocks++;
+    	}
+    	printf ("fapi2Phy UL Buffer Allocation completed\n");
+    }
+    else
+    {
+    	printf ("can't create WLS FAPI2PHY partition \n");
+    	return FAILURE;
+    }
+    return retval;
+}
+
+uint8_t nr5g_fapi_fapi2mac_wls_ready()
+{
+    int ret = SUCCESS;
+    ret = WLS_Ready1(nr5g_fapi_fapi2mac_wls_instance());
+    return ret;
+}
+
+
+inline uint8_t nr5g_fapi_fapi2phy_wls_ready()
+{
+    int ret = SUCCESS;
+    //NR5G_FAPI_LOG(TRACE_LOG, ("Waiting for L1 to respond in WLS Ready"));
+    ret = WLS_Ready(nr5g_fapi_fapi2phy_wls_instance());
+    return ret;
+}
+
+int main()
+{
+    uint8_t ret;
+    uint64_t p_msg;
+    uint8_t retval= FAILURE;
+    p_nr5g_fapi_wls_context_t pwls;
+
+    // DPDK init
+    ret = fapi_dpdk_init();
+    if (ret)
+    {
+        printf("\n[FAPI] DPDK Init - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[FAPI] DPDK Init - Done\n");
+
+    // WLS init
+    ret = fapi_wls_init(WLS_TEST_DEV_NAME, WLS_TEST_MEM_SIZE);
+    if(ret)
+    {
+        printf("\n[FAPI] WLS Init - Failed\n");
+        return FAILURE;
+    }
+    // Need to check for L1 and L2 started before attempting partition creation
+    // First let's wait for the L1 and L2 to be present
+    while (retval)
+    {
+    	retval = nr5g_fapi_fapi2phy_wls_ready();
+    }
+    // Now the L2 is up so let's make sure that the L1 was started first
+    retval=FAILURE;
+    while (retval)
+    {
+    	retval = nr5g_fapi_fapi2mac_wls_ready();
+    }
+    // Now that the L2 is up and has completed the Common Memory initialization the FT needs to initialize the FAPI2PHY buffers
+    pwls = nr5g_fapi_wls_context();
+    usleep(1000000);
+    ret = nr5g_fapi2Phy_wls_init(pwls);
+    if(ret)
+    {
+        printf("\n[FAPI] 2Phy WLS Init - Failed\n");
+        return FAILURE;
+    }
+    
+    printf("\n[FAPI] WLS Init - Done\n");
+
+    // Receive from MAC WLS
+    p_msg = fapi_mac_recv();
+    if (!p_msg)
+    {
+        printf("\n[FAPI] Receive from MAC - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[FAPI] Receive from MAC - Done\n");
+
+    // Sent to PHY WLS
+    ret = fapi_phy_send();
+    if (ret)
+    {
+        printf("\n[FAPI] Send to PHY - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[FAPI] Send to PHY - Done\n");
+
+    // Receive from PHY WLS
+    p_msg = fapi_phy_recv();
+    if (!p_msg)
+    {
+        printf("\n[FAPI] Receive from PHY - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[FAPI] Receive from PHY - Done\n");
+
+    // Sent to MAC WLS
+    ret = fapi_mac_send();
+    if (ret)
+    {
+        printf("\n[FAPI] Send to MAC - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[FAPI] Send to MAC - Done\n");
+
+
+    printf("\n[FAPI] Exiting...\n");
+
+    return SUCCESS;
+}
+
+uint8_t fapi_dpdk_init(void)
+{
+    char whitelist[32];
+    uint8_t i;
+
+    char *argv[] = {"fapi_app", "--proc-type=secondary",
+        "--file-prefix", "wls", whitelist};
+    
+    int argc = RTE_DIM(argv);
+
+    /* initialize EAL first */
+    sprintf(whitelist, "-w %s",  "0000:00:06.0");
+    printf("[FAPI] Calling rte_eal_init: ");
+
+    for (i = 0; i < RTE_DIM(argv); i++)
+    {
+        printf("%s ", argv[i]);
+    }
+    printf("\n");
+
+    if (rte_eal_init(argc, argv) < 0)
+        rte_panic("Cannot init EAL\n");
+
+    return SUCCESS;
+}
+
+uint8_t fapi_wls_init(const char *dev_name, unsigned long long mem_size)
+{
+    uint8_t *pMemZone;
+    static const struct rte_memzone *mng_memzone;
+    p_nr5g_fapi_wls_context_t p_wls_ctx = nr5g_fapi_wls_context();    
+    wls_drv_ctx_t *pDrv_ctx;
+
+    p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST] =
+     WLS_Open_Dual(dev_name, WLS_SLAVE_CLIENT, mem_size, &p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST]);
+    if((NULL == p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST]) && 
+            (NULL == p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST]))
+    {
+        return FAILURE;
+    }
+    g_shmem_size = mem_size;
+    p_wls_ctx->shmem_size = mem_size;
+    // Issue WLS_Alloc() for FAPI2MAC
+    p_wls_ctx->shmem = WLS_Alloc(
+            p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST],
+            p_wls_ctx->shmem_size);
+
+    if (NULL == p_wls_ctx->shmem)
+    {
+        printf("Unable to alloc WLS Memory for FAPI2MAC\n");
+        return FAILURE;
+    }            
+    p_wls_ctx->shmem = WLS_Alloc(
+            p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST],
+            p_wls_ctx->shmem_size);
+    p_wls_ctx->pWlsMemBase = p_wls_ctx->shmem;
+    p_wls_ctx->nTotalMemorySize = p_wls_ctx->shmem_size;
+    if (NULL == p_wls_ctx->shmem)
+    {
+        printf("Unable to alloc WLS Memory for FAPI2PHY\n");
+        return FAILURE;
+    }
+    
+    return SUCCESS;
+}    
+    
+
+uint64_t fapi_mac_recv()
+{
+    uint8_t  num_blks = 0;
+    uint64_t p_msg;
+    uint32_t msg_size;
+    uint16_t msg_id;
+    uint16_t flags;
+    uint32_t i;
+ 
+    WLS_HANDLE wls= nr5g_fapi_fapi2mac_wls_instance();
+    
+    for (i=0; i < NUM_TEST_MSGS; i++)
+    {
+    num_blks = WLS_Wait1(wls);
+    
+    if (num_blks)
+    {
+        p_msg = WLS_Get1(wls, &msg_size, &msg_id, &flags);
+    }
+    else
+    {
+        printf("\n[FAPI] FAPI2MAC WLS wait returned 0 blocks\n");
+    }
+    		if (p_msg)
+    		{
+    			printf("\n[FAPI] Receive from MAC Msg  %d-\n", i);
+    	  }
+    }    	  
+    return p_msg;
+}
+
+uint8_t fapi_phy_send()
+{
+    uint64_t pa_block = 0;
+    uint8_t ret = FAILURE;
+    uint32_t i;
+    WLS_HANDLE wls = nr5g_fapi_fapi2mac_wls_instance();
+    WLS_HANDLE wlsp = nr5g_fapi_fapi2phy_wls_instance();
+    
+    for (i=0; i < NUM_TEST_MSGS; i++)
+    {
+    pa_block = (uint64_t) WLS_DequeueBlock((void*) wls);
+    if (!pa_block)
+    {
+        printf("\n[FAPI] FAPI2MAC WLS Dequeue block error %d\n",i);
+        return FAILURE;
+    }
+
+    	ret = WLS_Put(wlsp, pa_block, WLS_TEST_MSG_SIZE, WLS_TEST_MSG_ID, (i== (NUM_TEST_MSGS-1))? WLS_TF_FIN:0);
+      if (ret)
+    	{
+        printf("\n[FAPI] Send to PHY %d- Failed\n",i);
+        return FAILURE;
+    	}
+    	else
+    	{
+    		printf("\n[FAPI] Send to PHY %d done \n",i);
+    	}
+    }
+    return ret;
+}
+
+uint64_t fapi_phy_recv()
+{
+    uint8_t  num_blks = 0;
+    uint64_t p_msg;
+    uint32_t msg_size;
+    uint16_t msg_id;
+    uint16_t flags;
+    uint32_t i=0;
+    WLS_HANDLE wls = nr5g_fapi_fapi2phy_wls_instance();
+    
+    while (1)
+    {
+    num_blks = WLS_Wait(wls);
+    printf("WLS_Wait returns %d\n",num_blks);
+    
+    if (num_blks)
+    {
+        p_msg = WLS_Get(wls, &msg_size, &msg_id, &flags);
+        printf("\n[FAPI] FAPI2PHY Received Block %d \n", i);
+        i++;
+        if (flags & WLS_TF_FIN)
+        {
+        	return p_msg;
+        }
+    }
+    else
+    {
+        printf("\n[FAPI] FAPI2MAC WLS wait returned 0 blocks\n");
+    }
+    }
+    return p_msg;
+}
+
+uint8_t fapi_mac_send()
+{
+    uint64_t pa_block = 0;
+    uint8_t ret = FAILURE;
+    uint32_t i;
+    WLS_HANDLE wls = nr5g_fapi_fapi2mac_wls_instance();
+    WLS_HANDLE wlsp = nr5g_fapi_fapi2phy_wls_instance();
+    
+    for (i=0; i < NUM_TEST_MSGS; i++)
+    {
+    pa_block = (uint64_t) WLS_DequeueBlock((void*) wlsp);
+    if (!pa_block)
+    {
+        printf("\n[FAPI] FAPI2MAC WLS Dequeue block %d error\n",i);
+        return FAILURE;
+    }
+
+    	ret = WLS_Put1(wls, pa_block, WLS_TEST_MSG_SIZE, WLS_TEST_MSG_ID, (i== (NUM_TEST_MSGS-1))? WLS_TF_FIN:0);
+    	printf("\n[FAPI] FAPI2MAC WLS Put1 block %d\n",i);
+    }
+    return ret;
+}
diff --git a/wls_lib/test/fapi/makefile b/wls_lib/test/fapi/makefile
new file mode 100644
index 0000000..655de05
--- /dev/null
+++ b/wls_lib/test/fapi/makefile
@@ -0,0 +1,162 @@
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+.SUFFIXES: .o .c .s .i .cpp
+
+# Makefile to build Testfapi application
+
+##############################################################
+#  Tools configuration
+##############################################################
+CC := icc
+CPP := icpc
+AS := as
+AR := ar
+LD := icc
+OBJDUMP := objdump
+
+ifeq ($(SHELL),cmd.exe)
+MD := mkdir.exe -p
+RM := rm.exe -rf
+else
+MD := mkdir -p
+RM := rm -rf
+endif
+
+##############################################################
+# TARGET
+##############################################################
+ifeq ($(RTE_TARGET),)
+    RTE_TARGET :=x86_64-native-linuxapp-icc
+endif
+##############################################################
+# DPDK
+##############################################################
+ifeq ($(RTE_SDK),)
+$(info Please make sure RTE_SDK points to DPDK folder (current version of DPDK is 18.08))
+    RTE_SDK := /opt/dpdk-18.08
+endif
+
+##############################################################
+# Projects folders
+##############################################################
+WLSDIR            := $(DIR_WIRELESS_WLS)
+BUILDDIR          := ../build/fapi
+SRCDIR            := $(CURDIR)
+
+wls_fapi_app_dep_file = $(BUILDDIR)/dep_file
+
+APP := ../bin/fapi/fapi_app
+
+INC := \
+ $(WLSDIR) \
+ $(SRCDIR) \
+ $(RTE_SDK)/$(RTE_TARGET)/include \
+
+INC := $(addprefix -I,$(INC))
+ 
+DEFS := USE_WO_LOCK _GNU_SOURCE NR5G
+
+ifneq ($(PRINTDBG),)
+DEFS := $(DEFS) PRINTF_DBG_OK
+endif
+
+ifeq ($(DEBUG_MODE),true)
+DEFS := $(DEFS) DEBUG_MODE
+endif
+
+DEFS := $(addprefix -D,$(DEFS))
+
+CFLAGS := -g -Wall -wd9 -Wno-deprecated-declarations -Wimplicit-function-declaration -fasm-blocks $(DEFS) $(INC)
+
+ifeq ($(PRINTDBG),)
+CFLAGS := $(CFLAGS) -Werror
+endif
+
+RTE_LIBS := -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,--whole-archive -Wl,-lrte_distributor -Wl,-lrte_kni -Wl,-lrte_pipeline -Wl,-lrte_table -Wl,-lrte_port -Wl,-lrte_timer -Wl,-lrte_hash -Wl,-lrte_lpm -Wl,-lrte_power -Wl,-lrte_acl -Wl,-lrte_meter -Wl,-lrte_sched -Wl,-lm -Wl,-lrt -Wl,--start-group -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_ip_frag -Wl,-lrte_ethdev -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_pci -Wl,-lrte_net -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_cfgfile -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ring -Wl,-lrt -Wl,-lm -Wl,-ldl -Wl,--end-group -Wl,--no-whole-archive
+LDFLAGS := -g -Wl,-lrt -Wl,-lpthread -Wl,-lhugetlbfs -Wl,-lm -Wl,-lnuma -L $(WLSDIR) -lwls
+
+LINUX_WLS_FAPI_APP_SRC := \
+	$(SRCDIR)/fapi_main.c \
+
+OBJS := $(LINUX_WLS_FAPI_APP_SRC:.c=.o)
+
+PROJECT_OBJ_DIR = $(BUILDDIR)
+
+OBJS := $(addprefix $(PROJECT_OBJ_DIR)/,$(OBJS))
+
+DIRLIST := $(sort $(dir $(OBJS)))
+
+CC_DEPS := $(addprefix __dep__,$(LINUX_WLS_FAPI_APP_SRC))
+
+GEN_DEP :=
+ifeq ($(wildcard $(wls_fapi_app_dep_file)),)
+GEN_DEP := regenerate_dep
+endif
+
+.PHONY: $(APP)
+$(APP): $(DIRLIST) echo_options $(GEN_DEP) $(OBJS)
+	@echo [LD] $(APP)
+	@$(CC) -o $(APP) $(OBJS) $(RTE_LIBS) $(LDFLAGS)
+#	$(OBJDUMP) -d $(APP) > $(APP).asm
+
+.PHONY : echo_options
+echo_options:
+	@echo [CFLAGS]	$(CFLAGS)
+	@echo [LDFAGS]	$(LDFLAGS)
+
+
+ifneq ($(wildcard $(wls_fapi_app_dep_file)),)
+include $(wls_fapi_app_dep_file)
+endif
+
+$(DIRLIST) :
+	-@$(MD) $@
+
+.PHONY : regenerate_dep
+regenerate_dep : clean_dep echo_regeenrate_dep  $(CC_DEPS)
+
+.PHONY: clean_dep
+clean_dep:
+	$(RM) $(wls_fapi_app_dep_file)
+
+.PHONY : echo_regeenrate_dep
+echo_regeenrate_dep:
+	@echo regenerating dep files
+
+
+.PHONY : CC_DEPS
+$(CC_DEPS):
+	@$(CC) -MM $(subst __dep__,,$@) -MT $(addprefix $(PROJECT_OBJ_DIR)/,$(patsubst %.c,%.o,$(subst __dep__,,$@))) $(CFLAGS) >> $(wls_fapi_app_dep_file)
+
+$(OBJS) : $(PROJECT_OBJ_DIR)/%.o: %.c
+	@echo [CC]    $(subst $(PROJECT_OBJ_DIR)/,,$@)
+	@$(CC) -c $(CFLAGS) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@))
+
+
+.PHONY: xclean
+xclean : clean_dep
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+	@$(RM) $(BUILDDIR)
+
+.PHONY: clean
+clean :
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+
diff --git a/wls_lib/test/mac/mac_main.c b/wls_lib/test/mac/mac_main.c
new file mode 100644
index 0000000..4d6c902
--- /dev/null
+++ b/wls_lib/test/mac/mac_main.c
@@ -0,0 +1,78 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * @brief This file is test MAC wls lib main process
+ * @file mac_main.c
+ * @ingroup group_testmacwls
+ * @author Intel Corporation
+ **/
+
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include "wls_lib.h"
+#include "mac_wls.h"
+
+#define WLS_TEST_DEV_NAME "wls"
+#define WLS_TEST_MSG_ID   1
+#define WLS_TEST_MSG_SIZE 100
+#define WLS_TEST_MEM_SIZE 2126512128 
+
+#define N_MAC_MSGS   16
+
+int main()
+{
+    p_fapi_api_queue_elem_t p_list_elem;
+    unsigned int ret;
+    unsigned int n= N_MAC_MSGS; 
+    unsigned int i;
+
+    // DPDK init
+    ret = mac_dpdk_init();
+    if (ret)
+    {
+        printf("\n[MAC] DPDK Init - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[MAC] DPDK Init - Done\n");
+
+    wls_mac_init(WLS_TEST_DEV_NAME, WLS_TEST_MEM_SIZE);
+    printf("\n[MAC] WLS Init - Done\n");
+
+    for (i=0; i< N_MAC_MSGS; i++)
+    {
+    p_list_elem = wls_mac_create_elem(WLS_TEST_MSG_ID, WLS_TEST_MSG_SIZE, 1, 0);
+      printf("\n[MAC] MAC Create Element %d- Done\n", i);
+
+    if(p_list_elem)
+    {
+        wls_mac_send_msg_to_phy((void *)p_list_elem);
+        	printf("\n[MAC] Send to FAPI %d- Done\n",i);
+    	}
+    }
+
+    // Receive from FAPI WLS
+    wls_mac_rx_task();
+
+    printf("\n[MAC] Exiting...\n");
+
+    return SUCCESS;
+}
+
diff --git a/wls_lib/test/mac/mac_wls.c b/wls_lib/test/mac/mac_wls.c
new file mode 100644
index 0000000..3f69884
--- /dev/null
+++ b/wls_lib/test/mac/mac_wls.c
@@ -0,0 +1,1322 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * @brief This file has Shared Memory interface functions between MAC and PHY
+ * @file testmac_wls.c
+ * @ingroup group_testmac
+ * @author Intel Corporation
+ **/
+
+
+#include <sys/ioctl.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sched.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <rte_eal.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+
+#define DPDK_WLS
+#include "wls.h"
+#include "wls_lib.h"
+#include "mac_wls.h"
+//#include "phy_printf.h"
+//#include "aux_sys.h"
+//#include "aux_timer.h"
+//#include "mlog_lnx.h"
+
+//#include "nr5g_testmac_config_test.h"
+//#include "nr5g_testmac_mac_phy_api_proc.h"
+
+
+#define MSG_MAXSIZE                         ( 16384 * 16 )
+
+
+#define TO_FREE_SIZE                        ( 10 )
+#define TOTAL_FREE_BLOCKS                   ( 50 * 12)
+#define ALLOC_TRACK_SIZE                    ( 16384 )
+
+#define MEMORY_CORRUPTION_DETECT
+#define MEMORY_CORRUPTION_DETECT_FLAG       (0xAB)
+
+typedef struct wls_mac_mem_array
+{
+    void **ppFreeBlock;
+    void *pStorage;
+    void *pEndOfStorage;
+    uint32_t nBlockSize;
+    uint32_t nBlockCount;
+} WLS_MAC_MEM_SRUCT, *PWLS_MAC_MEM_SRUCT;
+
+typedef struct wls_mac_ctx
+{
+    void *hWls;
+    void *pWlsMemBase;
+    WLS_MAC_MEM_SRUCT sWlsStruct;
+
+    uint32_t nTotalMemorySize;
+    uint32_t nTotalBlocks;
+    uint32_t nAllocBlocks;
+    uint32_t nTotalAllocCnt;
+    uint32_t nTotalFreeCnt;
+    uint32_t nTotalUlBufAllocCnt;
+    uint32_t nTotalUlBufFreeCnt;
+    uint32_t nTotalDlBufAllocCnt;
+    uint32_t nTotalDlBufFreeCnt;
+//  Support for FAPI Translator
+    uint32_t nPartitionMemSize;
+    void     *pPartitionMemBase;
+
+    volatile pthread_mutex_t lock;
+    volatile pthread_mutex_t lock_alloc;
+} WLS_MAC_CTX, *PWLS_MAC_CTX;
+
+static pthread_t *pwls_testmac_thread = NULL;
+static WLS_MAC_CTX wls_mac_iface;
+static int gwls_mac_ready = 0;
+static pid_t gwls_pid = 0;
+static uint32_t gToFreeListCnt[TO_FREE_SIZE] = {0};
+static uint64_t gpToFreeList[TO_FREE_SIZE][TOTAL_FREE_BLOCKS] = {{0L}};
+static uint8_t alloc_track[ALLOC_TRACK_SIZE];
+static uint64_t gTotalTick = 0, gUsedTick = 0;
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   ptr Pointer to display
+ *  @param[in]   size Size of data
+ *
+ *  @return  void
+ *
+ *  @description
+ *  This function displays content of Buffer - Used for debugging
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void wls_mac_show_data(void* ptr, uint32_t size)
+{
+    uint8_t *d = ptr;
+    int i;
+
+    for(i = 0; i < size; i++)
+    {
+        if ( !(i & 0xf) )
+            printf("\n");
+        printf("%02x ", d[i]);
+    }
+    printf("\n");
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param void
+ *
+ *  @return  Pointer to WLS_MAC_CTX stucture
+ *
+ *  @description
+ *  This function returns the WLS Local structure which has WLS related parameters
+ *
+**/
+//-------------------------------------------------------------------------------------------
+static PWLS_MAC_CTX wls_mac_get_ctx(void)
+{
+    return &wls_mac_iface;
+}
+
+void wls_mac_print_stats(void)
+{
+    PWLS_MAC_CTX pWls = wls_mac_get_ctx();
+
+    printf("wls_mac_free_list_all:\n");
+    printf("        nTotalBlocks[%d] nAllocBlocks[%d] nFreeBlocks[%d]\n", pWls->nTotalBlocks, pWls->nAllocBlocks, (pWls->nTotalBlocks- pWls->nAllocBlocks));
+    printf("        nTotalAllocCnt[%d] nTotalFreeCnt[%d] Diff[%d]\n", pWls->nTotalAllocCnt, pWls->nTotalFreeCnt, (pWls->nTotalAllocCnt- pWls->nTotalFreeCnt));
+    printf("        nDlBufAllocCnt[%d] nDlBufFreeCnt[%d] Diff[%d]\n", pWls->nTotalDlBufAllocCnt, pWls->nTotalDlBufFreeCnt, (pWls->nTotalDlBufAllocCnt- pWls->nTotalDlBufFreeCnt));
+    printf("        nUlBufAllocCnt[%d] nUlBufFreeCnt[%d] Diff[%d]\n\n", pWls->nTotalUlBufAllocCnt, pWls->nTotalUlBufFreeCnt, (pWls->nTotalUlBufAllocCnt- pWls->nTotalUlBufFreeCnt));
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   ptr Address to convert
+ *
+ *  @return  Converted address
+ *
+ *  @description
+ *  This function converts Virtual Address to Physical Address
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint64_t wls_mac_va_to_pa(void *ptr)
+{
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    uint64_t ret = (uint64_t)WLS_VA2PA(pWls->hWls, ptr);
+
+    //printf("wls_mac_va_to_pa: %p ->%p\n", ptr, (void*)ret);
+
+    return ret;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   ptr Address to convert
+ *
+ *  @return  Converted address
+ *
+ *  @description
+ *  This function converts Physical Address to Virtual Address
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void *wls_mac_pa_to_va(uint64_t ptr)
+{
+    PWLS_MAC_CTX pWls = wls_mac_get_ctx();
+    void *ret = WLS_PA2VA(pWls->hWls, ptr);
+
+    //printf("wls_mac_pa_to_va: %p -> %p\n", (void*)ptr, ret);
+
+    return ret;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pMemArray Pointer to WLS Memory management structure
+ *  @param[in]   pMemArrayMemory Pointer to flat buffer that was allocated
+ *  @param[in]   totalSize Total Size of flat buffer allocated
+ *  @param[in]   nBlockSize Size of each block that needs to be partitioned by memory manager
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function creates memory blocks from a flat buffer which will be used for communciation
+ *  between MAC and PHY
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_create_mem_array(PWLS_MAC_MEM_SRUCT pMemArray,
+                              void *pMemArrayMemory,
+                              uint32_t totalSize, uint32_t nBlockSize)
+{
+    int numBlocks = totalSize / nBlockSize;
+    void **ptr;
+    uint32_t i;
+
+    printf("wls_mac_create_mem_array: pMemArray[%p] pMemArrayMemory[%p] totalSize[%d] nBlockSize[%d] numBlocks[%d]\n",
+        pMemArray, pMemArrayMemory, totalSize, nBlockSize, numBlocks);
+
+    // Can't be less than pointer size
+    if (nBlockSize < sizeof(void *))
+    {
+        return FAILURE;
+    }
+
+    // Can't be less than one block
+    if (totalSize < sizeof(void *))
+    {
+        return FAILURE;
+    }
+
+    pMemArray->ppFreeBlock = (void **)pMemArrayMemory;
+    pMemArray->pStorage = pMemArrayMemory;
+    pMemArray->pEndOfStorage = ((unsigned long*)pMemArrayMemory) + numBlocks * nBlockSize / sizeof(unsigned long);
+    pMemArray->nBlockSize = nBlockSize;
+    pMemArray->nBlockCount = numBlocks;
+
+    // Initialize single-linked list of free blocks;
+    ptr = (void **)pMemArrayMemory;
+    for (i = 0; i < pMemArray->nBlockCount; i++)
+    {
+#ifdef MEMORY_CORRUPTION_DETECT
+        // Fill with some pattern
+        uint8_t *p = (uint8_t *)ptr;
+        uint32_t j;
+
+        p += (nBlockSize - 16);
+        for (j = 0; j < 16; j++)
+        {
+            p[j] = MEMORY_CORRUPTION_DETECT_FLAG;
+        }
+#endif
+
+        if (i == pMemArray->nBlockCount - 1)
+        {
+            *ptr = NULL;      // End of list
+        }
+        else
+        {
+            // Points to the next block
+            *ptr = (void **)(((uint8_t*)ptr) + nBlockSize);
+            ptr += nBlockSize / sizeof(unsigned long);
+        }
+    }
+
+    memset(alloc_track, 0, sizeof(uint8_t) * ALLOC_TRACK_SIZE);
+
+    return SUCCESS;
+}
+
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]    pMemArray Pointer to WLS Memory management structure
+ *  @param[out]   ppBlock Pointer where allocated memory block is stored
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function allocated a memory block from pool
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_alloc_mem_array(PWLS_MAC_MEM_SRUCT pMemArray, void **ppBlock)
+{
+    int idx;
+
+    if (pMemArray->ppFreeBlock == NULL)
+    {
+        printf("wls_mac_alloc_mem_array pMemArray->ppFreeBlock = NULL\n");
+        return FAILURE;
+    }
+
+    // FIXME: Remove after debugging
+    if (((void *) pMemArray->ppFreeBlock < pMemArray->pStorage) ||
+        ((void *) pMemArray->ppFreeBlock >= pMemArray->pEndOfStorage))
+    {
+        printf("wls_mac_alloc_mem_array ERROR: Corrupted MemArray;Arr=%p,Stor=%p,Free=%p\n",
+                pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock);
+        return FAILURE;
+    }
+
+    pMemArray->ppFreeBlock = (void **)((unsigned long)pMemArray->ppFreeBlock & 0xFFFFFFFFFFFFFFF0);
+    *pMemArray->ppFreeBlock = (void **)((unsigned long)*pMemArray->ppFreeBlock & 0xFFFFFFFFFFFFFFF0);
+
+    if ((*pMemArray->ppFreeBlock != NULL) &&
+        (((*pMemArray->ppFreeBlock) < pMemArray->pStorage) ||
+        ((*pMemArray->ppFreeBlock) >= pMemArray->pEndOfStorage)))
+    {
+        fprintf(stderr, "ERROR: Corrupted MemArray;Arr=%p,Stor=%p,Free=%p,Curr=%p\n",
+                pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock,
+                *pMemArray->ppFreeBlock);
+        return FAILURE;
+    }
+
+    *ppBlock = (void *) pMemArray->ppFreeBlock;
+    pMemArray->ppFreeBlock = (void **) (*pMemArray->ppFreeBlock);
+
+    idx = (((uint64_t)*ppBlock - (uint64_t)pMemArray->pStorage)) / pMemArray->nBlockSize;
+    if (alloc_track[idx])
+    {
+        printf("wls_mac_alloc_mem_array Double alloc Arr=%p,Stor=%p,Free=%p,Curr=%p\n",
+            pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock,
+            *pMemArray->ppFreeBlock);
+    }
+    else
+    {
+#ifdef MEMORY_CORRUPTION_DETECT
+        uint32_t nBlockSize = pMemArray->nBlockSize, i;
+        uint8_t *p = (uint8_t *)*ppBlock;
+
+        p += (nBlockSize - 16);
+        for (i = 0; i < 16; i++)
+        {
+            p[i] = MEMORY_CORRUPTION_DETECT_FLAG;
+        }
+#endif
+        alloc_track[idx] = 1;
+    }
+
+    //printf("Block allocd [%p,%p]\n", pMemArray, *ppBlock);
+
+    return SUCCESS;
+}
+
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pMemArray Pointer to WLS Memory management structure
+ *  @param[in]   pBlock Pointer to block that needs to be added back to pool
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function frees a WLS block of memory and adds it back to the pool
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_free_mem_array(PWLS_MAC_MEM_SRUCT pMemArray, void *pBlock)
+{
+    int idx;
+    unsigned long mask = (((unsigned long)pMemArray->nBlockSize) - 1);
+
+    pBlock = (void *)((unsigned long)pBlock & ~mask);
+
+    if ((pBlock < pMemArray->pStorage) || (pBlock >= pMemArray->pEndOfStorage))
+    {
+        printf("wls_mac_free_mem_array WARNING: Trying to free foreign block;Arr=%p,Blk=%p pStorage [%p .. %p]\n",
+               pMemArray, pBlock, pMemArray->pStorage, pMemArray->pEndOfStorage);
+
+        return FAILURE;
+    }
+
+    idx = (int)(((uint64_t)pBlock - (uint64_t)pMemArray->pStorage)) / pMemArray->nBlockSize;
+
+    if (alloc_track[idx] == 0)
+    {
+        printf("wls_mac_free_mem_array ERROR: Double free Arr=%p,Stor=%p,Free=%p,Curr=%p\n",
+            pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock,
+            pBlock);
+
+        return SUCCESS;
+
+    }
+    else
+    {
+#ifdef MEMORY_CORRUPTION_DETECT
+        uint32_t nBlockSize = pMemArray->nBlockSize, i;
+        uint8_t *p = (uint8_t *)pBlock;
+
+        p += (nBlockSize - 16);
+        for (i = 0; i < 16; i++)
+        {
+            if (p[i] != MEMORY_CORRUPTION_DETECT_FLAG)
+            {
+                printf("ERROR: Corruption\n");
+                wls_mac_print_stats();
+                exit(-1);
+            }
+        }
+#endif
+        alloc_track[idx] = 0;
+    }
+
+    if (((void *) pMemArray->ppFreeBlock) == pBlock)
+    {
+        // Simple protection against freeing of already freed block
+        return SUCCESS;
+    }
+
+    // FIXME: Remove after debugging
+    if ((pMemArray->ppFreeBlock != NULL)
+        && (((void *) pMemArray->ppFreeBlock < pMemArray->pStorage)
+        || ((void *) pMemArray->ppFreeBlock >= pMemArray->pEndOfStorage)))
+    {
+        printf("wls_mac_free_mem_array ERROR: Corrupted MemArray;Arr=%p,Stor=%p,Free=%p\n",
+                pMemArray, pMemArray->pStorage, pMemArray->ppFreeBlock);
+        return FAILURE;
+    }
+
+    // FIXME: Remove after debugging
+    if ((pBlock < pMemArray->pStorage) ||
+        (pBlock >= pMemArray->pEndOfStorage))
+    {
+        printf("wls_mac_free_mem_array ERROR: Invalid block;Arr=%p,Blk=%p\n",
+                pMemArray, pBlock);
+        return FAILURE;
+    }
+
+    *((void **)pBlock) = (void **)((unsigned long)pMemArray->ppFreeBlock & 0xFFFFFFFFFFFFFFF0);
+    pMemArray->ppFreeBlock = (void **) ((unsigned long)pBlock & 0xFFFFFFFFFFFFFFF0);
+
+    //printf("Block freed [%p,%p]\n", pMemArray, pBlock);
+
+    return SUCCESS;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  Pointer to the memory block
+ *
+ *  @description
+ *  This function allocates a block of memory from the pool
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void *wls_mac_alloc_buffer(uint32_t size, uint32_t loc)
+{
+    void *pBlock = NULL;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    PWLS_MAC_MEM_SRUCT pMemArray = &pWls->sWlsStruct;
+
+    pthread_mutex_lock((pthread_mutex_t *)&pWls->lock_alloc);
+
+    if (wls_mac_alloc_mem_array(&pWls->sWlsStruct, &pBlock) != SUCCESS)
+    {
+        printf("wls_mac_alloc_buffer alloc error size[%d] loc[%d]\n", size, loc);
+        wls_mac_print_stats();
+        exit(-1);
+    }
+    else
+    {
+        pWls->nAllocBlocks++;
+    }
+
+    //printf("----------------wls_mac_alloc_buffer: size[%d] loc[%d] buf[%p] nAllocBlocks[%d]\n", size, loc, pBlock, pWls->nAllocBlocks);
+
+    //printf("[%p]\n", pBlock);
+
+    pWls->nTotalAllocCnt++;
+    if (loc < MAX_DL_BUF_LOCATIONS)
+        pWls->nTotalDlBufAllocCnt++;
+    else if (loc < MAX_UL_BUF_LOCATIONS)
+        pWls->nTotalUlBufAllocCnt++;
+
+    pthread_mutex_unlock((pthread_mutex_t *)&pWls->lock_alloc);
+
+    return pBlock;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   *pMsg Pointer to free
+ *
+ *  @return  void
+ *
+ *  @description
+ *  This function frees a block of memory and adds it back to the pool
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void wls_mac_free_buffer(void *pMsg, uint32_t loc)
+{
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    PWLS_MAC_MEM_SRUCT pMemArray = &pWls->sWlsStruct;
+
+    pthread_mutex_lock((pthread_mutex_t *)&pWls->lock_alloc);
+
+    //printf("----------------wls_mac_free_buffer: buf[%p] loc[%d]\n", pMsg, loc);
+    if (wls_mac_free_mem_array(&pWls->sWlsStruct, (void *)pMsg) == SUCCESS)
+    {
+        pWls->nAllocBlocks--;
+    }
+    else
+    {
+        printf("wls_mac_free_buffer Free error\n");
+        wls_mac_print_stats();
+        exit(-1);
+    }
+
+    pWls->nTotalFreeCnt++;
+    if (loc < MAX_DL_BUF_LOCATIONS)
+        pWls->nTotalDlBufFreeCnt++;
+    else if (loc < MAX_UL_BUF_LOCATIONS)
+        pWls->nTotalUlBufFreeCnt++;
+
+    pthread_mutex_unlock((pthread_mutex_t *)&pWls->lock_alloc);
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  Number of free blocks
+ *
+ *  @description
+ *  This function queries the number of free blocks in the system
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_num_free_blocks(void)
+{
+    PWLS_MAC_CTX pWls = wls_mac_get_ctx();
+
+    return (pWls->nTotalBlocks- pWls->nAllocBlocks);
+}
+
+
+
+void wls_mac_free_list_all(void)
+{
+    PWLS_MAC_CTX pWls = wls_mac_get_ctx();
+    uint32_t idx;
+
+    for (idx = 0; idx < TO_FREE_SIZE; idx++)
+    {
+        wls_mac_free_list(idx);
+    }
+
+    wls_mac_print_stats();
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pWls Pointer to the WLS_MAC_CTX structure
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function created a partition and blocks of WLS memory for API exchange between MAC and PHY
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_create_partition(PWLS_MAC_CTX pWls)
+{
+    memset(pWls->pWlsMemBase, 0xCC, pWls->nTotalMemorySize);
+    pWls->pPartitionMemBase = pWls->pWlsMemBase;
+    pWls->nPartitionMemSize = pWls->nTotalMemorySize/2;
+    pWls->nTotalBlocks = pWls->nTotalMemorySize / MSG_MAXSIZE;
+    return wls_mac_create_mem_array(&pWls->sWlsStruct, pWls->pPartitionMemBase, pWls->nPartitionMemSize, MSG_MAXSIZE);
+}
+
+
+
+static volatile int gWlsMacPrintThreadInfo = 0;
+
+void wls_mac_print_thread_info(void)
+{
+    gWlsMacPrintThreadInfo = 1;
+
+    return;
+}
+
+void wls_mac_get_time_stats(uint64_t *pTotal, uint64_t *pUsed, uint32_t nClear)
+{
+    *pTotal = gTotalTick;
+    *pUsed = gUsedTick;
+
+    if (nClear)
+    {
+        gTotalTick = 0;
+        gUsedTick = 0;
+    }
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pMsgHeader Pointer to TxSdu Message Block
+ *  @param[in]   count Location in Free List Array
+ *  @param[in]   pToFreeList Array where all the blocks to free are stored
+ *
+ *  @return  New Location in free list array
+ *
+ *  @description
+ *  This function adds all the messages in a subframe coming from L1 to L2 to a free array to be
+ *  freed back to the queue at a later point in time.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_sdu_zbc_block_add_to_free(void* pMsgHeaderHead, int count, uint64_t *pToFreeList)
+{
+    fapi_msg_t *p_fapi_msg = (fapi_msg_t *) pMsgHeaderHead;
+
+    if (p_fapi_msg->msg_id == FAPI_TX_DATA_REQUEST)
+    {
+        fapi_tx_data_req_t *p_tx_data_req =  (fapi_tx_data_req_t *) p_fapi_msg;
+        p_fapi_api_queue_elem_t p_list_elm = ((p_fapi_api_queue_elem_t) p_tx_data_req) - 1;
+        p_fapi_api_queue_elem_t p_sdu_elm = p_list_elm->p_tx_data_elm_list;
+        while(p_sdu_elm)
+        {
+            if (count < TOTAL_FREE_BLOCKS)
+            {
+                pToFreeList[count++] = (uint64_t) p_sdu_elm;
+            }
+            else
+            {
+                printf("wls_mac_sdu_zbc_block_add_to_free: ERROR: Reached max Number of Free Blocks\n");
+                return count;
+            }
+            p_sdu_elm = p_sdu_elm->p_next;
+        }
+    }
+
+    return count;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pListElem Pointer to List element header
+ *  @param[in]   idx Subframe Number
+ *
+ *  @return  Number of blocks freed
+ *
+ *  @description
+ *  This function Frees all the blocks in a List Element Linked List coming from L1 by storing
+ *  them into an array to be freed at a later point in time.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_add_to_free(p_fapi_api_queue_elem_t pListElem, uint32_t idx)
+{
+    p_fapi_api_queue_elem_t pNextMsg = NULL;
+    void* pMsgHeader;
+    int count = gToFreeListCnt[idx], nZbcBlocks;
+
+    pNextMsg = pListElem;
+
+    while (pNextMsg)
+    {
+        if (count < TOTAL_FREE_BLOCKS)
+        {
+            gpToFreeList[idx][count] = (uint64_t)pNextMsg;
+        }
+        else
+        {
+            printf("wls_mac_add_to_free: ERROR: Reached max Number of Free Blocks\n");
+            return count;
+        }
+
+        if (pNextMsg->msg_type != FAPI_MSG_HEADER_IND)
+        {
+            pMsgHeader = (void *) (pNextMsg + 1);
+            count++;
+            count = wls_mac_sdu_zbc_block_add_to_free(pMsgHeader, count, gpToFreeList[idx]);
+        }
+
+        if (pNextMsg->p_next)
+        {
+            pNextMsg = (p_fapi_api_queue_elem_t)(pNextMsg->p_next);
+        }
+        else
+        {
+            pNextMsg = 0;
+        }
+    }
+
+    gpToFreeList[idx][count] = 0L;
+    gToFreeListCnt[idx] = count;
+
+    printf("To Free %d\n", count);
+
+    return count;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   idx subframe Number
+ *
+ *  @return  Number of blocks freed
+ *
+ *  @description
+ *  This function frees all blocks that have been added to the free array
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_free_list(uint32_t idx)
+{
+    p_fapi_api_queue_elem_t pNextMsg = NULL;
+    int count = 0;
+
+    if(idx >= TO_FREE_SIZE){
+        printf("Error idx %d\n", idx);
+        return 0;
+    }
+
+    pNextMsg = (p_fapi_api_queue_elem_t)gpToFreeList[idx][count];
+
+    while (pNextMsg)
+    {
+        wls_mac_free_buffer(pNextMsg, MIN_DL_BUF_LOCATIONS+0);
+        gpToFreeList[idx][count] = (uint64_t) NULL;
+        count++;
+        if (gpToFreeList[idx][count])
+            pNextMsg = (p_fapi_api_queue_elem_t)gpToFreeList[idx][count];
+        else
+            pNextMsg = 0;
+    }
+
+    printf("Free %d\n", count);
+    gToFreeListCnt[idx] = 0;
+
+    return count;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param void
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function is called at WLS init and waits in an infinite for L1 to respond back with some information
+ *  needed by the L2
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_ready(void)
+{
+    int ret = 0;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    ret = WLS_Ready(pWls->hWls);
+
+    return ret;
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  Number of blocks of APIs received
+ *
+ *  @description
+ *  This functions waits in a infinite loop for L1 to send a list of APIs to MAC. This is called
+ *  during runtime when L2 sends a API to L1 and then waits for response back.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_wait(void)
+{
+    int ret = 0;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+
+    ret = WLS_Wait(pWls->hWls);
+
+    return ret;
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[out]   data Location where First API from L1 is stored
+ *
+ *  @return  Size of Message sent from L1
+ *
+ *  @description
+ *  This function queries the APIs sent from L1 to L2 and gets the first pointer to the linked list
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_recv(uint64_t *data, uint16_t *nFlags)
+{
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    uint32_t msgSize = 0;
+    uint16_t msgType = 0;
+
+    *data = WLS_Get(pWls->hWls, &msgSize, &msgType, nFlags);
+
+    return msgSize;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pMsg Pointer to API block that needs to be sent to L1
+ *  @param[in]   MsgSize Size of Message
+ *  @param[in]   MsgTypeID Message Id
+ *  @param[in]   Flags Special Flags needed for WLS
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function adds a block of API from L2 to L1 which will be sent later
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_put(uint64_t pMsg, uint32_t MsgSize, uint16_t MsgTypeID, uint16_t Flags)
+{
+    int ret = 0;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+
+    //printf("wls_mac_put: %p size: %d type: %d nFlags: %d\n", (void*)pMsg, MsgSize, MsgTypeID, Flags);
+    //  wls_mac_show_data((void*)wls_alloc_buffer(pMsg), MsgSize);
+    ret = WLS_Put(pWls->hWls, pMsg, MsgSize, MsgTypeID, Flags);
+
+    return ret;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   pMsgHeader Pointer to the TxSduReq Message block
+ *  @param[in]   nFlags Special nFlags needed for WLS
+ *  @param[in]   nZbcBlocks Number of ZBC blocks in list
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function adds all the ZBC blocks in a TXSDU Message and prepares them to be sent to the L1
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_send_zbc_blocks(void *pMsgHeaderHead, uint16_t nFlags, int *nZbcBlocks, uint32_t nFlagsUrllc)
+{
+    fapi_tx_data_req_t *p_tx_data_req = (fapi_tx_data_req_t *) pMsgHeaderHead;
+    p_fapi_api_queue_elem_t p_list_elm = ((p_fapi_api_queue_elem_t) p_tx_data_req) - 1;
+    p_list_elm = p_list_elm->p_tx_data_elm_list;
+
+    int ret = 0;
+    uint8_t nMsgType;
+    uint32_t isLast, nPduLen;
+    uint16_t list_flags = nFlags;
+    void *pPayload = NULL;
+
+    printf("wls_mac_put ZBC blocks: %d\n", nFlags);
+
+    while (p_list_elm)
+    {
+        nPduLen = p_list_elm->msg_len + sizeof(fapi_api_queue_elem_t);
+        pPayload = (void *) p_list_elm;
+        nMsgType = FAPI_MSG_PHY_ZBC_BLOCK_REQ;
+
+        if (p_list_elm->p_next)
+            isLast = 0;
+        else
+            isLast = 1;
+
+        if ((list_flags & WLS_TF_FIN) && isLast)
+            nFlags = WLS_SG_LAST; // TXSDU.req was last block in the list hence ZBC block is last
+        else
+            nFlags = WLS_SG_NEXT; // more blocks in the list
+
+        printf("wls_mac_put 0x%016lx  msg type: %x nFlags %x\n", (uint64_t) pPayload, nMsgType, nFlags);
+        ret = wls_mac_put((uint64_t) pPayload, nPduLen, nMsgType, nFlags);
+        if (ret != 0)
+        {
+            printf("Error ZBC block 0x%016lx\n", (uint64_t) pPayload);
+            return FAILURE;
+        }
+        p_list_elm = p_list_elm->p_next;
+    }
+
+    return SUCCESS;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]    pMsgHeader Pointer to the TxSDuReq Message block
+ *  @param[out]   nZbcBlocks Number of ZBC blocks
+ *
+ *  @return  1 if this block is a TxSduReq message. 0 else.
+ *
+ *  @description
+ *  This function checks if a block is a TxSduReq messages and counts the number of ZBC blocks in this
+ *  API
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_is_sdu_zbc_block(void* pMsgHeaderHead, int *nZbcBlocks)
+{
+    fapi_tx_data_req_t *p_tx_data_req = (fapi_tx_data_req_t *) pMsgHeaderHead;
+    p_fapi_api_queue_elem_t p_list_elm = 
+        ((p_fapi_api_queue_elem_t) p_tx_data_req) - 1;
+    *nZbcBlocks = 0;
+
+    if (p_tx_data_req->header.msg_id == FAPI_TX_DATA_REQUEST &&
+            p_list_elm->p_tx_data_elm_list)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   data Pointer to the Linked list header
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function sends a list of APIs to the L1
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_send_msg_to_phy(void *data)
+{
+    uint32_t  ret = SUCCESS;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    PWLS_MAC_MEM_SRUCT pMemArray = &pWls->sWlsStruct;
+    p_fapi_api_queue_elem_t pCurrMsg = NULL;
+    p_fapi_api_queue_elem_t pListElem = NULL;
+    static uint32_t idx = 0;
+
+    fapi_msg_t *pMsgHeader;
+    uint16_t nFlags;
+    int nZbcBlocks = 0, isZbc = 0, count = 0;
+
+    printf("wls_mac_send_msg_to_phy\n");
+    printf("data (0x%lX) sending to phy...\n", (unsigned long)data);
+
+    pthread_mutex_lock((pthread_mutex_t *)&pWls->lock);
+
+    if (gwls_mac_ready)
+    {
+        pListElem = (p_fapi_api_queue_elem_t)data;
+        wls_mac_add_to_free(pListElem, idx);
+        count++;
+
+
+        ret = wls_mac_put(wls_mac_va_to_pa(pListElem),
+                pListElem->msg_len + sizeof(fapi_api_queue_elem_t),
+                pMsgHeader->msg_id, nFlags);
+        if (ret != 0)
+        {
+            printf("Error\n");
+            pthread_mutex_unlock((pthread_mutex_t *)&pWls->lock);
+            return FAILURE;
+        }
+    }
+
+    pthread_mutex_unlock((pthread_mutex_t *)&pWls->lock);
+    return ret;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  Number of blocks added
+ *
+ *  @description
+ *  This function add WLS blocks to the L1 Array which will be used by L1 in every TTI to
+ *  populate and send back APIs to the MAC
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_mac_add_blocks_to_ul(void)
+{
+    int ret = 0;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+
+    void *pMsg = wls_mac_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+0);
+
+    if(pMsg)
+    {
+        /* allocate blocks for UL transmittion */
+        while(WLS_EnqueueBlock(pWls->hWls,(uint64_t)wls_mac_va_to_pa(pMsg)))
+        {
+            ret++;
+            pMsg = wls_mac_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+1);
+            if(WLS_EnqueueBlock(pWls->hWls,(uint64_t)wls_mac_va_to_pa(pMsg)))
+            {
+                ret++;
+                pMsg = wls_mac_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+1);
+            }
+
+            if(!pMsg)
+                break;
+        }
+
+        // free not enqueued block
+        if(pMsg)
+        {
+            wls_mac_free_buffer(pMsg, MIN_UL_BUF_LOCATIONS+3);
+        }
+    }
+
+    return ret;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param[in]   data Thread Local Context Structure Pointer
+ *
+ *  @return  NULL
+ *
+ *  @description
+ *  This is the WLS Receiver thread that is created at Testmac Init and is responsible for receiving
+ *  APIs from L1 to MAC
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void *wls_mac_rx_task()
+{
+    void*    buffer_va = 0;
+    uint64_t      buffer_pa = 0;
+    uint32_t get,i, rc = 0;
+
+    uint32_t size  = 0;
+    uint64_t tWake = 0, tWakePrev = 0, tSleep = 0;
+    uint16_t nFlags;
+    p_fapi_api_queue_elem_t pElm   = NULL;
+    p_fapi_api_queue_elem_t pFirst = NULL;
+    p_fapi_api_queue_elem_t pPrev  = NULL;
+
+
+    usleep(1000);
+
+    wls_mac_ready();
+
+    while (1)
+    {
+        get = wls_mac_wait();
+
+        if (get == 0)
+        {
+            continue;
+        }
+        printf("Got %d messages from FAPI Translator\n", get);
+        while(get--)
+        {
+            size =  wls_mac_recv((uint64_t *)&buffer_pa, &nFlags);
+            buffer_va =  wls_mac_pa_to_va(buffer_pa);
+            pElm = (p_fapi_api_queue_elem_t) buffer_va;
+
+
+            if (pFirst == NULL)
+                pFirst = pElm;
+
+            if (nFlags != WLS_TF_FIN)
+            {
+                wls_mac_print_recv_list((p_fapi_api_queue_elem_t) pElm, i);
+                i++;
+						}
+            if(pPrev)
+                pPrev->p_next = pElm;
+
+            pPrev = pElm;
+
+            if ((nFlags & WLS_TF_FIN))
+            {
+                // send to MAC
+                if (pPrev)
+                {
+                    pPrev->p_next =  NULL;
+                }
+
+                wls_mac_print_recv_list((p_fapi_api_queue_elem_t) pFirst, i);
+
+                pFirst= NULL;
+                pPrev = NULL;
+                return NULL;
+            }
+            else
+            {
+            }
+        }
+        wls_mac_add_blocks_to_ul();
+
+    }
+
+    return NULL;
+}
+
+void wls_mac_print_recv_list(p_fapi_api_queue_elem_t list, uint32_t i)
+{
+    printf("\nMAC received response %d from FAPI\n",i);
+}
+
+p_fapi_api_queue_elem_t wls_mac_create_elem(uint16_t num_msg, uint32_t align_offset, uint32_t msg_type, uint32_t n_loc)
+{
+    p_fapi_api_queue_elem_t p_list_elem;
+
+    p_list_elem = (p_fapi_api_queue_elem_t)wls_mac_alloc_buffer(num_msg * align_offset + sizeof(fapi_api_queue_elem_t), n_loc);
+
+    //Fill header for link list of API messages
+    if (p_list_elem)
+    {
+        p_list_elem->msg_type = (uint8_t)msg_type;
+        p_list_elem->num_message_in_block = 1;
+        p_list_elem->align_offset = (uint16_t)align_offset;
+        p_list_elem->msg_len = num_msg * align_offset;
+        p_list_elem->p_next = NULL;
+    }
+
+    return p_list_elem;
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function initialized the WLS threads for the Testmac and allocates memory needed to
+ *  exchange APIs between MAC and PHY
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_init(char * wls_device_name, uint64_t nTotalMemorySize)
+{
+    uint32_t ret = FAILURE;
+    PWLS_MAC_CTX pWls =  wls_mac_get_ctx();
+    uint8_t *pMemZone;
+    static const struct rte_memzone *mng_memzone;
+    wls_drv_ctx_t *pDrv_ctx;
+
+    sleep(1);
+
+    pthread_mutex_init((pthread_mutex_t *)&pWls->lock, NULL);
+    pthread_mutex_init((pthread_mutex_t *)&pWls->lock_alloc, NULL);
+
+    pWls->nTotalAllocCnt = 0;
+    pWls->nTotalFreeCnt = 0;
+    pWls->nTotalUlBufAllocCnt = 0;
+    pWls->nTotalUlBufFreeCnt = 0;
+    pWls->nTotalDlBufAllocCnt = 0;
+    pWls->nTotalDlBufFreeCnt = 0;
+
+    pWls->hWls = WLS_Open(wls_device_name, WLS_MASTER_CLIENT, nTotalMemorySize);
+    if (pWls->hWls)
+    {
+        /* allocate chuck of memory */
+        pWls->pWlsMemBase = WLS_Alloc(pWls->hWls, nTotalMemorySize);
+        if (pWls->pWlsMemBase)
+        {
+            pWls->nTotalMemorySize = (uint32_t) nTotalMemorySize;
+
+            ret = wls_mac_create_partition(pWls);
+
+            if (ret == SUCCESS)
+            {
+                int nBlocks = 0;
+                gwls_mac_ready = 1;
+
+                nBlocks = WLS_EnqueueBlock(pWls->hWls, wls_mac_va_to_pa(wls_mac_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+2)));
+                /* allocate blocks for UL transmition */
+                while(WLS_EnqueueBlock(pWls->hWls, wls_mac_va_to_pa(wls_mac_alloc_buffer(0, MIN_UL_BUF_LOCATIONS+3))))
+                {
+                    nBlocks++;
+                }
+
+                printf("WLS inited ok [%d]\n\n", nBlocks);
+            }
+            else
+            {
+                printf("can't create WLS Partition");
+                return FAILURE;
+            }
+
+        }
+        else
+        {
+            printf("can't allocate WLS memory");
+            return FAILURE;
+        }
+    }
+    else
+    {
+        printf("can't open WLS instance");
+        return FAILURE;
+    }
+
+    return SUCCESS;
+}
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup group_testmac
+ *
+ *  @param   void
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function destroys the WLS layer for the testmac and de-allocates any memory used
+ *
+**/
+//-------------------------------------------------------------------------------------------
+uint32_t wls_mac_destroy(void)
+{
+    PWLS_MAC_CTX pWls = wls_mac_get_ctx();
+
+    if (pwls_testmac_thread)
+    {
+        pthread_cancel(*pwls_testmac_thread);
+
+        free(pwls_testmac_thread);
+        pwls_testmac_thread = NULL;
+
+        if(pWls->pWlsMemBase)
+        {
+            WLS_Free(pWls->hWls, pWls->pWlsMemBase);
+        }
+
+        WLS_Close(pWls->hWls);
+        printf("wls_mac_rx_task:          [PID: %6d]... Stopping\n", gwls_pid);
+    }
+
+    return SUCCESS;
+}
+
+uint8_t mac_dpdk_init()
+{
+    uint8_t retval;
+    char whitelist[32];
+    uint8_t i;
+
+    char *argv[] = {"mac_app", "--proc-type=secondary",
+        "--file-prefix", "wls", whitelist};
+    
+    int argc = RTE_DIM(argv);
+
+    /* initialize EAL first */
+    sprintf(whitelist, "-w %s",  "0000:00:06.0");
+    printf("[MAC] Calling rte_eal_init: ");
+
+    for (i = 0; i < RTE_DIM(argv); i++)
+    {
+        printf("%s ", argv[i]);
+    }
+    printf("\n");
+
+    if (rte_eal_init(argc, argv) < 0)
+        rte_panic("Cannot init EAL\n");
+
+    return SUCCESS;
+}
diff --git a/wls_lib/test/mac/mac_wls.h b/wls_lib/test/mac/mac_wls.h
new file mode 100644
index 0000000..59a0855
--- /dev/null
+++ b/wls_lib/test/mac/mac_wls.h
@@ -0,0 +1,77 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * @brief This file has Shared Memory interface functions between MAC and PHY
+ * @file testmac_wls.h
+ * @ingroup group_testmac
+ * @author Intel Corporation
+ **/
+
+#ifndef _TESTMAC_WLS_H_
+#define _TESTMAC_WLS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//#include "common_typedef.h"
+#include "fapi_interface.h"
+
+#define SUCCESS 0
+#define FAILURE 1
+
+#define MAX_NUM_LOCATIONS           (50)
+
+#define MIN_DL_BUF_LOCATIONS        (0)                                             /* Used for stats collection 0-49 */
+#define MIN_UL_BUF_LOCATIONS        (MIN_DL_BUF_LOCATIONS + MAX_NUM_LOCATIONS)      /* Used for stats collection 50-99 */
+
+#define MAX_DL_BUF_LOCATIONS        (MIN_DL_BUF_LOCATIONS + MAX_NUM_LOCATIONS)          /* Used for stats collection 0-49 */
+#define MAX_UL_BUF_LOCATIONS        (MIN_UL_BUF_LOCATIONS + MAX_NUM_LOCATIONS)          /* Used for stats collection 50-99 */
+
+typedef struct tagZBC_LIST_ITEM
+{
+    uint64_t pMsg;
+    uint32_t MsgSize;
+} ZBC_LIST_ITEM, *PZBC_LIST_ITEM;
+
+
+uint32_t wls_mac_init(char * wls_device_name, uint64_t nWlsMemorySize);
+void wls_mac_print_thread_info(void);
+uint32_t wls_mac_destroy(void);
+void *wls_mac_alloc_buffer(uint32_t size, uint32_t loc);
+uint32_t wls_mac_send_msg_to_phy(void *data);
+uint64_t wls_mac_va_to_pa(void *ptr);
+void *wls_mac_pa_to_va(uint64_t ptr);
+void wls_mac_free_buffer(void *pMsg, uint32_t loc);
+void wls_mac_get_time_stats(uint64_t *pTotal, uint64_t *pUsed, uint32_t nClear);
+void wls_mac_free_list_all(void);
+int wls_mac_free_list(uint32_t idx);
+p_fapi_api_queue_elem_t wls_mac_create_elem(uint16_t num_msg, uint32_t align_offset, uint32_t msg_type, uint32_t n_loc);
+void wls_mac_print_recv_list(p_fapi_api_queue_elem_t list, uint32_t i);
+uint8_t mac_dpdk_init();
+void *wls_mac_rx_task();
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* #ifndef _TESTMAC_WLS_H_ */
+
+
diff --git a/wls_lib/test/mac/makefile b/wls_lib/test/mac/makefile
new file mode 100644
index 0000000..b6ba703
--- /dev/null
+++ b/wls_lib/test/mac/makefile
@@ -0,0 +1,172 @@
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+.SUFFIXES: .o .c .s .i .cpp
+
+# Makefile to build TestMac application
+
+##############################################################
+#  Tools configuration
+##############################################################
+CC := icc
+CPP := icpc
+AS := as
+AR := ar
+LD := icc
+OBJDUMP := objdump
+
+ifeq ($(SHELL),cmd.exe)
+MD := mkdir.exe -p
+RM := rm.exe -rf
+else
+MD := mkdir -p
+RM := rm -rf
+endif
+
+##############################################################
+# TARGET
+##############################################################
+ifeq ($(RTE_TARGET),)
+    RTE_TARGET :=x86_64-native-linuxapp-icc
+endif
+##############################################################
+# DPDK
+##############################################################
+ifeq ($(RTE_SDK),)
+$(info Please make sure RTE_SDK points to DPDK folder (current version of DPDK is 18.08))
+    RTE_SDK := /opt/dpdk-18.08
+endif
+
+##############################################################
+# Projects folders
+##############################################################
+ORANDIR        	  := $(DIR_WIRELESS_ORAN)
+WLSDIR            := $(DIR_WIRELESS_WLS)
+BUILDDIR          := ../build/mac
+SRCDIR            := $(CURDIR)
+
+wls_mac_app_dep_file = $(BUILDDIR)/dep_file
+
+APP := ../bin/mac/mac_app
+
+INC := \
+ $(WLSDIR) \
+ $(SRCDIR) \
+ $(RTE_SDK)/$(RTE_TARGET)/include \
+ $(ORANDIR)/include \
+ $(ORANDIR) \
+ #$(FLEXRANDIR)/source/nr5g/api \
+ #$(FLEXRANDIR)/source/common \
+
+INC := $(addprefix -I,$(INC))
+ 
+DEFS := USE_WO_LOCK _GNU_SOURCE NR5G 
+
+ifneq ($(PRINTDBG),)
+DEFS := $(DEFS) PRINTF_DBG_OK
+endif
+
+ifeq ($(DEBUG_MODE),true)
+DEFS := $(DEFS) DEBUG_MODE
+endif
+
+DEFS := $(addprefix -D,$(DEFS))
+
+CFLAGS := -g -Wall -wd9 -Wno-deprecated-declarations -Wimplicit-function-declaration -fasm-blocks $(DEFS) $(INC)
+
+ifeq ($(PRINTDBG),)
+CFLAGS := $(CFLAGS) -Werror
+endif
+
+RTE_LIBS := -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,--whole-archive -Wl,-lrte_distributor -Wl,-lrte_kni -Wl,-lrte_pipeline -Wl,-lrte_table -Wl,-lrte_port -Wl,-lrte_timer -Wl,-lrte_hash -Wl,-lrte_lpm -Wl,-lrte_power -Wl,-lrte_acl -Wl,-lrte_meter -Wl,-lrte_sched -Wl,-lm -Wl,-lrt -Wl,--start-group -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_ip_frag -Wl,-lrte_ethdev -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_pci -Wl,-lrte_net -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_cfgfile -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ring -Wl,-lrt -Wl,-lm -Wl,-ldl -Wl,--end-group -Wl,--no-whole-archive
+LDFLAGS := -g  -Wl,-lrt -Wl,-lpthread -Wl,-lhugetlbfs -Wl,-lm -Wl,-lnuma -L $(WLSDIR) -lwls
+
+LINUX_WLS_MAC_APP_SRC := \
+	$(SRCDIR)/mac_main.c \
+	$(SRCDIR)/mac_wls.c \
+
+OBJS := $(LINUX_WLS_MAC_APP_SRC:.c=.o)
+
+PROJECT_OBJ_DIR = $(BUILDDIR)
+
+OBJS := $(addprefix $(PROJECT_OBJ_DIR)/,$(OBJS))
+
+DIRLIST := $(sort $(dir $(OBJS)))
+
+CC_DEPS := $(addprefix __dep__,$(LINUX_WLS_MAC_APP_SRC))
+
+GEN_DEP :=
+ifeq ($(wildcard $(wls_mac_app_dep_file)),)
+GEN_DEP := regenerate_dep
+endif
+
+.PHONY: $(APP)
+$(APP): $(DIRLIST) echo_options $(GEN_DEP) $(OBJS)
+	@echo [LD] $(APP)
+	@$(CC) -o $(APP) $(OBJS) $(RTE_LIBS) $(LDFLAGS)
+#	$(OBJDUMP) -d $(APP) > $(APP).asm
+
+.PHONY : echo_options
+echo_options:
+	@echo [CFLAGS]	$(CFLAGS)
+	@echo [LDFAGS]	$(LDFLAGS)
+
+
+ifneq ($(wildcard $(wls_mac_app_dep_file)),)
+include $(wls_mac_app_dep_file)
+endif
+
+$(DIRLIST) :
+	-@$(MD) $@
+
+.PHONY : regenerate_dep
+regenerate_dep : clean_dep echo_regeenrate_dep  $(CC_DEPS)
+
+.PHONY: clean_dep
+clean_dep:
+	$(RM) $(wls_mac_app_dep_file)
+
+.PHONY : echo_regeenrate_dep
+echo_regeenrate_dep:
+	@echo regenerating dep files
+
+
+.PHONY : CC_DEPS
+$(CC_DEPS):
+	@$(CC) -MM $(subst __dep__,,$@) -MT $(addprefix $(PROJECT_OBJ_DIR)/,$(patsubst %.c,%.o,$(subst __dep__,,$@))) $(CFLAGS) >> $(wls_mac_app_dep_file)
+
+$(OBJS) : $(PROJECT_OBJ_DIR)/%.o: %.c
+	@echo [CC]    $(subst $(PROJECT_OBJ_DIR)/,,$@)
+	@echo "";echo $@
+	@echo "";echo $(subst $(PROJECT_OBJ_DIR)/,,$@)
+	@echo "";echo $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@))
+	@echo ""
+	$(CC) -c $(CFLAGS) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@))
+
+
+.PHONY: xclean
+xclean : clean_dep
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+	@$(RM) $(BUILDDIR)
+
+.PHONY: clean
+clean :
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+
diff --git a/wls_lib/test/phy/makefile b/wls_lib/test/phy/makefile
new file mode 100644
index 0000000..f18d9d2
--- /dev/null
+++ b/wls_lib/test/phy/makefile
@@ -0,0 +1,162 @@
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+.SUFFIXES: .o .c .s .i .cpp
+
+# Makefile to build Testphy application
+
+##############################################################
+#  Tools configuration
+##############################################################
+CC := icc
+CPP := icpc
+AS := as
+AR := ar
+LD := icc
+OBJDUMP := objdump
+
+ifeq ($(SHELL),cmd.exe)
+MD := mkdir.exe -p
+RM := rm.exe -rf
+else
+MD := mkdir -p
+RM := rm -rf
+endif
+
+##############################################################
+# TARGET
+##############################################################
+ifeq ($(RTE_TARGET),)
+    RTE_TARGET :=x86_64-native-linuxapp-icc
+endif
+##############################################################
+# DPDK
+##############################################################
+ifeq ($(RTE_SDK),)
+$(info Please make sure RTE_SDK points to DPDK folder (current version of DPDK is 18.08))
+    RTE_SDK := /opt/dpdk-18.08
+endif
+
+##############################################################
+# Projects folders
+##############################################################
+WLSDIR            := $(DIR_WIRELESS_WLS)
+BUILDDIR          := ../build/phy
+SRCDIR            := $(CURDIR)
+
+wls_phy_app_dep_file = $(BUILDDIR)/dep_file
+
+APP := ../bin/phy/phy_app
+
+INC := \
+ $(WLSDIR) \
+ $(SRCDIR) \
+ $(RTE_SDK)/$(RTE_TARGET)/include \
+
+INC := $(addprefix -I,$(INC))
+ 
+DEFS := USE_WO_LOCK _GNU_SOURCE NR5G
+
+ifneq ($(PRINTDBG),)
+DEFS := $(DEFS) PRINTF_DBG_OK
+endif
+
+ifeq ($(DEBUG_MODE),true)
+DEFS := $(DEFS) DEBUG_MODE
+endif
+
+DEFS := $(addprefix -D,$(DEFS))
+
+CFLAGS := -g -Wall -wd9 -Wno-deprecated-declarations -Wimplicit-function-declaration -fasm-blocks $(DEFS) $(INC)
+
+ifeq ($(PRINTDBG),)
+CFLAGS := $(CFLAGS) -Werror
+endif
+
+RTE_LIBS := -L$(RTE_SDK)/$(RTE_TARGET)/lib -Wl,--whole-archive -Wl,-lrte_distributor -Wl,-lrte_kni -Wl,-lrte_pipeline -Wl,-lrte_table -Wl,-lrte_port -Wl,-lrte_timer -Wl,-lrte_hash -Wl,-lrte_lpm -Wl,-lrte_power -Wl,-lrte_acl -Wl,-lrte_meter -Wl,-lrte_sched -Wl,-lm -Wl,-lrt -Wl,--start-group -Wl,-lrte_kvargs -Wl,-lrte_mbuf -Wl,-lrte_ip_frag -Wl,-lrte_ethdev -Wl,-lrte_mempool -Wl,-lrte_mempool_ring -Wl,-lrte_ring -Wl,-lrte_bus_pci -Wl,-lrte_bus_vdev -Wl,-lrte_pci -Wl,-lrte_net -Wl,-lrte_eal -Wl,-lrte_cmdline -Wl,-lrte_cfgfile -Wl,-lrte_pmd_bond -Wl,-lrte_pmd_vmxnet3_uio -Wl,-lrte_pmd_i40e -Wl,-lrte_pmd_ixgbe -Wl,-lrte_pmd_e1000 -Wl,-lrte_pmd_ring -Wl,-lrt -Wl,-lm -Wl,-ldl -Wl,--end-group -Wl,--no-whole-archive
+LDFLAGS := -g -Wl,-lrt -Wl,-lpthread -Wl,-lhugetlbfs -Wl,-lm -Wl,-lnuma -L $(WLSDIR) -lwls
+
+LINUX_WLS_PHY_APP_SRC := \
+	$(SRCDIR)/phy_main.c \
+
+OBJS := $(LINUX_WLS_PHY_APP_SRC:.c=.o)
+
+PROJECT_OBJ_DIR = $(BUILDDIR)
+
+OBJS := $(addprefix $(PROJECT_OBJ_DIR)/,$(OBJS))
+
+DIRLIST := $(sort $(dir $(OBJS)))
+
+CC_DEPS := $(addprefix __dep__,$(LINUX_WLS_PHY_APP_SRC))
+
+GEN_DEP :=
+ifeq ($(wildcard $(wls_phy_app_dep_file)),)
+GEN_DEP := regenerate_dep
+endif
+
+.PHONY: $(APP)
+$(APP): $(DIRLIST) echo_options $(GEN_DEP) $(OBJS)
+	@echo [LD] $(APP)
+	@$(CC) -o $(APP) $(OBJS) $(RTE_LIBS) $(LDFLAGS)
+#	$(OBJDUMP) -d $(APP) > $(APP).asm
+
+.PHONY : echo_options
+echo_options:
+	@echo [CFLAGS]	$(CFLAGS)
+	@echo [LDFAGS]	$(LDFLAGS)
+
+
+ifneq ($(wildcard $(wls_phy_app_dep_file)),)
+include $(wls_phy_app_dep_file)
+endif
+
+$(DIRLIST) :
+	-@$(MD) $@
+
+.PHONY : regenerate_dep
+regenerate_dep : clean_dep echo_regeenrate_dep  $(CC_DEPS)
+
+.PHONY: clean_dep
+clean_dep:
+	$(RM) $(wls_phy_app_dep_file)
+
+.PHONY : echo_regeenrate_dep
+echo_regeenrate_dep:
+	@echo regenerating dep files
+
+
+.PHONY : CC_DEPS
+$(CC_DEPS):
+	@$(CC) -MM $(subst __dep__,,$@) -MT $(addprefix $(PROJECT_OBJ_DIR)/,$(patsubst %.c,%.o,$(subst __dep__,,$@))) $(CFLAGS) >> $(wls_phy_app_dep_file)
+
+$(OBJS) : $(PROJECT_OBJ_DIR)/%.o: %.c
+	@echo [CC]    $(subst $(PROJECT_OBJ_DIR)/,,$@)
+	@$(CC) -c $(CFLAGS) -o"$@" $(patsubst %.o,%.c,$(subst $(PROJECT_OBJ_DIR)/,,$@))
+
+
+.PHONY: xclean
+xclean : clean_dep
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+	@$(RM) $(BUILDDIR)
+
+.PHONY: clean
+clean :
+	@$(RM) $(OBJS)
+	@$(RM) $(APP)
+
diff --git a/wls_lib/test/phy/phy_main.c b/wls_lib/test/phy/phy_main.c
new file mode 100644
index 0000000..cf3dd61
--- /dev/null
+++ b/wls_lib/test/phy/phy_main.c
@@ -0,0 +1,217 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * @brief This file is test PHY wls lib main process
+ * @file phy_main.c
+ * @ingroup group_testphywls
+ * @author Intel Corporation
+ **/
+
+
+#include <stdint.h>
+#include <stdio.h>
+
+#include <rte_eal.h>
+#include <rte_cfgfile.h>
+#include <rte_string_fns.h>
+#include <rte_common.h>
+#include <rte_string_fns.h>
+#include <rte_lcore.h>
+#include <rte_debug.h>
+#include <rte_launch.h>
+
+#include "wls_lib.h"
+
+#define SUCCESS 0
+#define FAILURE 1
+#define WLS_TEST_DEV_NAME "wls"
+#define WLS_TEST_MSG_ID   1
+#define WLS_TEST_MSG_SIZE 100
+#define WLS_TEST_MEM_SIZE 2126512128
+#define NUM_PHY_MSGS  16
+
+typedef void* WLS_HANDLE;
+void *g_shmem;
+uint64_t g_shmem_size;
+
+WLS_HANDLE  g_fapi_wls, g_phy_wls;
+
+uint8_t    phy_dpdk_init(void);
+uint8_t    phy_wls_init(const char *dev_name, unsigned long long mem_size);
+uint64_t   phy_fapi_recv();
+uint8_t    phy_fapi_send();
+
+int main()
+{
+    int64_t ret;
+    uint64_t p_msg;
+
+    // DPDK init
+    ret = phy_dpdk_init();
+    if (ret)
+    {
+        printf("\n[PHY] DPDK Init - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[PHY] DPDK Init - Done\n");
+
+    // WLS init
+    ret = phy_wls_init(WLS_TEST_DEV_NAME, WLS_TEST_MEM_SIZE);
+    if(ret)
+    {
+        printf("\n[PHY] WLS Init - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[PHY] WLS Init - Done\n");
+
+    // Receive from MAC WLS
+    p_msg = phy_fapi_recv();
+    if (!p_msg)
+    {
+        printf("\n[PHY] Receive from FAPI - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[PHY] Receive from FAPI - Done\n");
+
+    // Sent to PHY WLS
+    ret = phy_fapi_send();
+    if (ret)
+    {
+        printf("\n[PHY] Send to FAPI - Failed\n");
+        return FAILURE;
+    }
+    printf("\n[PHY] Send to FAPI - Done\n");
+
+    printf("\n[PHY] Exiting...\n");
+
+    return SUCCESS;
+}
+
+uint8_t phy_dpdk_init(void)
+{
+    char whitelist[32];
+    uint8_t i;
+
+    char *argv[] = {"phy_app", "--proc-type=primary",
+        "--file-prefix", "wls", whitelist};
+    
+    int argc = RTE_DIM(argv);
+
+    /* initialize EAL first */
+    sprintf(whitelist, "-w %s",  "0000:00:06.0");
+    printf("[PHY] Calling rte_eal_init: ");
+
+    for (i = 0; i < RTE_DIM(argv); i++)
+    {
+        printf("%s ", argv[i]);
+    }
+    printf("\n");
+
+    if (rte_eal_init(argc, argv) < 0)
+        rte_panic("Cannot init EAL\n");
+
+    return SUCCESS;
+}
+
+uint8_t phy_wls_init(const char *dev_name, unsigned long long mem_size)
+{
+    g_phy_wls = WLS_Open(dev_name, WLS_SLAVE_CLIENT, mem_size);
+    if(NULL == g_phy_wls)
+    {
+        return FAILURE;
+    }
+    g_shmem_size = mem_size;
+
+    g_shmem = WLS_Alloc(g_phy_wls, g_shmem_size);
+    if (NULL == g_shmem)
+    {
+        printf("Unable to alloc WLS Memory\n");
+        return FAILURE;
+    }
+    return SUCCESS;
+}
+
+uint64_t phy_fapi_recv()
+{
+    uint8_t  num_blks = 0;
+    uint64_t p_msg;
+    uint32_t msg_size;
+    uint16_t msg_id;
+    uint16_t flags;
+	uint32_t i=0;
+
+		
+	while (1)
+	{
+    num_blks = WLS_Wait(g_phy_wls);
+            printf("WLS_Wait returns %d blocks\n",num_blks);
+    
+    if (num_blks)
+    {
+        p_msg = WLS_Get(g_phy_wls, &msg_size, &msg_id, &flags);
+        		if (p_msg)
+        		{
+        			printf("\n[PHY] FAPI2PHY WLS Received Block %d\n",i);
+        			i++;
+    }
+    else
+    {
+        	  	     printf("\n[PHY] FAPI2PHY WLS Get Error for msg %d\n",i);
+        	  	     break;
+        	    }
+                if (flags & WLS_TF_FIN)
+                {
+                    return p_msg;
+                }
+    		}
+    		else
+    		{
+        		printf("\n[PHY] FAPI2PHY WLS wait returned 0 blocks exiting \n");
+        		return FAILURE;
+    		}
+    	
+    }
+    return p_msg;
+}
+
+uint8_t phy_fapi_send()
+{
+    uint64_t pa_block = 0;
+    uint8_t ret = FAILURE;
+    uint32_t i;
+    
+    for (i=0 ; i < NUM_PHY_MSGS; i++)
+    {
+
+    pa_block = (uint64_t) WLS_DequeueBlock((void*) g_phy_wls);
+    if (!pa_block)
+    {
+        	printf("\n[PHY] FAPI2PHY WLS Dequeue block %d error\n",i);
+        return FAILURE;
+    }
+
+    	ret = WLS_Put(g_phy_wls, pa_block, WLS_TEST_MSG_SIZE, WLS_TEST_MSG_ID, (i== (NUM_PHY_MSGS-1))? WLS_TF_FIN:0);
+    	printf("\n[PHY] FAPI2PHY WLS Put Msg %d \n",i);
+    	if (ret)
+    	{
+    		printf("\n[PHY] FAPI2PHY WLS Put Msg Error %d \n",i);
+      }
+    }
+    return ret;
+}
diff --git a/wls_lib/testapp/Makefile b/wls_lib/testapp/Makefile
new file mode 100644
index 0000000..6fcd6f6
--- /dev/null
+++ b/wls_lib/testapp/Makefile
@@ -0,0 +1,61 @@
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+ifdef DPDK_WLS
+ifeq ($(RTE_SDK),)
+$(error "Please define RTE_SDK environment variable")
+endif
+
+# Default target, can be overridden by command line or environment
+RTE_TARGET ?= x86_64-native-linuxapp-icc
+include $(RTE_SDK)/mk/rte.vars.mk
+
+# binary name
+APP = wls_test
+
+LDFLAGS += -L../.. -l wls -lpthread -lhugetlbfs
+
+# all source are stored in SRCS-y
+SRCS-y := pool.c testapp.c
+
+CFLAGS += $(WERROR_FLAGS) -I../.. -DDPDK_WLS
+EXTRA_CFLAGS += -g
+
+include $(RTE_SDK)/mk/rte.extapp.mk
+
+else
+
+#EXTRA_CFLAGS +=-g -Wall -wd9 -I../
+EXTRA_CFLAGS +=-g -Wall -I../
+LDFLAGS += -L../ -lwls
+
+# all source are stored in SRCS-y
+SRCS-y := pool.c testapp.c
+wls_test: testapp.o pool.o
+	$(CC) $(LDFLAGS) $(EXTRA_CFLAGS) -o $@ testapp.o pool.o -L . -l wls -l pthread -l hugetlbfs
+
+CFLAGS += $(WERROR_FLAGS) -g -I../../
+testapp.o: testapp.c
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+
+pool.o: pool.c
+	$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -o $@ -c $<
+
+clean:
+	rm -f *.o wls_test
+endif
diff --git a/wls_lib/testapp/pool.c b/wls_lib/testapp/pool.c
new file mode 100644
index 0000000..015c03b
--- /dev/null
+++ b/wls_lib/testapp/pool.c
@@ -0,0 +1,189 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+#ifdef __KERNEL__
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#endif
+#include <string.h>
+#include <pthread.h>
+#include "pool.h"
+
+/*static void pool_mutex_destroy(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_destroy(pMutex);
+}*/
+
+static void pool_mutex_init(pthread_mutex_t* pMutex)
+{
+   pthread_mutexattr_t prior;
+   pthread_mutexattr_init(&prior);
+   pthread_mutexattr_setprotocol(&prior, PTHREAD_PRIO_INHERIT);
+   pthread_mutex_init(pMutex, &prior);
+   pthread_mutexattr_destroy(&prior);
+}
+
+static void pool_mutex_lock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_lock(pMutex);
+}
+
+static void pool_mutex_unlock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_unlock(pMutex);
+}
+
+
+unsigned int PoolInit (PPOOL pPool, void * pStorage, unsigned int nBlockNum, unsigned int nBlockSize, unsigned long long* pFreePtr, unsigned long long* pUsedPtr)
+{
+    unsigned int i;
+
+    memset (pPool, 0, sizeof (*pPool));
+
+#ifdef __KERNEL__
+    mutex_init(&pPool->lock);
+#else
+    pool_mutex_init(&pPool->lock);
+#endif
+
+    pPool->StoragePtr = (unsigned char*)pStorage;
+    pPool->BlockSize  = nBlockSize;
+    pPool->BlockNum   = nBlockNum;
+
+    pPool->FreePtr = pFreePtr;
+    pPool->UsedPtr = pUsedPtr;
+
+    // to put the indexes to the free storage
+
+    i = 0;
+
+    while (i < nBlockNum)
+    {
+        PoolFree (pPool, pPool->StoragePtr + (pPool->BlockSize * i));
+        i++;
+    }
+
+    return 0;
+}
+
+void* PoolAlloc(PPOOL pPool)
+{
+    unsigned long long nIndex;
+    void* ret  = NULL;
+
+#ifdef __KERNEL__
+    mutex_lock(&pPool->lock);
+#else
+    pool_mutex_lock(&pPool->lock);
+#endif
+
+    if (pPool->FreeGet == pPool->FreePut){
+#ifdef __KERNEL__
+        mutex_unlock(&pPool->lock);
+#else
+        pool_mutex_unlock(&pPool->lock);
+#endif
+        return ret;
+    }
+
+    nIndex = pPool->FreePtr[pPool->FreeGet++];
+
+    if (pPool->FreeGet >= (pPool->BlockNum+1))
+        pPool->FreeGet = 0;
+
+    ret = pPool->StoragePtr + (pPool->BlockSize * nIndex);
+
+#ifdef __KERNEL__
+    mutex_unlock(&pPool->lock);
+#else
+    pool_mutex_unlock(&pPool->lock);
+#endif
+
+    return ret;
+}
+
+unsigned int PoolFree(PPOOL pPool, void * pBlock)
+{
+    unsigned long long index;
+
+#ifdef __KERNEL__
+    mutex_lock(&pPool->lock);
+#else
+    pool_mutex_lock(&pPool->lock);
+#endif
+
+    index = (U64)((U64)pBlock - (U64)pPool->StoragePtr) / pPool->BlockSize;
+
+    pPool->FreePtr [pPool->FreePut ++] = index;
+
+    if (pPool->FreePut >= (pPool->BlockNum+1))
+        pPool->FreePut = 0;
+
+#ifdef __KERNEL__
+    mutex_unlock(&pPool->lock);
+#else
+    pool_mutex_unlock(&pPool->lock);
+#endif
+
+    return 1;
+}
+
+unsigned int PoolGetFreeNum(PPOOL pPool)
+{
+    unsigned int nCount;
+
+    if (pPool==NULL)
+        return 0;
+
+    if (pPool->FreePut >= pPool->FreeGet)
+    {
+        nCount = pPool->FreePut - pPool->FreeGet;
+    }
+    else
+    {
+        // the queue size is bigger on one element than a partition
+        // to prevent data loss
+
+        nCount = (pPool->BlockNum+1) - (pPool->FreeGet - pPool->FreePut);
+    }
+
+    return nCount;
+}
+
+unsigned int PoolGetAllocNum(PPOOL pPool)
+{
+    unsigned int nCount;
+
+    if (pPool==NULL)
+        return 0;
+
+    if (pPool->UsedPut >= pPool->UsedGet)
+    {
+        nCount = pPool->UsedPut - pPool->UsedGet;
+    }
+    else
+    {
+        // the queue size is bigger on one element than a partition
+        // to prevent data loss
+
+        nCount = (pPool->BlockNum+1) - (pPool->UsedGet - pPool->UsedPut);
+    }
+
+    return nCount;
+}
+
diff --git a/wls_lib/testapp/pool.h b/wls_lib/testapp/pool.h
new file mode 100644
index 0000000..bb1863e
--- /dev/null
+++ b/wls_lib/testapp/pool.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 _POOL_API_H_
+#define _POOL_API_H_
+
+#ifdef __KERNEL__
+#include <linux/mutex.h>
+#endif
+#include <pthread.h>
+#include "ttypes.h"
+
+
+typedef struct _POOL_
+{
+    unsigned char*      StoragePtr;     // The pointer to the storage where blocks are located
+    unsigned int      BlockNum;       // The number of blocks in storage
+    unsigned int      BlockSize;      // The size of block in bytes
+
+    unsigned long long*     FreePtr;        // The pointer to the storage with free object indexes
+    volatile unsigned long long     FreePut;        // PUT index used to put the new item to 'free' storage
+    volatile unsigned long long     FreeGet;        // GET index used to get the new free item from 'free' storage
+
+    unsigned long long*     UsedPtr;        // The pointer to the storage with 'Used' object indexes
+    volatile unsigned long long     UsedPut;        // PUT index used to put the new item to 'already used' storage
+    volatile unsigned long long     UsedGet;        // GET index used to get the item from 'already used' storage
+#ifdef __KERNEL__
+    struct mutex lock;
+#else
+    pthread_mutex_t lock;
+#endif
+}POOL, *PPOOL;
+
+
+unsigned int PoolInit (PPOOL pPool, void * pStorage, unsigned int nBlockNum, unsigned int nBlockSize, unsigned long long* pFreePtr, unsigned long long* pUsedPtr);
+void*  PoolAlloc(PPOOL pPool);
+unsigned int PoolFree(PPOOL pPool, void * pBlock);
+unsigned int PoolGetFreeNum(PPOOL pPool);
+unsigned int PoolGetAllocNum(PPOOL pPool);
+
+#endif //_POOL_API_H_
diff --git a/wls_lib/testapp/testapp.c b/wls_lib/testapp/testapp.c
new file mode 100644
index 0000000..1bd9a84
--- /dev/null
+++ b/wls_lib/testapp/testapp.c
@@ -0,0 +1,1486 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+/**
+ * WLS interface test application
+ * (contains functional unit tests and diagnostics to test wls
+ *  supported by WLS interface)
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>   // for printf
+#include <string.h>  // for memset
+#include <signal.h>  // for SIGINT
+#include <unistd.h>  // for usleep
+#include <stdlib.h>  // for rand
+#include <getopt.h>  // for getopt
+#include <sys/time.h>
+#include <pthread.h>
+#include <sched.h>
+#include "ttypes.h"
+#include "wls_lib.h"
+#include "pool.h"
+
+#define HANDLE	PVOID
+
+
+#define K 			1024
+#define M			(K*K)
+
+#define   DEFAULT_TEST_MEMORY_SIZE      256*M
+#define   DEFAUTL_TEST_BLOCK_SIZE       16*K
+
+#define   DEFAULT_MESSAGE_COUNT_PER_MS  10
+#define   DEFAULT_MAX_MESSAGE_SIZE      2000
+#define   DEFUALT_MIN_MESSAGE_SIZE      100
+
+#define   APP_QUEUE_SIZE   255   /* number of elements each queue of the WLS  being registered will have */
+#define   MAX_MESSAGES  1000   /* per ms */
+
+
+#ifndef TRUE
+#define TRUE  1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+typedef enum {
+    APP_TC_SANITY_TEST = 0,
+} APP_TEST_CASES;
+
+typedef struct tagAPP_PARAMS {
+    char *wls_dev_name;
+    int aff_core;
+    int test_id;
+    int rx_id;
+    int tx_id;
+    int n_messages;
+    int max_size;
+    int min_size;
+    int interface_count;
+    U8 master;
+    U8 debug;
+    U8 crc;
+    U8 trusted;
+} APP_PARAMS, *PAPP_PARAMS;
+
+typedef struct tagAPP_MESSAGE {
+    U32 id;
+} APP_MESSAGE, *PAPP_MESSAGE;
+
+typedef struct tagAPP_CONTEXT {
+    V32 ExitStatus;
+    HANDLE hWls;
+
+    U32 master;
+
+    PVOID shm_memory;
+
+    POOL Pool; // The pool descriptor
+    void* PoolStrPtr; // The pool storage pointer to keep indexes
+
+    U16 RxID;
+    U16 TxID;
+
+    U16 nInterfaces; // number of RX identifiers used by the APP
+    //
+    U16 InitQueueSize; // for invalid messages test (to trigger WLS  blocking)
+
+    //
+    U32 MsgPerMs;
+    U32 MaxMsgSize;
+    U32 MinMsgSize;
+
+    U32 TxCnt;
+    U32 RxCnt;
+
+    U64 nTxMsgs; // Messages transmitted
+    U64 nTxOctets; // Octets transmitted
+    U64 nRxMsgs; // Messages received
+    U64 nRxOcters; // Octets received
+    U64 Cycles; // number of 1ms cycles
+
+    int AppSanityMsgSize; // 4 or 8 depending on CRC feature
+    U8 Debug; // when TRUE app cycle is 1 sec, otherwise 1ms
+    U8 TrustedDataSource; // for trusted data sources ICC service removes msg validity checking.
+
+    void (*Receive)(HANDLE h);
+    void (*Transmit)(HANDLE h);
+
+    void (*ThreadReceive)(HANDLE h);
+    void (*ThreadTransmit)(HANDLE h);
+
+    int (*wls_put)(void* h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags);
+    unsigned long long (*wls_get)(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+    unsigned long long (*wls_wget)(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+
+    U8 *pLastRx; // used for scatter-gather test
+    U32 LastRxSize; // used for scatter-gather test
+
+    U32 *pServiceBuffer;
+
+    U32 TxMsgCnt;
+    PVOID TxMessages[MAX_MESSAGES];
+    U32 TxMessageSizes[MAX_MESSAGES]; // <-- required for Ping-Pong test to store received sizes
+    int core;
+
+} APP_CONTEXT, *PAPP_CONTEXT;
+
+APP_CONTEXT AppContext;
+
+static int pool_alloc = 0;
+static int pool_free = 0;
+
+static void ShowData(void* ptr, unsigned int size)
+{
+    U8 *d = ptr;
+    unsigned int i;
+
+    for (i = 0; i < size; i++) {
+        if (!(i & 0xf))
+            printf("\n");
+        printf("%02x ", d[i]);
+    }
+    printf("\n");
+}
+
+static void App_SigExitCallback(int signum)
+{
+    (void) signum;
+    AppContext.ExitStatus = TRUE;
+}
+
+static void* WlsVaToPa(void * ptr)
+{
+    PAPP_CONTEXT pCtx = &AppContext;
+    return (void*) WLS_VA2PA(pCtx->hWls, ptr);
+}
+
+static void* WlsPaToVa(void * ptr)
+{
+    PAPP_CONTEXT pCtx = &AppContext;
+    return (void*) WLS_PA2VA(pCtx->hWls, (U64) ptr);
+}
+
+static void* App_Alloc(void* h, unsigned long size)
+{
+    (void) h;
+    (void) size;
+    void * retval = NULL;
+    if (AppContext.master) {
+        retval = PoolAlloc(&(AppContext.Pool));
+        //printf("pPool->FreeGet  %d == pPool->FreePut %d\n", AppContext.Pool.FreeGet, AppContext.Pool.FreePut);
+    } else {
+        retval = (void*) WLS_DequeueBlock(AppContext.hWls);
+        if (retval)
+            retval = (void*) WlsPaToVa(retval);
+        else
+            printf("WLS_DequeueBlock returned null\n");
+    }
+
+    if (retval == NULL) {
+        printf("no memory %d %d\n", pool_alloc, pool_free);
+        exit(-1);
+    } else
+        pool_alloc++;
+
+    return retval;
+}
+
+static int App_Free(void* h, void* pMsg)
+{
+    (void) h;
+    if (AppContext.master)
+        if (pMsg) {
+            pool_free++;
+            return (PoolFree(&(AppContext.Pool), pMsg) == 1 ? 0 : -1);
+        } else {
+            printf("Free Null pointer\n");
+            exit(-1);
+        } else
+        return 0;
+}
+
+static int App_MemoryInit(void* h, unsigned long size, U32 BlockSize)
+{
+    int ret = 0;
+    unsigned long long* pUsed;
+    unsigned long long* pFree;
+    PAPP_CONTEXT pCtx = &AppContext;
+    U32 nBlocksSlave = 0;
+
+    U32 nElmNum = size / BlockSize - 1;
+
+    // We need to allocate the memory for indexes and to initialize the
+    // pool descriptor, (x+1) is used to prevent queues overflow
+
+    pCtx->PoolStrPtr = malloc((nElmNum + 1) * 4 * sizeof (unsigned long long));
+
+    if (pCtx->PoolStrPtr == NULL)
+        return -1;
+
+    pFree = (unsigned long long*) pCtx->PoolStrPtr;
+    pUsed = pFree + (nElmNum + 1);
+
+    ret = PoolInit(&pCtx->Pool, h, nElmNum, BlockSize, pFree, pUsed);
+
+    if (ret == 0) {
+
+        if (AppContext.master) {
+            int res = TRUE;
+            /* allocate blocks for Slave to Master transmittion */
+            while (res) {
+                void* pBlock = App_Alloc(AppContext.hWls, DEFAUTL_TEST_BLOCK_SIZE);
+                if (pBlock) {
+                    res = WLS_EnqueueBlock(AppContext.hWls, (U64) WlsVaToPa(pBlock));
+                    if (res)
+                        nBlocksSlave++;
+                    else
+                        App_Free(AppContext.hWls, pBlock);
+                } else
+                    res = FALSE;
+            }
+            printf("Slave has %d free blocks\n", nBlocksSlave);
+        }
+    }
+
+    return ret;
+}
+
+/********************************/
+
+#define FAST_CRC16    1
+
+#if (FAST_CRC16)
+const U8 mb_table_level1[] = {
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+    0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
+};
+
+const U8 mb_table_level2[] = {
+    0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2,
+    0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04,
+    0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E,
+    0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8,
+    0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
+    0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC,
+    0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6,
+    0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10,
+    0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32,
+    0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
+    0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE,
+    0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38,
+    0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA,
+    0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C,
+    0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
+    0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0,
+    0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62,
+    0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4,
+    0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE,
+    0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
+    0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA,
+    0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C,
+    0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76,
+    0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0,
+    0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
+    0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54,
+    0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E,
+    0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98,
+    0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A,
+    0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
+    0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86,
+    0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
+};
+
+#if 0
+// big endian CPU
+
+U16
+crc16(U16 crc, U8 data)
+{
+    U8 index;
+    U8 crc_Low = crc & 0xFF;
+    U8 crc_High = crc >> 8;
+
+    index = (crc_High ^ data) & 0xFF;
+    crc_High = crc_Low ^ mb_table_level1[ index ];
+    crc_Low = mb_table_level2[ index ];
+
+    return (crc_High << 8) | crc_Low;
+}
+#else
+// little endian CPU
+#if 0
+static U16 CRC16_Update(U16 crc, U8 data)
+{
+    U8 index;
+    U8 crc_High = crc >> 8;
+    U8 crc_Low = crc & 0xFF;
+
+    index = crc_Low ^ data;
+    crc_Low = crc_High ^ mb_table_level1[ index ];
+    crc_High = mb_table_level2[ index ];
+
+    return (crc_High << 8) | crc_Low;
+}
+#endif
+#endif
+
+#if 0
+/***********************************************
+ * CRC16  polynomial : X16 + X15 + X2 + 1       *
+ * FAST CRC16 routine                           *
+ * ---> pData - msg to be protected             *
+ * ---> size - msg size in bytes                *
+ * ---> aCRC - initializer (0xFFFF for 1st page)*
+ * <--- crc16                                   *
+ ***********************************************/
+static U16 CRC16_NoInit(U16 aCRC, U8 *pData, U16 size)
+{
+
+    if (!size)
+        return aCRC;
+    else {
+        U8 index;
+        U8 crc_High = aCRC >> 8;
+        U8 crc_Low = aCRC & 0xFF;
+
+        do {
+            index = crc_Low ^ *pData++;
+            crc_Low = crc_High ^ mb_table_level1[ index ];
+            crc_High = mb_table_level2[ index ];
+        } while (--size);
+
+        return (crc_High << 8) | crc_Low;
+    }
+}
+#endif
+
+#else // SLOW (canonic CRC16 calculation)
+
+/***********************************************
+ * CRC16  polynomial : X16 + X15 + X2 + 1       *
+ * ---> pData - msg to be protected             *
+ * ---> size - msg size in bytes                *
+ * ---> aCRC - initializer (0xFFFF for 1st page)*
+ * <--- crc16                                   *
+ ***********************************************/
+U16 CRC16_NoInit(U16 aCRC, U8 *pData, U16 size)
+{
+    U8 i, tmp;
+
+    if (!size)
+        return aCRC;
+
+    do {
+        aCRC ^= *pData++;
+        for (i = 0; i < 8; i++) {
+            tmp = aCRC & 0x01;
+            aCRC >>= 1;
+            if (tmp) {
+                aCRC ^= CRC16_DIVISOR;
+            }
+        }
+    } while (--size);
+    return aCRC;
+}
+
+#endif // FAST_CRC16
+
+
+#define CRC32_INIT_VAL  0xFFFFFFFF
+#define CRC32_DIVISOR   0xA0000001
+
+static U32 ICC_CRC32(U8 *pData, U32 size)
+{
+    U32 retval = CRC32_INIT_VAL;
+    U8 i, tmp;
+
+    if (!size)
+        return CRC32_INIT_VAL; // mean CRC error
+    do {
+        retval ^= *pData++;
+        for (i = 8; i > 0; --i) {
+            tmp = retval & 0x01;
+            retval >>= 1;
+            if (tmp) {
+                retval ^= CRC32_DIVISOR;
+            }
+        }
+    } while (--size);
+    return retval;
+}
+
+#if 0
+
+static U16 ICC_CRC16(U8 *pData, U16 size)
+{
+#define CRC16_ERROR (0xffff)
+    return CRC16_NoInit(CRC16_ERROR, pData, size); // use 0xFFFF as first initializer
+}
+#endif
+
+static int app_PutMessageCRC(void* h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags)
+{
+    U8 *p;
+    U64 pMsgVa = (U64) WlsPaToVa((void*) pMsg);
+
+    if (pMsgVa == 0)
+    {
+        return 0;
+    }
+    p = (U8 *) pMsgVa;
+#if 1
+    U32 crc = ICC_CRC32((U8 *) pMsgVa, MsgSize - sizeof (crc));
+    // CRC32
+    p[MsgSize - 4] = (crc >> 0) & 0xff;
+    p[MsgSize - 3] = (crc >> 8) & 0xff;
+    p[MsgSize - 2] = (crc >> 16) & 0xff;
+    p[MsgSize - 1] = (crc >> 24) & 0xff;
+#else
+    U16 crc = ICC_CRC16((U8 *) pMsg, MsgSize - sizeof (crc));
+    // CRC16
+    p[MsgSize - 2] = (crc >> 0) & 0xff;
+    p[MsgSize - 1] = (crc >> 8) & 0xff;
+#endif
+
+    return WLS_Put(h, (unsigned long long) pMsg, MsgSize, MsgTypeID, Flags);
+}
+
+static unsigned long long app_GetMessageCRC(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    U64 pMsgPa = WLS_Get(h, MsgSize, MsgTypeID, Flags);
+    U64 pMsg = (U64) WlsPaToVa((void*) pMsgPa);
+
+    if (pMsg) {
+        U32 size = *MsgSize;
+#if 1
+        U32 crc = ICC_CRC32((U8*) pMsg, size);
+#else
+        U16 crc = ICC_CRC16((U8*) pMsg, size);
+#endif
+
+        if (crc != 0) {
+            printf("CRC error detected for message %p, size_%lu\n", (void*) pMsg, (long) size);
+            ShowData((U8*) pMsg, size);
+        }
+    }
+    return pMsgPa;
+}
+
+static unsigned long long app_WGetMessageCRC(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    U64 pMsgPa = WLS_WGet(h, MsgSize, MsgTypeID, Flags);
+    U64 pMsg = (U64) WlsPaToVa((void*) pMsgPa);
+
+    if (pMsg) {
+        U32 size = *MsgSize;
+#if 1
+        U32 crc = ICC_CRC32((U8*) pMsg, size);
+#else
+        U16 crc = ICC_CRC16((U8*) pMsg, size);
+#endif
+
+        if (crc != 0) {
+            printf("CRC error detected for message %p, size_%lu\n", (void*) pMsg, (long) size);
+            ShowData((U8*) pMsg, size);
+        }
+    }
+    return pMsgPa;
+}
+
+static void CreateMessage(PAPP_MESSAGE p, U32 size)
+{
+    (void) size;
+    p->id = AppContext.TxCnt++;
+}
+
+static void CheckMessage(PAPP_MESSAGE p, U32 size)
+{
+    if (AppContext.RxCnt && p->id != AppContext.RxCnt) {
+        //		char buf[8*K];
+        printf("rx message(id_%llu)_%lx error expected_%lu, received_%lu\n", (long long) AppContext.nRxMsgs, (U64) p, (long) AppContext.RxCnt, (long) p->id);
+        ShowData(p, size);
+        //		if (TL_GetStatistics(AppContext.hWls, buf, sizeof(buf)))
+        //		printf("%s", buf);
+    }
+
+    AppContext.RxCnt = p->id;
+    AppContext.RxCnt += 1;
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_AllocMultiple
+ * @brief used to allocate multiple blocks of the same size from the WLS
+ *
+ * @param[h]  hWls - app thread WLS  handle
+ * @param[o]  pMsgs - ptr to beginning of array of points to allocated blocks
+ * @param[o]  pMsgSizes - array to write size for each allocated blocks
+ * @param[i]  nMsgs - number of blocks to allocate
+ * @return    U32 - number of allocated blocks
+ *
+ * @description
+ *    The routine is used allocate multiple blocks from the ICC service,
+ * the blocks are supposed to be same size blocks, satisfying
+ * appContext.MaxMsgSize parameter.
+ *    In case the service is unable to provide requested number of blocks,
+ * smaller count is allocated. The routine returns actual number of allocated
+ * blocks
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static U32 app_AllocMultiple(HANDLE hWls, PVOID *pMsgs, U32 *pMsgSizes, U32 nMsgs)
+{
+    unsigned n = 0;
+    unsigned i, j;
+
+    memset(pMsgs, 0x00, sizeof (PVOID) * nMsgs);
+
+    while (nMsgs--) {
+        pMsgs[n] = App_Alloc(hWls, AppContext.MaxMsgSize);
+        pMsgSizes[n] = AppContext.MaxMsgSize;
+        if (!pMsgs[n]) {
+            printf("empty pool allocated_%u out of %lu\n", n, (long) AppContext.MsgPerMs);
+            break;
+        }
+        n += 1;
+    }
+
+    // check for duplicated pointers
+    for (i = 0; i < n; i++) {
+        for (j = i + 1; j < n; j++) {
+            if (pMsgs[i] == pMsgs[j]) {
+                printf("duplicated pointer %p (msg_id1_%u, msg_id2_%u)\n", pMsgs[i], i, j);
+                break;
+            }
+        }
+    }
+
+    return n;
+    //ShowData(TxMessages, sizeof(TxMessages));
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_SanityTestTransmitter
+ * @brief transmitter of default test case (0).
+ *
+ * @param[h]  hWls - app thread WLS  handle
+ * @return    void
+ *
+ * @description
+ *    The routine is used in test case 0 (non-blocking sanity unit test)
+ * The transmitter does allocate multiple blocks of the same size from the ICC
+ * service. Then it fills each block with incremental counter and transfers
+ * to other application specified by parameter TxID.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_SanityTestTransmitter(HANDLE hWls)
+{
+    U8 *pMsg;
+    unsigned n = app_AllocMultiple(hWls, AppContext.TxMessages, AppContext.TxMessageSizes, AppContext.MsgPerMs);
+    unsigned fn = n;
+    unsigned cnt = 0;
+    unsigned k = 0;
+    unsigned alloc = n;
+
+    // lets transmit some message for test
+    while (n--) {
+        pMsg = AppContext.TxMessages[cnt++];
+        if (pMsg) {
+            U32 size = (rand() % AppContext.MaxMsgSize);
+
+            if (size < AppContext.MinMsgSize)
+                size = AppContext.MinMsgSize;
+
+            memset(pMsg, cnt, size);
+            CreateMessage((PAPP_MESSAGE) pMsg, size);
+            if ((AppContext.wls_put(hWls, (U64) WlsVaToPa(pMsg), size, AppContext.TxID, 0) != 0)) {
+                printf("could not send the message_%p\n", pMsg);
+                break;
+            } else {
+                k++;
+            }
+            AppContext.nTxOctets += size;
+            AppContext.nTxMsgs += 1;
+        }
+    }
+
+    if (alloc != k)
+        printf("inorrect sent %d alloc %d \n", k, alloc);
+
+    cnt = 0;
+    while (fn--) {
+        pMsg = AppContext.TxMessages[cnt++];
+        if (pMsg) {
+            if (App_Free(hWls, pMsg) != 0)
+                printf("could not release the message_%p\n", pMsg);
+        } else
+            printf("pMsg is NULL [%d]\n", cnt);
+    }
+    if (cnt != k) {
+        printf("inorrect free sent %d free %d \nQuiting...\n", k, cnt);
+        AppContext.ExitStatus = 1;
+    }
+}
+
+#if 0
+/**
+ *******************************************************************************
+ *
+ * @fn    app_ScatterGatherTransmitter
+ * @brief transmitter of default test case (15/16).
+ *
+ * @param[h]  hWls - app thread WLS  handle
+ * @return    void
+ *
+ * @description
+ *    The routine is used in test case 0 (non-blocking sanity unit test)
+ * The transmitter does allocate multiple blocks of the same size from the ICC
+ * service. Then it fills each block with incremental counter and transfers
+ * to other application specified by parameter TxID.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_ScatterGatherTransmitter(HANDLE hWls)
+{
+	U8 *pMsg;
+	unsigned n = app_AllocMultiple(hWls, AppContext.TxMessages, AppContext.TxMessageSizes, AppContext.MsgPerMs + 2);
+	unsigned i, cnt = 0, flags = 0;
+	U8 *p, *pOriginMsg = (U8 *)App_Alloc(hWls, AppContext.MaxMsgSize);
+	unsigned TotalSize = 0;
+
+    unsigned fn  = n;
+    unsigned k = 0;
+    unsigned alloc = n;
+
+	if (!pOriginMsg) {
+		printf("No memory for App_Alloc()\n");
+		return;
+	}
+
+	flags = rand() & 0xff;
+
+	for(i = 0; i < AppContext.MaxMsgSize; i++)
+		pOriginMsg[i] = (flags + i) & 0xff;
+
+	// scatter original message among several blocks
+	for(i = 0; i < n; i++)
+	{
+		U32 size = (rand() % (AppContext.MaxMsgSize / n));
+
+		if (size < AppContext.MinMsgSize)
+			size = AppContext.MinMsgSize;
+
+		TotalSize += size;
+		AppContext.TxMessageSizes[i] = size;
+		//printf("size%d=%lu\n", i, size);
+	}
+
+	// adjust size of the last block
+	if (TotalSize < AppContext.MaxMsgSize)
+	{
+		AppContext.TxMessageSizes[n - 1] += AppContext.MaxMsgSize - TotalSize;
+	}
+	else if (TotalSize > AppContext.MaxMsgSize)
+	{
+		printf("error: size of the scatted blocks exceeding size of the original message\n");
+	}
+
+	p = pOriginMsg;
+	for(i = 0; i < n; i++)
+	{
+		// copy data into the scattered blocks
+		pMsg = AppContext.TxMessages[i];
+		memcpy(pMsg, p, AppContext.TxMessageSizes[i]);
+		p += AppContext.TxMessageSizes[i];
+	}
+
+	// transmit original message first
+	if (AppContext.wls_put(hWls, (U64)WlsVaToPa(pOriginMsg), AppContext.MaxMsgSize, AppContext.TxID, 0) != 0)
+	{
+		printf("could not send the message_%p\n", pOriginMsg);
+		if (App_Free(hWls, pOriginMsg) != 0)
+			printf("could not release the message_%p\n", pOriginMsg);
+	}
+	else
+	{
+		AppContext.nTxOctets += AppContext.MaxMsgSize;
+		AppContext.nTxMsgs += 1;
+	}
+
+    if(pOriginMsg){
+        if (App_Free(hWls, pOriginMsg) != 0)
+            printf("could not release the message_%p\n", pMsg);
+    }
+    else
+        printf("pOriginMsg is NULL \n");
+
+	// transmit scattered messages following their creation order
+	while (n--)
+	{
+		pMsg = AppContext.TxMessages[cnt];
+		if (!cnt)
+			flags = WLS_SG_FIRST;
+		else if (n == 0)
+			flags = WLS_SG_LAST;
+		else
+			flags = WLS_SG_NEXT;
+
+		if (AppContext.wls_put(hWls, (U64)WlsVaToPa(pMsg), AppContext.TxMessageSizes[cnt], AppContext.TxID, flags) != 0)
+		{
+			printf("could not send the message_%p\n", pMsg);
+			if (App_Free(hWls, pMsg) != 0)
+				printf("could not release the message_%p\n", pMsg);
+		}
+        else
+            k++;
+
+		AppContext.nTxOctets += AppContext.TxMessageSizes[cnt];
+		AppContext.nTxMsgs += 1;
+		cnt++;
+	}
+    if(alloc != k)
+        printf("inorrect sent %d alloc %d \n", k, alloc);
+
+    cnt = 0;
+    while (fn--)
+    {
+        pMsg = AppContext.TxMessages[cnt++];
+        if(pMsg){
+            if (App_Free(hWls, pMsg) != 0)
+                printf("could not release the message_%p\n", pMsg);
+        }
+        else
+            printf("pMsg is NULL [%d]\n", cnt);
+    }
+    if(cnt != k)
+        printf("inorrect free sent %d free %d \n", k, cnt);
+}
+#endif
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_SanityTestReceiver
+ * @brief default sanity checking receiver used in multiple tests.
+ *
+ * @param[h]  hWls - app thread WLS  handle
+ * @return    void
+ *
+ * @description
+ *    The routine takes received messages and checks the sanity incremental
+ * counter to confirm the order. In case the counter does not correspond to
+ * expected counter (misordered message or incorrect message) an error is
+ * printed to STDOUT.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_SanityTestReceiver(HANDLE hWls)
+{
+    (void) hWls;
+    U32 MsgSize;
+    U8 *pMsg;
+    U8 *pMsgPa;
+    U8 *pMsgVa;
+    U8 TempBuf[16 * K];
+    unsigned short MsgTypeID;
+    unsigned short Flags;
+    U32 nBlocksSlave = 0;
+
+    // handle RX receiver
+    while (((pMsgPa = (U8 *) AppContext.wls_get(AppContext.hWls, &MsgSize, &MsgTypeID, &Flags)) != NULL)) {
+        pMsgVa = (U8 *) WlsPaToVa(pMsgPa);
+
+        if (pMsgVa == NULL) {
+            printf("va: %lx pa: %lx\n", (long) pMsgVa, (long) pMsgPa);
+            continue;
+        }
+
+        pMsg = pMsgVa;
+
+        if (((U64) pMsg & 0x3) == 0) {
+            // aligned message
+            CheckMessage((PAPP_MESSAGE) pMsg, MsgSize);
+        } else {
+            // misaligned message
+            printf("Unaligned message\n");
+            MsgSize = (MsgSize > sizeof (TempBuf)) ? sizeof (TempBuf) : MsgSize;
+            memcpy(TempBuf, pMsg, MsgSize);
+            // handle received message
+            CheckMessage((PAPP_MESSAGE) TempBuf, MsgSize);
+        }
+        App_Free(AppContext.hWls, pMsg);
+        AppContext.nRxOcters += MsgSize;
+        AppContext.nRxMsgs += 1;
+
+        if (AppContext.master) {
+            int res = TRUE;
+            /* allocate blocks for Slave to Master transmittion */
+            while (res) {
+                void* pBlock = App_Alloc(AppContext.hWls, DEFAUTL_TEST_BLOCK_SIZE);
+                if (pBlock) {
+                    res = WLS_EnqueueBlock(AppContext.hWls, (U64) WlsVaToPa(pBlock));
+                    if (res)
+                        nBlocksSlave++;
+                    else
+                        App_Free(AppContext.hWls, pBlock);
+                } else
+                    res = FALSE;
+            }
+        }
+
+    }
+}
+
+#if 0
+/**
+ *******************************************************************************
+ *
+ * @fn    app_ScatterGatherReceiver
+ * @brief scatter gather test receiver
+ *
+ * @param[h]  hWls - app thread WLS  handle
+ * @return    void
+ *
+ * @description
+ *    The routine takes received messages and checks the sanity incremental
+ * counter to confirm the order. In case the counter does not correspond to
+ * expected counter (misordered message or incorrect message) an error is
+ * printed to STDOUT.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_ScatterGatherReceiver(HANDLE hWls)
+{
+        (void)hWls;
+	U32 MsgSize;
+	U8  *pMsg;
+    U8  *pMsgPa;
+    U8  *pMsgVa;
+	U32 size;
+	U8 err = 0;
+    unsigned short MsgTypeID;
+    unsigned short Flags;
+
+	// handle RX receiver
+	while ((pMsgPa = (U8*) AppContext.wls_get(AppContext.hWls, &MsgSize, &MsgTypeID, &Flags)) != NULL)
+	{
+        pMsgVa = (U8  *)WlsPaToVa(pMsgPa);
+
+        pMsg = pMsgVa;
+
+		AppContext.nRxOcters += MsgSize;
+		AppContext.nRxMsgs += 1;
+
+		if (!AppContext.pLastRx)
+		{
+			AppContext.pLastRx = pMsg;
+			AppContext.LastRxSize = MsgSize;
+		}
+		else // compare with received and release both
+		{
+			U32 i;
+			if (AppContext.LastRxSize != MsgSize)
+				printf("received wrong size, unsync? try to re-run app both clusters\n");
+
+			size = MsgSize;
+			if (size > AppContext.LastRxSize)
+				size = AppContext.LastRxSize;
+
+			for(i = 0; i < size; i++)
+			{
+				if (pMsg[i] != AppContext.pLastRx[i])
+				{
+					// error content doesn't match
+					err = TRUE;
+					break;
+				}
+			}
+
+			if (err)
+			{
+				printf("content verification failed, scatter-gather test FAIL\n");
+				// terminate
+				AppContext.Receive = NULL;
+				AppContext.Transmit = NULL;
+				App_Free(AppContext.hWls, pMsg);
+				App_Free(AppContext.hWls, AppContext.pLastRx);
+				return;
+			}
+
+			App_Free(AppContext.hWls, pMsg);
+			App_Free(AppContext.hWls, AppContext.pLastRx);
+			AppContext.pLastRx = NULL;
+		}
+
+	}
+}
+
+
+
+static U32 app_GetTime(void)
+{
+	struct timeval tv;
+	U32 time_ms = 0;
+	if (gettimeofday(&tv, NULL) == 0)
+		time_ms = tv.tv_sec * 1000 + tv.tv_usec / 1000;
+	return time_ms;
+}
+#endif
+
+/******************************************************************************
+ *                                                                             *
+ *                       Application common routines                           *
+ *                                                                             *
+ ******************************************************************************/
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_UpdateStatistics
+ * @brief is used to update RX and TX statistics
+ *
+ * @param[n]  void
+ * @return    void
+ *
+ * @description
+ *    The routine prints out the statistics of received and transmitted
+ * messages.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+
+static void app_UpdateStatistics(void)
+{
+    AppContext.Cycles += 1;
+
+    if (AppContext.Debug || AppContext.Cycles % 1000 == 0) {
+        printf("Rx(id_%u) (%llu) - (%llu KiB)\n", AppContext.RxID, (long long) AppContext.nRxMsgs, (long long) AppContext.nRxOcters >> 10);
+        printf("Tx(id_%u) (%llu) - (%llu KiB)\n", AppContext.TxID, (long long) AppContext.nTxMsgs, (long long) AppContext.nTxOctets >> 10);
+    }
+
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_Help
+ * @brief prints app help content
+ *
+ * @param[n]  void
+ * @return    void
+ *
+ * @description
+ *    The routine is used to print help content to stdout.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_Help(void)
+{
+    char help_content[] =  \
+			"WLS test application\n\n"\
+			"Usage: testapp [-c <test>] [-r <rxid>] [-t <txid>] [-n <msgcount>]\n\n"\
+			"supports the following parameters:\n\n"
+                        "-c | --testcase <test number>     0 - default sanity test\n"\
+			"                                  1 - misaligned pointers test\n"\
+			"                                  2 - aligned 4 pointers test\n"\
+			"                                  3 - random pools test\n"\
+			"                                  4 - ping-pong (ZBC test)\n"\
+			"                                  5 - invalid messages test\n\n"\
+			"--trusted                    switches WLS  to trusted mode\n"\
+			"-r | --rxid <id>             used to specify RxTypeID\n"\
+			"-t | --txid <id>             used to specify TxTypeID\n"\
+			"-n | --msgcount <count>      used to specify number of messages per timeframe\n"\
+			"-l | --minsize  <size>       specifies MIN message size in bytes\n"\
+			"-s | --maxsize  <size>       specifies MAX message size in bytes\n"\
+			"--crc                        enables CRC generation and checking\n"\
+			"--debug                      increases sleep interval to 1 second\n"\
+			"-m | --master                set predefined rxid and txid\n";
+
+    printf("%s", help_content);
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_ParseArgs
+ * @brief is used to parse incoming app args
+ *
+ * @param[i]  argc - app arg count
+ * @param[i]  argv - array of args
+ * @param[o]  params - app startup params filled basing on args parse
+ * @return    number of parsed args
+ *
+ * @description
+ *    The routine is parse input args and convert them into app startup params
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static int app_ParseArgs(int argc, char ** argv, PAPP_PARAMS params)
+{
+    int c;
+    int *pInt;
+    int cnt = 0;
+
+    struct option long_options[] = {
+        {"wlsdev", required_argument, 0, 'w'},
+        {"affinity", required_argument, 0, 'a'},
+        {"testcase", required_argument, 0, 'c'},
+        {"rxid", required_argument, 0, 'r'},
+        {"txid", required_argument, 0, 't'},
+        {"msgcount", required_argument, 0, 'n'},
+        {"maxsize", required_argument, 0, 's'},
+        {"minsize", required_argument, 0, 'l'},
+        {"master", no_argument, 0, 'm'},
+        {"debug", no_argument, 0, 'd'}, /* slow down the app cycle from 1ms to 1s*/
+        {"icount", required_argument, 0, 'i'},
+        {"crc", no_argument, 0, 1},
+        {"trusted", no_argument, 0, 2},
+        {"help", no_argument, 0, 'h'},
+        {0, 0, 0, 0}
+    };
+
+    memset(params, 0, sizeof (*params));
+
+    // set default values here
+    params->interface_count = 1;
+
+    while (1) {
+        //int this_option_optind = optind ? optind : 1;
+        int option_index = 0;
+
+        c = getopt_long(argc, argv, "a:w:c:r:t:n:s:l:mdi:h", long_options, &option_index);
+
+        if (c == -1)
+            break;
+
+        cnt += 1;
+        pInt = NULL;
+
+        switch (c) {
+            case 'a': // test Case selection
+                pInt = &params->aff_core;
+                break;
+            case 'c': // test Case selection
+                pInt = &params->test_id;
+                break;
+            case 'r': // rx id selection
+                pInt = &params->rx_id;
+                break;
+            case 't': // tx id selection
+                pInt = &params->tx_id;
+                break;
+            case 's': // select message size
+                pInt = &params->max_size;
+                break;
+            case 'l': // select message size
+                pInt = &params->min_size;
+                break;
+            case 'n': // select number of messages
+                pInt = &params->n_messages;
+                break;
+            case 'i': // select number of interfaces to register
+                pInt = &params->interface_count;
+                break;
+            case 'm':
+                params->master = TRUE;
+                break;
+            case 'd':
+                params->debug = TRUE;
+                break;
+            case 'w':
+                params->wls_dev_name = optarg;
+                break;
+            case 'h':
+                app_Help();
+                exit(0);
+            case 2:
+                params->trusted = TRUE;
+                break;
+            case 1: // crc checking enabled
+                params->crc = TRUE;
+                break;
+        }
+
+        if (pInt && optarg) {
+            // get int arg
+            if (optarg[0] == '0' && (optarg[1] == 'x' || optarg[1] == 'X')) {
+                sscanf(optarg, "%x", (unsigned *) pInt);
+            } else {
+                *pInt = atoi(optarg);
+            }
+        }
+    }
+    return cnt;
+}
+
+static int app_set_affinity(int coreNum)
+{
+    cpu_set_t cpuset;
+    int i, rc;
+
+    /* set main thread affinity mask to CPU7 */
+
+    CPU_ZERO(&cpuset);
+    CPU_SET(coreNum, &cpuset);
+
+    rc = pthread_setaffinity_np(pthread_self(), sizeof (cpu_set_t), &cpuset);
+    if (rc) {
+        perror("pthread_setaffinity_np failed");
+        printf("pthread_setaffinity_np failed: %d", rc);
+    }
+
+    /* check the actual affinity mask assigned to the thread */
+
+    CPU_ZERO(&cpuset);
+
+    rc = pthread_getaffinity_np(pthread_self(), sizeof (cpu_set_t), &cpuset);
+
+    if (rc) {
+        perror("pthread_getaffinity_np failed");
+        printf("pthread_getaffinity_np failed: %d", rc);
+    }
+
+    printf("set affinity: ");
+    for (i = 0; i < CPU_SETSIZE; i++)
+        if (CPU_ISSET(i, &cpuset))
+            printf("    CPU %d\n", i);
+
+    if (!CPU_ISSET(coreNum, &cpuset)) {
+        printf("affinity failed");
+    }
+
+    /**
+       A new thread created by pthread_create(3) inherits a copy of its
+       creator's CPU affinity mask. */
+
+    return rc;
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_ApplyParams
+ * @brief is used to apply application startup parameters
+ *
+ * @param[i]  params - app startup params
+ * @return    void
+ *
+ * @description
+ *    The applies startup parameters
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_ApplyParams(PAPP_PARAMS params)
+{
+    // apply parameters
+    printf("selected test case %d - ", params->test_id);
+    switch (params->test_id) {
+        case APP_TC_SANITY_TEST:
+        default:
+            printf("NON-BLOCKING SANITY TEST\n");
+            AppContext.Receive = app_SanityTestReceiver;
+            AppContext.Transmit = app_SanityTestTransmitter;
+            break;
+    }
+
+    AppContext.wls_put = WLS_Put;
+    AppContext.wls_get = WLS_Get;
+    AppContext.wls_wget = WLS_WGet;
+
+    AppContext.MsgPerMs = DEFAULT_MESSAGE_COUNT_PER_MS;
+    AppContext.MaxMsgSize = DEFAULT_MAX_MESSAGE_SIZE;
+    AppContext.MinMsgSize = DEFUALT_MIN_MESSAGE_SIZE;
+    AppContext.AppSanityMsgSize = sizeof (APP_MESSAGE);
+
+    if (params->master) {
+        printf("WLS test app (supposed to run as MEMORY MASTER)\n");
+        AppContext.master = TRUE;
+        AppContext.RxID = 1;
+        AppContext.TxID = 2;
+    } else {
+        AppContext.master = FALSE;
+        AppContext.RxID = 2;
+        AppContext.TxID = 1;
+    }
+
+    if (params->rx_id)
+        AppContext.RxID = params->rx_id;
+
+    if (params->tx_id)
+        AppContext.TxID = params->tx_id;
+
+    if (params->n_messages && params->n_messages < MAX_MESSAGES)
+        AppContext.MsgPerMs = params->n_messages;
+
+    if (params->min_size && params->min_size >= 4)
+        AppContext.MinMsgSize = params->min_size;
+
+    // default is 1 RX interface
+    printf("if count = %u\n", params->interface_count);
+    AppContext.nInterfaces = 1;
+    if (params->interface_count == 0) {
+        printf("WLS test app started as simple data source, no RX ID will be specified\n");
+        AppContext.nInterfaces = 0;
+        AppContext.RxID = 0; // override RxID
+    } else if (params->interface_count <= 7) {
+        AppContext.nInterfaces = params->interface_count;
+    }
+
+
+    AppContext.TrustedDataSource = params->trusted;
+
+    if (params->crc) {
+        if (AppContext.MinMsgSize < 8)
+            AppContext.MinMsgSize = 8;
+
+        AppContext.wls_put = app_PutMessageCRC;
+        AppContext.wls_get = app_GetMessageCRC;
+        AppContext.wls_wget = app_WGetMessageCRC;
+
+        AppContext.AppSanityMsgSize += 4; // + sizeof CRC
+    }
+
+    if (params->max_size && params->max_size <= 16 * K)
+        AppContext.MaxMsgSize = params->max_size;
+
+    if (params->max_size < params->min_size)
+        params->max_size = params->min_size;
+
+    AppContext.Debug = params->debug;
+
+    if (params->aff_core) {
+        AppContext.core = params->aff_core;
+        app_set_affinity(AppContext.core);
+    }
+
+    printf("The application started with:\n");
+    printf("Core ................ %d\n", AppContext.core);
+    printf("Rx interface count .. %d\n", AppContext.nInterfaces);
+    printf("RxID ................ %d\n", AppContext.RxID);
+    printf("TxID ................ %d\n", AppContext.TxID);
+    if (AppContext.Debug)
+        printf("Generating .......... %lu Messages per second (DEBUG MODE)\n", (long) AppContext.MsgPerMs);
+    else
+        printf("Generating .......... %lu Messages per ms\n", (long) AppContext.MsgPerMs);
+    printf("Max Message Size .... %lu bytes\n", (long) AppContext.MaxMsgSize);
+    printf("Min Message Size .... %lu bytes\n", (long) AppContext.MinMsgSize);
+    printf("Number of threads ... 1\n");
+    printf("CRC checking ........ ");
+    if (params->crc)
+        printf("ENABLED\n");
+    else
+        printf("DISABLED\n");
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    app_ReleaseAllocatedBuffers
+ * @brief releases ICC buffers allocated by the application
+ *
+ * @param[n]  void
+ * @return    void
+ *
+ * @description
+ *    In process of making some tests when signal to close the application
+ * happens the app may keep some allocated buffers from the ICC pools. This
+ * routine does release these buffers back to ICC.
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ******************************************************************************/
+static void app_ReleaseAllocatedBuffers(void)
+{
+    if (AppContext.TxMsgCnt && AppContext.master)
+        do {
+            AppContext.TxMsgCnt -= 1;
+            App_Free(AppContext.hWls, AppContext.TxMessages[ AppContext.TxMsgCnt ]);
+        } while (AppContext.TxMsgCnt != 0);
+}
+
+/**
+ *******************************************************************************
+ *
+ * @fn    main
+ * @brief ICC test application main routine
+ *
+ * @param[n]  void
+ * @return    void
+ *
+ * @description
+ *    Contains logic of the test (one RX/TX thread)
+ *
+ * @references
+ * MS-111070-SP
+ *
+ * @ingroup icc_service_unit_test
+ *
+ ****************************************************************************/
+int main(int argc, char* argv[])
+{
+    int retval = 0;
+    APP_PARAMS params;
+
+    signal(SIGINT, App_SigExitCallback);
+
+    memset(&AppContext, 0, sizeof (AppContext));
+    memset(&params, 0, sizeof (params));
+
+#ifdef DPDK_WLS
+    int ret = rte_eal_init(argc, argv);
+    if (ret < 0)
+        rte_exit(EXIT_FAILURE, "Error with EAL initialization\n");
+
+    argc -= ret;
+    argv += ret;
+#endif
+
+    app_ParseArgs(argc, argv, &params);
+    app_ApplyParams(&params);
+
+    AppContext.InitQueueSize = APP_QUEUE_SIZE;
+
+    AppContext.hWls = WLS_Open(params.wls_dev_name, AppContext.master, DEFAULT_TEST_MEMORY_SIZE);
+
+    if (!AppContext.hWls) {
+        printf("could not register WLS client\n");
+        return 1;
+    } else {
+        printf("WLS has been registered\n");
+    }
+
+    AppContext.shm_memory = WLS_Alloc(AppContext.hWls, DEFAULT_TEST_MEMORY_SIZE);
+
+    if (AppContext.shm_memory == NULL) {
+        if (AppContext.master)
+            printf("could not create WLS shared memory\n");
+        else
+            printf("could not attach WLS shared memory\n");
+
+        return -1;
+    }
+
+    if (AppContext.master) {
+        if (App_MemoryInit(AppContext.shm_memory, DEFAULT_TEST_MEMORY_SIZE, DEFAUTL_TEST_BLOCK_SIZE) != 0) {
+            WLS_Free(AppContext.hWls, AppContext.shm_memory);
+            WLS_Close(AppContext.hWls);
+            exit(1);
+        }
+
+    }
+
+    // APPLICATION MAIN LOOP
+    while (!AppContext.ExitStatus && (AppContext.Receive || AppContext.Transmit)) {
+        if (AppContext.Receive)
+            AppContext.Receive(AppContext.hWls);
+
+        if (AppContext.Debug)
+            //usleep(10000); // 1 sec delay
+            sleep(1); // 1 sec delay
+        else
+            usleep(1000); // 1 ms delay
+
+        if (AppContext.Transmit)
+            AppContext.Transmit(AppContext.hWls);
+
+        app_UpdateStatistics();
+    }
+
+    app_ReleaseAllocatedBuffers();
+    printf("deregistering WLS  (TxTotal_%llu, RxTotal_%llu)\n", (long long) AppContext.nTxMsgs, (long long) AppContext.nRxMsgs);
+    WLS_Free(AppContext.hWls, AppContext.shm_memory);
+    WLS_Close(AppContext.hWls);
+    return retval;
+}
diff --git a/wls_lib/testapp/wls_test.sh b/wls_lib/testapp/wls_test.sh
new file mode 100644
index 0000000..79f154f
--- /dev/null
+++ b/wls_lib/testapp/wls_test.sh
@@ -0,0 +1,86 @@
+#!/bin/bash
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+
+COREMASK=2
+SECONDARY=1
+FPREFIX="wls"
+DPDK_WLS=0
+
+while getopts ":mpa:w:" opt; do
+  case ${opt} in
+    m )
+      SECONDARY=0
+      ;;
+    a )
+      COREMASK=$((1 << $OPTARG))
+      ;;
+    : )
+      echo "Invalid option: $OPTARG requires a core number"
+      exit 1
+      ;;
+    w )
+      #replace / with _ for dpdk file prefix
+      FPREFIX=${OPTARG////_}
+      ;;
+    : )
+      echo "Invalid option: $OPTARG requires dev wls path"
+      exit 1
+      ;;
+    p )
+      DPDK_WLS=1
+      ;;
+  esac
+done
+
+wlsTestBinary="wls_test"
+if [ $DPDK_WLS -eq 1 ]; then
+    if [ $SECONDARY -eq 0 ]; then
+        wlsTestBinary="build/wls_test -c $COREMASK -n 4 "
+        wlsTestBinary+="--file-prefix=$FPREFIX --socket-mem=3072 --"
+    else
+        wlsTestBinary="build/wls_test -c $COREMASK -n 4 "
+        wlsTestBinary+="--proc-type=secondary --file-prefix=$FPREFIX --"
+    fi
+fi
+
+ulimit -c unlimited
+
+export RTE_WLS=$PWD/..
+
+MACHINE_TYPE=`uname -m`
+
+if [ ${MACHINE_TYPE} == 'x86_64' ]; then
+    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$RTE_WLS
+
+    grep Huge /proc/meminfo
+
+    ulimit -c unlimited
+    echo 1 > /proc/sys/kernel/core_uses_pid
+    sysctl -w kernel.sched_rt_runtime_us=-1
+    for c in $(ls -d /sys/devices/system/cpu/cpu[0-9]*); do echo performance >$c/cpufreq/scaling_governor; done
+    sysctl -w kernel.shmmax=2147483648
+    sysctl -w kernel.shmall=2147483648
+fi
+
+wlsCmd="./${wlsTestBinary} $*"
+echo "Running... ${wlsCmd}"
+
+eval $wlsCmd
+
+exit 0
diff --git a/wls_lib/ttypes.h b/wls_lib/ttypes.h
new file mode 100644
index 0000000..4130efc
--- /dev/null
+++ b/wls_lib/ttypes.h
@@ -0,0 +1,195 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 __TTYPES_H__
+#define __TTYPES_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#ifndef TRUE
+#define TRUE    1
+#endif /*TRUE*/
+
+#ifndef FALSE
+#define FALSE   0
+#endif /*FALSE*/
+
+#ifndef NULL
+#define NULL   (void*)0
+#endif /*NULL*/
+
+/************************************************************************/
+/*     SINT64, SINT32, SINT16 and SINT8 definition                      */
+/************************************************************************/
+#ifndef _SINT64_
+#define _SINT64_
+typedef long long SINT64, *PSINT64;
+#endif /*_SINT64_*/
+
+#ifndef _SINT32_
+#define _SINT32_
+typedef int SINT32, *PSINT32;
+#endif /*_SINT32_*/
+
+#ifndef _SINT16_
+#define _SINT16_
+typedef short SINT16, *PSINT16;
+#endif /*_SINT16_*/
+
+#ifndef _SINT8_
+#define _SINT8_
+typedef char SINT8, *PSINT8;
+#endif /*_SINT8_*/
+
+#ifndef _PVOID_
+#define _PVOID_
+typedef void *PVOID;
+#endif /*_PVOID_*/
+
+#ifndef _BOOL_
+#define _BOOL_
+typedef unsigned char BOOL;
+#endif /*_BOOL_*/
+
+#ifndef _U8_
+typedef unsigned char  U8;      /* unsigned 8-bit  integer */
+#define _U8_
+#endif
+
+#ifndef _U16_
+typedef unsigned short U16;     /* unsigned 16-bit integer */
+#define _U16_
+#endif
+
+#ifndef _U32_
+typedef unsigned int   U32;     /* unsigned 32-bit integer */
+#define _U32_
+#endif
+
+#ifndef _U64_
+#ifdef __x86_64__
+typedef unsigned long  U64;     /* unsigned 64-bit integer */
+#else
+typedef unsigned long long  U64;     /* unsigned 64-bit integer */
+#endif
+#define _U64_
+#endif
+
+#ifndef _V8_
+typedef volatile unsigned char  V8;
+#define _V8_
+#endif
+
+#ifndef _V16_
+typedef volatile unsigned short V16;
+#define _V16_
+#endif
+
+#ifndef _V32_
+typedef volatile unsigned int  V32;
+#define _V32_
+#endif
+
+#ifndef _S8_
+typedef signed char  S8;         /* 8-bit  signed integer */
+#define _S8_
+#endif
+
+#ifndef _S16_
+typedef signed short S16;       /* 16-bit signed integer */
+#define _S16_
+#endif
+
+#ifndef _S32_
+typedef signed int   S32;        /* 32-bit signed integer */
+#define _S32_
+#endif
+
+#ifndef _S64_
+#ifdef __x86_64__
+typedef signed long  S64;          /* unsigned 64-bit integer */
+#else
+typedef signed long long  S64;     /* unsigned 64-bit integer */
+#endif
+#define _S64_
+#endif
+
+#ifndef _PVOID_
+#define _PVOID_
+typedef void *PVOID;
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#endif
+
+#define CONV_ENDIAN_32(v) ((v & 0xff) << 24 | (v >> 24) | ((v & 0xff00) << 8) | ((v & 0xff0000) >> 8));
+
+
+#ifndef MAX
+#define MAX(x, y) ((x) >= (y) ? (x) : (y))
+#endif
+
+#ifndef MIN
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+typedef void (*VOIDCB)(void);
+
+typedef void (*CALLBACK)(PVOID);
+
+#define BCTRL_LEN_MASK      0x0000FFFF
+#define BCTRL_BLAST_MASK    0x00010000
+
+#define DMAFCTRL_IRQEN      0x00000001
+#define DMAFCTRL_INBOFF     0x0000FFF0
+
+#define ARRAY_COUNT(v)      (sizeof(v)/sizeof(v[0]))
+
+#ifndef ROUND
+#define ROUND(x, dx)  (((x) + ((dx) - 1) ) & ~((dx) - 1))
+#endif
+
+#define REG32CLR(addr, clr_mask) ( REG32(addr) = REG32(addr) & (~(clr_mask)) )
+#define REG32SET(addr, set_mask) ( REG32(addr) = REG32(addr) | (set_mask) )
+#define REG32UPD(addr, clr_mask, set_mask) ( REG32(addr) = (REG32(addr) & (~(clr_mask))) | (set_mask) )
+
+// Standard function return types
+#ifndef _RESULTCODE_
+#define _RESULTCODE_
+typedef unsigned int RESULTCODE;
+#endif
+
+typedef unsigned int RETURNVALUE;
+
+#define SUCCESS                     0
+#define FAILURE                     1
+#define EXPIRED                     2       // Not an error - wait operation expired
+#define RESTART                     3       // Not an error - indicate we need to restart process
+
+//#define _DEBUG_
+
+#endif /*__SYSTYPES_H__ */
+
diff --git a/wls_lib/wls.h b/wls_lib/wls.h
new file mode 100644
index 0000000..d711a62
--- /dev/null
+++ b/wls_lib/wls.h
@@ -0,0 +1,352 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 __WLS_H__
+#define __WLS_H__
+
+#ifdef __KERNEL__
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#define MODNAME (KBUILD_MODNAME)
+#else /* __KERNEL__ */
+#include <sys/ioctl.h>
+#include <stdint.h>
+
+#ifdef DPDK_WLS
+#include <semaphore.h>
+#include <rte_common.h>
+#include <rte_atomic.h>
+#include <rte_memzone.h>
+#endif /* DPDK_WLS */
+
+#endif
+#include "ttypes.h"
+#include "syslib.h"
+
+#define WLS_PRINT(format, args...) printk(format, ##args)
+#define WLS_ERROR(format, args...) printk(KERN_ERR "wls err: " format,##args)
+
+#ifdef _DEBUG_
+#define WLS_DEBUG(format, args...)            \
+do {                                          \
+    printk(KERN_INFO "wls debug: " format,##args); \
+}while(0)
+#else /*_DEBUG_*/
+#define WLS_DEBUG(format, args...) do { } while(0)
+#endif /*_DEBUG_*/
+
+/******************************************************************************
+*                        Module error codes                                   *
+******************************************************************************/
+#define WLS_RC_MDMA_ID_ERROR                (-1)
+#define WLS_RC_MDMA_TASK_ERROR              (-2)
+#define WLS_RC_ALLOC_DELAY_MEM_ERROR        (-3)
+#define WLS_RC_ALLOC_BAR_MEM_ERROR          (-4)
+#define WLS_RC_ALLOC_TAR_MEM_ERROR          (-5)
+#define WLS_RC_PARAM_SIZE_ERROR             (-6)
+#define WLS_RC_WLS_HEAP_ALLOC_ERROR         (-7)
+#define WLS_RC_IRQ_ALLOC_ERROR              (-8)
+#define WLS_RC_DMA_ALLOC_ERROR              (-9)
+#define WLS_RC_TRANSACTION_ERROR            (-10)
+#define WLS_RC_PHY_CTX_ERROR                (-11)
+#define WLS_RC_KERNEL_HEAP_ALLOC_ERROR      (-12)
+#define WLS_RC_CONFIGURATION_ERROR          (-13)
+#define WLS_RC_THREAD_CREATION_ERROR        (-14)
+
+#define WLS_IOC_MAGIC 'W'
+#define WLS_IOC_OPEN         _IOWR(WLS_IOC_MAGIC, WLS_IOC_OPEN_NO,         uint64_t)
+#define WLS_IOC_CLOSE        _IOWR(WLS_IOC_MAGIC, WLS_IOC_CLOSE_NO,        uint64_t)
+#define WLS_IOC_PUT          _IOWR(WLS_IOC_MAGIC, WLS_IOC_PUT_NO,          uint64_t)
+#define WLS_IOC_EVENT        _IOWR(WLS_IOC_MAGIC, WLS_IOC_EVENT_NO,        uint64_t)
+#define WLS_IOC_WAIT         _IOWR(WLS_IOC_MAGIC, WLS_IOC_WAIT_NO,         uint64_t)
+#define WLS_IOC_WAKE_UP      _IOWR(WLS_IOC_MAGIC, WLS_IOC_WAKE_UP_NO,      uint64_t)
+#define WLS_IOC_CONNECT      _IOWR(WLS_IOC_MAGIC, WLS_IOC_CONNECT_NO,      uint64_t)
+#define WLS_IOC_FILL         _IOWR(WLS_IOC_MAGIC, WLS_IOC_FILL_NO,         uint64_t)
+
+enum {
+    WLS_IOC_OPEN_NO = 1,
+    WLS_IOC_CLOSE_NO,
+    WLS_IOC_PUT_NO,
+    WLS_IOC_EVENT_NO,
+    WLS_IOC_WAIT_NO,
+    WLS_IOC_WAKE_UP_NO,
+    WLS_IOC_CONNECT_NO,
+    WLS_IOC_FILL_NO,
+    WLS_IOC_COUNT,
+};
+
+enum {
+    WLS_FILL_PUSH = 1,
+    WLS_FILL_PULL,
+    WLS_FILL_MAX,
+};
+
+
+#define WLS_US_CLIENTS_MAX 64
+
+#define CACHE_LINE_SIZE 64                  /**< Cache line size. */
+#define CACHE_LINE_MASK (CACHE_LINE_SIZE-1) /**< Cache line mask. */
+
+#define CACHE_LINE_ROUNDUP(size) \
+    (CACHE_LINE_SIZE * ((size + CACHE_LINE_SIZE - 1) / CACHE_LINE_SIZE))
+
+#define DMA_ALIGNMENT_SIZE     256L
+
+// To make DMA we make sure that block starts on 256 bytes boundary
+#define DMA_ALIGNMENT_ROUNDUP(size) \
+    (DMA_ALIGNMENT_SIZE * ((size + DMA_ALIGNMENT_SIZE - 1) / DMA_ALIGNMENT_SIZE))
+
+/**< Return the first cache-aligned value greater or equal to size. */
+
+/**
+ * Force alignment to cache line.
+ */
+#define __wls_cache_aligned __attribute__((__aligned__(CACHE_LINE_SIZE)))
+
+#define WLS_HUGE_DEF_PAGE_SIZE                0x40000000LL
+#define WLS_IS_ONE_HUGE_PAGE(ptr, size, hp_size)  ((((unsigned long long)ptr & (~(hp_size - 1)))\
+        == (((unsigned long long)ptr + size - 1) & (~(hp_size - 1)))) ? 1 : 0)
+
+typedef struct hugepage_tabl_s
+{
+    union {
+        void     *pageVa;
+        uint64_t padding_pageVa;
+    };
+    uint64_t pagePa;
+}hugepage_tabl_t;
+
+#define DMA_MAP_MAX_BLOCK_SIZE 64*1024
+#define MAX_N_HUGE_PAGES 512
+#define UL_FREE_BLOCK_QUEUE_SIZE  384
+
+#define WLS_GET_QUEUE_N_ELEMENTS  384
+#define WLS_PUT_QUEUE_N_ELEMENTS  384
+
+#ifdef DPDK_WLS
+#define WLS_DEV_SHM_NAME_LEN      RTE_MEMZONE_NAMESIZE
+#else
+#define WLS_DEV_SHM_NAME_LEN          256
+#endif
+
+#define FIFO_LEN 384
+
+typedef struct wls_wait_req_s {
+    uint64_t wls_us_kernel_va;
+    uint64_t start_time;
+    uint64_t ctx;
+    uint64_t action;
+    uint64_t nMsg;
+}wls_wait_req_t;
+
+#ifdef DPDK_WLS
+typedef struct wls_sema_priv_s
+{
+    sem_t                     sem;
+    rte_atomic16_t            is_irq;
+    wls_wait_req_t            drv_block[FIFO_LEN];
+    volatile unsigned int     drv_block_put;
+    volatile unsigned int     drv_block_get;
+} wls_sema_priv_t;
+
+typedef struct wls_us_priv_s
+{
+    wls_sema_priv_t   sema;
+    U8                NeedToWakeUp;
+    U8                isWait;
+    volatile V32      pid;
+} wls_us_priv_t;
+
+#endif
+
+typedef struct wls_us_ctx_s
+{
+    union {
+       void *      wls_us_user_space_va;
+       uint64_t    padding_wls_us_user_space_va;
+    };
+
+    uint64_t       wls_us_kernel_va;
+
+    uint64_t       wls_us_pa;
+
+    uint32_t       wls_us_ctx_size;
+    uint32_t       HugePageSize;
+
+    union {
+        void*      alloc_buffer;
+        uint64_t   padding_alloc_buffer;
+    };
+
+    hugepage_tabl_t    hugepageTbl [MAX_N_HUGE_PAGES];
+
+    FASTQUEUE          ul_free_block_pq;
+    uint64_t           ul_free_block_storage[UL_FREE_BLOCK_QUEUE_SIZE * sizeof(uint64_t)];
+
+    WLS_MSG_QUEUE  get_queue;
+    WLS_MSG_HANDLE get_storage[WLS_GET_QUEUE_N_ELEMENTS];
+
+    WLS_MSG_QUEUE  put_queue;
+    WLS_MSG_HANDLE put_storage[WLS_PUT_QUEUE_N_ELEMENTS];
+
+    uint64_t           freePtrList[UL_FREE_BLOCK_QUEUE_SIZE * sizeof(uint64_t)];
+    uint32_t	       freeListIndex;
+    uint32_t         dualMode;
+
+    // dst userspace context address (kernel va)
+    uint64_t    dst_kernel_va;
+    // dst userspace context address (local user sapce va)
+
+    volatile uint64_t    dst_user_va;
+    // dst userspace context address (local user sapce va)
+    volatile uint64_t    dst_pa;
+
+    uint32_t  alloc_size;
+#ifdef DPDK_WLS
+    wls_us_priv_t wls_us_private;
+#else
+    HANDLE wls_us_private;
+#endif
+    uint32_t  mode;
+    uint32_t  secmode;
+    char wls_dev_name[WLS_DEV_SHM_NAME_LEN];
+    char wls_shm_name[WLS_DEV_SHM_NAME_LEN];
+}wls_us_ctx_t;
+
+
+
+typedef struct wls_fill_req_s {
+    uint64_t wls_us_kernel_va;
+    uint64_t ctx;
+    uint64_t action;
+    uint64_t nMsg;
+}wls_fill_req_t;
+
+typedef struct wls_connect_req_s {
+    uint64_t wls_us_kernel_va;
+}wls_connect_req_t;
+
+#ifdef __KERNEL__
+
+typedef struct wls_sema_priv_s
+{
+    wait_queue_head_t         queue;
+    atomic_t                  is_irq;
+    wls_wait_req_t            drv_block[FIFO_LEN];
+    volatile unsigned int     drv_block_put;
+    volatile unsigned int     drv_block_get;
+}wls_sema_priv_t;
+
+typedef struct wls_drv_ctx_s
+{
+    uint32_t            init_mask;
+    uint32_t            us_ctx_cout;
+    wls_us_ctx_t*       p_wls_us_ctx[WLS_US_CLIENTS_MAX];
+    wls_us_ctx_t*       p_wls_us_pa_ctx[WLS_US_CLIENTS_MAX];
+    uint32_t            nWlsClients;
+}wls_drv_ctx_t;
+
+#elif defined DPDK_WLS
+
+typedef struct wls_drv_ctx_s
+{
+    uint32_t            init_mask;
+    uint32_t            us_ctx_cout;
+    wls_us_ctx_t        p_wls_us_ctx[WLS_US_CLIENTS_MAX];
+    wls_us_ctx_t        p_wls_us_pa_ctx[WLS_US_CLIENTS_MAX];
+    uint32_t            nWlsClients;
+    pthread_mutex_t mng_mutex;
+}wls_drv_ctx_t;
+
+#endif
+typedef struct wls_open_req_s {
+    uint64_t  ctx;
+    uint64_t  ctx_pa;
+    uint32_t  size;
+}wls_open_req_t;
+
+typedef struct wls_close_req_s {
+    uint64_t  ctx;
+    uint64_t  ctx_pa;
+    uint32_t  size;
+}wls_close_req_t;
+
+typedef enum wls_events_num_s {
+    WLS_EVENT_IA_READY = 0,
+    WLS_EVENT_IA_STOP,
+    WLS_EVENT_IA_ERROR,
+    WLS_EVENT_MAX
+}wls_events_num_t;
+
+typedef struct wls_event_req_s {
+    uint64_t wls_us_kernel_va;
+    uint64_t event_to_wls;
+    uint64_t event_param;
+}wls_event_req_t;
+
+typedef struct wls_put_req_s {
+    uint64_t wls_us_kernel_va;
+}wls_put_req_t;
+
+typedef struct wls_wake_up_req_s {
+    uint64_t wls_us_kernel_va;
+    uint32_t id;
+    uint64_t ctx;
+}wls_wake_up_req_t;
+
+
+#define SYS_CPU_CLOCK              (2300000000L)
+#define CLOCK_PER_MS               (SYS_CPU_CLOCK/1000)
+#define CLOCK_PER_US               (SYS_CPU_CLOCK/1000000)
+
+static inline uint64_t
+wls_rdtsc(void)
+{
+    union {
+        uint64_t tsc_64;
+        struct {
+            uint32_t lo_32;
+            uint32_t hi_32;
+        };
+    } tsc;
+
+    asm volatile("rdtsc" :
+             "=a" (tsc.lo_32),
+             "=d" (tsc.hi_32));
+    return tsc.tsc_64;
+}
+
+static inline uint64_t rdtsc_ticks_diff(unsigned long curr, unsigned long prev)
+{
+    if (curr >= prev)
+        return (unsigned long)(curr - prev);
+    else
+        return (unsigned long)(0xFFFFFFFFFFFFFFFF - prev + curr);
+}
+
+void  wls_show_data(void* ptr, unsigned int size);
+void *wls_get_sh_ctx(void);
+void *wls_get_sh_ctx_pa(void);
+
+#endif /* __WLS_H__*/
+
diff --git a/wls_lib/wls_debug.h b/wls_lib/wls_debug.h
new file mode 100644
index 0000000..98cb4e6
--- /dev/null
+++ b/wls_lib/wls_debug.h
@@ -0,0 +1,38 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 __WLS_DEBUG_H__
+#define __WLS_DEBUG_H__
+
+#include <linux/types.h>
+
+/* mlog specific defenitions */
+
+#define PID_WLS_DRV_IOC_WAIT_WAKE_UP           77000
+#define PID_WLS_DRV_IOC_WAIT_WAKE_ENTRY        77001
+#define PID_WLS_DRV_IOC_PUT                    77002
+#define PID_WLS_DRV_ISR                        77003
+#define PID_WLS_DRV_IOC_FILL                   77004
+
+#define MLOG_VAR_MSG_BLOCK                     0xDEAD7000
+#define MLOG_VAR_MSG_TIME_SYNC                 0xDEAD7001
+#define MLOG_VAR_MSG_COPY_THREAD                   0xDEAD7002
+
+
+#endif /* __WLS_DEBUG_H__*/
+
diff --git a/wls_lib/wls_drv.c b/wls_lib/wls_drv.c
new file mode 100644
index 0000000..8b7543c
--- /dev/null
+++ b/wls_lib/wls_drv.c
@@ -0,0 +1,856 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/dma-mapping.h>
+#include <linux/kthread.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <asm/uaccess.h>
+
+#include "wls.h"
+#include "wls_drv.h"
+#include "wls_debug.h"
+
+#if defined(_MLOG_TRACE_)
+#include "mlog.h"
+#endif
+
+#define WLS_VERSION_X           0
+#define WLS_VERSION_Y           0
+#define WLS_VERSION_Z           3
+#define WLS_VERSION_RESERVED    0
+#define WLS_DRV_VERSION        ((WLS_VERSION_X << 24) | (WLS_VERSION_Y << 16) | (WLS_VERSION_Z << 8) | WLS_VERSION_RESERVED)
+
+#define WLS_DRV_VERSION_FORMAT "%d.%d.%d"
+#define WLS_DEV_DEVICE_FORMAT "wls%d"
+
+
+#define WLS_SEMA_COUNT 32
+#define WLS_MAX_CLIENTS 8
+
+
+typedef struct wls_us_priv_s
+{
+    wls_sema_priv_t   sema;
+
+    U8                NeedToWakeUp;
+    U8                isWait;
+
+    U32               pid;
+} wls_us_priv_t;
+
+char wls_driver_name[] = "wls";
+char wls_driver_version[10];
+char wls_dev_device_name[10];
+
+static long wls_ioctl(struct file * filp, unsigned int cmd, unsigned long arg);
+static int  wls_open(struct inode * inode, struct file * filp);
+static int  wls_release(struct inode * inode, struct file * filp);
+static int  wls_mmap(struct file * filp, struct vm_area_struct * vma);
+static int  wls_wait(wls_sema_priv_t *priv, unsigned long arg);
+static int  wls_wake_up_user_thread(char *buf, wls_sema_priv_t *semap);
+
+static struct file_operations wls_fops = {
+    .owner = THIS_MODULE,
+    .open = wls_open,
+    .release = wls_release,
+    .unlocked_ioctl = wls_ioctl,
+    .compat_ioctl = wls_ioctl,
+    .mmap = wls_mmap,
+};
+
+static struct wls_dev_t*  wls_dev[WLS_MAX_CLIENTS];
+static wls_drv_ctx_t    wls_drv_ctx[WLS_MAX_CLIENTS];
+
+static struct class * wls_class;
+
+/**********************************************************************
+*                      Module Parameters                              *
+**********************************************************************/
+int     wlsMaxClients               = 1;
+
+
+/**********************************************************************/
+module_param(wlsMaxClients,             int, S_IRUSR);
+
+/**********************************************************************/
+
+static wls_drv_ctx_t * wls_get_ctx(unsigned int id)
+{
+    if(id < WLS_MAX_CLIENTS)
+        return &wls_drv_ctx[id];
+    else
+        return NULL;
+}
+
+int wls_wake_up_user_thread(char *buf, wls_sema_priv_t *semap)
+{
+    if (likely(atomic_read(&semap->is_irq) < FIFO_LEN)) {
+        unsigned int put = semap->drv_block_put + 1;
+        if (put >= FIFO_LEN)
+            put = 0;
+        //copy data from user
+        memcpy(&semap->drv_block[put], buf, sizeof(wls_wait_req_t));
+
+        semap->drv_block_put = put;
+        atomic_inc(&semap->is_irq);
+#ifdef DEBUG
+        printk(KERN_INFO "[wls]:PUT: put=%d get=%d T=%lu is_irq=%d\n",
+               semap->drv_block_put, semap->drv_block_get,
+               semap->drv_block[put].start_time , atomic_read(&semap->is_irq));
+#endif /* DEBUG */
+        wake_up_interruptible(&semap->queue);
+    }
+
+    return 0;
+}
+
+void wls_show_data(void* ptr, unsigned int size)
+{
+    unsigned char *d = ptr;
+    int i;
+
+    for(i = 0; i < size; i++)
+    {
+        if ( !(i & 0xf) )
+            printk("\n");
+        printk("%02x ", d[i]);
+    }
+    printk("\n");
+}
+
+static int wls_open(struct inode * inode, struct file * filp)
+{
+    if((MINOR(inode->i_rdev ) >= 0) && (MINOR(inode->i_rdev) < wlsMaxClients) && (MINOR(inode->i_rdev ) < WLS_MAX_CLIENTS)){
+        filp->private_data = (void *)wls_dev[MINOR(inode->i_rdev)];
+        WLS_DEBUG("wls_open [%d] priv: 0x%p",MINOR(inode->i_rdev), filp->private_data);
+        WLS_DEBUG("wls_open PID [%d] ", current->pid);
+    } else {
+        WLS_ERROR("wls_open PID [%d] incorrect  inode->i_rdev %d", current->pid, MINOR(inode->i_rdev));
+    }
+
+    return 0;
+}
+
+static int wls_release(struct inode * inode, struct file * filp)
+{
+    struct wls_dev_t* wls_loc = NULL;
+    wls_us_ctx_t*     pUsCtx   = NULL;
+    wls_us_priv_t*    pUs_priv = NULL;
+    wls_drv_ctx_t*    pDrv_ctx = NULL;
+    int i = 0;
+
+    WLS_DEBUG("priv: 0x%p", filp->private_data);
+    WLS_DEBUG("wls_release PID [%d] ",current->pid);
+
+    if((MINOR(inode->i_rdev ) >= 0) && (MINOR(inode->i_rdev) < wlsMaxClients) && (MINOR(inode->i_rdev ) < WLS_MAX_CLIENTS)){
+        if (filp->private_data != NULL) {
+            wls_loc = (struct wls_dev_t*)filp->private_data;
+            if((void *)wls_dev[MINOR(inode->i_rdev)] == (void *)wls_loc){
+                pDrv_ctx = (wls_drv_ctx_t*)wls_loc->pWlsDrvCtx;
+                if(pDrv_ctx){
+                    for(i = 0; i < 2; i++ ){
+                        pUsCtx =  (wls_us_ctx_t*)pDrv_ctx->p_wls_us_ctx[i];
+                        if(pUsCtx){
+                            wls_us_ctx_t* dst = (wls_us_ctx_t*)pUsCtx->dst_kernel_va;
+                            wls_wait_req_t drv_block;
+                            if(dst){
+                                wls_us_priv_t* pDstPriv = (wls_us_priv_t*)dst->wls_us_private;
+                                if(pDstPriv){
+                                    drv_block.start_time = wls_rdtsc();
+                                    pDstPriv->NeedToWakeUp = 1;
+                                    wls_wake_up_user_thread((char *)&drv_block, &pDstPriv->sema);
+                                }
+                            }
+                            //un-link ctx
+                            pDrv_ctx->p_wls_us_ctx[i]->dst_kernel_va = (uint64_t)0;
+                            pDrv_ctx->p_wls_us_ctx[i]->dst_user_va = (uint64_t)0;
+                            pDrv_ctx->p_wls_us_ctx[i]->dst_pa = (uint64_t)0;
+                        }
+                    }
+
+                    for(i = 0; i < 2; i++ ){
+                        pUsCtx =  (wls_us_ctx_t*)pDrv_ctx->p_wls_us_ctx[i];
+
+                        if(pUsCtx){
+                            pUs_priv = (wls_us_priv_t*)pUsCtx->wls_us_private;
+                            if(pUs_priv){
+                                if(pUs_priv->pid == current->pid){
+                                    if (pUs_priv->isWait == 0){
+                                            pUsCtx->wls_us_private = NULL;
+                                            kfree(pUs_priv);
+                                            pDrv_ctx->p_wls_us_ctx[i]    = NULL;
+                                            dma_free_coherent(wls_loc->device, sizeof(wls_us_ctx_t),(void*)pUsCtx, (long)pDrv_ctx->p_wls_us_pa_ctx[i]);
+                                            pDrv_ctx->p_wls_us_pa_ctx[i] = NULL;
+                                            pDrv_ctx->nWlsClients--;
+                                    } else {
+                                        WLS_PRINT("Wait is in process\n");
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+            filp->private_data = NULL;
+        }
+    } else {
+        WLS_ERROR("wls_release PID [%d] incorrect  inode->i_rdev %d", current->pid, MINOR(inode->i_rdev));
+    }
+
+    return 0;
+}
+
+static int wls_wait(wls_sema_priv_t *priv, unsigned long arg)
+{
+    char __user *buf = (char __user *)arg;
+
+    if (!likely(atomic_read(&priv->is_irq))) {
+        if (unlikely(wait_event_interruptible(priv->queue, atomic_read(&priv->is_irq)))) {
+            return -ERESTARTSYS;
+        }
+    }
+
+    atomic_dec(&priv->is_irq);
+
+    if (priv->drv_block_put != priv->drv_block_get) {
+        unsigned int get = priv->drv_block_get + 1;
+
+        if (get >= FIFO_LEN)
+            get = 0;
+
+        if (copy_to_user(buf, &priv->drv_block[get], sizeof(wls_wait_req_t))) {
+            return -EFAULT;
+        }
+
+        priv->drv_block_get = get;
+
+#ifdef DEBUG
+        printk(KERN_INFO "[wls]:GET: put=%d get=%d T=%lu is_irq=%d\n",
+               priv->drv_block_put, priv->drv_block_get,
+               priv->drv_block[get].start_time, atomic_read(&priv->is_irq));
+#endif /* DEBUG */
+
+    } else {
+#ifdef DEBUG
+        printk(KERN_ERR "[wls]: wrong computation of queueing\n");
+#endif /* DEBUG */
+    }
+
+    return 0;
+}
+
+static unsigned wls_is_us_opened(wls_open_req_t *param)
+{
+    // TODO: add check
+
+        return 0;
+}
+
+wls_us_ctx_t *wls_create_us_ctx(wls_open_req_t *param, struct wls_dev_t * wls_loc)
+{
+    wls_us_ctx_t*     pUsCtx = NULL;
+    wls_drv_ctx_t*    pDrv_ctx =  wls_loc->pWlsDrvCtx;
+
+    // check if instance already registered
+    if(wls_is_us_opened(param))
+        goto err0;
+
+    // allocate memory for shared portion
+    pUsCtx = (wls_us_ctx_t*)dma_alloc_coherent(NULL, param->size, (dma_addr_t *)&param->ctx_pa, GFP_KERNEL);
+    WLS_DEBUG("wls_create_us_ctx: pUsCtx  0x%016lx\n", (unsigned long)pUsCtx);
+    if (pUsCtx){
+        // allocate memory for private
+        wls_us_priv_t *pUs_priv = kmalloc(sizeof(wls_us_priv_t), GFP_KERNEL);
+
+        if(pUs_priv == NULL)
+            goto err1;
+        // init shared
+        memset (pUsCtx, 0, sizeof(wls_us_ctx_t));
+
+        SFL_DefQueue(&pUsCtx->ul_free_block_pq, pUsCtx->ul_free_block_storage, UL_FREE_BLOCK_QUEUE_SIZE * sizeof(void*));
+        WLS_PRINT("ul free: off %lx\n",((U64) &pUsCtx->ul_free_block_pq -(U64)pUsCtx));
+
+        WLS_MsgDefineQueue(&pUsCtx->get_queue, pUsCtx->get_storage, WLS_GET_QUEUE_N_ELEMENTS, 0);
+        WLS_PRINT("get_queue: off %lx\n",((U64) &pUsCtx->get_queue -(U64)pUsCtx));
+
+        WLS_MsgDefineQueue(&pUsCtx->put_queue, pUsCtx->put_storage, WLS_PUT_QUEUE_N_ELEMENTS, 0);
+        WLS_PRINT("put_queue: off %lx\n",((U64) &pUsCtx->put_queue -(U64)pUsCtx));
+
+        // init private
+        memset (pUs_priv, 0, sizeof(wls_us_priv_t));
+        init_waitqueue_head(&pUs_priv->sema.queue);
+        atomic_set(&pUs_priv->sema.is_irq, 0);
+
+        pUs_priv->pid = current->pid;
+
+        pUsCtx->wls_us_private = pUs_priv;
+        WLS_DEBUG("wls_create_us_ctx: pUsCtx->wls_us_private 0x%016lx\n", (unsigned long)pUsCtx->wls_us_private);
+    } else
+        goto err0;
+
+    pDrv_ctx->p_wls_us_ctx[pDrv_ctx->nWlsClients]      = pUsCtx;
+    pDrv_ctx->p_wls_us_pa_ctx[pDrv_ctx->nWlsClients++] = (wls_us_ctx_t*)param->ctx_pa;
+
+    if(pDrv_ctx->p_wls_us_ctx[0] && pDrv_ctx->p_wls_us_ctx[1])
+    {
+        //link ctx
+        pDrv_ctx->p_wls_us_ctx[0]->dst_kernel_va = (uint64_t)pDrv_ctx->p_wls_us_ctx[1];
+        pDrv_ctx->p_wls_us_ctx[0]->dst_pa        = (uint64_t) pDrv_ctx->p_wls_us_pa_ctx[1];
+
+        pDrv_ctx->p_wls_us_ctx[1]->dst_kernel_va = (uint64_t)pDrv_ctx->p_wls_us_ctx[0];
+        pDrv_ctx->p_wls_us_ctx[1]->dst_pa        = (uint64_t) pDrv_ctx->p_wls_us_pa_ctx[0];
+
+        pDrv_ctx->p_wls_us_ctx[0]->dst_kernel_va = (uint64_t)pDrv_ctx->p_wls_us_ctx[1];
+        pDrv_ctx->p_wls_us_ctx[0]->dst_pa        = (uint64_t) pDrv_ctx->p_wls_us_pa_ctx[1];
+        pDrv_ctx->p_wls_us_ctx[1]->dst_kernel_va = (uint64_t)pDrv_ctx->p_wls_us_ctx[0];
+        pDrv_ctx->p_wls_us_ctx[1]->dst_pa        = (uint64_t) pDrv_ctx->p_wls_us_pa_ctx[0];
+
+        WLS_DEBUG("link: 0 <-> 1: 0: 0x%016lx 1: 0x%016lx\n", (long unsigned int)pDrv_ctx->p_wls_us_ctx[0]->dst_kernel_va,
+                                                              (long unsigned int)pDrv_ctx->p_wls_us_ctx[1]->dst_kernel_va);
+    }
+
+    return pUsCtx;
+
+//err2:
+    kfree(pUsCtx->wls_us_private);
+err1:
+    dma_free_coherent(wls_loc->device, param->size, pUsCtx, param->ctx_pa);
+err0:
+    return NULL;
+}
+
+
+int wls_destroy_us_ctx(wls_close_req_t *param,     struct wls_dev_t * wls_prv)
+{
+    wls_us_ctx_t*  pUsCtx   = NULL;
+    wls_us_priv_t* pUs_priv = NULL;
+
+    wls_drv_ctx_t*    pDrv_ctx =  wls_prv->pWlsDrvCtx;
+
+    if(pDrv_ctx->p_wls_us_ctx[0] && pDrv_ctx->p_wls_us_ctx[1])
+    {
+        //link ctx
+        pDrv_ctx->p_wls_us_ctx[0]->dst_kernel_va = (uint64_t)0;
+        pDrv_ctx->p_wls_us_ctx[1]->dst_kernel_va = (uint64_t)0;
+        pDrv_ctx->p_wls_us_ctx[0]->dst_user_va = (uint64_t)0;
+        pDrv_ctx->p_wls_us_ctx[1]->dst_user_va = (uint64_t)0;
+        pDrv_ctx->p_wls_us_ctx[0]->dst_pa = (uint64_t)0;
+        pDrv_ctx->p_wls_us_ctx[1]->dst_pa = (uint64_t)0;
+
+        WLS_DEBUG("un-link: 0 <-> 1: 0: 0x%016lx 1: 0x%016lx\n", (long unsigned int)pDrv_ctx->p_wls_us_ctx[0]->dst_kernel_va,
+                                                                 (long unsigned int)pDrv_ctx->p_wls_us_ctx[1]->dst_kernel_va);
+    }
+
+    pUsCtx =  (wls_us_ctx_t*)param->ctx;
+
+    if(pUsCtx){
+        pUs_priv = (wls_us_priv_t*)pUsCtx->wls_us_private;
+        if(pUs_priv){
+            if (pUs_priv->isWait == 0){
+
+                pUsCtx->wls_us_private = NULL;
+                kfree(pUs_priv);
+                if(param->ctx_pa){
+                    if( pDrv_ctx->p_wls_us_ctx[0] == pUsCtx){
+                        pDrv_ctx->p_wls_us_ctx[0]    = NULL;
+                        pDrv_ctx->p_wls_us_pa_ctx[0] = NULL;
+                    } else {
+                        pDrv_ctx->p_wls_us_ctx[1]    = NULL;
+                        pDrv_ctx->p_wls_us_pa_ctx[1] = NULL;
+                    }
+                    pDrv_ctx->nWlsClients--;
+                    dma_free_coherent(wls_prv->device, param->size, pUsCtx, param->ctx_pa);
+                }else{
+                    WLS_ERROR("param->ctx_pa is NULL\n");
+                }
+            } else
+                WLS_PRINT("Wait is in process\n");
+        }
+    }
+
+    return 0;
+}
+
+static int wls_process_wait(wls_us_ctx_t* pUsCtx)
+{
+    int n = WLS_GetNumItemsInTheQueue(&pUsCtx->get_queue);
+
+    return n;
+}
+
+static int wls_process_put(wls_us_ctx_t *src, wls_us_ctx_t *dst)
+{
+    int ret = 0;
+    WLS_MSG_HANDLE hMsg;
+    int n = 0;
+
+    wls_us_priv_t* pDstPriv    =  NULL;
+    wls_wait_req_t drv_block;
+
+    WLS_DEBUG("offset get_queue %lx\n",(U64)&src->get_queue - (U64)src);
+
+    n = WLS_GetNumItemsInTheQueue(&src->put_queue);
+
+    while(n--)
+    {
+        if (WLS_MsgDequeue(&src->put_queue, &hMsg, NULL, (void*)src))
+        {
+           WLS_DEBUG("WLS_Get %lx %d type %d\n",(U64) hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID);
+           if(WLS_MsgEnqueue(&dst->get_queue, hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID, hMsg.flags, NULL,  (void*)dst) == FALSE){ // try to send
+               if(WLS_MsgEnqueue(&src->put_queue, hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID, hMsg.flags, NULL, (void*)src) == FALSE){ // return back
+                   WLS_ERROR("wls_process_put: Cannot return block to back to queue \n");
+                   ret = -1;
+               }
+               break;
+           }
+        }
+        else{
+            ret = -1;
+            break;
+        }
+
+    }
+
+    if(dst->wls_us_private){
+        pDstPriv = (wls_us_priv_t*)dst->wls_us_private;
+
+        drv_block.start_time = wls_rdtsc();
+        pDstPriv->NeedToWakeUp = 1;
+        wls_wake_up_user_thread((char *)&drv_block, &pDstPriv->sema);
+    }
+    else
+        ret = -1;
+
+    return ret;
+}
+
+static long wls_ioctl(struct file * filp, unsigned int cmd, unsigned long arg)
+{
+    struct wls_dev_t * wls_prv = (struct wls_dev_t *)filp->private_data;
+    void __user * to = (void __user *)arg;
+    const void __user * from = (const void __user *)arg;
+    long ret = 0;
+
+    WLS_DEBUG("wls_ioctl PID [%d] ", current->pid);
+
+    if (_IOC_TYPE(cmd) != WLS_IOC_MAGIC) {
+        return -ENOTTY;
+    }
+
+    if (_IOC_NR(cmd) >= WLS_IOC_COUNT) {
+        return -ENOTTY;
+    }
+
+    switch (cmd) {
+        case WLS_IOC_OPEN: {
+            wls_open_req_t param;
+
+            WLS_DEBUG("WLS_IOC_OPEN wls_us_ctx_t %ld\n", sizeof(wls_us_ctx_t));
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes from user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+
+            if (sizeof(wls_drv_ctx_t) >= param.size){
+                WLS_ERROR("incorrect size %lu > %u\n", sizeof(wls_drv_ctx_t), param.size);
+                ret = -1;
+                break;
+            }
+
+            param.ctx = (uint64_t)wls_create_us_ctx(&param, wls_prv);
+            if (param.ctx == 0) {
+                WLS_ERROR("could not copy %lu bytes to  user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+
+            WLS_DEBUG("WLS_IOC_OPEN: kva %lx pa %lx sz [%d]\n", (long unsigned int)param.ctx, (long unsigned int)param.ctx_pa, param.size);
+
+            ret = copy_to_user(to, (const void *)&param, sizeof(wls_open_req_t));
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes to  user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+        } break;
+        case WLS_IOC_CLOSE: {
+            wls_close_req_t param;
+
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes from user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+            WLS_DEBUG("WLS_IOC_CLOSE: kva %lx pa %lx sz [%d]\n", (long unsigned int)param.ctx,  (long unsigned int)param.ctx_pa, param.size);
+
+            ret = wls_destroy_us_ctx(&param, wls_prv);
+
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes from user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+        } break;
+        case WLS_IOC_PUT: {
+            wls_put_req_t param;
+            wls_us_ctx_t*  pUsCtx   = NULL;
+
+#if defined(_MLOG_TRACE_)
+            unsigned long t = MLOG_GETTICK();
+#endif
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes from user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+
+            pUsCtx = (wls_us_ctx_t*)param.wls_us_kernel_va;
+            if (pUsCtx == NULL) {
+                WLS_ERROR("Transaction failed %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            if(pUsCtx->dst_kernel_va)
+                ret = wls_process_put(pUsCtx, (wls_us_ctx_t*)pUsCtx->dst_kernel_va);
+
+            if (ret != 0) {
+                WLS_ERROR("Transaction failed %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            /* clean up for next time */
+#if defined(_MLOG_TRACE_)
+            MLogTask(PID_WLS_DRV_IOC_PUT, t, MLOG_GETTICK());
+#endif
+        } break;
+        case WLS_IOC_EVENT: {
+            wls_event_req_t param;
+
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+
+            if (ret != 0) {
+                WLS_ERROR("Event %ld failed %ld\n", (unsigned long)param.event_to_wls,
+                            (unsigned long)ret);
+                break;
+            }
+        }break;
+        case WLS_IOC_WAIT: {
+            wls_wait_req_t param;
+            wls_us_ctx_t*  pUsCtx  =  NULL;
+            wls_us_priv_t* pUsPriv =  NULL;
+#if defined(_MLOG_TRACE_)
+            unsigned long t = MLOG_GETTICK();
+            MLogTask(PID_WLS_DRV_IOC_WAIT_WAKE_ENTRY, t, 1250+t);
+#endif
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+            if (ret != 0) {
+                WLS_ERROR("Wait failed %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            WLS_DEBUG("Wait pUsCtx 0x%016lx\n", (unsigned long)param.wls_us_kernel_va);
+            pUsCtx  = (wls_us_ctx_t*)  param.wls_us_kernel_va;
+            if(pUsCtx == NULL) {
+                ret = -EINVAL;
+                WLS_ERROR("Wait failed on User context %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            pUsPriv = (wls_us_priv_t*) pUsCtx->wls_us_private;
+            WLS_DEBUG("Wait pUsPriv 0x%016lx\n", (unsigned long)pUsPriv);
+
+            if(pUsPriv == NULL) {
+                ret = -EINVAL;
+                WLS_ERROR("Wait failed %ld\n", (unsigned long)ret);
+                break;
+            }
+            pUsPriv->isWait = 1;
+            wls_wait(&pUsPriv->sema, (unsigned long)from);
+            pUsPriv->isWait = 0;
+            memset(&param, 0, sizeof(wls_wait_req_t));
+            param.nMsg = wls_process_wait(pUsCtx);
+
+#if defined(_MLOG_TRACE_)
+            t = MLOG_GETTICK();
+#endif
+            ret = copy_to_user(to, (const void *)&param, sizeof(wls_wait_req_t));
+            if (ret != 0) {
+                WLS_ERROR("could not copy %lu bytes to  user 0x%08lx",
+                            (unsigned long)ret, (unsigned long)from);
+                break;
+            }
+#if defined(_MLOG_TRACE_)
+            MLogTask(PID_WLS_DRV_IOC_WAIT_WAKE_UP, t, MLOG_GETTICK());
+#endif
+        } break;
+        case WLS_IOC_WAKE_UP: {
+            wls_wait_req_t param;
+            wls_us_ctx_t*  pUsCtx  =  NULL;
+            wls_us_priv_t* pUsPriv =  NULL;
+            wls_wait_req_t drv_block;
+
+            ret = copy_from_user((void *)&param, from, sizeof(param));
+            if (ret != 0) {
+              WLS_ERROR("WLS_IOC_WAKE_UP failed %ld\n",
+                          (unsigned long)ret);
+              break;
+            }
+
+            WLS_DEBUG("Wait pUsCtx 0x%016lx\n", (unsigned long)param.wls_us_kernel_va);
+            pUsCtx  = (wls_us_ctx_t*)  param.wls_us_kernel_va;
+            if(pUsCtx == NULL) {
+                ret = -EINVAL;
+                WLS_ERROR("Wait failed on User context %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            pUsPriv = (wls_us_priv_t*) pUsCtx->wls_us_private;
+            WLS_DEBUG("Wait pUsPriv 0x%016lx\n", (unsigned long)pUsPriv);
+
+            if(pUsPriv == NULL) {
+                ret = -EINVAL;
+                WLS_ERROR("Wait failed %ld\n", (unsigned long)ret);
+                break;
+            }
+
+            drv_block.start_time = wls_rdtsc();
+            wls_wake_up_user_thread((char *)&drv_block, &pUsPriv->sema);
+        } break;
+        case WLS_IOC_CONNECT: {
+          wls_connect_req_t param;
+          wls_us_priv_t* pUsPriv    =  NULL;
+          wls_wait_req_t drv_block;
+
+          ret = copy_from_user((void *)&param, from, sizeof(param));
+          if (ret != 0) {
+              WLS_ERROR("WLS_IOC_WAKE_UP failed %ld\n",
+                          (unsigned long)ret);
+              break;
+          }
+
+          pUsPriv = (wls_us_priv_t*)param.wls_us_kernel_va;
+          drv_block.start_time = wls_rdtsc();
+          wls_wake_up_user_thread((char *)&drv_block, &pUsPriv->sema);
+        } break;
+        default:{
+            WLS_ERROR("unknown ioctl cmd: '0x%08x'", cmd);
+            BUG();
+        } break;
+    }
+
+    if (ret != 0) {
+        WLS_ERROR("cmd_%x failed: %ld", cmd, ret);
+    }
+
+    return ret;
+}
+
+static int wls_mmap(struct file * filp, struct vm_area_struct * vma)
+{
+    struct wls_dev_t * wls = (struct wls_dev_t *)filp->private_data;
+
+    WLS_DEBUG("priv: 0x%p", filp->private_data);
+    WLS_DEBUG("WLS_mmap : mmap function called \n");
+    WLS_DEBUG("vma->start =%lx\n",vma->vm_start);
+    WLS_DEBUG("vma->end =%lx\n",vma->vm_end);
+
+    // non cached
+//    vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+    WLS_DEBUG("vma->pgoff =%lx\n",vma->vm_pgoff);
+
+    if (wls == NULL) {
+        WLS_ERROR("WLS is NULL");
+        return -EIO;
+    }
+
+    return  remap_pfn_range(vma,vma->vm_start,vma->vm_pgoff,vma->vm_end-vma->vm_start,\
+                            vma->vm_page_prot);
+}
+
+static int __init wls_init(void)
+{
+    struct wls_dev_t* wls_dev_loc = NULL;
+    int res = 0;
+    dev_t dev_no = 0;
+    int minor_no = 0;
+    int dev_cnt = 0;
+
+    memset(&wls_dev[0], 0, sizeof(struct wls_dev_t*) * WLS_MAX_CLIENTS);
+
+    snprintf(wls_driver_version, 10, WLS_DRV_VERSION_FORMAT, WLS_VERSION_X, WLS_VERSION_Y, WLS_VERSION_Z);
+
+    WLS_PRINT("Intel(R) Wireless Subsystem Communication interface - %s\n", wls_driver_version);
+    WLS_PRINT("Copyright(c) 2014 Intel Corporation.\n");
+    //WLS_PRINT("Build: Date: %s Time: %s\n", __DATE__, __TIME__);
+
+    if ((wlsMaxClients > WLS_MAX_CLIENTS) || (wlsMaxClients < 1))
+    {
+        WLS_ERROR("Invalid wlsMaxClients %d\n", wlsMaxClients);
+        wlsMaxClients = 1;
+    }
+    for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+        wls_dev_loc  = (struct wls_dev_t *)kzalloc(sizeof(struct wls_dev_t), GFP_KERNEL);
+        WLS_DEBUG("wls_dev_loc %d %p", dev_cnt, wls_dev_loc);
+
+        if (wls_dev_loc == NULL) {
+            WLS_ERROR("no free memory (wanted %ld bytes)", sizeof(struct wls_dev_t));
+            res = -ENOMEM;
+            goto err0;
+        }
+        wls_dev[dev_cnt] = wls_dev_loc;
+        WLS_DEBUG("wls_init [%d]: 0x%p",dev_cnt, wls_dev[dev_cnt]);
+    }
+
+    res = alloc_chrdev_region(&dev_no, minor_no, wlsMaxClients, MODNAME);
+    if (res < 0) {
+        WLS_ERROR("failed alloc char dev region: %d", res);
+        goto err0;
+    }
+
+    wls_class = class_create(THIS_MODULE, wls_driver_name);
+
+    wls_dev_loc =  wls_dev[0];
+    wls_dev_loc->dev_no = dev_no;
+
+    cdev_init(&wls_dev_loc->cdev, &wls_fops);
+    wls_dev_loc->cdev.owner = THIS_MODULE;
+    wls_dev_loc->cdev.ops = &wls_fops;
+    res = cdev_add(&wls_dev_loc->cdev, dev_no, wlsMaxClients);
+
+    if (res) {
+        WLS_ERROR("failed add char dev: %d", res);
+        res = -1;
+        goto err2;
+    }
+
+    if (IS_ERR((void *)wls_class)) {
+        WLS_ERROR("failed create class");
+        res = -EIO;
+        goto err1;
+    }
+
+    for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+        wls_dev_loc  = wls_dev[dev_cnt];
+        if (wls_dev_loc == NULL ) {
+            WLS_ERROR("wls_dev_loc is NULL");
+            goto err2;
+        }
+
+        if(wlsMaxClients > 1){
+            snprintf(wls_dev_device_name, 10, WLS_DEV_DEVICE_FORMAT, dev_cnt);
+        } else {
+            snprintf(wls_dev_device_name, 10, "%s", MODNAME);
+        }
+
+        wls_dev_loc->dev_no = MKDEV(MAJOR(dev_no), dev_cnt);
+        wls_dev_loc->device = device_create(wls_class, NULL, wls_dev_loc->dev_no, NULL, wls_dev_device_name);
+
+        if (IS_ERR((void *)wls_dev_loc->device)) {
+            WLS_ERROR("failed create / device");
+            res = -2;
+            goto err2;
+        }
+
+        dev_info(wls_dev_loc->device, "Device: %s\n", wls_dev_device_name);
+        mutex_init(&wls_dev_loc->lock);
+
+        wls_dev_loc->pWlsDrvCtx = wls_get_ctx(dev_cnt);
+
+        if (wls_dev_loc->pWlsDrvCtx ==  NULL) {
+            WLS_ERROR("failed wls_get_ctx(%d)", dev_cnt);
+            res = -3;
+            goto err2;
+        }
+
+        //return res;
+//        dev_no++;
+        continue;
+    }
+    WLS_PRINT("init %d /dev/wlsX communication devices [0-%d]\n", dev_cnt, dev_cnt-1);
+    return res;
+
+    err2:
+         for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+            wls_dev_loc  = wls_dev[dev_cnt];
+            if(wls_dev_loc){
+                device_destroy(wls_class, wls_dev_loc->dev_no);
+                cdev_del(&wls_dev_loc->cdev);
+            }
+         }
+        class_destroy(wls_class);
+    err1:
+        unregister_chrdev_region(wls_dev[0]->dev_no, wlsMaxClients);
+    err0:
+        for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+            if(wls_dev[dev_cnt]){
+                kfree(wls_dev[dev_cnt]);
+                wls_dev[dev_cnt] = NULL;
+            }
+        }
+
+        WLS_ERROR("init failed");
+        return -1;
+}
+
+static void __exit wls_exit(void)
+{
+    struct wls_dev_t* wls_dev_loc = NULL;
+    int dev_cnt = 0;
+
+    if (wls_dev[0]) {
+        for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+            wls_dev_loc  = wls_dev[dev_cnt];
+            device_destroy(wls_class, wls_dev_loc->dev_no);
+        }
+        wls_dev_loc =  wls_dev[0];
+
+        cdev_del(&wls_dev_loc->cdev);
+        class_destroy(wls_class);
+
+        unregister_chrdev_region(wls_dev[0]->dev_no, wlsMaxClients);
+
+        for (dev_cnt = 0; dev_cnt < wlsMaxClients; dev_cnt++){
+            if(wls_dev[dev_cnt]){
+                kfree(wls_dev[dev_cnt]);
+                wls_dev[dev_cnt] = NULL;
+            }
+        }
+    }
+
+    WLS_PRINT("Intel(R) Wireless Subsystem Communication interface - %s was removed\n", wls_driver_version);
+}
+
+MODULE_DESCRIPTION("Wirelsess Sybsytem Communication interface");
+MODULE_AUTHOR("Intel Corporation");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("WLS_DRV_VERSION_FORMAT");
+
+module_init(wls_init);
+module_exit(wls_exit);
diff --git a/wls_lib/wls_drv.h b/wls_lib/wls_drv.h
new file mode 100644
index 0000000..24af067
--- /dev/null
+++ b/wls_lib/wls_drv.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 __WLS_DRV_H__
+#define __WLS_DRV_H__
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/ioctl.h>
+#include <linux/cdev.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include "ttypes.h"
+
+struct wls_dev_t {
+    struct mutex lock;
+    dev_t dev_no;
+    struct class * class;
+    struct device * device;
+    struct cdev cdev;
+    wait_queue_head_t queue;
+    atomic_t qwake;
+    struct task_struct * thread;
+    wls_drv_ctx_t* pWlsDrvCtx;
+};
+
+#endif /* __WLS_DRC_H__*/
+
diff --git a/wls_lib/wls_lib.c b/wls_lib/wls_lib.c
new file mode 100644
index 0000000..83c82ff
--- /dev/null
+++ b/wls_lib/wls_lib.c
@@ -0,0 +1,836 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "ttypes.h"
+#include "wls_lib.h"
+#include "wls.h"
+#include "syslib.h"
+
+#define WLS_MAP_SHM 1
+
+#define WLS_PHY_SHM_FILE_NAME "/tmp/phyappshm"
+
+#define HUGE_PAGE_FILE_NAME "/mnt/huge/page"
+
+#define DIV_ROUND_OFFSET(X,Y) ( X/Y + ((X%Y)?1:0) )
+
+#define WLS_LIB_USER_SPACE_CTX_SIZE DMA_MAP_MAX_BLOCK_SIZE
+
+#define PLIB_ERR(x, args...)   printf("wls_lib: "x, ## args);
+#define PLIB_INFO(x, args...)  printf("wls_lib: "x, ## args);
+
+#ifdef _DEBUG_
+#define PLIB_DEBUG(x, args...)  printf("wls_lib debug: "x, ## args);
+#else
+#define PLIB_DEBUG(x, args...)  do { } while(0)
+#endif
+
+#ifdef __x86_64__
+#define WLS_LIB_MMAP mmap
+#else
+#define WLS_LIB_MMAP mmap64
+#endif
+
+extern int gethugepagesizes(long pagesizes[], int n_elem);
+extern int hugetlbfs_unlinked_fd(void);
+
+
+static pthread_mutex_t wls_put_lock;
+static pthread_mutex_t wls_get_lock;
+
+static int           wls_dev_fd =  0;
+static wls_us_ctx_t* wls_us_ctx =  NULL;
+
+static uint64_t wls_kernel_va_to_user_va(void *pWls_us, uint64_t ptr);
+
+int ipc_file = 0;
+
+static int wls_VirtToPhys(void* virtAddr, uint64_t* physAddr)
+{
+    int          mapFd;
+    uint64_t     page;
+    unsigned int pageSize;
+    unsigned long virtualPageNumber;
+
+    mapFd = open ("/proc/self/pagemap" , O_RDONLY );
+    if (mapFd < 0 )
+    {
+        PLIB_ERR("Could't open pagemap file\n");
+        return -1;
+    }
+
+    /*get standard page size*/
+    pageSize = getpagesize();
+
+    virtualPageNumber = (unsigned long) virtAddr / pageSize ;
+
+    lseek(mapFd , virtualPageNumber * sizeof(uint64_t) , SEEK_SET );
+
+    if(read(mapFd ,&page , sizeof(uint64_t)) < 0 )
+    {
+        close(mapFd);
+        PLIB_ERR("Could't read pagemap file\n");
+        return -1;
+    }
+
+    *physAddr = (( page & 0x007fffffffffffffULL ) * pageSize );
+
+    close(mapFd);
+
+    return 0;
+}
+
+static void wls_mutex_destroy(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_destroy(pMutex);
+}
+
+static void wls_mutex_init(pthread_mutex_t* pMutex)
+{
+   pthread_mutexattr_t prior;
+   pthread_mutexattr_init(&prior);
+   pthread_mutexattr_setprotocol(&prior, PTHREAD_PRIO_INHERIT);
+   pthread_mutex_init(pMutex, &prior);
+   pthread_mutexattr_destroy(&prior);
+}
+
+static void wls_mutex_lock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_lock(pMutex);
+}
+
+static void wls_mutex_unlock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_unlock(pMutex);
+}
+
+static uint64_t wls_kernel_va_to_user_va(void *pWls_us, uint64_t ptr)
+{
+    unsigned long ret = 0;
+    wls_us_ctx_t* pUs = (wls_us_ctx_t*)pWls_us;
+
+    uint64_t kva = (uint64_t) pUs->wls_us_kernel_va;
+    uint64_t uva = (uint64_t) pUs->wls_us_user_space_va;
+
+    ret = (uva + (ptr - kva));
+
+    PLIB_DEBUG("kva %lx to uva %lx [offset %d]\n",kva, ret, (kva - ret));
+    return ret;
+}
+
+static uint64_t wls_kernel_va_to_user_va_dest(void *pWls_us, uint64_t ptr)
+{
+    unsigned long ret = 0;
+    wls_us_ctx_t* pUs = (wls_us_ctx_t*)pWls_us;
+
+    uint64_t kva = (uint64_t) pUs->dst_kernel_va;
+    uint64_t uva = (uint64_t) pUs->dst_user_va;
+
+    ret = (uva + (ptr - kva));
+
+    PLIB_DEBUG("kva %lx to uva %lx [offset %d]\n",kva, ret, (kva - ret));
+    return ret;
+}
+
+
+void* WLS_Open(const char *ifacename, unsigned int mode, unsigned long long nWlsMemorySize)
+{
+    wls_us_ctx_t*  pWls_us = NULL;
+    unsigned int   ret = 0;
+    wls_open_req_t params;
+    int i, len;
+    char temp[WLS_DEV_SHM_NAME_LEN];
+
+#ifdef __x86_64__
+    params.ctx  = 64L;
+#else
+    params.ctx  = 32L;
+#endif
+
+    params.ctx_pa = 0;
+    params.size   = WLS_LIB_USER_SPACE_CTX_SIZE;
+
+    if(sizeof(wls_us_ctx_t) >= 64*1024){
+        PLIB_ERR("WLS_Open %ld \n", sizeof(wls_us_ctx_t));
+        return NULL;
+    }
+
+    if (!wls_us_ctx) {
+        PLIB_INFO("Open %s 0x%08lx\n", ifacename, WLS_IOC_OPEN);
+
+        if ((wls_dev_fd = open(ifacename, O_RDWR | O_SYNC)) < 0){
+            PLIB_ERR("Open filed [%d]\n", wls_dev_fd);
+            return NULL;
+        }
+        /* allocate block in shared space */
+        if((ret = ioctl(wls_dev_fd, WLS_IOC_OPEN, &params)) < 0) {
+            PLIB_ERR("Open filed [%d]\n", ret);
+            return NULL;
+        }
+
+        PLIB_DEBUG("params: kernel va 0x%016llx pa 0x%016llx size %ld\n",
+           params.ctx, params.ctx_pa, params.size);
+
+        if (params.ctx_pa) {
+            /* remap to user space the same block */
+            pWls_us = (wls_us_ctx_t*) WLS_LIB_MMAP(NULL,
+                                      params.size,
+                                      PROT_READ|PROT_WRITE ,
+                                      MAP_SHARED,
+                                      wls_dev_fd,
+                                      params.ctx_pa);
+
+            if( pWls_us == MAP_FAILED ){
+                PLIB_ERR("mmap has failed (%d:%s) 0x%016lx [size %d]\n", errno, strerror(errno),params.ctx_pa, params.size);
+                return NULL;
+            }
+
+            PLIB_DEBUG("Local: pWls_us 0x%016p\n", pWls_us);
+
+            PLIB_DEBUG("size wls_us_ctx_t %d\n", sizeof(wls_us_ctx_t));
+            PLIB_DEBUG("    ul free  : off 0x%016lx\n",((unsigned long) &pWls_us->ul_free_block_pq -(unsigned long)pWls_us));
+            PLIB_DEBUG("    get_queue: off 0x%016lx\n",((unsigned long) &pWls_us->get_queue -(unsigned long)pWls_us));
+            PLIB_DEBUG("    put_queue: off 0x%016lx\n",((unsigned long) &pWls_us->put_queue -(unsigned long)pWls_us));
+
+            //memset(pWls_us, 0, params.size);
+
+            pWls_us->padding_wls_us_user_space_va = 0LL;
+
+            pWls_us->wls_us_user_space_va = pWls_us;
+
+            pWls_us->wls_us_kernel_va = (uint64_t) params.ctx;
+            pWls_us->wls_us_pa        = (uint64_t) params.ctx_pa;
+            pWls_us->wls_us_ctx_size  =  params.size;
+
+            PLIB_INFO("User Space Lib Context: us va 0x%016lx kernel va 0x%016lx pa 0x%016lx size %d \n",
+                (uintptr_t)pWls_us->wls_us_user_space_va,
+                pWls_us->wls_us_kernel_va,
+                pWls_us->wls_us_pa,
+                pWls_us->wls_us_ctx_size);
+
+            wls_mutex_init(&wls_put_lock);
+            wls_mutex_init(&wls_get_lock);
+
+            pWls_us->mode = mode;
+            PLIB_INFO("\nMode %d\n", pWls_us->mode);
+
+            PLIB_INFO("\nWLS device %s [%d]\n", ifacename, (int)strlen(ifacename));
+            strncpy(temp, ifacename, WLS_DEV_SHM_NAME_LEN - 1);
+            len = strlen(ifacename);
+            if (len < WLS_DEV_SHM_NAME_LEN - 1)
+                strncpy(pWls_us->wls_dev_name, temp, len);
+            else
+                strncpy(pWls_us->wls_dev_name, temp, WLS_DEV_SHM_NAME_LEN - 1);
+            for(i = 0; i < MIN(strlen(pWls_us->wls_dev_name),WLS_DEV_SHM_NAME_LEN); i++)
+                if(pWls_us->wls_dev_name[i] != '/')
+                    pWls_us->wls_shm_name[i] = pWls_us->wls_dev_name[i];
+                else
+                    pWls_us->wls_shm_name[i] = '_';
+
+            wls_us_ctx = pWls_us;
+        }
+        else {
+            PLIB_ERR("Open filed: incorrect allocation \n");
+            return NULL;
+        }
+    }
+
+    return wls_us_ctx;
+}
+
+int WLS_Ready(void* h)
+{
+    int ret = 0;
+    wls_event_req_t params;
+
+    if (!wls_us_ctx || !wls_dev_fd){
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    params.event_to_wls =  WLS_EVENT_IA_READY;
+    params.event_param = 0;
+
+    /* free block in shared space */
+    if((ret = ioctl(wls_dev_fd, WLS_IOC_EVENT, &params)) < 0) {
+        PLIB_ERR("Event filed [%d]\n", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+int WLS_Close(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*)h;
+    wls_close_req_t params;
+    int ret = 0;
+
+    if (!wls_us_ctx || !wls_dev_fd){
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    if ((unsigned long)pWls_us !=  (unsigned long )wls_us_ctx){
+        PLIB_ERR("Incorret handle %lx [expected %lx]\n", (unsigned long)pWls_us, (unsigned long )wls_us_ctx);
+        return -1;
+    }
+
+    params.ctx    = pWls_us->wls_us_kernel_va;
+    params.ctx_pa = pWls_us->wls_us_pa;
+    params.size   = pWls_us->wls_us_ctx_size;
+
+    /* free block in shared space */
+    if((ret = ioctl(wls_dev_fd, WLS_IOC_CLOSE, &params)) < 0) {
+        PLIB_ERR("Close filed [%d]\n", ret);
+        return 0;
+    }
+
+    /* unmap to user space */
+    munmap(pWls_us, pWls_us->wls_us_ctx_size);
+
+    wls_mutex_destroy(&wls_put_lock);
+    wls_mutex_destroy(&wls_get_lock);
+
+    close(wls_dev_fd);
+
+    wls_us_ctx = NULL;
+    wls_dev_fd = 0;
+
+    return 0;
+}
+
+
+void* WLS_Alloc(void* h, unsigned int size)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    long          pageSize[1];
+    long          hugePageSize;
+    long          nHugePage;
+
+    hugepage_tabl_t*  pHugePageTlb = &pWls_us->hugepageTbl[0];
+
+    void*  pvirtAddr = NULL;
+    int    count;
+    int    fd;
+
+    char shm_file_name[256];
+
+    fd = hugetlbfs_unlinked_fd();
+
+    if (fd < 0)
+        PLIB_ERR("Unable to open temp file in hugetlbfs (%s)", strerror(errno));
+
+    gethugepagesizes(pageSize,1);
+    hugePageSize = pageSize[0];
+
+    PLIB_INFO("hugePageSize on the system is %ld\n", hugePageSize);
+
+    /* calculate total number of hugepages */
+    nHugePage =  DIV_ROUND_OFFSET(size, hugePageSize);
+
+    if (nHugePage >= MAX_N_HUGE_PAGES){
+        PLIB_INFO("not enough hugepages: need %ld  system has %d\n", nHugePage,  MAX_N_HUGE_PAGES);
+        return NULL;
+    }
+
+    if(pHugePageTlb == NULL )
+    {
+        PLIB_INFO("Table memory allocation failed\n");
+        return NULL;
+    }
+
+#if WLS_MAP_SHM
+{
+    snprintf(shm_file_name, WLS_DEV_SHM_NAME_LEN, "%s_%s", WLS_PHY_SHM_FILE_NAME, pWls_us->wls_shm_name);
+    PLIB_INFO("shm open %s\n", shm_file_name);
+    ipc_file = open(shm_file_name, O_CREAT); // | O_EXCL  maybe sometimes in future.. ;-)
+    if(ipc_file == -1){
+        PLIB_ERR("open  failed (%s)\n", strerror(errno) );
+        return NULL;
+    }
+
+    key_t  key = ftok(shm_file_name, '4');
+    int shm_handle = shmget(key, size, SHM_HUGETLB|SHM_R|SHM_W);
+    if(shm_handle == -1){
+         PLIB_INFO("Create shared memory\n");
+         shm_handle = shmget(key, size, SHM_HUGETLB | IPC_CREAT | SHM_R | SHM_W);
+    }
+    else
+        PLIB_INFO("Attach to shared memory\n");
+
+    if(shm_handle == -1){
+        PLIB_ERR("shmget has failed (%s) [size %ld]\n", strerror(errno), nHugePage * hugePageSize);
+        return NULL;
+    }
+
+    pvirtAddr = shmat(shm_handle, 0, /*SHM_RND*/0);
+}
+#else
+    /* Allocate required number of pages */
+    pvirtAddr = mmap(0,(nHugePage * hugePageSize), (PROT_READ|PROT_WRITE), MAP_SHARED, fd,0);
+#endif
+    if(pvirtAddr == MAP_FAILED )
+    {
+        PLIB_ERR("mmap has failed (%s) [size %ld]\n", strerror(errno), nHugePage * hugePageSize);
+        return NULL;
+    }
+
+    PLIB_INFO("pvirtAddr  0x%016lx\n", (unsigned long)pvirtAddr);
+
+    for(count = 0 ; count < nHugePage ; count++ )
+    {
+        /*Incremented virtual address to next hugepage to create table*/
+        pHugePageTlb[count].pageVa =  ((unsigned char*)pvirtAddr + \
+                                                ( count * hugePageSize ));
+        /*Creating dummy page fault in process for each page
+                                                inorder to get pagemap*/
+        *(unsigned char*)pHugePageTlb[count].pageVa = 1;
+
+        if(wls_VirtToPhys((uint64_t*) pHugePageTlb[count].pageVa,
+                    &pHugePageTlb[count].pagePa ) == -1)
+        {
+            munmap(pvirtAddr, (nHugePage * hugePageSize));
+            PLIB_ERR("Virtual to physical conversion failed\n");
+            return NULL;
+        }
+
+        //PLIB_INFO("id %d va 0x%016p pa 0x%016llx [%ld]\n", count, (uintptr_t)pHugePageTlb[count].pageVa, (uint64_t) pHugePageTlb[count].pagePa, hugePageSize);
+    }
+
+    PLIB_INFO("WLS_Alloc: 0x%016lx [%d]\n", (unsigned long)pvirtAddr, size);
+
+    close(fd);
+
+    pWls_us->HugePageSize = (uint32_t)hugePageSize;
+    pWls_us->alloc_buffer = pvirtAddr;
+    pWls_us->alloc_size   = (uint32_t)(nHugePage * hugePageSize);
+
+    if (pWls_us->mode == WLS_MASTER_CLIENT){
+        wls_us_ctx_t* pWls_usRem = NULL;
+        PLIB_INFO("Connecting to remote peer ...\n");
+        while (pWls_us->dst_pa == 0) // wait for slave
+                       ;
+
+        /* remap to user space the same block */
+        pWls_usRem = (wls_us_ctx_t*) WLS_LIB_MMAP(NULL,
+                                  sizeof(wls_us_ctx_t),
+                                  PROT_READ|PROT_WRITE ,
+                                  MAP_SHARED,
+                                  wls_dev_fd,
+                                  pWls_us->dst_pa);
+
+        if( pWls_us == MAP_FAILED ){
+            PLIB_ERR("mmap has failed (%d:%s) 0x%016lx \n", errno, strerror(errno),pWls_us->dst_pa);
+            return NULL;
+        }
+
+        PLIB_INFO("Remote: pWls_us 0x%p\n", pWls_usRem);
+
+        PLIB_INFO("size wls_us_ctx_t %ld\n", sizeof(wls_us_ctx_t));
+        PLIB_INFO("    ul free  : off 0x%016lx\n",((unsigned long) &pWls_usRem->ul_free_block_pq -(unsigned long)pWls_usRem));
+        PLIB_INFO("    get_queue: off 0x%016lx\n",((unsigned long) &pWls_usRem->get_queue -(unsigned long)pWls_usRem));
+        PLIB_INFO("    put_queue: off 0x%016lx\n",((unsigned long) &pWls_usRem->put_queue -(unsigned long)pWls_usRem));
+
+        pWls_us->dst_user_va = (uint64_t) pWls_usRem  ;
+    }
+
+
+    return pvirtAddr;
+}
+
+int WLS_Free(void* h, PVOID pMsg)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    if ((unsigned long)pMsg != (unsigned long)pWls_us->alloc_buffer) {
+        PLIB_ERR("incorrect pMsg %lx [expected %lx]\n", (unsigned long)pMsg ,(unsigned long)pWls_us->alloc_buffer);
+        return -1;
+    }
+
+    if (pWls_us->mode == WLS_MASTER_CLIENT){
+        if(pWls_us->dst_user_va){
+            munmap((void*)pWls_us->dst_user_va, sizeof(wls_us_ctx_t));
+            pWls_us->dst_user_va = 0;
+        }
+    }
+
+    PLIB_DEBUG("WLS_Free 0x%016lx", (unsigned long)pMsg);
+#if WLS_MAP_SHM
+    shmdt(pMsg);
+    close (ipc_file);
+#else
+    munmap(pMsg, pWls_us->alloc_size);
+#endif
+
+
+
+    return 0;
+}
+
+int WLS_Put(void* h, unsigned long long  pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+    int ret = 0;
+
+    if ((unsigned long)h != (unsigned long)wls_us_ctx) {
+        PLIB_ERR("Incorrect user space context %lx [%lx]\n", (unsigned long)h, (unsigned long)wls_us_ctx);
+        return -1;
+    }
+
+    if(!WLS_IS_ONE_HUGE_PAGE(pMsg, MsgSize, WLS_HUGE_DEF_PAGE_SIZE)) {
+        PLIB_ERR("WLS_Put input error: buffer is crossing 2MB page boundary 0x%016llx size %ld\n", pMsg, (unsigned long)MsgSize);
+    }
+
+    wls_mutex_lock(&wls_put_lock);
+
+    if ((WLS_FLAGS_MASK & Flags)){ // multi block transaction
+        if (Flags & WLS_TF_SYN){
+            PLIB_DEBUG("WLS_SG_FIRST\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID, Flags,  wls_kernel_va_to_user_va, (void*)pWls_us))
+            {
+               PLIB_DEBUG("WLS_Get %lx %d type %d\n",(U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if ((Flags & WLS_TF_SCATTER_GATHER) && !(Flags & WLS_TF_SYN) && !(Flags & WLS_TF_FIN)){
+            PLIB_DEBUG("WLS_SG_NEXT\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID, Flags,  wls_kernel_va_to_user_va, (void*)pWls_us))
+            {
+               PLIB_DEBUG("WLS_Put %lx %d type %d\n",(U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if (Flags & WLS_TF_FIN) {
+            wls_put_req_t params;
+            PLIB_DEBUG("WLS_SG_LAST\n");
+            params.wls_us_kernel_va = pWls_us->wls_us_kernel_va;
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID, Flags,  wls_kernel_va_to_user_va, (void*)pWls_us))
+            {
+               PLIB_DEBUG("WLS_Put %lx %d type %d\n",(U64) pMsg, MsgSize, MsgTypeID);
+            }
+
+            PLIB_DEBUG("List: call WLS_IOC_PUT\n");
+            if((ret = ioctl(wls_dev_fd, WLS_IOC_PUT, &params)) < 0) {
+                PLIB_ERR("Put filed [%d]\n", ret);
+                wls_mutex_unlock(&wls_put_lock);
+                return -1;
+            }
+        } else
+            PLIB_ERR("unsaported flags %x\n", WLS_FLAGS_MASK & Flags);
+    } else {  // one block transaction
+        wls_put_req_t params;
+        params.wls_us_kernel_va = pWls_us->wls_us_kernel_va;
+        if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID, Flags,  wls_kernel_va_to_user_va, (void*)pWls_us))
+        {
+           PLIB_DEBUG("WLS_Put %lx %d type %d\n",(U64) pMsg, MsgSize, MsgTypeID);
+        }
+
+        PLIB_DEBUG("One block: call WLS_IOC_PUT\n");
+        if((ret = ioctl(wls_dev_fd, WLS_IOC_PUT, &params)) < 0) {
+            PLIB_ERR("Put filed [%d]\n", ret);
+            wls_mutex_unlock(&wls_put_lock);
+            return -1;
+        }
+    }
+    wls_mutex_unlock(&wls_put_lock);
+
+    return 0;
+}
+
+int WLS_Check(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    if ((unsigned long)h != (unsigned long)wls_us_ctx) {
+        PLIB_ERR("Incorrect user space context %lx [%lx]\n", (unsigned long)h, (unsigned long)wls_us_ctx);
+        return 0;
+    }
+
+    PLIB_DEBUG("offset get_queue %lx\n",(U64)&pWls_us->get_queue - (U64)pWls_us);
+
+    return WLS_GetNumItemsInTheQueue(&pWls_us->get_queue);
+}
+
+
+unsigned long long WLS_Get(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+    WLS_MSG_HANDLE hMsg;
+    uint64_t pMsg = NULL;
+
+    if ((unsigned long)h != (unsigned long)wls_us_ctx) {
+        PLIB_ERR("Incorrect user space context %lx [%lx]\n", (unsigned long)h, (unsigned long)wls_us_ctx);
+        return 0;
+    }
+
+    PLIB_DEBUG("offset get_queue %lx\n",(U64)&pWls_us->get_queue - (U64)pWls_us);
+    wls_mutex_lock(&wls_get_lock);
+
+    if (WLS_MsgDequeue(&pWls_us->get_queue, &hMsg, wls_kernel_va_to_user_va, (void*)pWls_us))
+    {
+       PLIB_DEBUG("WLS_Get %lx %d type %d\n",(U64) hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID);
+       pMsg         = hMsg.pIaPaMsg;
+       *MsgSize     = hMsg.MsgSize;
+       *MsgTypeID   = hMsg.TypeID;
+       *Flags       = hMsg.flags;
+    }
+
+    wls_mutex_unlock(&wls_get_lock);
+
+    return pMsg;
+}
+
+int   WLS_WakeUp(void* h)
+{
+    int ret;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+    wls_wake_up_req_t params;
+
+    if (!wls_us_ctx || !wls_dev_fd){
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    params.ctx              = (uint64_t)pWls_us;
+    params.wls_us_kernel_va = (uint64_t)pWls_us->wls_us_kernel_va;
+
+    PLIB_DEBUG("WLS_WakeUp\n");
+
+    if((ret = ioctl(wls_dev_fd, WLS_IOC_WAKE_UP, &params)) < 0) {
+        PLIB_ERR("Wake Up filed [%d]\n", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+int   WLS_Wait(void* h)
+{
+    int ret;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+    wls_wait_req_t params;
+
+    if (!wls_us_ctx || !wls_dev_fd){
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    params.ctx              = (uint64_t)pWls_us;
+    params.wls_us_kernel_va = (uint64_t)pWls_us->wls_us_kernel_va;
+    params.action           =  0;
+    params.nMsg             =  0;
+
+    PLIB_DEBUG("WLS_Wait\n");
+
+    if((ret = ioctl(wls_dev_fd, WLS_IOC_WAIT, &params)) < 0) {
+        PLIB_ERR("Wait filed [%d]\n", ret);
+        return ret;
+    }
+
+    return params.nMsg;
+}
+
+unsigned long long  WLS_WGet(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    uint64_t pRxMsg = WLS_Get(h, MsgSize, MsgTypeID, Flags);
+
+    if (pRxMsg)
+        return pRxMsg;
+
+    WLS_Wait(h);
+    return WLS_Get(h, MsgSize, MsgTypeID, Flags);
+}
+
+unsigned long long  WLS_VA2PA(void* h, PVOID pMsg)
+{
+    uint64_t      ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    unsigned long    alloc_base;
+    hugepage_tabl_t* pHugePageTlb;
+    uint64_t    hugePageBase;
+    uint64_t    hugePageOffet;
+    unsigned int     count = 0;
+
+    uint64_t    HugePageMask = ((unsigned long)pWls_us->HugePageSize - 1);
+
+    if(pWls_us->alloc_buffer == NULL){
+        PLIB_ERR("WLS_VA2PA: nothing was allocated [%ld]\n", ret);
+        return  (uint64_t)ret;
+    }
+
+    alloc_base     = (unsigned long)pWls_us->alloc_buffer;
+
+    pHugePageTlb   = &pWls_us->hugepageTbl[0];
+
+    hugePageBase   = (uint64_t)pMsg & ~HugePageMask;
+    hugePageOffet  = (uint64_t)pMsg & HugePageMask;
+
+    count          = (hugePageBase - alloc_base) / pWls_us->HugePageSize;
+
+    PLIB_DEBUG("WLS_VA2PA %lx base %llx off %llx  count %d\n", (unsigned long)pMsg,
+        (uint64_t)hugePageBase, (uint64_t)hugePageOffet, count);
+
+    ret = pHugePageTlb[count].pagePa + hugePageOffet;
+
+    return (uint64_t) ret;
+}
+
+void* WLS_PA2VA(void* h, unsigned long long  pMsg)
+{
+    unsigned long    ret = NULL;
+    wls_us_ctx_t*    pWls_us = (wls_us_ctx_t* )h;
+
+    hugepage_tabl_t* pHugePageTlb;
+    uint64_t         hugePageBase;
+    uint64_t         hugePageOffet;
+    unsigned int     count;
+    int              i;
+    uint64_t         HugePageMask = ((uint64_t)pWls_us->HugePageSize - 1);
+
+    if(pWls_us->alloc_buffer == NULL){
+        PLIB_ERR("WLS_PA2VA: nothing was allocated [%ld]\n", ret);
+        return  (void*)ret;
+    }
+
+    pHugePageTlb   = &pWls_us->hugepageTbl[0];
+
+    hugePageBase   = (uint64_t)pMsg & ~HugePageMask;
+    hugePageOffet  = (uint64_t)pMsg &  HugePageMask;
+
+    count          = pWls_us->alloc_size / pWls_us->HugePageSize;
+
+    PLIB_DEBUG("WLS_PA2VA %llx base %llx off %llx  count %d\n", (uint64_t)pMsg,
+        (uint64_t)hugePageBase, (uint64_t)hugePageOffet, count);
+
+    for (i = 0; i < count; i++) {
+        if (pHugePageTlb[i].pagePa == hugePageBase)
+        {
+            ret = (unsigned long)pHugePageTlb[i].pageVa;
+            ret += hugePageOffet;
+            return  (void*)ret;
+        }
+    }
+
+    return (void*) (ret);
+}
+
+int WLS_EnqueueBlock(void* h, unsigned long long pMsg)
+{
+    int ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    if (!wls_us_ctx || !wls_dev_fd){
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    if(pWls_us->mode == WLS_SLAVE_CLIENT){
+        PLIB_ERR("Slave doesn't support memory allocation\n");
+        return -1;
+    }
+
+    if(pMsg == 0){
+        PLIB_ERR("WLS_EnqueueBlock: Null\n");
+        return -1;
+    }
+
+    if(pWls_us->dst_kernel_va){
+        if (pWls_us->dst_user_va)
+        {
+            wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t* )pWls_us->dst_user_va;
+            ret = SFL_WlsEnqueue(&pDstWls_us->ul_free_block_pq, pMsg, wls_kernel_va_to_user_va_dest, pWls_us);
+            if(ret == 1){
+                unsigned long* ptr = (unsigned long*)WLS_PA2VA(pWls_us, pMsg);
+                if(ptr){
+                    *ptr = 0xFFFFFFFFFFFFFFFF;
+                }
+            }
+        }
+        else
+            ret = -1;
+    }
+    else
+        ret = -1;
+
+    PLIB_DEBUG("SFL_WlsEnqueue %d\n", ret);
+    return ret;
+}
+
+unsigned long long WLS_DequeueBlock(void* h)
+{
+    unsigned long long retval = NULL;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+
+    if(pWls_us->mode == WLS_SLAVE_CLIENT){
+        // local
+        retval = SFL_WlsDequeue(&pWls_us->ul_free_block_pq, wls_kernel_va_to_user_va, h );
+    } else if(pWls_us->dst_kernel_va) {
+        // remote
+        if (pWls_us->dst_user_va)
+        {
+            wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t* )pWls_us->dst_user_va;
+            retval = SFL_WlsDequeue(&pDstWls_us->ul_free_block_pq, wls_kernel_va_to_user_va_dest, pWls_us);
+            if(retval){
+                unsigned long* ptr = (unsigned long*)WLS_PA2VA(pWls_us, retval);
+                if(ptr){
+                    if(*ptr != 0xFFFFFFFFFFFFFFFF){
+                        PLIB_ERR("WLS_EnqueueBlock: incorrect content pa: 0x%016lx: 0x%016lx\n", (unsigned long)retval, *ptr);
+                    }
+                }
+            }
+        }
+    }
+
+    return retval;
+}
+
+int WLS_NumBlocks(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t* )h;
+    int n = 0;
+
+    if(pWls_us->mode == WLS_SLAVE_CLIENT){
+        // local
+        n = SFL_GetNumItemsInTheQueue(&pWls_us->ul_free_block_pq);
+    } else if(pWls_us->dst_kernel_va) {
+        // remote
+        if (pWls_us->dst_user_va)
+        {
+            wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t* )pWls_us->dst_user_va;
+            n = SFL_GetNumItemsInTheQueue(&pDstWls_us->ul_free_block_pq);
+        }
+    }
+
+    return n;
+}
+
+
diff --git a/wls_lib/wls_lib.h b/wls_lib/wls_lib.h
new file mode 100644
index 0000000..e38ea43
--- /dev/null
+++ b/wls_lib/wls_lib.h
@@ -0,0 +1,482 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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 _WLS_LIB_H_
+#define _WLS_LIB_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** WLS driver client operates as slave in terms of management of shared memory */
+#define WLS_SLAVE_CLIENT   0
+/** WLS driver client operates as master in terms of management of shared memory */
+#define WLS_MASTER_CLIENT  1
+/** WLS Open Dual Options */
+#define WLS_SEC_NA     0
+#define WLS_SEC_MASTER 1
+/** WLS Open Dual enabled */
+#define WLS_SINGLE_MODE 0
+#define WLS_DUAL_MODE   1
+
+
+/* definitions PUT/GET Flags */
+#define WLS_TF_SCATTER_GATHER  (1 << 15)
+#define WLS_TF_URLLC           (1 << 10)
+#define WLS_TF_SYN             (1 << 9)
+#define WLS_TF_FIN             (1 << 8)
+#define WLS_FLAGS_MASK         (0xFF00)
+
+/** First block in Scatter/Gather sequence of blocks */
+#define WLS_SG_FIRST               (WLS_TF_SCATTER_GATHER | WLS_TF_SYN)
+/** Next block in Scatter/Gather sequence of blocks */
+#define WLS_SG_NEXT                (WLS_TF_SCATTER_GATHER)
+/** Last block in Scatter/Gather sequence of blocks */
+#define WLS_SG_LAST                (WLS_TF_SCATTER_GATHER | WLS_TF_FIN)
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   ifacename - pointer to string with device driver name (/dev/wls)
+ *  @param[in]   modef     - mode of operation (Master or Slave)
+ *
+ *  @return  pointer to WLS handle
+ *
+ *  @description
+ *  Function opens the WLS interface and registers as instance in the kernel space driver.
+ *  Control section of shared memory is mapped to application memory.
+ *  pointer (handle) of WLS interface is returned for future use by WLS functions
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void* WLS_Open(const char *ifacename, unsigned int mode, unsigned long long nWlsMemorySize);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   ifacename - pointer to string with device driver name (/dev/wls)
+ *  @param[in]   modef     - mode of operation (Master or Slave)
+ *
+ *  @return  pointer to second WLS handle while first WLS_handle is returned to the argument handle1 location
+ *
+ *  @description
+ *  Function opens the WLS interface and registers two instances in the kernel space driver.
+ *  Control section of shared memory is mapped to application memory.
+ *  pointer (handle) of WLS interface is returned for future use by WLS functions
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void* WLS_Open_Dual(const char *ifacename, unsigned int mode, unsigned long long nWlsMemorySize, void** handle1);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h - handle of WLS interface to close
+ *
+ *  @return  0 - in case of success
+ *
+ *  @description
+ *  Function closes the WLS interface and deregisters as instance in the kernel space driver.
+ *  Control section of shared memory is unmapped form user space application
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Close(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h - handle of second WLS interface within same app to close
+ *
+ *  @return  0 - in case of success
+ *
+ *  @description
+ *  Function closes a second WLS interface open from a same process and deregisters as instance in the kernel space driver.
+ *  Control section of shared memory is unmapped form user space application
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Close1(void* h);
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h - handle of WLS interface to check status
+ *
+ *  @return  1 - in case of success
+ *
+ *  @description
+ *  Function checks state of remote peer of WLS interface and returns 1 if remote peer is available
+ *  (one to one connection is established)
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Ready(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h - handle of second WLS interface within the same app to check status
+ *
+ *  @return  1 - in case of success
+ *
+ *  @description
+ *  Function checks state of remote peer of WLS interface and returns 1 if remote peer is available
+ *  (one to one connection is established)
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Ready1(void* h);
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h    - handle of WLS interface
+ *  @param[in]   size - size of memory block to allocate
+ *
+ *  @return  void*    - pointer to allocated memory block or NULL if no memory available
+ *
+ *  @description
+ *  Function allocates memory block for data exchange shared memory. Memory block is backed
+ *  by huge pages.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+void* WLS_Alloc(void* h, unsigned int size);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   pMsg - pointer to WLS memory
+*
+*  @return  0 - if operation is successful
+*
+*  @description
+*  Function frees memory block for data exchange shared memory. Memory block is backed
+*  by huge pages
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Free(void* h, void* pMsg);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h    - handle of WLS interface
+ *  @param[in]   pMsg - pointer to memory block (physical address) with data to be transfered to remote peer.
+ *                      pointer should belong to WLS memory allocated via WLS_Alloc()
+ *  @param[in]   MsgSize - size of memory block to send (should be less than 2 MB)
+ *  @param[in]   MsgTypeID - application specific identifier of message type
+ *  @param[in]   Flags - Scatter/Gather flag if memory block has multiple chunks
+ *
+ *  @return  0 - if successful
+ *          -1 - if error
+ *
+ *  @description
+ *  Function puts memory block (or group of blocks) allocated from WLS memory into interface
+ *  for transfer to remote peer.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Put(void* h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h    - handle of second WLS interface within same app
+ *  @param[in]   pMsg - pointer to memory block (physical address) with data to be transfered to remote peer.
+ *                      pointer should belong to WLS memory allocated via WLS_Alloc()
+ *  @param[in]   MsgSize - size of memory block to send (should be less than 2 MB)
+ *  @param[in]   MsgTypeID - application specific identifier of message type
+ *  @param[in]   Flags - Scatter/Gather flag if memory block has multiple chunks
+ *
+ *  @return  0 - if successful
+ *          -1 - if error
+ *
+ *  @description
+ *  Function puts memory block (or group of blocks) allocated from WLS memory into interface
+ *  for transfer to remote peer.
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Put1(void* h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags);
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h    - handle of WLS interface
+ *
+ *  @return  number of blocks available
+ *
+ *  @description
+ *  Function checks if there are memory blocks with data from remote peer and returns number of blocks
+ *  available for "get" operation
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Check(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+ *
+ *  @param[in]   h    - handle of second WLS interface within same app
+ *
+ *  @return  number of blocks available
+ *
+ *  @description
+ *  Function checks if there are memory blocks with data from remote peer and returns number of blocks
+ *  available for "get" operation
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Check1(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   *MsgSize - pointer to set size of memory block
+*  @param[in]   *MsgTypeID - pointer to application specific identifier of message type
+*  @param[in]   *Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
+*
+*  @return  pointer to memory block (physical address) with data received from remote peer
+*           NULL -  if error
+*
+*  @description
+*  Function gets memory block from interface received from remote peer. Function is non-blocking
+*  operation and returns NULL if no blocks available
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_Get(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of second WLS interface within same app
+*  @param[in]   *MsgSize - pointer to set size of memory block
+*  @param[in]   *MsgTypeID - pointer to application specific identifier of message type
+*  @param[in]   *Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
+*
+*  @return  pointer to memory block (physical address) with data received from remote peer
+*           NULL -  if error
+*
+*  @description
+*  Function gets memory block from interface received from remote peer. Function is non-blocking
+*  operation and returns NULL if no blocks available
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_Get1(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*
+*  @return  number of blocks available for get
+*
+*  @description
+*  Function waits for new memory block from remote peer. Function is blocking call and returns number
+*  of blocks received.
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Wait(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle second of WLS interface within same app
+*
+*  @return  number of blocks available for get
+*
+*  @description
+*  Function waits for new memory block from remote peer. Function is blocking call and returns number
+*  of blocks received.
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_Wait1(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*
+*  @return  0 - if successful
+*
+*  @description
+*  Function performs "wakeup" notification to remote peer to unblock "wait" operations pending
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_WakeUp(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of second  WLS interface within same app
+*
+*  @return  0 - if successful
+*
+*  @description
+*  Function performs "wakeup" notification to remote peer to unblock "wait" operations pending
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_WakeUp1(void* h);
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   *MsgSize - pointer to set size of memory block
+*  @param[in]   *MsgTypeID - pointer to application specific identifier of message type
+*  @param[in]   *Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
+*
+*  @return  pointer to memory block (physical address) with data received from remote peer
+*           NULL -  if error
+*
+*  @description
+*  Function gets memory block from interface received from remote peer. Function is blocking
+*  operation and waits till next memory block from remote peer.
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_WGet(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of second WLS interface within the same app
+*  @param[in]   *MsgSize - pointer to set size of memory block
+*  @param[in]   *MsgTypeID - pointer to application specific identifier of message type
+*  @param[in]   *Flags - pointer to Scatter/Gather flag if memory block has multiple chunks
+*
+*  @return  pointer to memory block (physical address) with data received from remote peer
+*           NULL -  if error
+*
+*  @description
+*  Function gets memory block from interface received from remote peer. Function is blocking
+*  operation and waits till next memory block from remote peer.
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_WGet1(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   pMsg - virtual address of WLS memory block.
+*
+*  @return  physical address of WLS memory block
+*           NULL - if error
+*
+*  @description
+*  Function converts virtual address (VA) to physical address (PA)
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_VA2PA(void* h, void* pMsg);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   pMsg - physical address of WLS memory block.
+*
+*  @return  virtual address of WLS memory block
+*           NULL - if error
+*
+*  @description
+*  Function converts physical address (PA) to virtual address (VA)
+*
+**/
+//-------------------------------------------------------------------------------------------
+void* WLS_PA2VA(void* h, unsigned long long pMsg);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*  @param[in]   pMsg - physical address of WLS memory block.
+*
+*  @return  0 - if successful
+*          -1 - if error
+*
+*  @description
+*  Function is used by master to provide memory blocks to slave for next slave to master transfer
+*  of data.
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_EnqueueBlock(void* h, unsigned long long pMsg);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    -- handle of second WLS interface within the same app
+*  @param[in]   pMsg - physical address of WLS memory block.
+*
+*  @return  0 - if successful
+*          -1 - if error
+*
+*  @description
+*  Function is used by master to provide memory blocks to slave for next slave to master transfer
+*  of data.
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_EnqueueBlock1(void* h, unsigned long long pMsg);
+
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*
+*  @return  0   - pointer (physical address) of WLS memory block
+*          NULL - if error
+*
+*  @description
+*  Function is used by master and slave to get block from master to slave queue of available memory
+*  blocks.
+*
+**/
+//-------------------------------------------------------------------------------------------
+unsigned long long WLS_DequeueBlock(void* h);
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup wls_mod
+*
+*  @param[in]   h    - handle of WLS interface
+*
+*  @return  number of blocks in slave to master queue
+*
+*  @description
+*  Function returns number of current available block provided by master for new transfer
+*  of data from slave.
+*
+**/
+//-------------------------------------------------------------------------------------------
+int WLS_NumBlocks(void* h);
+
+#ifdef __cplusplus
+}
+#endif
+#endif //_WLS_LIB_H_
diff --git a/wls_lib/wls_lib_dpdk.c b/wls_lib/wls_lib_dpdk.c
new file mode 100644
index 0000000..f05b12c
--- /dev/null
+++ b/wls_lib/wls_lib_dpdk.c
@@ -0,0 +1,1343 @@
+/******************************************************************************
+*
+*   Copyright (c) 2019 Intel.
+*
+*   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.
+*
+*******************************************************************************/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <pthread.h>
+
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include <rte_eal.h>
+#include <rte_errno.h>
+#include <rte_lcore.h>
+#include <rte_memory.h>
+#include <rte_memzone.h>
+#include <rte_branch_prediction.h>
+
+#include "ttypes.h"
+#include "wls_lib.h"
+#include "wls.h"
+#include "syslib.h"
+
+#define WLS_MAP_SHM 1
+
+#define WLS_PHY_SHM_FILE_NAME "hp_"
+
+#define HUGE_PAGE_FILE_NAME "/mnt/huge/page"
+
+#define DIV_ROUND_OFFSET(X,Y) ( X/Y + ((X%Y)?1:0) )
+
+#define WLS_LIB_USER_SPACE_CTX_SIZE DMA_MAP_MAX_BLOCK_SIZE
+
+#define PLIB_ERR(x, args...)   printf("wls_lib: "x, ## args);
+#define PLIB_INFO(x, args...)  printf("wls_lib: "x, ## args);
+
+#ifdef _DEBUG_
+#define PLIB_DEBUG(x, args...)  printf("wls_lib debug: "x, ## args);
+#else
+#define PLIB_DEBUG(x, args...)  do { } while(0)
+#endif
+
+extern int gethugepagesizes(long pagesizes[], int n_elem);
+
+
+static uint32_t get_hugepagesz_flag(uint64_t hugepage_sz)
+{
+    unsigned size_flag = 0;
+
+    switch (hugepage_sz) {
+        case RTE_PGSIZE_256K:
+            size_flag = RTE_MEMZONE_256KB;
+            break;
+        case RTE_PGSIZE_2M:
+            size_flag = RTE_MEMZONE_2MB;
+            break;
+        case RTE_PGSIZE_16M:
+            size_flag = RTE_MEMZONE_16MB;
+            break;
+        case RTE_PGSIZE_256M:
+            size_flag = RTE_MEMZONE_256MB;
+            break;
+        case RTE_PGSIZE_512M:
+            size_flag = RTE_MEMZONE_512MB;
+            break;
+        case RTE_PGSIZE_1G:
+            size_flag = RTE_MEMZONE_1GB;
+            break;
+        case RTE_PGSIZE_4G:
+            size_flag = RTE_MEMZONE_4GB;
+            break;
+        case RTE_PGSIZE_16G:
+            size_flag = RTE_MEMZONE_16GB;
+            break;
+        default:
+            PLIB_INFO("Unknown hugepage size %lu\n", hugepage_sz);
+            break;
+    }
+    return size_flag;
+}
+
+static pthread_mutex_t wls_put_lock;
+static pthread_mutex_t wls_get_lock;
+static pthread_mutex_t wls_put_lock1;
+static pthread_mutex_t wls_get_lock1;
+
+static wls_us_ctx_t* wls_us_ctx = NULL;
+static wls_us_ctx_t* wls_us_ctx1 = NULL;
+static const struct rte_memzone *hp_memzone = NULL;
+static long hugePageSize = WLS_HUGE_DEF_PAGE_SIZE;
+static uint64_t gWlsMemorySize = 0;
+
+static inline int wls_check_ctx(void *h)
+{
+    if (h != wls_us_ctx) {
+        PLIB_ERR("Incorrect user space context\n");
+        return -1;
+    }
+    return 0;
+}
+
+static inline int wls_check_ctx1(void *h)
+{
+    if (h != wls_us_ctx1) {
+        PLIB_ERR("Incorrect user space context1\n");
+        return -1;
+    }
+    return 0;
+}
+
+static int wls_VirtToPhys(const void* virtAddr, uint64_t* physAddr)
+{
+    int mapFd;
+    uint64_t page;
+    unsigned int pageSize;
+    unsigned long virtualPageNumber;
+
+    mapFd = open("/proc/self/pagemap", O_RDONLY);
+    if (mapFd < 0) {
+        PLIB_ERR("Could't open pagemap file\n");
+        return -1;
+    }
+
+    /*get standard page size*/
+    pageSize = getpagesize();
+
+    virtualPageNumber = (unsigned long) virtAddr / pageSize;
+
+    lseek(mapFd, virtualPageNumber * sizeof (uint64_t), SEEK_SET);
+
+    if (read(mapFd, &page, sizeof (uint64_t)) < 0) {
+        close(mapFd);
+        PLIB_ERR("Could't read pagemap file\n");
+        return -1;
+    }
+
+    *physAddr = ((page & 0x007fffffffffffffULL) * pageSize);
+
+    close(mapFd);
+
+    return 0;
+}
+
+static void wls_mutex_destroy(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_destroy(pMutex);
+}
+
+static void wls_mutex_init(pthread_mutex_t* pMutex)
+{
+    pthread_mutexattr_t prior;
+    pthread_mutexattr_init(&prior);
+    pthread_mutexattr_setprotocol(&prior, PTHREAD_PRIO_INHERIT);
+    pthread_mutex_init(pMutex, &prior);
+    pthread_mutexattr_destroy(&prior);
+}
+
+static void wls_mutex_lock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_lock(pMutex);
+}
+
+static void wls_mutex_unlock(pthread_mutex_t* pMutex)
+{
+    pthread_mutex_unlock(pMutex);
+}
+
+static int wls_initialize(const char *ifacename, uint64_t nWlsMemorySize)
+{
+    int ret;
+    pthread_mutexattr_t attr;
+
+    uint64_t nSize = nWlsMemorySize + sizeof(wls_drv_ctx_t);
+    uint8_t *pMemZone;
+    const struct rte_memzone *mng_ctx_memzone;
+    wls_drv_ctx_t *mng_ctx;
+
+    mng_ctx_memzone = rte_memzone_reserve_aligned(ifacename, nSize, rte_socket_id(), get_hugepagesz_flag(hugePageSize), hugePageSize);
+    if (mng_ctx_memzone == NULL) {
+        PLIB_ERR("Cannot reserve memory zone[%s]: %s\n", ifacename, rte_strerror(rte_errno));
+        return -1;
+    }
+
+    pMemZone = ((uint8_t *)mng_ctx_memzone->addr) + nWlsMemorySize;
+    memset(pMemZone, 0, sizeof(wls_drv_ctx_t));
+    mng_ctx = (wls_drv_ctx_t *)pMemZone;
+
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
+    if (ret = pthread_mutex_init(&mng_ctx->mng_mutex, &attr)) {
+        PLIB_ERR("Failed to initialize mng_mutex %d\n", ret);
+        pthread_mutexattr_destroy(&attr);
+        return ret;
+    }
+    pthread_mutexattr_destroy(&attr);
+    PLIB_DEBUG("Run wls_initialized\n");
+    return 0;
+}
+
+static wls_us_ctx_t* wls_create_us_ctx(wls_drv_ctx_t *pDrv_ctx)
+{
+    int idx;
+
+    wls_mutex_lock(&pDrv_ctx->mng_mutex);
+
+    if (unlikely(pDrv_ctx->nWlsClients >= WLS_US_CLIENTS_MAX)) {
+        PLIB_ERR("Maximum number of clients reached");
+        wls_mutex_unlock(&pDrv_ctx->mng_mutex);
+        return NULL;
+    }
+
+    wls_us_ctx_t *pUsCtx = &pDrv_ctx->p_wls_us_ctx[pDrv_ctx->nWlsClients];
+    wls_us_priv_t *pUs_priv = &pUsCtx->wls_us_private;
+
+    PLIB_DEBUG("wls_create_us_ctx for %d client\n", pDrv_ctx->nWlsClients);
+    memset(pUsCtx, 0, sizeof (wls_us_ctx_t));
+
+    SFL_DefQueue(&pUsCtx->ul_free_block_pq, pUsCtx->ul_free_block_storage,
+        UL_FREE_BLOCK_QUEUE_SIZE * sizeof (void*));
+    WLS_MsgDefineQueue(&pUsCtx->get_queue, pUsCtx->get_storage, WLS_GET_QUEUE_N_ELEMENTS, 0);
+    WLS_MsgDefineQueue(&pUsCtx->put_queue, pUsCtx->put_storage, WLS_PUT_QUEUE_N_ELEMENTS, 0);
+
+    memset(pUs_priv, 0, sizeof (wls_us_priv_t));
+    if (sem_init(&pUs_priv->sema.sem, 1, 0)) {
+        PLIB_ERR("Failed to initialize semaphore %s\n", strerror(errno));
+        memset(pUsCtx, 0, sizeof (wls_us_ctx_t));
+        wls_mutex_unlock(&pDrv_ctx->mng_mutex);
+        return NULL;
+    }
+    rte_atomic16_init(&pUs_priv->sema.is_irq);
+
+    idx = pDrv_ctx->nWlsClients;
+    pDrv_ctx->p_wls_us_ctx[idx].dst_user_va = (uint64_t) & pDrv_ctx->p_wls_us_ctx[idx ^ 1];
+    PLIB_INFO("link: %d <-> %d\n", idx, idx ^ 1);
+
+    pUs_priv->pid = getpid();
+    pDrv_ctx->nWlsClients++;
+
+    wls_mutex_unlock(&pDrv_ctx->mng_mutex);
+    return pUsCtx;
+}
+
+static int wls_destroy_us_ctx(wls_us_ctx_t *pUsCtx, wls_drv_ctx_t *pDrv_ctx)
+{
+    wls_us_priv_t* pUs_priv = NULL;
+
+    wls_mutex_lock(&pDrv_ctx->mng_mutex);
+    if (pDrv_ctx->p_wls_us_ctx[0].wls_us_private.pid
+        && pDrv_ctx->p_wls_us_ctx[1].wls_us_private.pid) {
+        PLIB_INFO("un-link: 0 <-> 1\n");
+        pDrv_ctx->p_wls_us_ctx[0].dst_user_va = 0ULL;
+        pDrv_ctx->p_wls_us_ctx[1].dst_user_va = 0ULL;
+    }
+
+
+    if (pUsCtx) {
+        pUs_priv = (wls_us_priv_t*) & pUsCtx->wls_us_private;
+        if (pUs_priv) {
+            sem_destroy(&pUs_priv->sema.sem);
+            memset(pUsCtx, 0, sizeof (wls_us_ctx_t));
+            pDrv_ctx->nWlsClients--;
+        }
+    }
+
+    wls_mutex_unlock(&pDrv_ctx->mng_mutex);
+    return 0;
+}
+
+static int wls_destroy_us_ctx1(wls_us_ctx_t *pUsCtx, wls_drv_ctx_t *pDrv_ctx)
+{
+    wls_us_priv_t* pUs_priv = NULL;
+
+    wls_mutex_lock(&pDrv_ctx->mng_mutex);
+    if (pDrv_ctx->p_wls_us_ctx[2].wls_us_private.pid
+        && pDrv_ctx->p_wls_us_ctx[3].wls_us_private.pid) {
+        PLIB_INFO("un-link: 2 <-> 3\n");
+        pDrv_ctx->p_wls_us_ctx[2].dst_user_va = 0ULL;
+        pDrv_ctx->p_wls_us_ctx[3].dst_user_va = 0ULL;
+    }
+
+
+    if (pUsCtx) {
+        pUs_priv = (wls_us_priv_t*) & pUsCtx->wls_us_private;
+        if (pUs_priv) {
+            sem_destroy(&pUs_priv->sema.sem);
+            memset(pUsCtx, 0, sizeof (wls_us_ctx_t));
+            pDrv_ctx->nWlsClients--;
+        }
+    }
+
+    wls_mutex_unlock(&pDrv_ctx->mng_mutex);
+    return 0;
+}
+
+static int wls_wake_up_user_thread(char *buf, wls_sema_priv_t *semap)
+{
+    if (unlikely(rte_atomic16_read(&semap->is_irq) >= FIFO_LEN))
+        return 0;
+
+    unsigned int put = semap->drv_block_put + 1;
+    if (put >= FIFO_LEN)
+        put = 0;
+    memcpy(&semap->drv_block[put], buf, sizeof (wls_wait_req_t));
+    semap->drv_block_put = put;
+    rte_atomic16_inc(&semap->is_irq);
+    PLIB_DEBUG("PUT: put=%d get=%d T=%lu is_irq=%d\n",
+            semap->drv_block_put, semap->drv_block_get,
+            semap->drv_block[put].start_time, rte_atomic16_read(&semap->is_irq));
+    sem_post(&semap->sem);
+    return 0;
+}
+
+static int wls_process_put(wls_us_ctx_t *src, wls_us_ctx_t *dst)
+{
+    int ret = 0;
+    WLS_MSG_HANDLE hMsg;
+    int n = 0;
+
+    wls_us_priv_t* pDstPriv = NULL;
+    wls_wait_req_t drv_block;
+
+    if (NULL == src || NULL == dst) {
+        PLIB_DEBUG("Bad input addresses\n");
+        return -1;
+    }
+
+    n = WLS_GetNumItemsInTheQueue(&src->put_queue);
+
+    while (n--) {
+        if (WLS_MsgDequeue(&src->put_queue, &hMsg, NULL, (void*) src) == FALSE) {
+            PLIB_ERR("WLS_MsgDequeue src failed\n");
+            ret = -1;
+            break;
+        }
+        PLIB_DEBUG("WLS_Get %lx %d type %d\n", (U64) hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID);
+        if (WLS_MsgEnqueue(&dst->get_queue, hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID,
+                hMsg.flags, NULL, (void*) dst) == FALSE) { // try to send
+            if (WLS_MsgEnqueue(&src->put_queue, hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID,
+                    hMsg.flags, NULL, (void*) src) == FALSE) { // return back
+                PLIB_ERR("wls_process_put: Cannot return block to back to queue \n");
+                ret = -1;
+            }
+            break;
+        }
+    }
+
+    if (dst->wls_us_private.pid) {
+        pDstPriv = (wls_us_priv_t*) & dst->wls_us_private;
+
+        drv_block.start_time = wls_rdtsc();
+        pDstPriv->NeedToWakeUp = 1;
+        wls_wake_up_user_thread((char *) &drv_block, &pDstPriv->sema);
+    } else
+        ret = -1;
+
+    return ret;
+}
+
+static int wls_process_wakeup(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+    wls_wait_req_t drv_block;
+    if (wls_check_ctx(h))
+        return -1;
+    if (!pWls_us->wls_us_private.pid) {
+        PLIB_ERR("wakeup failed");
+        return -1;
+    }
+    drv_block.start_time = wls_rdtsc();
+    wls_wake_up_user_thread((char *) &drv_block, &pWls_us->wls_us_private.sema);
+
+    return ret;
+}
+
+static int wls_process_wakeup1(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+    wls_wait_req_t drv_block;
+    if (wls_check_ctx1(h))
+        return -1;
+    if (!pWls_us->wls_us_private.pid) {
+        PLIB_ERR("wakeup failed");
+        return -1;
+    }
+    drv_block.start_time = wls_rdtsc();
+    wls_wake_up_user_thread((char *) &drv_block, &pWls_us->wls_us_private.sema);
+
+    return ret;
+}
+
+static int wls_wait(wls_sema_priv_t *priv)
+{
+    if (!rte_atomic16_read(&priv->is_irq)) {
+        if (sem_wait(&priv->sem) || !rte_atomic16_read(&priv->is_irq)) {
+            return -1;
+        }
+    }
+
+    rte_atomic16_dec(&priv->is_irq);
+
+    if (priv->drv_block_put != priv->drv_block_get) {
+        unsigned int get = priv->drv_block_get + 1;
+
+        if (get >= FIFO_LEN)
+            get = 0;
+
+        priv->drv_block_get = get;
+
+        PLIB_DEBUG("GET: put=%d get=%d T=%lu is_irq=%d\n",
+                priv->drv_block_put, priv->drv_block_get,
+                priv->drv_block[get].start_time, rte_atomic16_read(&priv->is_irq));
+    } else {
+        PLIB_DEBUG("[wrong computation of queueing\n");
+    }
+
+    return 0;
+}
+
+static int wls_process_wait(void* h)
+{
+    wls_us_ctx_t* pUsCtx = (wls_us_ctx_t*) h;
+
+    if (wls_check_ctx(h))
+        return -1;
+
+    if (pUsCtx == NULL) {
+        PLIB_ERR("Wait failed on User context");
+        return -1;
+    }
+
+
+    if (!pUsCtx->wls_us_private.pid) {
+        PLIB_ERR("Wait failed");
+        return -1;
+    }
+    pUsCtx->wls_us_private.isWait = 1;
+    wls_wait(&pUsCtx->wls_us_private.sema);
+    pUsCtx->wls_us_private.isWait = 0;
+    return WLS_GetNumItemsInTheQueue(&pUsCtx->get_queue);
+}
+
+static int wls_process_wait1(void* h)
+{
+    wls_us_ctx_t* pUsCtx = (wls_us_ctx_t*) h;
+
+    if (wls_check_ctx1(h))
+        return -1;
+
+    if (pUsCtx == NULL) {
+        PLIB_ERR("Wait failed on User context");
+        return -1;
+    }
+
+
+    if (!pUsCtx->wls_us_private.pid) {
+        PLIB_ERR("Wait failed");
+        return -1;
+    }
+    pUsCtx->wls_us_private.isWait = 1;
+    wls_wait(&pUsCtx->wls_us_private.sema);
+    pUsCtx->wls_us_private.isWait = 0;
+    return WLS_GetNumItemsInTheQueue(&pUsCtx->get_queue);
+}
+
+
+void* WLS_Open(const char *ifacename, unsigned int mode, unsigned long long nWlsMemorySize)
+{
+    wls_us_ctx_t* pWls_us = NULL;
+    int i, len;
+    char temp[WLS_DEV_SHM_NAME_LEN] = {0};
+    uint8_t *pMemZone;
+
+    gethugepagesizes(&hugePageSize, 1);
+
+    if (wls_us_ctx)
+        return wls_us_ctx;
+
+    strncpy(temp, ifacename, WLS_DEV_SHM_NAME_LEN - 1);
+    PLIB_INFO("Open %s (DPDK memzone)\n", temp);
+
+    static const struct rte_memzone *mng_memzone;
+    mng_memzone = rte_memzone_lookup(temp);
+    if ((mng_memzone == NULL)&&(RTE_PROC_PRIMARY==rte_eal_process_type())) {
+        wls_initialize(temp, nWlsMemorySize);
+    }
+
+    mng_memzone = rte_memzone_lookup(temp);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot initialize wls shared memory: %s\n", temp);
+        return NULL;
+    }
+
+    pMemZone = ((uint8_t *)mng_memzone->addr) + nWlsMemorySize;
+
+    PLIB_INFO("WLS_Open %p\n", pMemZone);
+    if ((pWls_us = wls_create_us_ctx((wls_drv_ctx_t *)pMemZone)) == NULL) {
+        PLIB_ERR("WLS_Open failed to create context\n");
+        return NULL;
+    }
+
+    PLIB_DEBUG("Local: pWls_us %p\n", pWls_us);
+
+    pWls_us->padding_wls_us_user_space_va = 0LL;
+    pWls_us->wls_us_user_space_va = pWls_us;
+    pWls_us->wls_us_ctx_size = sizeof (*pWls_us);
+    gWlsMemorySize = nWlsMemorySize;
+
+    wls_mutex_init(&wls_put_lock);
+    wls_mutex_init(&wls_get_lock);
+
+    pWls_us->mode = mode;
+    pWls_us->secmode = WLS_SEC_NA;
+    pWls_us->dualMode = WLS_SINGLE_MODE;
+    PLIB_INFO("Mode %d\n", pWls_us->mode);
+
+    PLIB_INFO("WLS shared management memzone: %s\n", temp);
+    strncpy(pWls_us->wls_dev_name, temp, WLS_DEV_SHM_NAME_LEN - 1);
+    wls_us_ctx = pWls_us;
+
+    return wls_us_ctx;
+}
+
+void* WLS_Open_Dual(const char *ifacename, unsigned int mode, unsigned long long nWlsMemorySize, void** handle1)
+{
+    wls_us_ctx_t* pWls_us = NULL;
+    wls_us_ctx_t* pWls_us1 = NULL;
+    int i, len;
+    char temp[WLS_DEV_SHM_NAME_LEN] = {0};
+    uint8_t *pMemZone;
+
+    gethugepagesizes(&hugePageSize, 1);
+
+    if (wls_us_ctx)
+        return wls_us_ctx;
+
+    strncpy(temp, ifacename, WLS_DEV_SHM_NAME_LEN - 1);
+    PLIB_INFO("Open %s (DPDK memzone)\n", temp);
+
+    static const struct rte_memzone *mng_memzone;
+    mng_memzone = rte_memzone_lookup(temp);
+    if ((mng_memzone == NULL)&&(RTE_PROC_PRIMARY==rte_eal_process_type())) {
+        wls_initialize(temp, nWlsMemorySize);
+    }
+
+    mng_memzone = rte_memzone_lookup(temp);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot initialize wls shared memory: %s\n", temp);
+        return NULL;
+    }
+
+    pMemZone = ((uint8_t *)mng_memzone->addr) + nWlsMemorySize;
+    PLIB_INFO("nWlsMemorySize is %llu\n", nWlsMemorySize);
+    PLIB_INFO("WLS_Open Dual 1 %p\n", pMemZone);
+     if ((pWls_us = wls_create_us_ctx((wls_drv_ctx_t *)pMemZone)) == NULL) {
+        PLIB_ERR("WLS_Open Dual 1 failed to create context\n");
+        return NULL;
+     }
+     else
+     {    
+        PLIB_DEBUG("Local: pWls_us %p\n", pWls_us);
+ 
+        pWls_us->padding_wls_us_user_space_va = 0LL;
+        pWls_us->wls_us_user_space_va = pWls_us;
+        pWls_us->wls_us_ctx_size = sizeof (*pWls_us);
+        gWlsMemorySize = nWlsMemorySize;
+
+        wls_mutex_init(&wls_put_lock);
+        wls_mutex_init(&wls_get_lock);
+
+        pWls_us->mode = mode;
+        pWls_us->secmode = WLS_SEC_MASTER;
+        pWls_us->dualMode = WLS_DUAL_MODE;
+        PLIB_INFO("Mode %d SecMode %d \n", pWls_us->mode, pWls_us->secmode);
+
+        PLIB_INFO("WLS shared management memzone 1: %s\n", temp);
+        strncpy(pWls_us->wls_dev_name, temp, WLS_DEV_SHM_NAME_LEN - 1);
+        wls_us_ctx = pWls_us;
+	    PLIB_INFO("pWLs_us is %p\n", wls_us_ctx);
+	    *handle1 = pWls_us;  // Now the first context is for L1-FT_iapi
+     }
+     
+     // Create second context to support the second wls shared memory interface
+     if ((pWls_us1 = wls_create_us_ctx((wls_drv_ctx_t *)pMemZone)) == NULL) {
+        PLIB_ERR("WLS_Open Dual failed to create context 1\n");
+        return NULL;
+     }
+     else
+     {
+        PLIB_DEBUG("Local: pWls_us1 %p\n", pWls_us1);
+        pWls_us1->padding_wls_us_user_space_va = 0LL;
+        pWls_us1->wls_us_user_space_va = pWls_us1;
+        pWls_us1->wls_us_ctx_size = sizeof (*pWls_us1);
+        gWlsMemorySize = nWlsMemorySize;
+
+        wls_mutex_init(&wls_put_lock1);
+        wls_mutex_init(&wls_get_lock1);
+
+        pWls_us1->mode = mode;
+        pWls_us1->secmode = WLS_SEC_NA;
+        PLIB_INFO("Mode %d Secmode %d\n", pWls_us1->mode, pWls_us1->secmode);
+
+        PLIB_INFO("WLS shared management memzone 2: %s\n", temp);
+        strncpy(pWls_us1->wls_dev_name, temp, WLS_DEV_SHM_NAME_LEN - 1);
+    	wls_us_ctx1 = pWls_us1; // Now the second context is for the L2-FT_fapi
+	    PLIB_INFO("pWLs_us1 is %p\n", wls_us_ctx1);
+
+     }
+
+    return wls_us_ctx1; // returning second context preserves the L2 legacy code
+}
+
+int WLS_Ready(void* h)
+{
+    wls_us_ctx_t *pWls_us = (wls_us_ctx_t*) h;
+    wls_us_ctx_t *pWls_usRem = (wls_us_ctx_t*) pWls_us->dst_user_va;
+
+    if (!wls_us_ctx) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    if (pWls_usRem->wls_us_private.pid) {
+        return 0;
+    }
+    return -1;
+}
+
+int WLS_Ready1(void* h)
+{
+    wls_us_ctx_t *pWls_us = (wls_us_ctx_t*) h;
+    wls_us_ctx_t *pWls_usRem = (wls_us_ctx_t*) pWls_us->dst_user_va;
+
+    if (!wls_us_ctx1) {
+        PLIB_ERR("Library was not opened for Context 1\n");
+        return -1;
+    }
+
+    if (pWls_usRem->wls_us_private.pid) {
+        return 0;
+    }
+    return -1;
+}
+
+int WLS_Close(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+    uint8_t *pMemZone;
+    wls_drv_ctx_t *pDrv_ctx;
+
+    if (!wls_us_ctx) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    if (wls_check_ctx(h))
+        return -1;
+
+    static const struct rte_memzone *mng_memzone;
+    mng_memzone = rte_memzone_lookup(pWls_us->wls_dev_name);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot find mng memzone: %s %s\n",
+                    pWls_us->wls_dev_name, rte_strerror(rte_errno));
+        return -1;
+    }
+
+    pMemZone = ((uint8_t *)mng_memzone->addr) + gWlsMemorySize;
+    pDrv_ctx = (wls_drv_ctx_t *)pMemZone;
+
+    PLIB_INFO("WLS_Close\n");
+    if ((ret = wls_destroy_us_ctx(pWls_us, pDrv_ctx)) < 0) {
+        PLIB_ERR("Close failed [%d]\n", ret);
+        return ret;
+    }
+
+    wls_mutex_destroy(&wls_put_lock);
+    wls_mutex_destroy(&wls_get_lock);
+
+    wls_us_ctx = NULL;
+
+    if (0 == pDrv_ctx->nWlsClients) {
+        wls_mutex_destroy(&pDrv_ctx->mng_mutex);
+        rte_memzone_free(mng_memzone);
+    }
+    return 0;
+}
+
+int WLS_Close1(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+    uint8_t *pMemZone;
+    wls_drv_ctx_t *pDrv_ctx;
+
+    if (!wls_us_ctx1) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    if (wls_check_ctx1(h))
+        return -1;
+
+    static const struct rte_memzone *mng_memzone;
+    mng_memzone = rte_memzone_lookup(pWls_us->wls_dev_name);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot find mng memzone: %s %s\n",
+                    pWls_us->wls_dev_name, rte_strerror(rte_errno));
+        return -1;
+    }
+
+    pMemZone = ((uint8_t *)mng_memzone->addr) + gWlsMemorySize;
+    pDrv_ctx = (wls_drv_ctx_t *)pMemZone;
+
+    PLIB_INFO("WLS_Close1\n");
+    if ((ret = wls_destroy_us_ctx1(pWls_us, pDrv_ctx)) < 0) {
+        PLIB_ERR("Close failed [%d]\n", ret);
+        return ret;
+    }
+
+    wls_mutex_destroy(&wls_put_lock1);
+    wls_mutex_destroy(&wls_get_lock1);
+
+    wls_us_ctx1 = NULL;
+
+    if (0 == pDrv_ctx->nWlsClients) {
+        wls_mutex_destroy(&pDrv_ctx->mng_mutex);
+        rte_memzone_free(mng_memzone);
+    }
+    return 0;
+}
+
+void* WLS_Alloc(void* h, unsigned int size)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    static const struct rte_memzone *mng_memzone;
+    long nHugePage;
+    void *pvirtAddr = NULL;
+    int count;
+
+    if ((NULL != hp_memzone)&&(pWls_us->dualMode != WLS_DUAL_MODE)) {
+        PLIB_ERR("Memory zone already reserved\n");
+        return hp_memzone->addr;
+    }
+
+    hugepage_tabl_t* pHugePageTlb = &pWls_us->hugepageTbl[0];
+    hugepage_tabl_t* pHugePageTlb1 = &pWls_us->hugepageTbl[1];
+    hugepage_tabl_t* pHugePageTlb2 = &pWls_us->hugepageTbl[2];
+
+    PLIB_INFO("hugePageSize on the system is %ld\n", hugePageSize);
+
+    /* calculate total number of hugepages */
+    nHugePage = DIV_ROUND_OFFSET(size, hugePageSize);
+
+    if (nHugePage >= MAX_N_HUGE_PAGES) {
+        PLIB_INFO("not enough hugepages: need %ld  system has %d\n", nHugePage, MAX_N_HUGE_PAGES);
+        return NULL;
+    }
+
+    mng_memzone = rte_memzone_lookup(pWls_us->wls_dev_name);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot initialize wls shared memory: %s\n", pWls_us->wls_dev_name);
+        return NULL;
+    }
+
+    hp_memzone = (struct rte_memzone *)mng_memzone;
+    pvirtAddr = (void *)hp_memzone->addr;
+    PLIB_DEBUG("pvirtAddr is %p\n", pvirtAddr);
+    if (rte_eal_process_type() == RTE_PROC_PRIMARY) {
+        memset(pvirtAddr, 0, sizeof (wls_drv_ctx_t));
+    }
+
+    for (count = 0; count < nHugePage; count++) {
+        /*Increment virtual address to next hugepage to create table*/
+        pHugePageTlb[count].pageVa = ((unsigned char*) pvirtAddr + \
+                                                (count * hugePageSize));
+        /*Creating dummy page fault in process for each page
+                                                inorder to get pagemap*/
+        *(unsigned char*) pHugePageTlb[count].pageVa = 1;
+
+        if (wls_VirtToPhys((uint64_t*) pHugePageTlb[count].pageVa,
+                &pHugePageTlb[count].pagePa) == -1) {
+            PLIB_ERR("Virtual to physical conversion failed\n");
+            return NULL;
+        }
+    }
+
+    PLIB_DEBUG("count is %d, pHugePageTlb->pageVa is %p  pHugePageTlb1->pageVa is %p  pHugePageTlb2->pageVa is %p\n",count, pHugePageTlb->pageVa, pHugePageTlb1->pageVa, pHugePageTlb2->pageVa);
+    PLIB_INFO("WLS_Alloc [%d] bytes\n", size);
+
+    pWls_us->HugePageSize = (uint32_t) hugePageSize;
+    pWls_us->alloc_buffer = pvirtAddr;
+    pWls_us->alloc_size = (uint32_t) (nHugePage * hugePageSize);
+
+    if ((pWls_us->mode == WLS_MASTER_CLIENT)||(pWls_us->secmode == WLS_SEC_MASTER)) {
+        wls_us_ctx_t *pWls_usRem = (wls_us_ctx_t*) pWls_us->dst_user_va;
+        PLIB_INFO("Connecting to remote peer ...\n");
+        while (pWls_usRem->wls_us_private.pid == 0) { // wait for slave
+        }
+
+        PLIB_INFO("Connected to remote peer\n");
+        pWls_us->dst_user_va = (uint64_t) pWls_usRem;
+    }
+    return pvirtAddr;
+}
+
+int WLS_Free(void* h, PVOID pMsg)
+{
+    static const struct rte_memzone *mng_memzone;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    wls_drv_ctx_t *pDrv_ctx;
+
+    mng_memzone = rte_memzone_lookup(pWls_us->wls_dev_name);
+    if (mng_memzone == NULL) {
+        PLIB_ERR("Cannot find mng memzone: %s %s\n",
+                    pWls_us->wls_dev_name, rte_strerror(rte_errno));
+        return -1;
+    }
+    pDrv_ctx = mng_memzone->addr;
+
+    if (pMsg !=  pWls_us->alloc_buffer) {
+        PLIB_ERR("incorrect pMsg %p [expected %p]\n", pMsg, pWls_us->alloc_buffer);
+        return -1;
+    }
+
+    if ((pWls_us->mode == WLS_MASTER_CLIENT)||(pWls_us->mode == WLS_SEC_MASTER)) {
+        if (pWls_us->dst_user_va) {
+            pWls_us->dst_user_va = 0;
+        }
+    }
+
+    PLIB_DEBUG("WLS_Free %s\n", shm_name);
+    if ( (1 == pDrv_ctx->nWlsClients) && hp_memzone)
+        hp_memzone = NULL;
+    return 0;
+}
+
+int WLS_Put(void *h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+
+    if (wls_check_ctx(h))
+        return -1;
+
+    if (!WLS_IS_ONE_HUGE_PAGE(pMsg, MsgSize, hugePageSize)) {
+        PLIB_ERR("WLS_Put input error: buffer is crossing 2MB page boundary %lx size %u\n",
+                (U64) pMsg, MsgSize);
+    }
+
+    wls_mutex_lock(&wls_put_lock);
+
+    if ((WLS_FLAGS_MASK & Flags)) { // multi block transaction
+        if (Flags & WLS_TF_SYN) {
+            PLIB_DEBUG("WLS_SG_FIRST\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Get %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if ((Flags & WLS_TF_SCATTER_GATHER)
+                    && !(Flags & WLS_TF_SYN)
+                    && !(Flags & WLS_TF_FIN)) {
+            PLIB_DEBUG("WLS_SG_NEXT\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if (Flags & WLS_TF_FIN) {
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+
+            PLIB_DEBUG("List: call wls_process_put\n");
+            if (pWls_us->dst_user_va) {
+                if ((ret = wls_process_put(pWls_us, (wls_us_ctx_t*) pWls_us->dst_user_va)) < 0) {
+                    PLIB_ERR("Put failed [%d]\n", ret);
+                    wls_mutex_unlock(&wls_put_lock);
+                    return -1;
+                }
+            }
+        } else
+            PLIB_ERR("unsaported flags %x\n", WLS_FLAGS_MASK & Flags);
+    } else { // one block transaction
+        if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                Flags, NULL, (void*) pWls_us)) {
+            PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+        }
+
+        PLIB_DEBUG("One block: call wls_process_put\n");
+        if (likely(pWls_us->dst_user_va)) {
+            if ((ret = wls_process_put(pWls_us, (wls_us_ctx_t*) pWls_us->dst_user_va)) < 0) {
+                PLIB_ERR("Put failed [%d]\n", ret);
+                wls_mutex_unlock(&wls_put_lock);
+                return -1;
+            }
+        } else {
+            PLIB_ERR("Destination address is empty\n");
+            wls_mutex_unlock(&wls_put_lock);
+            return -1;
+        }
+    }
+    wls_mutex_unlock(&wls_put_lock);
+
+    return 0;
+}
+
+int WLS_Put1(void *h, unsigned long long pMsg, unsigned int MsgSize, unsigned short MsgTypeID, unsigned short Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int ret = 0;
+
+    if (wls_check_ctx1(h))
+        return -1;
+
+    if (!WLS_IS_ONE_HUGE_PAGE(pMsg, MsgSize, hugePageSize)) {
+        PLIB_ERR("WLS_Put input error: buffer is crossing 2MB page boundary %lx size %u\n",
+                (U64) pMsg, MsgSize);
+    }
+
+    wls_mutex_lock(&wls_put_lock1);
+
+    if ((WLS_FLAGS_MASK & Flags)) { // multi block transaction
+        if (Flags & WLS_TF_SYN) {
+            PLIB_DEBUG("WLS_SG_FIRST\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Get %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if ((Flags & WLS_TF_SCATTER_GATHER)
+                    && !(Flags & WLS_TF_SYN)
+                    && !(Flags & WLS_TF_FIN)) {
+            PLIB_DEBUG("WLS_SG_NEXT\n");
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+        } else if (Flags & WLS_TF_FIN) {
+            if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                    Flags, NULL, (void*) pWls_us)) {
+                PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+            }
+
+            PLIB_DEBUG("List: call wls_process_put\n");
+            if (pWls_us->dst_user_va) {
+                if ((ret = wls_process_put(pWls_us, (wls_us_ctx_t*) pWls_us->dst_user_va)) < 0) {
+                    PLIB_ERR("Put failed [%d]\n", ret);
+                    wls_mutex_unlock(&wls_put_lock1);
+                    return -1;
+                }
+            }
+        } else
+            PLIB_ERR("unsupported flags %x\n", WLS_FLAGS_MASK & Flags);
+    } else { // one block transaction
+        if (WLS_MsgEnqueue(&pWls_us->put_queue, pMsg, MsgSize, MsgTypeID,
+                Flags, NULL, (void*) pWls_us)) {
+            PLIB_DEBUG("WLS_Put %lx %d type %d\n", (U64) pMsg, MsgSize, MsgTypeID);
+        }
+
+        PLIB_DEBUG("One block: call wls_process_put\n");
+        if (likely(pWls_us->dst_user_va)) {
+            if ((ret = wls_process_put(pWls_us, (wls_us_ctx_t*) pWls_us->dst_user_va)) < 0) {
+                PLIB_ERR("Put failed [%d]\n", ret);
+                wls_mutex_unlock(&wls_put_lock1);
+                return -1;
+            }
+        } else {
+            PLIB_ERR("Destination address is empty\n");
+            wls_mutex_unlock(&wls_put_lock1);
+            return -1;
+        }
+    }
+    wls_mutex_unlock(&wls_put_lock1);
+
+    return 0;
+}
+
+int WLS_Check(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (wls_check_ctx(h))
+        return 0;
+
+    return WLS_GetNumItemsInTheQueue(&pWls_us->get_queue);
+}
+
+int WLS_Check1(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (wls_check_ctx1(h))
+        return 0;
+
+    return WLS_GetNumItemsInTheQueue(&pWls_us->get_queue);
+}
+
+unsigned long long WLS_Get(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    WLS_MSG_HANDLE hMsg;
+    uint64_t pMsg = (uint64_t) NULL;
+
+    if (wls_check_ctx(h))
+    {
+    	PLIB_ERR("WLS_Get fails wls_check_ctx\n");
+        return 0;
+    }
+
+    wls_mutex_lock(&wls_get_lock);
+
+    if (WLS_MsgDequeue(&pWls_us->get_queue, &hMsg, NULL, (void*) pWls_us)) {
+        PLIB_DEBUG("WLS_Get %lx %d type %d\n", (U64) hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID);
+        pMsg = hMsg.pIaPaMsg;
+        *MsgSize = hMsg.MsgSize;
+        *MsgTypeID = hMsg.TypeID;
+        *Flags = hMsg.flags;
+    }
+
+    wls_mutex_unlock(&wls_get_lock);
+
+    return pMsg;
+}
+
+unsigned long long WLS_Get1(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    WLS_MSG_HANDLE hMsg;
+    uint64_t pMsg = (uint64_t) NULL;
+
+    if (wls_check_ctx1(h))
+        return 0;
+
+    wls_mutex_lock(&wls_get_lock1);
+
+    if (WLS_MsgDequeue(&pWls_us->get_queue, &hMsg, NULL, (void*) pWls_us)) {
+        PLIB_DEBUG("WLS_Get %lx %d type %d\n", (U64) hMsg.pIaPaMsg, hMsg.MsgSize, hMsg.TypeID);
+        pMsg = hMsg.pIaPaMsg;
+        *MsgSize = hMsg.MsgSize;
+        *MsgTypeID = hMsg.TypeID;
+        *Flags = hMsg.flags;
+    }
+
+    wls_mutex_unlock(&wls_get_lock1);
+
+    return pMsg;
+}
+
+int WLS_WakeUp(void* h)
+{
+    if (!wls_us_ctx) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+    if (wls_check_ctx(h))
+        return -1;
+
+    PLIB_DEBUG("WLS_WakeUp\n");
+
+    return wls_process_wakeup(h);
+
+
+    return 0;
+}
+
+int WLS_WakeUp1(void* h)
+{
+    if (!wls_us_ctx1) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+    if (wls_check_ctx1(h))
+        return -1;
+
+    PLIB_DEBUG("WLS_WakeUp1\n");
+
+    return wls_process_wakeup1(h);
+
+
+    return 0;
+}
+
+int WLS_Wait(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (!wls_us_ctx || (wls_us_ctx != pWls_us)) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    return wls_process_wait(h);
+}
+
+int WLS_Wait1(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (!wls_us_ctx1 || (wls_us_ctx1 != pWls_us)) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+    return wls_process_wait1(h);
+}
+
+unsigned long long WLS_WGet(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    uint64_t pRxMsg = WLS_Get(h, MsgSize, MsgTypeID, Flags);
+
+    if (pRxMsg)
+        return pRxMsg;
+
+    WLS_Wait(h);
+    return WLS_Get(h, MsgSize, MsgTypeID, Flags);
+}
+
+unsigned long long WLS_WGet1(void* h, unsigned int *MsgSize, unsigned short *MsgTypeID, unsigned short *Flags)
+{
+    uint64_t pRxMsg = WLS_Get1(h, MsgSize, MsgTypeID, Flags);
+
+    if (pRxMsg)
+        return pRxMsg;
+
+    WLS_Wait1(h);
+    return WLS_Get1(h, MsgSize, MsgTypeID, Flags);
+}
+
+unsigned long long WLS_VA2PA(void* h, PVOID pMsg)
+{
+    uint64_t ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    unsigned long alloc_base;
+    hugepage_tabl_t* pHugePageTlb;
+    uint64_t hugePageBase;
+    uint64_t hugePageOffet;
+    unsigned int count = 0;
+
+    uint64_t HugePageMask = ((unsigned long) pWls_us->HugePageSize - 1);
+    if (pWls_us->alloc_buffer == NULL) {
+        PLIB_ERR("WLS_VA2PA: nothing was allocated [%ld]\n", ret);
+        return (uint64_t) ret;
+    }
+
+    alloc_base = (unsigned long) pWls_us->alloc_buffer;
+
+    pHugePageTlb = &pWls_us->hugepageTbl[0];
+
+    hugePageBase = (uint64_t) pMsg & ~HugePageMask;
+    hugePageOffet = (uint64_t) pMsg & HugePageMask;
+
+    count = (hugePageBase - alloc_base) / pWls_us->HugePageSize;
+    PLIB_DEBUG("WLS_VA2PA %lx base %llx off %llx  count %u\n", (unsigned long) pMsg,
+            (uint64_t) hugePageBase, (uint64_t) hugePageOffet, count);
+
+    ret = pHugePageTlb[count].pagePa + hugePageOffet;
+
+    //printf("       WLS_VA2PA: %p -> %p   HugePageSize[%d] HugePageMask[%p] count[%d] pagePa[%p] hugePageBase[%p] alloc_buffer[%p] hugePageOffet[%lld]\n",
+    //    pMsg, (void*)ret, pWls_us->HugePageSize, (void*)HugePageMask, count, (void*)pHugePageTlb[count].pagePa, (void*)hugePageBase, pWls_us->alloc_buffer, hugePageOffet);
+
+    return (uint64_t) ret;
+}
+
+void* WLS_PA2VA(void* h, unsigned long long pMsg)
+{
+    unsigned long ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    hugepage_tabl_t* pHugePageTlb;
+    uint64_t hugePageBase;
+    uint64_t hugePageOffet;
+    unsigned int count;
+    int i;
+    uint64_t HugePageMask = ((uint64_t) pWls_us->HugePageSize - 1);
+
+    if (pWls_us->alloc_buffer == NULL) {
+        PLIB_ERR("WLS_PA2VA: nothing was allocated [%ld]\n", ret);
+        return (void*) ret;
+    }
+
+    pHugePageTlb = &pWls_us->hugepageTbl[0];
+
+    hugePageBase = (uint64_t) pMsg & ~HugePageMask;
+    hugePageOffet = (uint64_t) pMsg & HugePageMask;
+
+    count = pWls_us->alloc_size / pWls_us->HugePageSize;
+
+    PLIB_DEBUG("WLS_PA2VA %llx base %llx off %llx  count %d\n", (uint64_t) pMsg,
+            (uint64_t) hugePageBase, (uint64_t) hugePageOffet, count);
+
+    for (i = 0; i < count; i++) {
+        if (pHugePageTlb[i].pagePa == hugePageBase) {
+            ret = (unsigned long) pHugePageTlb[i].pageVa;
+            ret += hugePageOffet;
+            return (void*) ret;
+        }
+    }
+
+    //printf("       WLS_VA2PA: %p -> %p   HugePageSize[%d] HugePageMask[%p] count[%d] pagePa[%p] hugePageBase[%p] alloc_buffer[%p] hugePageOffet[%lld]\n",
+    //    (void*)pMsg, (void*)ret, pWls_us->HugePageSize, (void*)HugePageMask, count, (void*)pHugePageTlb[count].pagePa, (void*)hugePageBase, pWls_us->alloc_buffer, hugePageOffet);
+
+    return (void*) (ret);
+}
+
+int WLS_EnqueueBlock(void* h, unsigned long long pMsg)
+{
+    int ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (!wls_us_ctx) {
+        PLIB_ERR("Library was not opened\n");
+        return -1;
+    }
+
+
+    if (pMsg == 0) {
+        PLIB_ERR("WLS_EnqueueBlock: Null\n");
+        return -1;
+    }
+
+    if (pWls_us->dst_user_va) {
+        wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t*) pWls_us->dst_user_va;
+        ret = SFL_WlsEnqueue(&pDstWls_us->ul_free_block_pq, pMsg, NULL, pWls_us);
+        if (ret == 1) {
+            unsigned long* ptr = (unsigned long*) WLS_PA2VA(pWls_us, pMsg);
+            if (ptr) {
+                *ptr = 0xFFFFFFFFFFFFFFFF;
+            }
+        }
+    } else
+        ret = -1;
+
+    PLIB_DEBUG("SFL_WlsEnqueue %d\n", ret);
+    return ret;
+}
+
+int WLS_EnqueueBlock1(void* h, unsigned long long pMsg)
+{
+    int ret = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if (!wls_us_ctx1) {
+        PLIB_ERR("Library was not opened for a second context\n");
+        return -1;
+    }
+
+    if (pWls_us->mode == WLS_SLAVE_CLIENT) {
+        PLIB_ERR("Slave doesn't support memory allocation\n");
+        return -1;
+    }
+
+    if (pMsg == 0) {
+        PLIB_ERR("WLS_EnqueueBlock: Null\n");
+        return -1;
+    }
+
+    if (pWls_us->dst_user_va) {
+        wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t*) pWls_us->dst_user_va;
+        ret = SFL_WlsEnqueue(&pDstWls_us->ul_free_block_pq, pMsg, NULL, pWls_us);
+        if (ret == 1) {
+            unsigned long* ptr = (unsigned long*) WLS_PA2VA(pWls_us, pMsg);
+            if (ptr) {
+                *ptr = 0xFFFFFFFFFFFFFFFF;
+            }
+        }
+    } else
+        ret = -1;
+
+    PLIB_DEBUG("SFL_WlsEnqueue %d\n", ret);
+    return ret;
+}
+
+
+unsigned long long WLS_DequeueBlock(void* h)
+{
+    unsigned long long retval = 0;
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+
+    if ((pWls_us->mode == WLS_SLAVE_CLIENT)&&(pWls_us->secmode==WLS_SEC_NA))
+    {
+        // local
+        return SFL_WlsDequeue(&pWls_us->ul_free_block_pq, NULL, h);
+    }    
+    if (!pWls_us->dst_user_va)
+    {
+        return retval;
+    }
+        // remote
+    wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t*) pWls_us->dst_user_va;
+    retval = SFL_WlsDequeue(&pDstWls_us->ul_free_block_pq, NULL, pDstWls_us);
+    if (retval) {
+        unsigned long* ptr = (unsigned long*) WLS_PA2VA(pWls_us, retval);
+        if (ptr) {
+            if (*ptr != 0xFFFFFFFFFFFFFFFF) {
+                PLIB_ERR("WLS_EnqueueBlock: incorrect content pa: 0x%016lx: 0x%016lx\n",
+                         (unsigned long) retval, *ptr);
+            }
+        }
+    }
+
+    return retval;
+}
+
+int WLS_NumBlocks(void* h)
+{
+    wls_us_ctx_t* pWls_us = (wls_us_ctx_t*) h;
+    int n = 0;
+
+    if (pWls_us->mode == WLS_SLAVE_CLIENT) {
+        // local
+        n = SFL_GetNumItemsInTheQueue(&pWls_us->ul_free_block_pq);
+    } else if (pWls_us->dst_user_va) {
+        // remote
+        wls_us_ctx_t* pDstWls_us = (wls_us_ctx_t*) pWls_us->dst_user_va;
+        n = SFL_GetNumItemsInTheQueue(&pDstWls_us->ul_free_block_pq);
+    }
+
+    return n;
+}
diff --git a/wls_lib/wls_start.sh b/wls_lib/wls_start.sh
new file mode 100644
index 0000000..66ae2e6
--- /dev/null
+++ b/wls_lib/wls_start.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+###############################################################################
+#
+#   Copyright (c) 2019 Intel.
+#
+#   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.
+#
+###############################################################################
+#
+#  File: wls_start.sh
+#        Test script to load wls module.
+#
+
+export RTE_WLS=`pwd`
+
+#
+# Unloads wls.ko.
+#
+remove_wls_module()
+{
+    echo "Unloading WLS module"
+    /sbin/lsmod | grep -s wls > /dev/null
+    if [ $? -eq 0 ] ; then
+        sudo /sbin/rmmod wls
+    fi
+}
+
+#
+# Loads new wls.ko
+#
+load_wls_module()
+{
+    if [ ! -f $RTE_WLS/wls.ko ];then
+        echo "## ERROR: Folder does not have the WLS Kernel Module."
+        echo "       To fix, please try to rebuild WLS"
+        return
+    fi
+
+    remove_wls_module
+
+    /sbin/lsmod | grep -s wls > /dev/null
+    if [ $? -eq	1 ] ; then
+        if [ -f /lib/modules/$(uname -r)/updates/drivers/intel/wls/wls.ko ] ; then
+            echo "Loading WLS module"
+            sudo /sbin/modprobe wls wlsMaxClients=4
+	else
+	    echo "No module. WLS is not istalled? do 'make install'"
+        fi
+    fi
+}
+
+load_wls_module
+
+
+