/* =========================================================================
 *  This code (or certain portions of it) is released for use under license
 *  from Imagination Technologies Limited.
 *  Copyright (c) 2019 Imagination Technologies Limited
 *  Copyright 2018-2022 NXP
 *
 *  NXP Confidential. This software is owned or controlled by NXP and may only
 *  be used strictly in accordance with the applicable license terms. By
 *  expressly accepting such terms or by downloading, installing, activating
 *  and/or otherwise using the software, you are agreeing that you have read,
 *  and that you agree to comply with and are bound by, such license terms. If
 *  you do not agree to be bound by the applicable license terms, then you may
 *  not retain, install, activate or otherwise use the software.
 *  <<< PFE Restricted Software >>>
 *
 * ========================================================================= */

/**
 * @file        pfe_hif_chnl_mcal.c
 * @brief       The HIF channel module source file.
 * @details     This file contains HIF channel-related functionality abstracted using
 *              configurable, HW-specific calls. Each HW platform shall supply its own
 *              pfe_hif_csr.h header implementing the HW-specific parts.
 *
 *              Default Mode
 *              ------------
 *              Default mode allows user to transmit and receive buffers using their
 *              physical addresses. There is no other functionality and only the
 *              default API is sufficient to handle the data-path:
 *                  - pfe_hif_chnl_can_accept_tx_num()
 *                  - pfe_hif_chnl_tx()
 *                  - pfe_hif_chnl_supply_rx_buffer()
 *                  - pfe_hif_chnl_rx()
 *
 *              TX example:
 *              if pfe_hif_chnl_can_accept_tx_num() is TRUE then
 *                pfe_hif_chnl_tx()
 *              endif
 *
 *              RX example:
 *              Supply RX buffers
 *              while pfe_hif_chnl_can_accept_rx_buf() do
 *                pfe_hif_chnl_supply_rx_buf()
 *              endwhile
 *
 *              Receive
 *              while (1)
 *                if pfe_hif_chnl_rx() then
 *                  1 Process the buffer
*                   2 pfe_hif_chnl_supply_rx_buf()
 *                endif
 *              endwhile
 *
 *              RX Buffer Management Mode
 *              -------------------------
 *              In case the PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED is set to TRUE the PFE HIF
 *              channel module provides full RX buffer management functionality. It
 *              creates pool of buffers and transparently populates the RX ring. Instead
 *              of default RX API the extended version is provided:
 *                  - pfe_hif_chnl_rx_va()
 *                  - pfe_hif_chnl_release_buf()
 *
 *              Every buffer received via pfe_hif_chnl_rx_va() must be subsequently
 *              released by the pfe_hif_chnl_release_rx_va(). With the RX management
 *              support also the pfe_hif_chnl_get_meta_size() is available for
 *              sanity check implementation related to size of the pre-allocated
 *              buffer-related meta storage.
 *
 *              TX example: The same as in the Default Mode case.
 *
 *              RX example:
 *              Sanity check
 *              if my metadata size does not match pfe_hif_chnl_get_meta_size()
 *                Throw error
 *              endif
 *
 *              Receive
 *              while (1)
 *                if pfe_hif_chnl_rx_va() then
 *                  1 Use pre-allocated metadata storage to bind custom data with the buffer for better performance
 *                  2 Process the buffer
 *                  3 When finished, call the pfe_hif_chnl_release_buf()
 *                endif
 *              endwhile
 *
 */

/*==================================================================================================
*                              SOURCE FILE VERSION INFORMATION
==================================================================================================*/
#define PFE_SRC_VERSION_CHECK 43100440

#include "pfe_cfg.h"
#include "oal.h"
#include "hal.h"

#if !defined(PFE_CFG_DETACHED_MINIHIF)
#include "Eth_43_PFE.h"
#include "pfe_cbus.h"
#endif /* PFE_CFG_DETACHED_MINIHIF */

#include "pfe_platform_cfg.h"
#include "pfe_hif_chnl.h"
#include "pfe_hif_ring.h"

#define DUMMY_TX_BUF_LEN        (64U + sizeof(pfe_ct_hif_tx_hdr_t))
#define DUMMY_RX_BUF_LEN        1544U

#if !defined(PFE_CFG_DETACHED_MINIHIF)
/* TODO: remove support for multiple HIF channels and put this directly into the pfe_hif_t (not just pointers) */
#ifdef PFE_CFG_HIF_NOCPY_SUPPORT
static pfe_hif_ring_t rx_rings_memory_nocpy;
static pfe_hif_ring_t tx_rings_memory_nocpy;
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
static pfe_hif_ring_t rx_rings_memory[HIF_CFG_MAX_CHANNELS];
static pfe_hif_ring_t tx_rings_memory[HIF_CFG_MAX_CHANNELS];
#endif /* PFE_CFG_DETACHED_MINIHIF */

/*==================================================================================================
                                       GLOBAL VARIABLES
==================================================================================================*/

#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
    #if !defined(PFE_CFG_DETACHED_MINIHIF)
        #define ETH_43_PFE_START_SEC_VAR_CLEARED_UNSPECIFIED_BUF_MEM
        #include "Eth_43_PFE_MemMap.h"
    #endif /* PFE_CFG_DETACHED_MINIHIF */
/* It should be static, but it is not to avoid issues with memory mapping in some compilers */
__attribute__((aligned(8))) uint8_t rx_buf_mem[ETH_43_PFE_MAX_RXBUF_POOLSZ];
__attribute__((aligned(8))) uint8_t dummy_tx_buf[DUMMY_TX_BUF_LEN];
__attribute__((aligned(8))) uint8_t dummy_rx_buf[DUMMY_RX_BUF_LEN];
    #if !defined(PFE_CFG_DETACHED_MINIHIF)
        #define ETH_43_PFE_STOP_SEC_VAR_CLEARED_UNSPECIFIED_BUF_MEM
        #include "Eth_43_PFE_MemMap.h"
    #endif /* PFE_CFG_DETACHED_MINIHIF */

/*==================================================================================================
                                       LOCAL VARIABLES
==================================================================================================*/

#define ETH_43_PFE_START_SEC_VAR_CLEARED_8
#include "Eth_43_PFE_MemMap.h"

static __attribute__((aligned(4))) uint8_t rx_meta_buf_mem[ETH_43_PFE_MAX_RXBUF_META_POOLSZ];

#define ETH_43_PFE_STOP_SEC_VAR_CLEARED_8
#include "Eth_43_PFE_MemMap.h"

#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)

#define ETH_43_PFE_START_SEC_VAR_CLEARED_8
#include "Eth_43_PFE_MemMap.h"

static __attribute__((aligned(4))) uint8_t rx_meta_buf_mem_hifnocpy[ETH_43_PFE_BMU2_BUF_CNT*ETH_43_PFE_META_BUF_SIZE];

#define ETH_43_PFE_STOP_SEC_VAR_CLEARED_8
#include "Eth_43_PFE_MemMap.h"

#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

/*==================================================================================================
                                   LOCAL FUNCTION PROTOTYPES
==================================================================================================*/

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_START_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */

#if !defined(PFE_CFG_DETACHED_MINIHIF)
static errno_t pfe_hif_chnl_flush_rx_bd_fifo(pfe_hif_chnl_t *chnl) __attribute__((cold));
static __attribute__((cold)) errno_t pfe_hif_chnl_flush_rx_bd_fifo_send_pkt(pfe_hif_chnl_t *chnl, uint32_t ii, void **tx_buf_va);
static __attribute__((cold)) errno_t pfe_hif_chnl_create_cfg(pfe_hif_chnl_t **chnl, const pfe_bmu_t *bmu, bool_t minihif);
#endif /* PFE_CFG_DETACHED_MINIHIF */

#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
static inline void rx_pool_init(rx_pool_t *pool, const void *memory,const void *meta_memory,
                                uint16_t buf_size, uint16_t buf_num
                               );
static void pfe_hif_chnl_refill_rx_buffers(pfe_hif_chnl_t *chnl) __attribute__((hot));
static inline void *rx_pool_get(rx_pool_t *pool);
static inline void *rx_pool_get_meta_buf(const rx_pool_t *pool, const void *buf_va);
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

static errno_t pfe_hif_chnl_init_tx_rx_ring(pfe_hif_chnl_t *chnl, pfe_hif_ring_t **tx_ring, pfe_hif_ring_t **rx_ring);
/*==================================================================================================
                                       LOCAL FUNCTIONS
==================================================================================================*/

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)

/*
 * @brief   Increment buffer allocation counter
 * @details To monitor how many BMU buffers have been allocated
 *          by a channel instance we need to provide a SW counter.
 */
static void pfe_hif_chnl_alloc_inc(pfe_hif_chnl_t *chnl)
{
    (void)oal_spinlock_lock(&chnl->a_lock);

    chnl->a_cnt++;

    (void)oal_spinlock_unlock(&chnl->a_lock);
}

/*
 * @brief   Decrement buffer allocation counter
 * @details To monitor how many BMU buffers have been allocated
 *          by a channel instance we need to provide a SW counter.
 */
static void pfe_hif_chnl_alloc_dec(pfe_hif_chnl_t *chnl)
{
    (void)oal_spinlock_lock(&chnl->a_lock);
    chnl->a_cnt--;

    (void)oal_spinlock_unlock(&chnl->a_lock);
}

/*
 * @brief   Get state of allocation counter
 * @details To monitor how many BMU buffers have been allocated
 *          by a channel instance we need to provide a SW counter.
 * @return  Current number of allocated buffers.
 */
static uint32_t pfe_hif_chnl_get_alloc_cnt(pfe_hif_chnl_t *chnl)
{
    uint32_t ret;

    ret = chnl->a_cnt;

    return ret;
}
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
static inline void rx_pool_init(rx_pool_t *pool, const void *memory,const void *meta_memory,
                                uint16_t buf_size, uint16_t buf_num
                               )
{
    pool->base_addr = (addr_t)memory;
    pool->meta_base_addr = (addr_t)meta_memory;
    pool->buf_size = buf_size;
    pool->buf_number = buf_num;
    pool->get_idx = 0U;
}

/* This function provides one Rx buffer on each call.
   It should be used to feed buffers to channel during initialization. Then the buffers remain there
   until the channel is destroyed. There is no need to return the buffer to the pool. */
static inline void *rx_pool_get(rx_pool_t *pool)
{
    void *pvBuffer = NULL;

    if (pool->get_idx < pool->buf_number)
    {
        pvBuffer = (void *)(pool->base_addr + ((addr_t)(pool->get_idx) * pool->buf_size));
        pool->get_idx++;
    }
    return pvBuffer;
}

/* Get meta buffer related to Rx buffer  */
static inline void *rx_pool_get_meta_buf(const rx_pool_t *pool, const void *buf_va)
{
    uint32_t index;

    /* Calculate index of provided Rx buffer */
    index = (uint16_t)(((addr_t)buf_va - pool->base_addr) / pool->buf_size);
    /* Calculate address of meta buffer with same index */
    return (void *)(pool->meta_base_addr + (index * ETH_43_PFE_META_BUF_SIZE));
}
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

#if !defined(PFE_CFG_DETACHED_MINIHIF)
/**
 * @brief       Channel master ISR
 * @param[in]   chnl The channel instance
 * @return      EOK if interrupt has been handled
 */
__attribute__((hot)) errno_t pfe_hif_chnl_isr(pfe_hif_chnl_t *chnl)
{
    errno_t ret;
    pfe_hif_chnl_event_t events;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        (void)oal_spinlock_lock(&chnl->lock);

        /* Run the low-level ISR to identify and process the interrupt */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            ret = pfe_hif_nocpy_cfg_isr(chnl->cbus_base_va, &events);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            ret = pfe_hif_chnl_cfg_isr(chnl->cbus_base_va, chnl->id, &events);
        }

        (void)oal_spinlock_unlock(&chnl->lock);

        /*  Run callbacks for identified interrupts here */
        if ((uint32_t)HIF_CHNL_EVT_RX_IRQ == ((uint32_t)events & (uint32_t)HIF_CHNL_EVT_RX_IRQ))
        {
            if (NULL != chnl->rx_cbk.cbk)
            {
                chnl->rx_cbk.cbk(chnl->rx_cbk.arg);
            }
            else
            {
                NXP_LOG_DEBUG("Unhandled HIF_CHNL_EVT_RX_IRQ detected\n");
            }
        }

        if ((uint32_t)HIF_CHNL_EVT_TX_IRQ == ((uint32_t)events & (uint32_t)HIF_CHNL_EVT_TX_IRQ))
        {
            if (NULL != chnl->tx_cbk.cbk)
            {
                chnl->tx_cbk.cbk(chnl->tx_cbk.arg);
            }
            else
            {
                NXP_LOG_DEBUG("Unhandled HIF_CHNL_EVT_TX_IRQ detected\n");
            }
        }
    }

    return ret;
}

/**
 * @brief       Mask channel interrupts
 * @param[in]   chnl The channel instance
 */
void pfe_hif_chnl_irq_mask(pfe_hif_chnl_t *chnl)
{
    (void)oal_spinlock_lock(&chnl->lock);

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        /* HIF_NOCPY */
        pfe_hif_nocpy_cfg_irq_mask(chnl->cbus_base_va);
    }
    else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    {
        pfe_hif_chnl_cfg_irq_mask(chnl->cbus_base_va, chnl->id);
    }

    (void)oal_spinlock_unlock(&chnl->lock);
}

/**
 * @brief       Unmask channel interrupts
 * @param[in]   chnl The channel instance
 */
void pfe_hif_chnl_irq_unmask(pfe_hif_chnl_t *chnl)
{
    (void)oal_spinlock_lock(&chnl->lock);
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        /* HIF_NOCPY */
        pfe_hif_nocpy_cfg_irq_unmask(chnl->cbus_base_va);
    }
    else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    {
        pfe_hif_chnl_cfg_irq_unmask(chnl->cbus_base_va, chnl->id);
    }

    (void)oal_spinlock_unlock(&chnl->lock);
}
#endif /* PFE_CFG_DETACHED_MINIHIF */

#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
/**
 * @brief   Supply fresh RX buffers to the channel
 * @details Function populates channel's RX resource with buffer from internal pool
 */
__attribute__((hot)) static void pfe_hif_chnl_refill_rx_buffers(pfe_hif_chnl_t *chnl)
{
    const void *new_buffer_va;
    const void *new_buffer_pa;
    errno_t ret;
    bool_t flags = FALSE;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        while (TRUE == pfe_hif_chnl_can_accept_rx_buf(chnl))
        {
            new_buffer_va = rx_pool_get(&(chnl->rx_pool));

            if (likely(NULL != new_buffer_va))
            {
                /*  Get physical address */
                new_buffer_pa = new_buffer_va;
                ret = pfe_hif_chnl_supply_rx_buf(chnl, new_buffer_pa, ETH_43_PFE_CFG_HIF_RX_BUF_SIZE);
                if (unlikely(EOK != ret))
                {
                    NXP_LOG_WARNING("HIF channel did not accept new RX buffer\n");
                    flags =  TRUE;
                }
            }
            else
            {
                /*  No more Rx buffers available, leave remaining BD empty */
                flags =  TRUE;
            }

            if(TRUE == flags)
            {
                break;
            }
        }
    }
}
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

#if !defined(PFE_CFG_DETACHED_MINIHIF)

/**
 * @brief           Create new HIF channel instance
 * @details         Creates and initializes HIF channel instance
 * @param[inout]    chnl Pointer to channel
 * @param[in]       bmu If set, the channel will use it to allocate RX buffers. It is mandatory
 *                  for HIF NOCPY channel abstraction.
 * @param[in]       minihif If minihif is TRUE, it means that this function is used to configure the HIF channel for the minihif purpose.
 * @return          EOK if success, error code otherwise
 */
static __attribute__((cold)) errno_t pfe_hif_chnl_create_cfg(pfe_hif_chnl_t **chnl, const pfe_bmu_t *bmu, bool_t minihif)
{
    errno_t ret = EOK;

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    /* When PFE_CFG_HIF_NOCPY_SUPPORT is defined and ((*chnl)->id != PFE_HIF_CHNL_NOCPY_ID), it means this HIF channel is used in minihif scenario */
    if ((*chnl)->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
        if (NULL == (*chnl)->bmu)
        {
            NXP_LOG_ERROR("HIF NOCPY channel requires BMU instance\n");
            ret = EINVAL;
#if defined(NXP_LOG_ENABLED)
            (void)oal_spinlock_destroy(&(*chnl)->a_lock);
#endif /* NXP_LOG_ENABLED */
            (void)oal_spinlock_destroy(&(*chnl)->lock);
            (void)oal_spinlock_destroy(&(*chnl)->rx_lock);
        }
        else
        {
            ret = EOK;
        }
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */
       /* HIF_NOCPY does not need per-channel initialization */
    }
    else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    {
#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
        if (NULL != bmu)
        {
            /* This is not supported. SW buffer pool will be used instead. */
            NXP_LOG_WARNING("BMU-based RX buffer pool not supported for standard HIF channels. SW pool will be used instead.\n");
        }
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

        if (EOK != oal_spinlock_lock(&(*chnl)->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

        ret = pfe_hif_chnl_cfg_init((*chnl)->cbus_base_va, (*chnl)->id);

        if (EOK != ret)
        {
            NXP_LOG_ERROR("HIF channel cfg init failed\n");
        }
        else
        {
            ret = pfe_hif_chnl_init(*chnl, minihif);
            if (EOK != ret)
            {
                NXP_LOG_ERROR("HIF channel init failed\n");
                (void)oal_spinlock_destroy(&(*chnl)->lock);
                (void)oal_spinlock_destroy(&(*chnl)->rx_lock);
            }
            else
            {
                if (EOK != oal_spinlock_unlock(&(*chnl)->lock))
                {
                    NXP_LOG_DEBUG("Mutex unlock failed\n");
                }
            }
        }
    }

    return ret;
}

/**
 * @brief           Create new HIF channel instance
 * @details         Creates and initializes HIF channel instance
 * @param[inout]    chnl Pointer to channel
 * @param[in]       cbus_base_va CBUS base virtual address
 * @param[in]       id Channel identifier to bind SW instance to a real HW HIF channel
 * @param[in]       bmu If set, the channel will use it to allocate RX buffers. It is mandatory
 *                  for HIF NOCPY channel abstraction.
 * @param[in]       minihif If minihif is TRUE, it means that this function is used to configure the HIF channel for the minihif purpose.
 * @return          EOK if success, error code otherwise
 */
__attribute__((cold)) errno_t pfe_hif_chnl_create(pfe_hif_chnl_t *chnl, addr_t cbus_base_va, uint32_t id, const pfe_bmu_t *bmu, bool_t minihif)
{
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL_ADDR == cbus_base_va))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if !defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            NXP_LOG_ERROR("HIF NOCPY support is not enabled\n");
            ret = ENODEV;
        }
        else if (id >= HIF_CFG_MAX_CHANNELS)
        {
            NXP_LOG_ERROR("Unsupported channel ID\n");
            ret = ENODEV;
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            if (NULL == chnl)
            {
                ret = EINVAL;
            }
            else
            {
                chnl->cbus_base_va = cbus_base_va;
                chnl->id = id;
                chnl->tx_ring = NULL;
                chnl->rx_ring = NULL;
#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED) && defined(PFE_CFG_HIF_NOCPY_SUPPORT)
                chnl->bmu = bmu;
                (void)memset((void*)&chnl->rx_pool, 0, sizeof(rx_pool_t));
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED && PFE_CFG_HIF_NOCPY_SUPPORT */

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
                chnl->a_lock = PFE_CHNL_A_LOCK_MUTEX;
                if (EOK != oal_spinlock_init(&chnl->a_lock))
                {
                    NXP_LOG_ERROR("Channel BMU allocation mutex initialization failed\n");
                    ret = ENOLCK;
                }
                else
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
                {
                    chnl->lock = PFE_CHNL_LOCK_MUTEX;
                    if (EOK != oal_spinlock_init(&chnl->lock))
                    {
                        NXP_LOG_ERROR("Channel mutex initialization failed\n");

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
                        if (EOK != oal_spinlock_destroy(&chnl->a_lock))
                        {
                            NXP_LOG_WARNING("Could not properly destroy mutex\n");
                        }
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
                        ret = ENOLCK;
                    }
                    else
                    {
                        chnl->rx_lock = PFE_CHNL_RX_LOCK_MUTEX;
                        if (EOK != oal_spinlock_init(&chnl->rx_lock))
                        {
                            NXP_LOG_ERROR("Channel RX mutex initialization failed\n");

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
                            (void)oal_spinlock_destroy(&chnl->a_lock);
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

                            (void)oal_spinlock_destroy(&chnl->lock);
                            ret = ENOLCK;
                        }
                        else
                        {
                            ret = pfe_hif_chnl_create_cfg(&chnl, bmu, minihif);
                        }
                    }
                }
            }
        }
    }

    return ret;
}

/**
 * @brief       Get channel identifier
 * @param[in]   chnl The channel instance
 * @return      The identifier of the channel
 */
__attribute__((pure, cold)) uint32_t pfe_hif_chnl_get_id(const pfe_hif_chnl_t *chnl)
{
    uint32_t chnl_id;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        chnl_id = UINT_MAX; /* Available via oal_types.h */
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        chnl_id = chnl->id;
    }
    return chnl_id;
}

/**
 * @brief       Enable TX
 * @details     Activate the TX ring and enable TX ring interrupts
 * @param[in]   chnl The channel instance
 * @retval      EOK Success
 * @retval      EFAULT TX ring not found
 */
__attribute__((cold)) errno_t pfe_hif_chnl_tx_enable(pfe_hif_chnl_t *chnl)
{
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (NULL == chnl->tx_ring)
        {
            NXP_LOG_ERROR("Can't enable TX: TX ring not set\n");
            ret = EFAULT;
        }
        else
        {
            if (EOK != oal_spinlock_lock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex lock failed\n");
            }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
            if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
            {
                /* HIF_NOCPY */
                pfe_hif_nocpy_cfg_tx_enable(chnl->cbus_base_va);
            }
            else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
            {
                /* HIF */
                pfe_hif_chnl_cfg_tx_enable(chnl->cbus_base_va, chnl->id);
            }

            if (EOK != oal_spinlock_unlock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex unlock failed\n");
            }
            ret = EOK;
        }
    }
    return ret;
}

/**
 * @brief       Disable TX
 * @details     De-activate the TX ring and disable TX ring interrupts. All buffers
 *              previously committed for transmission via pfe_hif_chnl_tx() are marked
 *              as "transmitted" and related TX confirmations can be retrieved via
 *              pfe_hif_chnl_get_tx_conf().
 * @param[in]   chnl The channel instance
 */
__attribute__((cold)) void pfe_hif_chnl_tx_disable(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

        /* Stop data transmission */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_tx_disable(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            pfe_hif_chnl_cfg_tx_disable(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex unlock failed\n");
        }
    }
}

/**
 * @brief       Enable RX
 * @details     Activate the RX ring and enable RX ring interrupts
 * @param[in]   chnl The channel instance
 * @retval      EOK Success
 * @retval      EFAULT RX ring not found
 */
__attribute__((cold)) errno_t pfe_hif_chnl_rx_enable(pfe_hif_chnl_t *chnl)
{
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (NULL == chnl->rx_ring)
        {
            NXP_LOG_ERROR("Can't enable RX: RX ring not set\n");
            ret = EFAULT;
        }
        else
        {
            if (EOK != oal_spinlock_lock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex lock failed\n");
            }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
            if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
            {
                /* HIF_NOCPY */
                pfe_hif_nocpy_cfg_rx_enable(chnl->cbus_base_va);
            }
            else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
            {
                /* HIF */
                pfe_hif_chnl_cfg_rx_enable(chnl->cbus_base_va, chnl->id);
            }

            if (EOK != oal_spinlock_unlock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex unlock failed\n");
            }
            ret = EOK;
        }
    }

    return ret;
}

/**
 * @brief       Disable RX
 * @details     De-activate the RX ring
 * @param[in]   chnl The channel instance
 * @note        Must not be preempted by pfe_hif_chnl_supply_rx_buf()
 */
__attribute__((cold)) void pfe_hif_chnl_rx_disable(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

        /* Stop data reception */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_rx_disable(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            pfe_hif_chnl_cfg_rx_disable(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex unlock failed\n");
        }
    }
}

/**
 * @brief       Trigger RX DMA
 * @details     One can trigger the HW to start processing of the RX ring.
 *              This is needed when the RX ring is modified after the
 *              pfe_hif_chnl_supply_rx_buf() call(s).
 * @param[in]   chnl The channel instance
 */
__attribute__((hot)) void pfe_hif_chnl_rx_dma_start(const pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /* No resource protection here, DMA trigger is atomic. */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_rx_dma_start(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            pfe_hif_chnl_cfg_rx_dma_start(chnl->cbus_base_va, chnl->id);
        }
    }
}

/**
 * @brief       Trigger TX DMA
 * @details     Trigger the HW to start processing of the TX ring. Needed
 *              after TX ring is modified after the pfe_hif_chnl_tx() call(s).
 * @param[in]   chnl The channel instance
 */
__attribute__((hot)) void pfe_hif_chnl_tx_dma_start(const pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /* No resource protection here. DMA trigger is atomic. */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_tx_dma_start(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            pfe_hif_chnl_cfg_tx_dma_start(chnl->cbus_base_va, chnl->id);
        }
    }
}

/**
 * @brief       Attach event callback
 * @param[in]   chnl The channel instance
 * @param[in]   event Event triggering the handler
 * @param[in]   isr The ISR
 * @param[in]   arg The ISR argument
 * @return      EOK if success, error code otherwise
 */
errno_t pfe_hif_chnl_set_event_cbk(pfe_hif_chnl_t *chnl, pfe_hif_chnl_event_t event, pfe_hif_chnl_cbk_t cbk, void *arg)
{
    errno_t ret = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

        if (HIF_CHNL_EVT_TX_IRQ == event)
        {
            /*  TX callback */
            chnl->tx_cbk.arg = arg;
            chnl->tx_cbk.cbk = cbk;
        }
        else if (HIF_CHNL_EVT_RX_IRQ == event)
        {
            /*  RX callback */
            chnl->rx_cbk.arg = arg;
            chnl->rx_cbk.cbk = cbk;
        }
        else if (HIF_CHNL_EVT_RX_OOB == event)
        {
            /*  Out of RX buffers event handler */
            chnl->rx_oob_cbk.arg = arg;
            chnl->rx_oob_cbk.cbk = cbk;
        }
        else
        {
            /*  More events need to be supported here */
            ret = EINVAL;
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex unlock failed\n");
        }
    }

    return ret;
}

/**
 * @brief       Disable RX interrupt
 * @param[in]   chnl The channel instance
 * @return      EOK if success, error code otherwise
 */
__attribute__((hot)) void pfe_hif_chnl_rx_irq_mask(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_rx_irq_mask(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            pfe_hif_chnl_cfg_rx_irq_mask(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }
    }
}

/**
 * @brief       Enable RX interrupt
 * @param[in]   chnl The channel instance
 * @return      EOK if success, error code otherwise
 */
__attribute__((hot)) void pfe_hif_chnl_rx_irq_unmask(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_rx_irq_unmask(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            pfe_hif_chnl_cfg_rx_irq_unmask(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex unlock failed\n");
        }
    }
}

/**
 * @brief       Disable TX interrupt
 * @param[in]   chnl The channel instance
 * @return      EOK if success, error code otherwise
 */
__attribute__((hot)) void pfe_hif_chnl_tx_irq_mask(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_tx_irq_mask(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            pfe_hif_chnl_cfg_tx_irq_mask(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex unlock failed\n");
        }
    }
}

/**
 * @brief       Enable TX interrupt
 * @param[in]   chnl The channel instance
 * @return      EOK if success, error code otherwise
 */
__attribute__((hot)) void pfe_hif_chnl_tx_irq_unmask(pfe_hif_chnl_t *chnl)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if (EOK != oal_spinlock_lock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            pfe_hif_nocpy_cfg_tx_irq_unmask(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            pfe_hif_chnl_cfg_tx_irq_unmask(chnl->cbus_base_va, chnl->id);
        }

        if (EOK != oal_spinlock_unlock(&chnl->lock))
        {
            NXP_LOG_DEBUG("Mutex lock failed\n");
        }
    }
}
#endif /* PFE_CFG_DETACHED_MINIHIF */

/**
 * @brief       Get TX confirmation status
 * @details     After pfe_hif_chnl_tx() call the HIF channel will transmit the
 *              supplied buffer. Once the transmission has been done a TX confirmation
 *              is generated. This function can be used to query the channel whether
 *              some new TX confirmations have been generated and are ready to be
 *              processed.
 * @param[in]   chnl The channel instance
 * @return      TRUE if channel got new TX confirmation, FALSE otherwise
 */
__attribute__((pure, hot)) bool_t pfe_hif_chnl_has_tx_conf(const pfe_hif_chnl_t *chnl)
{
    bool_t has_tx_conf;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        has_tx_conf = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        has_tx_conf = (0U == pfe_hif_ring_get_fill_level(chnl->tx_ring)) ? FALSE : TRUE;
    }
    return has_tx_conf;
}

/**
 * @brief       Query if new RX buffer can be supplied
 * @param       chnl The channel instance
 * @return      TRUE if RX resource can accept new buffer
 */
__attribute__((pure, hot)) bool_t pfe_hif_chnl_can_accept_rx_buf(const pfe_hif_chnl_t *chnl)
{
    bool_t accept_rx;
    uint32_t fill_level;
    uint32_t ring_len;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        accept_rx = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /*  A single entry must remain unused within the ring
            because HIF expects that. */
        fill_level = pfe_hif_ring_get_fill_level(chnl->rx_ring);
        ring_len = pfe_hif_ring_get_len(chnl->rx_ring);

        accept_rx = ( fill_level < ( ring_len - 1U));
    }
    return accept_rx;
}

/**
 * @brief       Check if channel can accept number of TX requests
 * @param[in]   chnl The channel instance
 * @param[in]   num Number of TX requests
 * @retval      TRUE Channel can accept 'num' TX requests (buffers)
 * @retval      FALSE Not enough space in TX FIFO
 */
__attribute__((pure, hot)) bool_t pfe_hif_chnl_can_accept_tx_num(const pfe_hif_chnl_t *chnl, uint16_t num)
{
    bool_t accept_tx;
    uint32_t fill_level;
    uint32_t ring_len;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        accept_tx  = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /*  A single entry must remain unused within the ring because HIF expects that. */
        fill_level = pfe_hif_ring_get_fill_level(chnl->tx_ring);
        ring_len = pfe_hif_ring_get_len(chnl->tx_ring);

        accept_tx = ((ring_len - 1U - fill_level) >= num);
    }
    return accept_tx;
}

/**
 * @brief       Check if the TX FIFO is empty
 * @param[in]   chnl The channel instance
 * @retval      TRUE TX FIFO is empty
 * @retval      FALSE TX FIFO contains entries waiting for transmission
 */
__attribute__((pure, hot)) bool_t pfe_hif_chnl_tx_fifo_empty(const pfe_hif_chnl_t *chnl)
{
    bool_t is_empty;
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        is_empty = TRUE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        is_empty = (0U == pfe_hif_ring_get_fill_level(chnl->tx_ring));
    }
    return is_empty;
}

/**
 * @brief       Get the RX FIFO depth
 * @param[in]   chnl The channel instance
 * @return      Size of the RX FIFO in number of entries
 */
__attribute__((pure, cold)) uint32_t pfe_hif_chnl_get_rx_fifo_depth(const pfe_hif_chnl_t *chnl)
{
    uint32_t rx_fifo_depth;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        rx_fifo_depth = 0U;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        rx_fifo_depth = pfe_hif_ring_get_len(chnl->rx_ring);
    }
    return rx_fifo_depth;
}

/**
 * @brief       Get the TX FIFO depth
 * @param[in]   chnl The channel instance
 * @return      Size of the TX FIFO in number of entries
 */
__attribute__((pure, cold)) uint32_t pfe_hif_chnl_get_tx_fifo_depth(const pfe_hif_chnl_t *chnl)
{
    uint32_t tx_fifo_depth;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        tx_fifo_depth = 0U;
    }
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        tx_fifo_depth = pfe_hif_ring_get_len(chnl->tx_ring);
    }
    return tx_fifo_depth;
}

/**
 * @brief       Request transmission of a buffer
 * @note        The TX resource availability shall be checked before this function
 *              is called using the pfe_hif_chnl_can_accept_tx_buf() call.
 * @note        Function is __NOT__ reentrant
 * @param[in]   chnl The channel instance
 * @param[in]   buf_pa Physical address of the buffer to be transmitted
 * @param[in]   buf_va Virtual address of the buffer to be transmitted
 * @param[in]   len Length of the buffer in bytes
 * @param[in]   lifm The last-in-frame indicator. Complete packet can consist
 *                   of multiple buffers. The last one shall be marked with
 *                   lifm=TRUE.
 * @retval      EOK Success
 * @retval      ENOSPC TX queue is full
 * @retval      EIO Internal error
 */
__attribute__((hot)) errno_t pfe_hif_chnl_tx(const pfe_hif_chnl_t *chnl, const void *buf_pa, const void *buf_va, uint32_t len, bool_t lifm)
{
    errno_t ret = EOK;
    uint32_t lmem_header_size;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == buf_pa) || (NULL == buf_va)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if (TRUE == HAL_HANDLE_CACHE)
        /*  Flush cache over the buffer */
        oal_mm_cache_flush(buf_va, buf_pa, len);
#endif /* HAL_HANDLE_CACHE */

        (void)buf_va;
        lmem_header_size = pfe_hif_chnl_get_lmem_hdr_size(chnl);
        /*  Enqueue the buffer into TX ring */
        ret = pfe_hif_ring_enqueue_buf(chnl->tx_ring, buf_pa, len, lmem_header_size, lifm);

#if !defined(PFE_CFG_DETACHED_MINIHIF)
        if (TRUE == lifm)
        {
            /*  Trigger the DMA */
            pfe_hif_chnl_tx_dma_start(chnl);
        }
#endif /* PFE_CFG_DETACHED_MINIHIF */
    }

    return ret;
}

/**
 * @brief       Get TX confirmation
 * @details     Each frame transmitted via pfe_hif_chnl_tx() will produce exactly
 *              one TX confirmation which can be retrieved by this function.
 * @param[in]   chnl The channel instance
 * @retval      EOK Next frame has been transmitted
 * @retval      EAGAIN No pending confirmations
 */
__attribute__((hot)) errno_t pfe_hif_chnl_get_tx_conf(pfe_hif_chnl_t *chnl)
{
    bool_t lifm;
    errno_t ret = EAGAIN;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /*  Get all transmitted chunks but only the last-in-frame
            will be reported as TX confirmation. */
        while (EOK == pfe_hif_ring_dequeue_plain(chnl->tx_ring, &lifm))
        {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
            if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
            {
                /* Decrement BMU allocations counter. It is here because we expect that
                   the PFE HW just released a TX buffer previously allocated from BMU
                   pool within the pfe_hif_chnl_tx(). */
                pfe_hif_chnl_alloc_dec(chnl);
            }
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

            if (TRUE == lifm)
            {
                ret = EOK;
                break;
            }
        }
    }
    return ret;
}

#if (FALSE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
/**
 * @brief       Receive a buffer
 * @details     When channel has received some data into RX buffer then this
 *              function will retrieve it.
 * @note        The RX resource availability shall be checked before this function
 *              is called using the pfe_hif_chnl_can_accept_rx_buf() call.
 * @param[in]   chnl The channel instance
 * @param[out]  buf_pa Pointer to memory where pointer to the received data shall
 *                     be written (physical address, as seen by host)
 * @param[out]  len Pointer to memory where length in bytes of the received
 *                  data shall be written
 * @param[out]  lifm Pointer to memory where the last-in-frame flag shall be
 *                   written
 * @retval      EOK Buffer received
 * @retval      EAGAIN No more data to receive right now
 */
__attribute__((hot)) errno_t pfe_hif_chnl_rx(pfe_hif_chnl_t *chnl, void **buf_pa, uint32_t *len, bool_t *lifm)
{
    errno_t err;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == buf_pa) || (NULL == len) || (NULL == lifm) || (NULL == chnl->rx_ring)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        err = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {

        err = pfe_hif_ring_dequeue_buf(chnl->rx_ring, buf_pa, len, lifm);

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* Increment BMU allocations counter. We have not allocated a buffer from BMU
               directly but the HW did that and then provided us the buffer. Therefore we
               need to properly handle it (release it once it has been processed). So we
               are counting it as allocated buffer here... */
            pfe_hif_chnl_alloc_inc(chnl);
        }
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

        /*  Check if ring has enough RX buffers */
        if (unlikely(0U == pfe_hif_ring_get_fill_level(chnl->rx_ring)))
        {
            /*  Out of RX buffers */
            if (likely(NULL != chnl->rx_oob_cbk.cbk))
            {
                chnl->rx_oob_cbk.cbk(chnl->rx_oob_cbk.arg);
            }
        }
    }

    return err;
}
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */
#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)

/**
 * @brief       Receive a buffer (virtual address)
 * @details     When channel has received some data into RX buffer then this
 *              function will retrieve it.
 * @note        The RX resource availability shall be checked before this function
 *              is called using the pfe_hif_chnl_can_accept_rx_buf() call.
 * @param[in]   chnl The channel instance
 * @param[out]  buf_va Pointer to memory where pointer to the received data shall
 *                     be written (virtual address, as seen by host)
 * @param[out]  len Pointer to memory where length in bytes of the received
 *                  data shall be written
 * @param[out]  lifm Pointer to memory where the last-in-frame flag shall be
 *                   written
 * @param[out]  meta Pointer to memory where pointer to pre-allocated memory
 *                   associated with the returned buffer shall be stored. Size
 *                   of the memory can be obtained by the pfe_hif_chnl_get_meta_size().
 * @retval      EOK Buffer received
 * @retval      EAGAIN No more data to receive right now
 * @retval      ENOMEM Out of memory
 */
__attribute__((hot)) errno_t pfe_hif_chnl_rx_va(pfe_hif_chnl_t *chnl, void **buf_va, uint32_t *len, bool_t *lifm, void **meta)
{
    errno_t ret;
    void *buf_pa;
    pfe_ct_hif_rx_hdr_t *hif_hdr_ptr = NULL;
    uint32_t vlan_header;
    uint32_t flags_map[4] = {HIF_RX_HIF0_VLAN, HIF_RX_HIF1_VLAN, HIF_RX_HIF2_VLAN, HIF_RX_HIF3_VLAN};

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    const pfe_ct_post_cls_hdr_t *pcls_hdr = NULL;
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == buf_va) || (NULL == len) || (NULL == lifm) || (NULL == meta)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        ret = pfe_hif_ring_dequeue_buf(chnl->rx_ring, &buf_pa, len, lifm);
        if (EOK == ret)
        {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
            if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
            {
                /* HIF NOCPY */

                /* Addresses coming from the ring are physical addresses of buffers provided by BMU. The
                   buffer contains so called post-classification header the PFE classifier is internally
                   using as well as specific HIF header. Headers start from buffer offset 0x0 and we shall
                   strip-off the post-classification header here since upper layers do not know about such
                   thing. The space can be (and will be) used as the buffer-specific metadata storage. */
                *buf_va = pfe_bmu_get_va(chnl->bmu, (addr_t)buf_pa);
#if defined(PFE_CFG_NULL_ARG_CHECK)
                if (unlikely(NULL == *buf_va))
                {
                    NXP_LOG_DEBUG("Fatal: BMU converted p0x%p to v0x0\n", (uint32_t)(addr_t)buf_pa);
                }
                else
#endif /* PFE_CFG_NULL_ARG_CHECK */
                {
                    /* Get pointer to the pre-allocated memory location where
                       a buffer-related metadata can be stored. */
                    *meta = &rx_meta_buf_mem_hifnocpy[(((addr_t)*buf_va / PFE_CFG_BMU2_BUF_SIZE) % ETH_43_PFE_BMU2_BUF_CNT)*ETH_43_PFE_META_BUF_SIZE];

                    /* Get post-classification header to get data offset */
                    pcls_hdr = (pfe_ct_post_cls_hdr_t *)*buf_va;

                    /* Skip the post-classification header to gain space for metadata storage.
                       The pfe_hif_chnl_release_buf() must be aware of this adjustment before
                       it will attempt to release buffer back to BMU hardware pool. This will
                       ensure the caller will receive also the HIF TX header but can reuse it
                        for custom purposes (see the pfe_hif_chnl_get_meta_size()). */
                    *buf_va = (void *)((addr_t)*buf_va + pcls_hdr->reserved[0]);

#if (TRUE == HAL_HANDLE_CACHE)
                    /* Invalidate cache over the buffer */
                    oal_mm_cache_inval(*buf_va, buf_pa, *len);
#endif /* HAL_HANDLE_CACHE */

#if defined(NXP_LOG_ENABLED)
                    /* Increment BMU allocations counter. We have not allocated a buffer from BMU
                       directly but the HW did that and then provided us the buffer. Therefore we
                       need to properly handle it (release it once it has been processed). So we
                       are counting this reception as allocated buffer here... */
                    pfe_hif_chnl_alloc_inc(chnl);
#endif /* NXP_LOG_ENABLED */
                }
            }
            else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
            {
                /* Return virtual address */
                *buf_va = buf_pa;

                hif_hdr_ptr = (pfe_ct_hif_rx_hdr_t *) *buf_va;
                /* Check if pkt is tagged */
                if ((oal_ntohl(hif_hdr_ptr->flags) & (flags_map[chnl->id])) != (uint32_t)0)
                {
                    /* On HIF hw is adding the vlan tag at 12 byte offset from the beggining
                       of the buffer. Copy the vlan header to the right position at offset 12
                       of the pkt witch is after the hif rx header. */
                    vlan_header = hif_hdr_ptr->rx_timestamp_s;
                    (void)memmove(((uint8_t *)*buf_va) + (2U * MAC_ADDRESS_SIZE), ((uint8_t *)*buf_va) + (sizeof(pfe_ct_hif_rx_hdr_t)), (2U * MAC_ADDRESS_SIZE) + (sizeof(vlan_header)));
                    (void)memcpy(((uint8_t *)*buf_va) + (sizeof(pfe_ct_hif_rx_hdr_t)) + (2U * MAC_ADDRESS_SIZE), &vlan_header, sizeof(vlan_header));
                }

#if (TRUE == HAL_HANDLE_CACHE)
                /* Invalidate cache over the received data area */
                oal_mm_cache_inval(*buf_va, buf_pa, *len);
#endif /* HAL_HANDLE_CACHE */

                /* Return pointer to the pre-allocated memory location where
                   a buffer-related metadata can be stored. */
                *meta = rx_pool_get_meta_buf(&chnl->rx_pool, *buf_va);
            }
        }

        /* Check if ring has enough RX buffers */
        if (0U == pfe_hif_ring_get_fill_level(chnl->rx_ring))
        {
            /* Out of RX buffers */
            if (NULL != chnl->rx_oob_cbk.cbk)
            {
                chnl->rx_oob_cbk.cbk(chnl->rx_oob_cbk.arg);
            }
        }
    }

    return ret;
}

/**
 * @brief       Get size of metadata storage returned by the pfe_hif_chnl_rx_va()
 * @details     When driver is willing to use the pre-allocated storage associated
 *              with every RX buffer it must not write more data than is the
 *              pre-allocated block size. To ensure that, it shall call this API
 *              to get maximum number of bytes which can be written to the 'meta'
 *              memory location returned by the pfe_hif_chnl_rx_va().
 * @param[in]   chnl The channel instance
 * @return      Size of the metadata storage pointed by the 'meta' arugument of
 *              the pfe_hif_chnl_rx_va().
 */
__attribute__((cold)) uint32_t pfe_hif_chnl_get_meta_size(const pfe_hif_chnl_t *chnl)
{
    uint32_t meta_size;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        meta_size = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF NOCPY */

            /* In case of HIF NOCPY we're using whole RX packet header headroom
               for metadata storage. The headroom includes post-classification
               header and the HIF header. Both can be overwritten by custom data. */
            meta_size = sizeof(pfe_ct_post_cls_hdr_t) + sizeof(pfe_ct_hif_rx_hdr_t);
        }
        else
#elif !defined(PFE_CFG_NULL_ARG_CHECK)
        (void)chnl;
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            meta_size = ETH_43_PFE_META_BUF_SIZE;
        }
    }

    return meta_size;
}

/**
 * @brief       Release a channel provided buffer
 * @param[in]   chnl The channel instance
 * @param[in]   buf_va Pointer to buffer to be released (virtual)
 * @retval      EOK if success, error code otherwise
 */
__attribute__((hot)) errno_t pfe_hif_chnl_release_buf(pfe_hif_chnl_t *chnl, const void *buf_va)
{
    addr_t buf_pa;
    errno_t ret;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF NOCPY */

            /* Get physical address */
            buf_pa = (addr_t)pfe_bmu_get_pa(chnl->bmu, (addr_t)buf_va);

            /* Apply the correction due to post-classification header skip done
               during the buffer reception. */
            buf_pa = buf_pa - sizeof(pfe_ct_post_cls_hdr_t);

            /* Release the buffer to BMU pool. Resource protection is embedded. */
            pfe_bmu_free_buf(chnl->bmu, buf_pa);

#if defined(NXP_LOG_ENABLED)
            /* Decrement BMU allocations counter */
            pfe_hif_chnl_alloc_dec(chnl);
#endif /* NXP_LOG_ENABLED */

            ret = EOK;
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            buf_pa = (addr_t)buf_va;

            if (unlikely(NULL == (void *)buf_pa))
            {
                NXP_LOG_ERROR("VA->PA conversion failed, origin buffer VA: v0x%p\n", buf_va);
            }

#if (TRUE == HAL_HANDLE_CACHE)
            /* Without this flush the invalidation does not properly work. Recycled buffers
               are not properly invalidated when this line is missing. */
            oal_mm_cache_flush(buf_va, (void *)buf_pa, ETH_43_PFE_CFG_HIF_RX_BUF_SIZE);
#endif /* HAL_HANDLE_CACHE */

            if (unlikely(EOK != oal_spinlock_lock(&chnl->rx_lock)))
            {
                NXP_LOG_DEBUG("Mutex lock failed\n");
            }

            /* Release the buffer to ring */
            ret = pfe_hif_ring_enqueue_buf(chnl->rx_ring, (void *)buf_pa, ETH_43_PFE_CFG_HIF_RX_BUF_SIZE, 0U, TRUE);

            if (unlikely(EOK != oal_spinlock_unlock(&chnl->rx_lock)))
            {
                NXP_LOG_DEBUG("Mutex unlock failed\n");
            }
        }
    }

    return ret;
}
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

/**
 * @brief       Supply RX buffer to be used for data reception
 * @param[in]   chnl The channel instance
 * @param[in]   buf_pa The RX buffer to be supplied (physical address, as seen
 *                     by host
 * @param[in]   size Size of the supplied buffer in bytes
 * @return      EOK Success
 * @note        Must not be preempted by pfe_hif_chnl_rx_disable()
 */
__attribute__((hot)) errno_t pfe_hif_chnl_supply_rx_buf(const pfe_hif_chnl_t *chnl, const void *buf_pa, uint32_t size)
{
    errno_t ret = EOK;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == buf_pa)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            (void)chnl;
            (void)buf_pa;
            (void)size;
            /* There is nothing to supply to HIF NOCPY */
            ret = EINVAL;
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            ret = pfe_hif_ring_enqueue_buf(chnl->rx_ring, buf_pa, size, 0U, TRUE);
            if (unlikely(EOK != ret))
            {
                NXP_LOG_WARNING("pfe_hif_ring_enqueue_buf() failed: %d\n", ret);
            }
        }
    }

    return ret;
}

/**
 * @brief       Assign RX BD ring
 * @details     Configure RX buffer descriptor ring address of the channel.
 *              This binds channel with a RX BD ring.
 * @param[in]   chnl The channel instance
 * @param[in]   ring The ring instance
 * @retval      EOK Success
 * @retval      EFAULT Invalid ring instance
 */
__attribute__((cold)) errno_t pfe_hif_chnl_set_rx_ring(pfe_hif_chnl_t *chnl, pfe_hif_ring_t *ring)
{
    const void *rx_ring_pa, *wb_tbl_pa;
    errno_t ret;
#if !defined(PFE_CFG_DETACHED_MINIHIF)
    uint32_t wb_tbl_len = 0U;
#endif /*PFE_CFG_DETACHED_MINIHIF*/

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == ring)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        rx_ring_pa = pfe_hif_ring_get_base_pa(ring);
        wb_tbl_pa = pfe_hif_ring_get_wb_tbl_pa(ring);

        if (NULL == rx_ring_pa)
        {
            NXP_LOG_ERROR("RX ring physical address is NULL\n");
            ret = EFAULT;
        }
        else
        {
            if (EOK != oal_spinlock_lock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex lock failed\n");
            }

            /* Access HW only if CBUS address was provided */
#if !defined(PFE_CFG_DETACHED_MINIHIF)
            if(NULL_ADDR != chnl->cbus_base_va)
            {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
                if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
                {
                    (void)wb_tbl_pa;
                    (void)wb_tbl_len;
                    /* HIF_NOCPY */
                    pfe_hif_nocpy_cfg_set_rx_bd_ring_addr(chnl->cbus_base_va, rx_ring_pa);
                }
                else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
                {
                    /* HIF */
                    pfe_hif_chnl_cfg_set_rx_bd_ring_addr(chnl->cbus_base_va, chnl->id, rx_ring_pa);
                    if (NULL != wb_tbl_pa)
                    {
                        wb_tbl_len = pfe_hif_ring_get_wb_tbl_len(ring);
                        pfe_hif_chnl_cfg_set_rx_wb_table(chnl->cbus_base_va, chnl->id, wb_tbl_pa, wb_tbl_len);
                    }
                }
            }
#else
            (void)wb_tbl_pa;
#endif /*PFE_CFG_DETACHED_MINIHIF*/

            chnl->rx_ring = ring;

            if (EOK != oal_spinlock_unlock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex unlock failed\n");
            }
            ret = EOK;
        }
    }

    return ret;
}

/**
 * @brief       Assign TX BD ring
 * @details     Configure TX buffer descriptor ring address of the channel.
 *              This binds channel with a TX BD ring.
 * @param[in]   chnl The channel instance
 * @param[in]   ring The ring instance
 * @retval      EOK Success
 * @retval      EFAULT Invalid ring instance
 */
__attribute__((cold)) errno_t pfe_hif_chnl_set_tx_ring(pfe_hif_chnl_t *chnl, pfe_hif_ring_t *ring)
{
    const void *tx_ring_pa, *wb_tbl_pa;
    errno_t ret;
#if !defined(PFE_CFG_DETACHED_MINIHIF)
    uint32_t wb_tbl_len = 0U;
#endif /*PFE_CFG_DETACHED_MINIHIF*/

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely((NULL == chnl) || (NULL == ring)))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        tx_ring_pa = pfe_hif_ring_get_base_pa(ring);
        wb_tbl_pa = pfe_hif_ring_get_wb_tbl_pa(ring);

        if (NULL == tx_ring_pa)
        {
            NXP_LOG_ERROR("TX ring physical address is NULL\n");
            ret = EFAULT;
        }
        else
        {
            if (EOK != oal_spinlock_lock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex lock failed\n");
            }

            /* Access HW only if CBUS address was provided */
#if !defined(PFE_CFG_DETACHED_MINIHIF)
            if(NULL_ADDR != chnl->cbus_base_va)
            {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
                if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
                {
                    (void)wb_tbl_pa;
                    (void)wb_tbl_len;
                    /* HIF_NOCPY */
                    pfe_hif_nocpy_cfg_set_tx_bd_ring_addr(chnl->cbus_base_va, tx_ring_pa);
                }
                else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
                {
                    /* HIF */
                    pfe_hif_chnl_cfg_set_tx_bd_ring_addr(chnl->cbus_base_va, chnl->id, tx_ring_pa);

                    if (NULL != wb_tbl_pa)
                    {
                        wb_tbl_len = pfe_hif_ring_get_wb_tbl_len(ring);
                        pfe_hif_chnl_cfg_set_tx_wb_table(chnl->cbus_base_va, chnl->id, wb_tbl_pa, wb_tbl_len);
                    }
                }
            }
#else
            (void)wb_tbl_pa;
#endif /*PFE_CFG_DETACHED_MINIHIF*/

            chnl->tx_ring = ring;

            if (EOK != oal_spinlock_unlock(&chnl->lock))
            {
                NXP_LOG_DEBUG("Mutex unlock failed\n");
            }
            ret = EOK;
        }
    }

    return ret;
}

/**
 * @brief       Initialize a channel
 * @details     Function prepares the HIF channel according to user-supplied parameters.
 *              This includes allocation of resources and configuration of the hardware.
 *              Routine must be called before RX or TX functionality is enabled.
 * @param[in]   chnl The channel instance
 * @return      EOK Success
 */
static errno_t pfe_hif_chnl_init_tx_rx_ring(pfe_hif_chnl_t *chnl, pfe_hif_ring_t **tx_ring, pfe_hif_ring_t **rx_ring)
{
    errno_t ret = EOK;
    bool_t bNocpy = FALSE;

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        if (PFE_HIF_CHNL_NOCPY_ID == chnl->id)
        {
            bNocpy = TRUE;
        }
    }
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

#if !defined(PFE_CFG_DETACHED_MINIHIF)
    ret = pfe_hif_ring_create(*rx_ring, PFE_HIF_RX_RING_CFG_LENGTH, TRUE, bNocpy);
    if (EOK != ret)
    {
        NXP_LOG_ERROR("Couldn't create RX BD ring\n");
        /*  Destroy unattached rings */
        (void)pfe_hif_ring_destroy(*rx_ring);
        ret = EFAULT;
    }
    /*  Bind RX BD ring to channel */
    else if (EOK != pfe_hif_chnl_set_rx_ring(chnl, *rx_ring))
    {
        /*  Destroy unattached rings */
        (void)pfe_hif_ring_destroy(*rx_ring);
        ret = EFAULT;
    }
    else
    {
        ret = pfe_hif_ring_create(*tx_ring, PFE_HIF_TX_RING_CFG_LENGTH, FALSE, bNocpy);
        if (EOK != ret)
        {
            NXP_LOG_ERROR("Couldn't create TX BD ring\n");
            /*  Destroy unattached rings */
            (void)pfe_hif_ring_destroy(*rx_ring);
            (void)pfe_hif_ring_destroy(*tx_ring);
            ret = EFAULT;
        }
        /*  Bind TX BD ring to channel */
        else if (EOK != pfe_hif_chnl_set_tx_ring(chnl, *tx_ring))
        {
            /*  Destroy unattached rings */
            (void)pfe_hif_ring_destroy(*rx_ring);
            (void)pfe_hif_ring_destroy(*tx_ring);
            ret = EFAULT;
        }
        else
        {
            /* Required by Misra */
        }
    }
#endif /*PFE_CFG_DETACHED_MINIHIF*/

    return ret;
}

/**
 * @brief       Initialize a channel
 * @details     Function prepares the HIF channel according to user-supplied parameters.
 *              This includes allocation of resources and configuration of the hardware.
 *              Routine must be called before RX or TX functionality is enabled.
 * @param[in]   chnl The channel instance
 * @param[in]   minihif If minihif is TRUE, it means that this function is used to configure the HIF channel for the minihif purpose.
 * @return      EOK Success
 */
__attribute__((cold)) errno_t pfe_hif_chnl_init(pfe_hif_chnl_t *chnl, bool_t minihif)
{
    errno_t ret = EOK;
#if !defined(PFE_CFG_DETACHED_MINIHIF)
    pfe_hif_ring_t *tx_ring = NULL, *rx_ring = NULL;
#endif /* PFE_CFG_DETACHED_MINIHIF */

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        ret = EINVAL;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            if (NULL == chnl->bmu)
            {
                NXP_LOG_ERROR("Channel requires BMU instance\n");
                ret = EFAULT;
            }
            else
            {
                tx_ring = &tx_rings_memory_nocpy;
                rx_ring = &rx_rings_memory_nocpy;
                ret = pfe_hif_chnl_init_tx_rx_ring(chnl, &tx_ring, &rx_ring);
                if(EOK == ret)
                {
                    /* HIF NOCPY does not need external RX buffers */
                    rx_pool_init(&(chnl->rx_pool), NULL, &rx_meta_buf_mem_hifnocpy[0U], 0U, 0U);
                }
            }
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
#if !defined(PFE_CFG_DETACHED_MINIHIF)
            /* When the HIF channel is used for minihif purpose (TRUE == minihif), we won't allocate and attach TX and RX rings here.
               TX and RX rings will be allocated and attached by pfe_hif_chnl_set_tx_rx_ring() function after that. */
            if (FALSE == minihif)
            {
                tx_ring = &tx_rings_memory[chnl->id];
                rx_ring = &rx_rings_memory[chnl->id];
                ret = pfe_hif_chnl_init_tx_rx_ring(chnl, &tx_ring, &rx_ring);
                if(EOK == ret)
                {
                    /* Initialize RX buffer pool. */
                    NXP_LOG_INFO("Initializing RX buffer pool. Depth: %u; Buffer Size: %d; Cache Line Size: %d\n",
                                 (uint_t)pfe_hif_chnl_get_rx_fifo_depth(chnl), ETH_43_PFE_CFG_HIF_RX_BUF_SIZE, HAL_CACHE_LINE_SIZE);

                    rx_pool_init( &(chnl->rx_pool),
                                  &rx_buf_mem[0U],
                                  &rx_meta_buf_mem[0U],
                                  ETH_43_PFE_CFG_HIF_RX_BUF_SIZE,
                                  ETH_43_PFE_CFG_HIF_RX_BUF_NUM
                                );

                    /* Populate the RX ring */
                    pfe_hif_chnl_refill_rx_buffers(chnl);
                }
            }
#endif /* PFE_CFG_DETACHED_MINIHIF */
        }
    }

    return ret;
}

#if !defined(PFE_CFG_DETACHED_MINIHIF)
/**
 * @brief       Get the RX BD processor state
 * @param[in]   chnl The channel instance
 * @return      TRUE if the BDP is active, FALSE otherwise
 */
__attribute__((hot)) bool_t pfe_hif_chnl_is_rx_dma_active(const pfe_hif_chnl_t *chnl)
{
    bool_t is_rx_active;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        is_rx_active = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /* No protection here. Getting DMA status is atomic. */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            is_rx_active = pfe_hif_nocpy_cfg_is_rx_dma_active(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            is_rx_active = pfe_hif_chnl_cfg_is_rx_dma_active(chnl->cbus_base_va, chnl->id);
        }
    }

    return is_rx_active;
}

/**
 * @brief       Get the TX BD processor state
 * @param[in]   chnl The channel instance
 * @return      TRUE if the BDP is active, FALSE otherwise
 */
__attribute__((hot)) bool_t pfe_hif_chnl_is_tx_dma_active(const pfe_hif_chnl_t *chnl)
{
    bool_t is_tx_active;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        is_tx_active = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        /* No protection here. Getting DMA status is atomic. */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            is_tx_active = pfe_hif_nocpy_cfg_is_tx_dma_active(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            is_tx_active = pfe_hif_chnl_cfg_is_tx_dma_active(chnl->cbus_base_va, chnl->id);
        }
    }

    return is_tx_active;
}
/**
 * @brief       Send dummy pkt to self Hif channel
 * @param[in]   chnl The channel instance
 * @param[in]   ii The counter number of entry
 * @param[in]   tx_buf_va Tx buffer virtual address
 * @return      EOK if success, error code otherwise
 */
static __attribute__((cold)) errno_t pfe_hif_chnl_flush_rx_bd_fifo_send_pkt(pfe_hif_chnl_t *chnl, uint32_t ii, void **tx_buf_va)
{
    errno_t ret = EOK;
    void *rx_buf_va = NULL;
    void *buf_pa;
    uint32_t len;
    bool_t lifm;
    const void *tx_buf_pa, *rx_buf_pa;
    uint32_t count = ii;

    tx_buf_pa = *tx_buf_va;
    rx_buf_va = (void *)(&dummy_rx_buf);
    rx_buf_pa = rx_buf_va;

    while (FALSE == pfe_hif_chnl_cfg_is_rx_bdp_fifo_empty(chnl->cbus_base_va, chnl->id))
    {
        if (0U == pfe_hif_ring_get_fill_level(chnl->rx_ring))
        {
            /*  Provide single RX buffer */
            if (EOK != pfe_hif_chnl_supply_rx_buf(chnl, rx_buf_pa, DUMMY_RX_BUF_LEN))
            {
                NXP_LOG_ERROR("Can't provide dummy RX buffer\n");
            }
        }

        /*  Send dummy packet to self HIF channel */
        if (EOK != pfe_hif_chnl_tx(chnl, tx_buf_pa, *tx_buf_va, DUMMY_TX_BUF_LEN, TRUE))
        {
            NXP_LOG_ERROR("Dummy frame TX failed\n");
        }

        /*  Wait */
        oal_time_usleep(500U);

        /*  Do TX confirmations */
        while (EOK == pfe_hif_chnl_get_tx_conf(chnl))
        {
            ;
        }

        /*  Do plain RX */
        while (EOK == pfe_hif_ring_dequeue_buf(chnl->rx_ring, &buf_pa, &len, &lifm))
        {
            ;
        }

        /*  Decrement timeout counter */
        if (count > 0U)
        {
            count--;
        }
        else
        {
            NXP_LOG_ERROR("RX BD ring flush timed-out\n");
            ret = ETIMEDOUT;
            /*  Drain all in case when flush process has somehow failed */
            while (EOK == pfe_hif_ring_drain_buf(chnl->rx_ring, &buf_pa))
            {
                ;
            }

            /* free pointer tx_buf_va ,rx_buf_va */
            *tx_buf_va = NULL;
            rx_buf_va = NULL;
            break;
        }
    }

    return ret;
}
/**
 * @brief       Flush RX BDP buffer
 * @details     When channel is stopped the fetched BDs are remaining in internal
 *              buffer and don't get flushed once channel is re-enabled. This
 *              causes memory corruption when channel driver is stopped and then
 *              started with other BD rings because HIF is missing possibility
 *              to reset particular channels separately without affecting the
 *              other channels.
 * @param[in]   chnl The channel instance
 * @return      EOK if success, error code otherwise
 */
static __attribute__((cold)) errno_t pfe_hif_chnl_flush_rx_bd_fifo(pfe_hif_chnl_t *chnl)
{
    void *tx_buf_va = NULL;
    pfe_ct_hif_tx_hdr_t *tx_hdr;
    uint32_t ii;
    errno_t ret = EOK;

    tx_buf_va = (void *)(&dummy_tx_buf);
    tx_hdr = (pfe_ct_hif_tx_hdr_t *)tx_buf_va;

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        tx_hdr->e_phy_ifs = oal_htonl((uint32_t)1U << PFE_PHY_IF_ID_HIF_NOCPY);
    }
    else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    {
        tx_hdr->e_phy_ifs = oal_htonl((uint32_t)1U << ((uint8_t)PFE_PHY_IF_ID_HIF0 + (uint8_t)chnl->id));
    }

    tx_hdr->flags = (pfe_ct_hif_tx_flags_t)(HIF_TX_INJECT|HIF_TX_IHC);
    tx_hdr->chid = (uint8_t)chnl->id;

    /*  Activate the channel */
    (void)pfe_hif_chnl_rx_enable(chnl);
    (void)pfe_hif_chnl_tx_enable(chnl);

    /*  Get maximum number of tries */
    ii = pfe_hif_ring_get_len(chnl->rx_ring);

    /*  Try to flush the internal BD FIFO. Send dummy frames to the
        channel until the BDP RX FIFO is empty. */
    ret = pfe_hif_chnl_flush_rx_bd_fifo_send_pkt(chnl, ii, &tx_buf_va);

    return ret;
}

/**
 * @brief       Destroy HIF channel instance
 * @param[in]   chnl The channel instance
 */
static __attribute__((cold)) void pfe_hif_chnl_destroy_chnl(pfe_hif_chnl_t *chnl)
{
    /*  Disable and finalize the channel */
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        /* HIF NOCPY will do the finalization */
    }
    else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    {
        pfe_hif_chnl_cfg_fini(chnl->cbus_base_va, chnl->id);
    }

    if (EOK != oal_spinlock_unlock(&chnl->lock))
    {
        NXP_LOG_DEBUG("Mutex unlock failed\n");
    }

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
#if defined(NXP_LOG_ENABLED)
        if (EOK != oal_spinlock_destroy(&chnl->a_lock))
        {
            NXP_LOG_WARNING("Could not properly destroy allocation counter mutex\n");
        }
#endif /* NXP_LOG_ENABLED */
    }
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

    if (EOK != oal_spinlock_destroy(&chnl->lock))
    {
        NXP_LOG_WARNING("Could not properly destroy channel mutex\n");
    }

    if (EOK != oal_spinlock_destroy(&chnl->rx_lock))
    {
        NXP_LOG_WARNING("Could not properly destroy channel RX mutex\n");
    }
}

static __attribute__((cold)) void pfe_hif_chnl_destroy_rings(pfe_hif_chnl_t *chnl)
{
    /*  Disable the HIF channel BDP/DMA */
    pfe_hif_chnl_rx_disable(chnl);
    pfe_hif_chnl_tx_disable(chnl);

    /*  Destroy rings */
    if (NULL != chnl->rx_ring)
    {
        (void)pfe_hif_ring_destroy(chnl->rx_ring);
        chnl->rx_ring = NULL;
    }

    if (NULL != chnl->tx_ring)
    {
        if (TRUE != pfe_hif_chnl_cfg_is_tx_bdp_fifo_empty(chnl->cbus_base_va, chnl->id))
        {
            NXP_LOG_WARNING("HIF channel TX FIFO is not empty\n");
        }

        (void)pfe_hif_ring_destroy(chnl->tx_ring);
        chnl->tx_ring = NULL;
    }

    if (EOK != oal_spinlock_lock(&chnl->lock))
    {
        NXP_LOG_DEBUG("Mutex lock failed\n");
    }
    pfe_hif_chnl_destroy_chnl(chnl);
}

static __attribute__((cold)) void pfe_hif_chnl_destroy_inval_rx_ring(pfe_hif_chnl_t *chnl)
{
#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
    void *buf_pa = NULL;
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    uint32_t ii;
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

#if (TRUE == PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED)
    /*  Drain RX buffers (the ones enqueued in RX ring) */
    while (EOK == pfe_hif_ring_drain_buf(chnl->rx_ring, &buf_pa))
    {
        /*Do Nothing*/
        ;
    }
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            for (ii=0U; ii < chnl->rx_ring->length; ii++)
            {
                /* HIF NOCPY buffers are provided by BMU so return them to BMU */
                pfe_bmu_free_buf(chnl->bmu, (addr_t)PFE_CFG_MEMORY_PFE_TO_PHYS(((pfe_hif_nocpy_bd_t *)chnl->rx_ring->base_va)[ii].data));
            }
        }
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
#if defined(NXP_LOG_ENABLED)
    if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
    {
        /* Sanity check to verify if the HIF RX ring and the upper SW layers
           have properly returned all RX and TX buffers back to the BMU. We're
           using the allocations counter here to determine delta between number
           of allocated buffers (either TX buffers we have directly allocated
           or received buffers which have been allocated by the PFE HW) and
           number of released buffers. */
        if (0U != pfe_hif_chnl_get_alloc_cnt(chnl))
        {
            NXP_LOG_WARNING("Some buffers not returned to the BMU\n");
        }
        else
        {
            NXP_LOG_INFO("All buffers returned to the BMU\n");
        }
    }
#endif /* NXP_LOG_ENABLED */
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
#endif /* PFE_HIF_CHNL_CFG_RX_BUFFERS_ENABLED */

    /* Invalidate the RX ring */
    pfe_hif_ring_invalidate(chnl->rx_ring);

    /* Here the ring should be empty. Execute HIF channel BDP shutdown
       procedure to ensure that channel will not keep any content in internal buffers. */
    if (EOK != pfe_hif_chnl_flush_rx_bd_fifo(chnl))
    {
        NXP_LOG_ERROR("FATAL: Could not flush RX BD FIFO\n");
    }
}

/**
 * @brief       Destroy HIF channel instance
 * @param[in]   chnl The channel instance
 */
__attribute__((cold)) void pfe_hif_chnl_destroy(pfe_hif_chnl_t *chnl)
{
    if (NULL != chnl)
    {
        /*  Disable channel interrupts */
        pfe_hif_chnl_irq_mask(chnl);
        pfe_hif_chnl_rx_irq_mask(chnl);
        pfe_hif_chnl_tx_irq_mask(chnl);

        /*  Disable RX/TX DMA */
        pfe_hif_chnl_rx_disable(chnl);
        pfe_hif_chnl_tx_disable(chnl);

        /*  Uninstall callbacks */
        chnl->rx_cbk.cbk = NULL;
        chnl->tx_cbk.cbk = NULL;
        chnl->rx_oob_cbk.cbk = NULL;

        if (NULL != chnl->rx_ring)
        {
            pfe_hif_chnl_destroy_inval_rx_ring(chnl);
        }

        pfe_hif_chnl_destroy_rings(chnl);
    }
}
#endif /* PFE_CFG_DETACHED_MINIHIF */

/**
 * @brief       Dump of SW client channel rings
 * @details     Dumps particular ring
 * @param[in]   chnl The client channel instance
 * @param[in]   dump_rx True if RX ring has to be dumped
 * @param[in]   dump_tx True if TX ring has to be dumped
 */
__attribute__((cold)) void pfe_hif_chnl_dump_ring(const pfe_hif_chnl_t *chnl, bool_t dump_rx, bool_t dump_tx)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        if(dump_rx)
        {
            pfe_hif_ring_dump(chnl->rx_ring, "RX");
        }

        if(dump_tx)
        {
            pfe_hif_ring_dump(chnl->tx_ring, "TX");
        }
    }
}

#if !defined(PFE_CFG_DETACHED_MINIHIF)

#if defined(PFE_CFG_TEXT_STATS)
/**
 * @brief       Return HIF channel runtime statistics in text form
 * @details     Function writes formatted text into given buffer.
 * @param[in]   chnl        The channel instance
 * @param[in]   buf         Pointer to the buffer to write to
 * @param[in]   buf_len     Buffer length
 * @param[in]   verb_level  Verbosity level, number of data written to the buffer
 * @return      Number of bytes written to the buffe
 */
__attribute__((cold)) uint32_t pfe_hif_chnl_get_text_statistics(const pfe_hif_chnl_t *chnl, char_t *buf, uint32_t buf_len, uint8_t verb_level)
{
    uint32_t len = 0U;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        len = 0U;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            len += pfe_hif_nocpy_chnl_cfg_get_text_stat(chnl->cbus_base_va, buf, buf_len, verb_level);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            /* HIF */
            len += pfe_hif_chnl_cfg_get_text_stat(chnl->cbus_base_va, chnl->id, buf, buf_len, verb_level);
        }
    }

    return len;
}

#endif /* defined(PFE_CFG_TEXT_STATS) */

/**
 * @brief       Get number of transmitted packets (from PFE to HOST)
 * @param[in]   emac The channel instance
 * @return      Number of transmitted packets
 */
uint32_t pfe_hif_chnl_get_tx_cnt(const pfe_hif_chnl_t *chnl)
{
    uint32_t tx_cnt;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        tx_cnt = 0xffffffffU;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            tx_cnt = pfe_hif_nocpy_cfg_get_tx_cnt(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            tx_cnt = pfe_hif_chnl_cfg_get_tx_cnt(chnl->cbus_base_va, chnl->id);
        }
    }

    return tx_cnt;
}

/**
 * @brief       Get number of received packets (from HOST to PFE)
 * @param[in]   emac The channel instance
 * @return      Number of received packets
 */
uint32_t pfe_hif_chnl_get_rx_cnt(const pfe_hif_chnl_t *chnl)
{
    uint32_t rx_cnt;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        rx_cnt = 0xffffffffU;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            /* HIF_NOCPY */
            rx_cnt = pfe_hif_nocpy_cfg_get_rx_cnt(chnl->cbus_base_va);
        }
        else
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
        {
            rx_cnt = pfe_hif_chnl_cfg_get_rx_cnt(chnl->cbus_base_va, chnl->id);
        }
    }

    return rx_cnt;
}
#endif /* PFE_CFG_DETACHED_MINIHIF */

/**
 * @brief       Check if TX ring in  HIF channel contains less than watermark-specified
 *              number of free entries
  @param[in]    chnl        The channel instance
 * @return      TRUE if ring contains less than watermark-specified number
 *              of free entries
 */
bool_t pfe_hif_chnl_tx_fifo_is_below_wm(const pfe_hif_chnl_t *chnl)
{
    bool_t is_below_wm;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        is_below_wm = FALSE;
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        is_below_wm =  pfe_hif_ring_is_below_wm(chnl->tx_ring);
    }
    return is_below_wm;
}

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
/**
 * @brief       Allocate buffer via BMU
 * @param[in]   chnl The channel instance
 * @return      Allocated buffer pointer (virtual)
 */
__attribute__((hot)) void *pfe_hif_chnl_bmu_alloc_buf_va(pfe_hif_chnl_t *chnl)
{
    void *tx_buf_pa;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        return NULL;
    }
#endif /* PFE_CFG_NULL_ARG_CHECK */

    tx_buf_pa = pfe_bmu_alloc_buf(chnl->bmu);
    if (unlikely(NULL == tx_buf_pa))
    {
        NXP_LOG_ERROR("BMU can't allocate TX buffer\n");
        return NULL;
    }
    else
    {
#if defined(NXP_LOG_ENABLED)
        /*  Increment BMU allocations counter */
        pfe_hif_chnl_alloc_inc(chnl);
#endif /* NXP_LOG_ENABLED */

        /*  Get VA */
        return (void *) pfe_bmu_get_va(chnl->bmu, (addr_t)tx_buf_pa);
    }
}

/**
 * @brief       Convert virtual buffer address to physical one
 * @param[in]   chnl The channel instance
 * @param[in]   va The address to be converted
 * @return      Associated virtual address or NULL if failed
 */
__attribute__((hot)) void *pfe_hif_chnl_bmu_get_buf_pa(const pfe_hif_chnl_t *chnl, addr_t va)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
        return NULL;
    }
#endif /* PFE_CFG_NULL_ARG_CHECK */

    return (void *) pfe_bmu_get_pa(chnl->bmu, (addr_t)va);
}

/**
 * @brief       Free buffer via BMU
 * @param[in]   chnl The channel instance
 * @param[in]   va Pointer (virtual) to the buffer to be freed.
 * @note        Thread safe
 */
__attribute__((hot)) void pfe_hif_chnl_bmu_free_buf(pfe_hif_chnl_t *chnl, addr_t va)
{
    void *tx_buf_pa;
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
    else
#endif /* PFE_CFG_NULL_ARG_CHECK */
    {
        tx_buf_pa = (void *)pfe_bmu_get_pa(chnl->bmu, (addr_t)va);

        pfe_bmu_free_buf(chnl->bmu, (addr_t)tx_buf_pa);

#if defined(NXP_LOG_ENABLED)
        /*  Decrement BMU allocations counter */
        pfe_hif_chnl_alloc_dec(chnl);
#endif /* NXP_LOG_ENABLED */
    }
}
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */

/**
 * @brief       Set the value of LMEM Header size used by the HW
 * @param[in]   chnl The channel instance
 * @param[in]   lmem_header_size The size of LMEM Header
 */
void pfe_hif_chnl_set_lmem_hdr_size(pfe_hif_chnl_t *chnl, uint16_t lmem_header_size)
{
#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
#endif /* PFE_CFG_NULL_ARG_CHECK */

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
    chnl->lmem_header_size = lmem_header_size;
#else
    (void)lmem_header_size;
    (void)chnl;
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
}

/**
 * @brief       Get the value of LMEM Header size used by the HW
 * @param[in]   chnl The channel instance
 * @return      The size of LMEM Header
 * @warning     Function shall be used only for HIF_NOCPY channels
 */
uint16_t pfe_hif_chnl_get_lmem_hdr_size(const pfe_hif_chnl_t *chnl)
{
    uint16_t lmem_header_size = 0U;

#if defined(PFE_CFG_NULL_ARG_CHECK)
    if (unlikely(NULL == chnl))
    {
        NXP_LOG_ERROR("NULL argument received\n");
    }
#endif /* PFE_CFG_NULL_ARG_CHECK */

#if defined(PFE_CFG_HIF_NOCPY_SUPPORT)
        if (chnl->id >= PFE_HIF_CHNL_NOCPY_ID)
        {
            lmem_header_size = chnl->lmem_header_size;
        }
#endif /* PFE_CFG_HIF_NOCPY_SUPPORT */
    (void)chnl;

    return lmem_header_size;
}

#ifdef PFE_CFG_TARGET_OS_AUTOSAR
#define ETH_43_PFE_STOP_SEC_CODE
#include "Eth_43_PFE_MemMap.h"
#endif /* PFE_CFG_TARGET_OS_AUTOSAR */
