FAPI TM, WLS_LIB and ODULOW documentation
[o-du/phy.git] / fapi_5g / source / framework / wls / lib / nr5g_fapi_wls.c
diff --git a/fapi_5g/source/framework/wls/lib/nr5g_fapi_wls.c b/fapi_5g/source/framework/wls/lib/nr5g_fapi_wls.c
new file mode 100644 (file)
index 0000000..2ae97b0
--- /dev/null
@@ -0,0 +1,915 @@
+/******************************************************************************
+*
+*   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 This file has Shared Memory interface functions between FAPI and PHY
+ * @defgroup nr5g_fapi_source_framework_wls_lib_group
+ **/
+
+#include "nr5g_fapi_framework.h"
+#include "nr5g_fapi_internal.h"
+#include "nr5g_fapi_wls.h"
+#include "nr5g_fapi_config_loader.h"
+#include "nr5g_fapi_log.h"
+#include "nr5g_fapi_memory.h"
+
+nr5g_fapi_wls_context_t g_wls_ctx;
+
+static uint32_t g_to_free_send_list_cnt[TO_FREE_SIZE] = { 0 };
+static uint64_t g_to_free_send_list[TO_FREE_SIZE][TOTAL_FREE_BLOCKS] = { {0L} };
+static uint32_t g_to_free_recv_list_cnt[TO_FREE_SIZE] = { 0 };
+static uint64_t g_to_free_recv_list[TO_FREE_SIZE][TOTAL_FREE_BLOCKS] = { {0L} };
+
+static uint8_t alloc_track[ALLOC_TRACK_SIZE];
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param void
+ *
+ *  @return  A pointer to WLS Context stucture
+ *
+ *  @description
+ *  This function returns the WLS Context structure which has WLS related parameters
+ *
+**/
+//------------------------------------------------------------------------------
+inline p_nr5g_fapi_wls_context_t nr5g_fapi_wls_context(
+    )
+{
+    return &g_wls_ctx;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @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 nr5g_fapi_wls_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 nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param   N/A
+ *
+ *  @return  N/A
+ *
+ *  @description
+ *  This function prints to the console FAPI stats
+ *
+**/
+//------------------------------------------------------------------------------
+void nr5g_fapi_wls_print_stats(
+    void)
+{
+    p_nr5g_fapi_wls_context_t pWls = nr5g_fapi_wls_context();
+    printf("          nTotalBlocks[%5d]  nAllocBlocks[%5d]  nFreeBlocks[%5d]\n",
+        pWls->nTotalBlocks, pWls->nAllocBlocks,
+        (pWls->nTotalBlocks - pWls->nAllocBlocks));
+    printf("        nTotalAllocCnt[%5d] nTotalFreeCnt[%5d]         Diff[%5d]\n",
+        pWls->nTotalAllocCnt, pWls->nTotalFreeCnt,
+        (pWls->nTotalAllocCnt - pWls->nTotalFreeCnt));
+    uint32_t nFinalTotalDlBufAllocCnt = 0, nFinalTotalDlBufFreeCnt = 0, idx;
+
+//#define PRINTF_DEBUG(fmt, args...) //printf(fmt, ## args)
+#define PRINTF_DEBUG(fmt, args...)
+    PRINTF_DEBUG("\n");
+    PRINTF_DEBUG("\n        nDlBufAllocCnt: \n");
+    for (idx = 0; idx < MEM_STAT_DEFAULT; idx++) {
+        nFinalTotalDlBufAllocCnt += pWls->nTotalDlBufAllocCnt[idx];
+        PRINTF_DEBUG("[%3d:%5d] ", idx, pWls->nTotalDlBufAllocCnt[idx]);
+    }
+    PRINTF_DEBUG("\n");
+    PRINTF_DEBUG("\n         nDlBufFreeCnt: \n");
+    for (idx = 0; idx < MEM_STAT_DEFAULT; idx++) {
+        nFinalTotalDlBufFreeCnt += pWls->nTotalDlBufFreeCnt[idx];
+        PRINTF_DEBUG("[%3d:%5d] ", idx, pWls->nTotalDlBufFreeCnt[idx]);
+    }
+    PRINTF_DEBUG("\n\n");
+
+    printf("        nDlBufAllocCnt[%5d] nDlBufFreeCnt[%5d]         Diff[%5d]\n",
+        nFinalTotalDlBufAllocCnt, nFinalTotalDlBufFreeCnt,
+        (nFinalTotalDlBufAllocCnt - nFinalTotalDlBufFreeCnt));
+    printf
+        ("        nUlBufAllocCnt[%5d] nUlBufFreeCnt[%5d]         Diff[%5d]\n\n",
+        pWls->nTotalUlBufAllocCnt, pWls->nTotalUlBufFreeCnt,
+        (pWls->nTotalUlBufAllocCnt - pWls->nTotalUlBufFreeCnt));
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]   ptr Address to convert
+ *
+ *  @return  Converted address
+ *
+ *  @description
+ *  This function converts Virtual Address to Physical Address
+ *
+**/
+//------------------------------------------------------------------------------
+uint64_t nr5g_fapi_wls_va_to_pa(
+    WLS_HANDLE h_wls,
+    void *ptr)
+{
+    return ((uint64_t) WLS_VA2PA(h_wls, ptr));
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]   ptr Address to convert
+ *
+ *  @return  Converted address
+ *
+ *  @description
+ *  This function converts Physical Address to Virtual Address
+ *
+**/
+//------------------------------------------------------------------------------
+void *nr5g_fapi_wls_pa_to_va(
+    WLS_HANDLE h_wls,
+    uint64_t ptr)
+{
+    return ((void *)WLS_PA2VA(h_wls, ptr));
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group 
+ *
+ *  @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
+ *
+**/
+//------------------------------------------------------------------------------
+uint8_t wls_fapi_add_blocks_to_ul(
+    void)
+{
+    int num_blocks = 0;
+    p_nr5g_fapi_wls_context_t pWls = nr5g_fapi_wls_context();
+    WLS_HANDLE h_wls = pWls->h_wls[NR5G_FAPI2PHY_WLS_INST];
+
+    void *pMsg = wls_fapi_alloc_buffer(0, MIN_UL_BUF_LOCATIONS);
+    if (!pMsg) {
+        return num_blocks;
+    }
+
+    /* allocate blocks for UL transmittion */
+    while (WLS_EnqueueBlock(h_wls, nr5g_fapi_wls_va_to_pa(h_wls, pMsg)) > 0) {
+        num_blocks++;
+        pMsg = wls_fapi_alloc_buffer(0, MIN_UL_BUF_LOCATIONS);
+        if (!pMsg)
+            break;
+    }
+
+    // free not enqueued block
+    if (pMsg) {
+        wls_fapi_free_buffer(pMsg, MIN_UL_BUF_LOCATIONS);
+    }
+
+    return num_blocks;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param          A pointer to the phy instance table.
+ *
+ *  @return         0 if SUCCESS
+ *
+ *  @description    This function initializes WLS layer primitives and allocates
+ *                  memory needed to exchange APIs between FAPI and PHY.
+**/
+//------------------------------------------------------------------------------
+uint8_t nr5g_fapi_wls_init(
+    p_nr5g_fapi_cfg_t cfg)
+{
+    p_nr5g_fapi_wls_context_t p_wls_ctx = nr5g_fapi_wls_context();
+    const char *dev_name = "wls";
+
+    if (p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST] &&
+        p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST]) {
+        // NR5G_FAPI_LOG(ERROR_LOG, ("WLS instance already opened!"));
+        return FAILURE;
+    }
+
+    p_wls_ctx->shmem_size = cfg->wls.shmem_size;
+    p_wls_ctx->h_wls[NR5G_FAPI2MAC_WLS_INST] =
+        WLS_Open_Dual(dev_name /*"wls"cfg->wls.device_name */ ,
+        WLS_SLAVE_CLIENT,
+        cfg->wls.shmem_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])) {
+        // NR5G_FAPI_LOG(ERROR_LOG,("[NR5G_FAPI_ WLS] WLS instance connected."));
+        return FAILURE;
+    }
+    // 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\n");
+        return FAILURE;
+    }
+
+    pthread_mutex_init((pthread_mutex_t *)
+            &p_wls_ctx->fapi2phy_lock_send, NULL);
+    pthread_mutex_init((pthread_mutex_t *)
+            &p_wls_ctx->fapi2phy_lock_alloc, NULL);
+    pthread_mutex_init((pthread_mutex_t *)
+            &p_wls_ctx->fapi2mac_lock_send, NULL);
+    pthread_mutex_init((pthread_mutex_t *)
+            &p_wls_ctx->fapi2mac_lock_alloc, NULL);
+    return SUCCESS;
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]   pMemArray Pointer to WLS Memory Management Structure
+ *  @param[in]   pMemArrayMmeory 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 partitoned by the memory manager
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function creates memory blocks from a flat buffer which will be used for communication between FAPI and PHY
+ *
+**/
+//-------------------------------------------------------------------------------------------
+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);
+        }
+    }
+
+    NR5G_FAPI_MEMSET(alloc_track, sizeof(uint8_t) * ALLOC_TRACK_SIZE, 0,
+        sizeof(uint8_t) * ALLOC_TRACK_SIZE);
+
+    return SUCCESS;
+}
+
+//-------------------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]   pWls Pointer to the nr5g_fapi_wls_ctx structure
+ *
+ *  @return  0 if SUCCESS
+ *
+ *  @description
+ *  This function created a partition and blocks of WLS memory for API exchange between FAPI and PHY
+ *
+**/
+//-------------------------------------------------------------------------------------------
+int wls_fapi_create_partition(
+    p_nr5g_fapi_wls_context_t pWls)
+{
+#define WLS_HUGE_DEF_PAGE_SIZEA    0x40000000LL
+    static long hugePageSize = WLS_HUGE_DEF_PAGE_SIZEA;
+    //   NR5G_FAPI_MEMSET(pWls->pWlsMemBase , 0xCC, pWls->nTotalMemorySize);  // This is done by the Master Only
+    pWls->pPartitionMemBase =
+        (void *)(((uint8_t *) pWls->pWlsMemBase) + hugePageSize);
+    pWls->nPartitionMemSize = (pWls->nTotalMemorySize - hugePageSize);
+
+    pWls->nTotalBlocks = pWls->nPartitionMemSize / MSG_MAXSIZE;
+    return wls_fapi_create_mem_array(&pWls->sWlsStruct, pWls->pPartitionMemBase,
+        pWls->nPartitionMemSize, MSG_MAXSIZE);
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]      A pointer to the FAPI Memory Structure (Initialized by L2)
+ *  @param[out]     ppBlock Pointer where the allocated memory block is stored     
+ *
+ *  @return         0 if SUCCESS
+ *
+ *  @description    This function allocates a memory block from the pool
+ *
+**/
+//------------------------------------------------------------------------------
+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);
+        return FAILURE;
+    } 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 nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]      A pointer to the FAPI Memory Structure (Initialized by L2)
+ *  @param[in]      pBlock Pointer where the allocated memory block is stored     
+ *
+ *  @return         0 if SUCCESS
+ *
+ *  @description    This function frees a WLS block of memory and adds 
+ *                  it back to the pool
+ *
+**/
+//------------------------------------------------------------------------------
+uint32_t wls_fapi_free_mem_array(
+    PWLS_FAPI_MEM_STRUCT 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_fapi_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_fapi_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");
+                nr5g_fapi_wls_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_fapi_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_fapi_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 nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param          size (if 0 fixed size from pool)
+ *  @param          Number of locations
+ *
+ *  @return         0 if SUCCESS
+ *
+ *  @description    This function initializes WLS layer primitives and allocates
+ *                  memory needed to exchange APIs between FAPI and PHY.
+**/
+//------------------------------------------------------------------------------
+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();
+
+    if (pthread_mutex_lock((pthread_mutex_t *) & pWls->fapi2phy_lock_alloc)) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("unable to get lock alloc pthread mutex"));
+        exit(-1);
+    }
+
+    if (wls_fapi_alloc_mem_array(&pWls->sWlsStruct, &pBlock) != SUCCESS) {
+        printf("wls_fapi_alloc_buffer alloc error size[%d] loc[%d]\n", size,
+            loc);
+        nr5g_fapi_wls_print_stats();
+        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[loc]++;
+    else if (loc < MAX_UL_BUF_LOCATIONS)
+        pWls->nTotalUlBufAllocCnt++;
+
+    if (pthread_mutex_unlock((pthread_mutex_t *) & pWls->fapi2phy_lock_alloc)) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("unable to unlock alloc pthread mutex"));
+        exit(-1);
+    }
+
+    return pBlock;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param[in]      *pMsg Pointer to free
+ *
+ *  @return         void
+ *
+ *  @descriptioni   This function frees a block of memory and adds it back to 
+ *                  the pool.
+ *
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_free_buffer(
+    void *pMsg,
+    uint32_t loc)
+{
+    p_nr5g_fapi_wls_context_t pWls = nr5g_fapi_wls_context();
+
+    if (pthread_mutex_lock((pthread_mutex_t *) & pWls->fapi2phy_lock_alloc)) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("unable to lock alloc pthread mutex"));
+        exit(-1);
+    }
+    //printf("----------------wls_fapi_free_buffer: buf[%p] loc[%d]\n", pMsg, loc);
+    if (wls_fapi_free_mem_array(&pWls->sWlsStruct, (void *)pMsg) == SUCCESS) {
+        pWls->nAllocBlocks--;
+    } else {
+        printf("wls_fapi_free_buffer Free error\n");
+        nr5g_fapi_wls_print_stats();
+        exit(-1);
+    }
+
+    pWls->nTotalFreeCnt++;
+    if (loc < MAX_DL_BUF_LOCATIONS)
+        pWls->nTotalDlBufFreeCnt[loc]++;
+    else if (loc < MAX_UL_BUF_LOCATIONS)
+        pWls->nTotalUlBufFreeCnt++;
+
+    if (pthread_mutex_unlock((pthread_mutex_t *) & pWls->fapi2phy_lock_alloc)) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("unable to unlock alloc pthread mutex"));
+        exit(-1);
+    }
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @param          A pointer to a wls instance
+ *
+ *  @return         0 if SUCCESS
+ *
+ *  @description    This function initializes the WLS layer FAPI2PHY interface 
+ *                  primitives and allocates memory needed to exchange APIs 
+ *                  between FAPI and PHY.
+**/
+//------------------------------------------------------------------------------
+uint8_t nr5g_fapi2Phy_wls_init(
+    p_nr5g_fapi_wls_context_t pwls)
+{
+    int nBlocks = 0;
+    uint8_t retval = SUCCESS;
+
+    retval = wls_fapi_create_partition(pwls);
+    if ((nBlocks = wls_fapi_add_blocks_to_ul()) == 0) {
+        return FAILURE;
+    }
+
+    return retval;
+}
+
+uint8_t get_stats_location(
+    uint8_t msg_type)
+{
+    uint8_t loc;
+    switch (msg_type) {
+        case MSG_TYPE_PHY_CONFIG_REQ:
+            loc = MEM_STAT_CONFIG_REQ;
+            break;
+        case MSG_TYPE_PHY_START_REQ:
+            loc = MEM_STAT_START_REQ;
+            break;
+        case MSG_TYPE_PHY_STOP_REQ:
+            loc = MEM_STAT_STOP_REQ;
+            break;
+        case MSG_TYPE_PHY_SHUTDOWN_REQ:
+            loc = MEM_STAT_SHUTDOWN_REQ;
+            break;
+        case MSG_TYPE_PHY_DL_CONFIG_REQ:
+            loc = MEM_STAT_DL_CONFIG_REQ;
+            break;
+        case MSG_TYPE_PHY_UL_CONFIG_REQ:
+            loc = MEM_STAT_UL_CONFIG_REQ;
+            break;
+        case MSG_TYPE_PHY_UL_DCI_REQ:
+            loc = MEM_STAT_UL_DCI_REQ;
+            break;
+        case MSG_TYPE_PHY_TX_REQ:
+            loc = MEM_STAT_TX_REQ;
+            break;
+        case MSG_TYPE_PHY_DL_IQ_SAMPLES:
+            loc = MEM_STAT_DL_IQ_SAMPLES;
+            break;
+        case MSG_TYPE_PHY_UL_IQ_SAMPLES:
+            loc = MEM_STAT_UL_IQ_SAMPLES;
+            break;
+        default:
+            loc = MEM_STAT_DEFAULT;
+    }
+
+    return loc;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @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.
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_add_recv_apis_to_free(
+    PMAC2PHY_QUEUE_EL pListElem,
+    uint32_t idx)
+{
+    PMAC2PHY_QUEUE_EL pNextMsg = NULL;
+    L1L2MessageHdr *p_msg_header = NULL;
+    PRXULSCHIndicationStruct p_phy_rx_ulsch_ind = NULL;
+    int count, i;
+    uint8_t *ptr = NULL;
+
+    WLS_HANDLE h_wls;
+    p_nr5g_fapi_wls_context_t p_wls_ctx = nr5g_fapi_wls_context();
+    h_wls = p_wls_ctx->h_wls[NR5G_FAPI2PHY_WLS_INST];
+
+    count = g_to_free_recv_list_cnt[idx];
+    pNextMsg = pListElem;
+    while (pNextMsg) {
+        if (count >= TOTAL_FREE_BLOCKS) {
+            NR5G_FAPI_LOG(ERROR_LOG, ("%s: Reached max capacity of free list.\n"
+                    "\t\t\t\tlist index: %d list count: %d max list count: %d",
+                    __func__, idx, count, TOTAL_FREE_BLOCKS));
+            return;
+        }
+
+        g_to_free_recv_list[idx][count++] = (uint64_t) pNextMsg;
+        p_msg_header = (PL1L2MessageHdr) (pNextMsg + 1);
+        if (p_msg_header->nMessageType == MSG_TYPE_PHY_RX_ULSCH_IND) {
+            p_phy_rx_ulsch_ind = (PRXULSCHIndicationStruct) p_msg_header;
+            for (i = 0; i < p_phy_rx_ulsch_ind->nUlsch; i++) {
+                ptr = p_phy_rx_ulsch_ind->sULSCHPDUDataStruct[i].pPayload;
+                ptr = (uint8_t *) nr5g_fapi_wls_pa_to_va(h_wls, (uint64_t) ptr);
+
+                if (ptr) {
+                    g_to_free_recv_list[idx][count++] = (uint64_t) ptr;
+                }
+            }
+        }
+        pNextMsg = pNextMsg->pNext;
+    }
+
+    g_to_free_recv_list[idx][count] = 0L;
+    g_to_free_recv_list_cnt[idx] = count;
+
+    NR5G_FAPI_LOG(DEBUG_LOG, ("To Free %d\n", count));
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group 
+ *
+ *  @param[in]      idx subframe Number
+ *
+ *  @return         Number of blocks freed
+ *
+ *  @description    This function frees all blocks that have been added to the 
+ *                  free array
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_free_recv_free_list(
+    uint32_t idx)
+{
+    PMAC2PHY_QUEUE_EL pNextMsg = NULL;
+    int count = 0;
+
+    if (idx >= TO_FREE_SIZE) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("%s: list index: %d\n", __func__, idx));
+        return;
+    }
+
+    pNextMsg = (PMAC2PHY_QUEUE_EL) g_to_free_recv_list[idx][count];
+    while (pNextMsg) {
+        wls_fapi_free_buffer(pNextMsg, MIN_UL_BUF_LOCATIONS);
+        g_to_free_recv_list[idx][count++] = 0L;
+        if (g_to_free_recv_list[idx][count])
+            pNextMsg = (PMAC2PHY_QUEUE_EL) g_to_free_recv_list[idx][count];
+        else
+            pNextMsg = 0L;
+    }
+
+    NR5G_FAPI_LOG(DEBUG_LOG, ("Free %d\n", count));
+    g_to_free_recv_list_cnt[idx] = 0;
+
+    return;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group
+ *
+ *  @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.
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_add_send_apis_to_free(
+    PMAC2PHY_QUEUE_EL pListElem,
+    uint32_t idx)
+{
+    PMAC2PHY_QUEUE_EL pNextMsg = NULL;
+    L1L2MessageHdr *p_msg_header = NULL;
+    PRXULSCHIndicationStruct p_phy_rx_ulsch_ind = NULL;
+    int count, i;
+    uint8_t *ptr = NULL;
+
+    count = g_to_free_send_list_cnt[idx];
+    pNextMsg = pListElem;
+    while (pNextMsg) {
+        if (count >= TOTAL_FREE_BLOCKS) {
+            NR5G_FAPI_LOG(ERROR_LOG, ("%s: Reached max capacity of free list.\n"
+                    "\t\t\t\tlist index: %d list count: %d max list count: %d",
+                    __func__, idx, count, TOTAL_FREE_BLOCKS));
+            return;
+        }
+
+        g_to_free_send_list[idx][count++] = (uint64_t) pNextMsg;
+        p_msg_header = (PL1L2MessageHdr) (pNextMsg + 1);
+        if (p_msg_header->nMessageType == MSG_TYPE_PHY_RX_ULSCH_IND) {
+            p_phy_rx_ulsch_ind = (PRXULSCHIndicationStruct) p_msg_header;
+            for (i = 0; i < p_phy_rx_ulsch_ind->nUlsch; i++) {
+                ptr = p_phy_rx_ulsch_ind->sULSCHPDUDataStruct[i].pPayload;
+                if (ptr) {
+                    g_to_free_send_list[idx][count++] = (uint64_t) ptr;
+                }
+            }
+        }
+        pNextMsg = pNextMsg->pNext;
+    }
+
+    g_to_free_send_list[idx][count] = 0L;
+    g_to_free_send_list_cnt[idx] = count;
+
+    NR5G_FAPI_LOG(DEBUG_LOG, ("To Free %d\n", count));
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group 
+ *
+ *  @param[in]      idx subframe Number
+ *
+ *  @return         Number of blocks freed
+ *
+ *  @description    This function frees all blocks that have been added to the 
+ *                  free array
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_free_send_free_list(
+    uint32_t idx)
+{
+    PMAC2PHY_QUEUE_EL pNextMsg = NULL;
+    L1L2MessageHdr *p_msg_header = NULL;
+    int count = 0, loc = 0;
+
+    if (idx >= TO_FREE_SIZE) {
+        NR5G_FAPI_LOG(ERROR_LOG, ("%s: list index: %d\n", __func__, idx));
+        return;
+    }
+
+    pNextMsg = (PMAC2PHY_QUEUE_EL) g_to_free_send_list[idx][count];
+    while (pNextMsg) {
+        p_msg_header = (PL1L2MessageHdr) (pNextMsg + 1);
+        loc = get_stats_location(p_msg_header->nMessageType);
+        wls_fapi_free_buffer(pNextMsg, loc);
+        g_to_free_send_list[idx][count++] = 0L;
+        if (g_to_free_send_list[idx][count])
+            pNextMsg = (PMAC2PHY_QUEUE_EL) g_to_free_send_list[idx][count];
+        else
+            pNextMsg = 0L;
+    }
+
+    NR5G_FAPI_LOG(DEBUG_LOG, ("Free %d\n", count));
+    g_to_free_send_list_cnt[idx] = 0;
+
+    return;
+}
+
+//------------------------------------------------------------------------------
+/** @ingroup nr5g_fapi_source_framework_wls_lib_group 
+ *
+ *  @param[in]      idx subframe Number
+ *
+ *  @return         Number of blocks freed
+ *
+ *  @description    This function frees all blocks that have been added to the 
+ *                  free array
+**/
+//------------------------------------------------------------------------------
+void wls_fapi_free_list_all(
+    void)
+{
+    uint32_t idx;
+
+    for (idx = 0; idx < TO_FREE_SIZE; idx++) {
+        wls_fapi_free_send_free_list(idx);
+        wls_fapi_free_recv_free_list(idx);
+    }
+
+    nr5g_fapi_wls_print_stats();
+}