root/usr/src/uts/common/io/bnxe/577xx/drivers/common/lm/l5/lm_l5.c
#include "lm5710.h"
#include "everest_iscsi_constants.h"
#include "everest_l5cm_constants.h"
#include "577xx_int_offsets.h"
#include "bd_chain.h"
#include "command.h"
#include "lm_sp_req_mgr.h"
#include "lm_l4sp.h"
#include "lm_l4if.h"
#include "lm_l5if.h"
#include "mm_l5if.h"
#include "mm_l4if.h"
#include "mm.h"



u32_t lm_get_pbl_entries(
    IN  u32_t bufferSize
    )
{
    return CEIL_DIV(bufferSize, LM_PAGE_SIZE);
}



lm_status_t lm_alloc_pbl_mem(
    IN  struct _lm_device_t *pdev,
    IN  u32_t pbl_entries,
    OUT lm_address_t** pbl_virt,
    OUT lm_address_t *pbl_phy,
    OUT void** pbl_virt_table,
    IN  u8_t rt_mem,
    OUT u32_t *pbl_size,
    IN  u8_t mm_cli_idx
    )
{

    if (CHK_NULL(pdev) || (pbl_entries == 0) ||
        CHK_NULL(pbl_virt) || CHK_NULL(pbl_phy) ||
        CHK_NULL(pbl_size))
    {
        /* allocPblMem - illegal pblSize */
        return LM_STATUS_INVALID_PARAMETER;
    }

    *pbl_size = pbl_entries * sizeof(lm_address_t);

    if (rt_mem)
    {
        *pbl_virt = (lm_address_t *)mm_rt_alloc_phys_mem(pdev,
                                                        *pbl_size,
                                                        pbl_phy,
                                                        0,
                                                        mm_cli_idx);
        if CHK_NULL(*pbl_virt)
        {
            *pbl_size = 0;

            return LM_STATUS_RESOURCE;
        }

        *pbl_virt_table = (void *)mm_rt_alloc_mem(pdev,
                                                   pbl_entries * sizeof(void *),
                                                   mm_cli_idx);

        if CHK_NULL(*pbl_virt_table)
        {
            *pbl_size = 0;
            mm_rt_free_phys_mem(pdev, *pbl_size, *pbl_virt, *pbl_phy, mm_cli_idx);
            *pbl_virt = NULL;

            return LM_STATUS_RESOURCE;
        }
    }
    else
    {
        *pbl_virt = (lm_address_t *)mm_alloc_phys_mem_align(pdev,
                                                        *pbl_size,
                                                        pbl_phy,
                                                        LM_PAGE_SIZE,
                                                        0,
                                                        mm_cli_idx);
        if CHK_NULL(*pbl_virt)
        {
            *pbl_size = 0;

            return LM_STATUS_RESOURCE;
        }

        *pbl_virt_table = (void *)mm_alloc_mem(pdev,
                                                pbl_entries * sizeof(void *),
                                                mm_cli_idx);

        if CHK_NULL(*pbl_virt_table)
        {
            *pbl_size = 0;
            *pbl_virt = NULL;

            return LM_STATUS_RESOURCE;
        }
    }

    return LM_STATUS_SUCCESS;
}



lm_status_t lm_create_pbl(
    IN  struct _lm_device_t *pdev,
    IN  void* buf_base_virt,
    IN  lm_address_t* buf_base_phy,
    IN  u32_t buffer_size,
    OUT lm_address_t** pbl_virt,
    OUT lm_address_t* pbl_phy,
    OUT void** pbl_virt_table,
    OUT u32_t *pbl_entries,
    OUT u32_t *pbl_size,
    IN  u8_t rt_mem,
    IN  u8_t mm_cli_idx)
{
    lm_status_t lm_status;

    if (CHK_NULL(pdev) || CHK_NULL(buf_base_virt) ||
        CHK_NULL(buf_base_phy) || CHK_NULL(pbl_virt) ||
        CHK_NULL(pbl_phy) || CHK_NULL(pbl_virt_table) ||
        CHK_NULL(pbl_entries) || CHK_NULL(pbl_size))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    *pbl_entries = lm_get_pbl_entries(buffer_size);

    lm_status = lm_alloc_pbl_mem(pdev, *pbl_entries, pbl_virt, pbl_phy, pbl_virt_table, rt_mem, pbl_size, mm_cli_idx);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        *pbl_entries = 0;

        return lm_status;
    }

    lm_status = lm_bd_chain_pbl_set_ptrs(buf_base_virt, *buf_base_phy, *pbl_virt, *pbl_virt_table, *pbl_entries);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        if (rt_mem)
        {
            mm_rt_free_phys_mem(pdev, *pbl_size, *pbl_virt, *pbl_phy, mm_cli_idx);
            mm_rt_free_mem(pdev, *pbl_virt_table, *pbl_entries * sizeof(void *), mm_cli_idx);
        }

        *pbl_entries = 0;
        *pbl_size = 0;

        return lm_status;
    }

    return LM_STATUS_SUCCESS;
}



lm_status_t
lm_l5_alloc_eq(
    IN      struct _lm_device_t  *pdev,
    IN      lm_eq_chain_t        *eq_chain,
    IN      lm_eq_addr_t         *eq_addr_save,
    IN      u16_t                page_cnt,
    IN      u8_t                 cli_idx)
{
    u32_t                mem_size     = 0;

    /* check arguments */
    if ((CHK_NULL(pdev) || CHK_NULL(eq_chain) || !page_cnt) ||
        (LM_CLI_IDX_FCOE != cli_idx) && (LM_CLI_IDX_ISCSI != cli_idx))
    {
        return LM_STATUS_FAILURE;
    }

    DbgMessage(pdev, INFORMi | INFORMl5sp, "#lm_alloc_eq, eq_chain=%p, page_cnt=%d\n", eq_chain, page_cnt);

    /* alloc the chain */
    mem_size = page_cnt * LM_PAGE_SIZE;

    if(!eq_addr_save->b_allocated)
    {
        eq_chain->bd_chain.bd_chain_virt = mm_alloc_phys_mem(pdev,
                                                             mem_size,
                                                             &eq_chain->bd_chain.bd_chain_phy,
                                                             0,
                                                             cli_idx);

        if (ERR_IF(!eq_chain->bd_chain.bd_chain_virt))
        {
            DbgBreakIf(DBG_BREAK_ON(MEMORY_ALLOCATION_FAILURE));
            return LM_STATUS_RESOURCE;
        }

        eq_addr_save->bd_chain_virt = eq_chain->bd_chain.bd_chain_virt ;
        eq_addr_save->bd_chain_phy.as_u64 = eq_chain->bd_chain.bd_chain_phy.as_u64;
        eq_addr_save->b_allocated = TRUE;
        // For debugging
        eq_addr_save->prev_mem_size = mem_size;
    }
    else
    {
        DbgBreakIf(mem_size != eq_addr_save->prev_mem_size);
        eq_chain->bd_chain.bd_chain_virt = eq_addr_save->bd_chain_virt;
        eq_chain->bd_chain.bd_chain_phy.as_u64 = eq_addr_save->bd_chain_phy.as_u64;
    }
    mm_memset(eq_chain->bd_chain.bd_chain_virt, 0, mem_size);

    eq_chain->bd_chain.page_cnt = page_cnt;


    return LM_STATUS_SUCCESS;
} /* lm_alloc_eq */



lm_status_t
lm_sc_setup_eq(
    IN struct _lm_device_t *pdev,
    IN u32_t                idx,
    IN const u8_t           is_chain_mode)
{
    lm_bd_chain_t * bd_chain;
    u16_t volatile * sb_indexes;

    /* check arguments */
    if(CHK_NULL(pdev) || ERR_IF((ARRSIZE(pdev->iscsi_info.run_time.eq_chain) <= idx)))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    DbgMessage(pdev, INFORMi|INFORMl5sp, "#lm_sc_setup_eq, idx=%d\n",idx);

    bd_chain = &LM_SC_EQ(pdev, idx).bd_chain;
    lm_bd_chain_setup(pdev, bd_chain, bd_chain->bd_chain_virt,
                      bd_chain->bd_chain_phy, (u16_t)bd_chain->page_cnt, sizeof(struct iscsi_kcqe), 1/*0*/, is_chain_mode);

    /* verify that EQ size is not too large */
    if(bd_chain->capacity > MAX_EQ_SIZE_ISCSI(is_chain_mode))
    {
        DbgBreakIf(bd_chain->capacity > MAX_EQ_SIZE_ISCSI(is_chain_mode));
        return LM_STATUS_FAILURE;
    }

    DbgMessage(pdev, INFORMi, "is eq %d, bd_chain %p, bd_left %d\n",
        idx,
        bd_chain->next_bd,
        bd_chain->bd_left);
    DbgMessage(pdev, INFORMi, "   bd_chain_phy 0x%x%08x\n",
        bd_chain->bd_chain_phy.as_u32.high,
        bd_chain->bd_chain_phy.as_u32.low);

    // Assign the EQ chain consumer pointer to the consumer index in the status block.
    if( idx >= ARRSIZE(pdev->vars.status_blocks_arr) )
    {
        DbgBreakIf( idx >= ARRSIZE(pdev->vars.status_blocks_arr) );
        return LM_STATUS_FAILURE;
    }

    sb_indexes = lm_get_sb_indexes(pdev, (u8_t)idx);
    sb_indexes[HC_INDEX_ISCSI_EQ_CONS] = 0;
    LM_SC_EQ(pdev, idx).hw_con_idx_ptr = sb_indexes + HC_INDEX_ISCSI_EQ_CONS;
/*
    if (IS_E2(pdev)) {
        pdev->vars.status_blocks_arr[idx].host_hc_status_block.e2_sb->sb.index_values[HC_INDEX_ISCSI_EQ_CONS] = 0;
        LM_SC_EQ(pdev, idx).hw_con_idx_ptr =
            &(pdev->vars.status_blocks_arr[idx].host_hc_status_block.e2_sb->sb.index_values[HC_INDEX_ISCSI_EQ_CONS]);
    } else {
        pdev->vars.status_blocks_arr[idx].host_hc_status_block.e1x_sb->sb.index_values[HC_INDEX_ISCSI_EQ_CONS] = 0;
        LM_SC_EQ(pdev, idx).hw_con_idx_ptr =
            &(pdev->vars.status_blocks_arr[idx].host_hc_status_block.e1x_sb->sb.index_values[HC_INDEX_ISCSI_EQ_CONS]);
    }
 */
    LM_SC_EQ(pdev, idx).hc_sb_info.hc_sb = STATUS_BLOCK_NORMAL_TYPE; //STATUS_BLOCK_CSTORM_TYPE;
    LM_SC_EQ(pdev, idx).hc_sb_info.hc_index_value = HC_INDEX_ISCSI_EQ_CONS;

    return LM_STATUS_SUCCESS;
} /* lm_sc_setup_eq */
/**
 *
 * @description
 * Allocate EQ PBL to pass to FW in init ramrod
 * @param pdev
 * @param eq_chain
 * @param pbl
 * @param eq_addr_save
 *
 * @return lm_status_t
 */
lm_status_t
lm_fc_alloc_eq_pbl(
    IN struct   _lm_device_t  *pdev,
    IN          lm_eq_chain_t *eq_chain,
    IN          lm_fcoe_pbl_t *pbl,
    IN          lm_eq_addr_t  *eq_addr_save)
{
    lm_status_t     lm_status = LM_STATUS_SUCCESS;

    /* check arguments */
    if(CHK_NULL(pdev))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    DbgMessage(pdev, INFORMi|INFORMl5sp, "#lm_fc_alloc_eq_pbl\n");

    // For D3 case
    if(FALSE == pbl->allocated)
    {
        lm_status = lm_create_pbl(pdev,
                                  eq_chain->bd_chain.bd_chain_virt,
                                  &(eq_chain->bd_chain.bd_chain_phy),
                                  eq_addr_save->prev_mem_size,
                                  &pbl->pbl_phys_table_virt,
                                  &pbl->pbl_phys_table_phys,
                                  &pbl->pbl_virt_table,
                                  &pbl->pbl_entries,
                                  &pbl->pbl_size,
                                  FALSE,
                                  LM_CLI_IDX_FCOE);

        if (lm_status != LM_STATUS_SUCCESS)
        {
            mm_mem_zero(&(pbl) ,sizeof(lm_fcoe_pbl_t));
            return LM_STATUS_FAILURE;
        }
        pbl->allocated = TRUE;
    }
    return lm_status;
}

lm_status_t
lm_fc_setup_eq(
    IN struct   _lm_device_t  *pdev,
    IN          u32_t         idx,
    IN const    u8_t          is_chain_mode)
{
    lm_bd_chain_t   * bd_chain;
    lm_fcoe_pbl_t   * pbl;
    u16_t volatile  * sb_indexes;

    /* check arguments */
    if(CHK_NULL(pdev) || ERR_IF((ARRSIZE(pdev->fcoe_info.run_time.eq_chain) <= idx)))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    DbgMessage(pdev, INFORMi|INFORMl5sp, "#lm_fc_setup_eq, idx=%d\n",idx);

    bd_chain = &LM_FC_EQ(pdev, idx).bd_chain;
    pbl = &LM_FC_PBL(pdev, idx);
    lm_bd_chain_pbl_setup(pdev, bd_chain, bd_chain->bd_chain_virt,
                      bd_chain->bd_chain_phy, pbl->pbl_virt_table, pbl->pbl_phys_table_virt,
                      (u16_t)bd_chain->page_cnt, sizeof(struct fcoe_kcqe),
                      1/*0*/); /* EQ is considered full of blank entries */

    /* verify that EQ size is not too large */
    if (bd_chain->capacity > MAX_EQ_SIZE_FCOE(is_chain_mode))
    {
        DbgBreakIf(bd_chain->capacity > MAX_EQ_SIZE_FCOE(is_chain_mode));
        return LM_STATUS_FAILURE;
    }

    DbgMessage(pdev, INFORMi, "fc eq %d, bd_chain %p, bd_left %d\n",
        idx,
        bd_chain->next_bd,
        bd_chain->bd_left);
    DbgMessage(pdev, INFORMi, "   bd_chain_phy 0x%x%08x\n",
        bd_chain->bd_chain_phy.as_u32.high,
        bd_chain->bd_chain_phy.as_u32.low);

    // Assign the EQ chain consumer pointer to the consumer index in the status block.
    if (idx >= ARRSIZE(pdev->vars.status_blocks_arr))
    {
        DbgBreakIf( idx >= ARRSIZE(pdev->vars.status_blocks_arr) );
        return LM_STATUS_FAILURE;
    }

    sb_indexes = lm_get_sb_indexes(pdev, (u8_t)idx);
    sb_indexes[HC_INDEX_FCOE_EQ_CONS] = 0;
    LM_FC_EQ(pdev, idx).hw_con_idx_ptr = sb_indexes + HC_INDEX_FCOE_EQ_CONS;
/*
    if (IS_E2(pdev)) {
        pdev->vars.status_blocks_arr[idx].host_hc_status_block.e2_sb->sb.index_values[HC_INDEX_FCOE_EQ_CONS] = 0;
        LM_FC_EQ(pdev, idx).hw_con_idx_ptr =
            &(pdev->vars.status_blocks_arr[idx].host_hc_status_block.e2_sb->sb.index_values[HC_INDEX_FCOE_EQ_CONS]);
    } else {
        pdev->vars.status_blocks_arr[idx].host_hc_status_block.e1x_sb->sb.index_values[HC_INDEX_FCOE_EQ_CONS] = 0;
        LM_FC_EQ(pdev, idx).hw_con_idx_ptr =
            &(pdev->vars.status_blocks_arr[idx].host_hc_status_block.e1x_sb->sb.index_values[HC_INDEX_FCOE_EQ_CONS]);
    }
*/
    LM_FC_EQ(pdev, idx).hc_sb_info.hc_sb = STATUS_BLOCK_NORMAL_SL_TYPE; //STATUS_BLOCK_USTORM_TYPE;
    LM_FC_EQ(pdev, idx).hc_sb_info.hc_index_value = HC_INDEX_FCOE_EQ_CONS;

    return LM_STATUS_SUCCESS;
} /* lm_fc_setup_eq */




/** Description
 *  Callback function for cids being recylced
 */
void lm_sc_recycle_cid_cb(
    struct _lm_device_t *pdev,
    void *cookie,
    s32_t cid)
{
    lm_status_t lm_status;
    lm_sp_req_common_t * sp_req = NULL;
    lm_iscsi_state_t * iscsi = (lm_iscsi_state_t *)cookie;

    if (CHK_NULL(pdev) || CHK_NULL(iscsi))
    {
        DbgBreakIf(1);
        return;
    }

    MM_ACQUIRE_TOE_LOCK(pdev);

    /* un-block the manager... */
    lm_set_cid_state(pdev, iscsi->cid, LM_CID_STATE_VALID);

    if (iscsi->hdr.status == STATE_STATUS_INIT_CONTEXT)
    {
        lm_status = lm_sc_init_iscsi_context(pdev,
                                             iscsi,
                                             &iscsi->pending_ofld1,
                                             &iscsi->pending_ofld2,
                                             &iscsi->pending_ofld3);

        mm_sc_complete_offload_request(pdev, iscsi, lm_status);
    }

    /* we can now unblock any pending slow-paths */
    lm_sp_req_manager_unblock(pdev, cid, &sp_req);

    MM_RELEASE_TOE_LOCK(pdev);
}


void lm_sc_comp_cb(struct _lm_device_t *pdev, struct sq_pending_command *pending)
{
    struct iscsi_kcqe kcqe  = {0};
    lm_iscsi_state_t *iscsi = NULL;
    u32_t            cid;
    u8_t             cmd;


    if (CHK_NULL(pdev) || CHK_NULL(pending))
    {
        return;
    }

    cmd = pending->cmd;
    cid = pending->cid;

    iscsi = lm_cid_cookie(pdev, ISCSI_CONNECTION_TYPE, cid);

    if (iscsi)
    {
        kcqe.iscsi_conn_id         = iscsi->iscsi_conn_id;
        kcqe.iscsi_conn_context_id = HW_CID(pdev, cid);
    }

    kcqe.completion_status = LM_STATUS_SUCCESS; /* TODO_ER: Fixme: do we want this?? maybe ok since l5 is aware of er... */

    kcqe.op_code = cmd; /* In iSCSI they are the same */

    kcqe.flags |= (ISCSI_KWQE_LAYER_CODE << ISCSI_KWQE_HEADER_LAYER_CODE_SHIFT);

    lm_sc_complete_slow_path_request(pdev, &kcqe);
}

lm_status_t
lm_sc_alloc_resc(
    IN struct _lm_device_t *pdev
    )
{
    u8_t        mm_cli_idx  = LM_RESOURCE_ISCSI;
    u8_t        *chk_buf    = NULL;
    u16_t       i           = 0;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    mm_mem_zero(&pdev->iscsi_info, sizeof(lm_iscsi_info_t));

    /* Allocate global buffer */
    pdev->iscsi_info.bind.global_buff_base_virt = (u8_t*)mm_alloc_phys_mem(pdev,
                                                                      ISCSI_GLOBAL_BUF_SIZE,
                                                                      &pdev->iscsi_info.bind.global_buff_base_phy,
                                                                      0,
                                                                      mm_cli_idx);
    if CHK_NULL(pdev->iscsi_info.bind.global_buff_base_virt)
    {
        return LM_STATUS_RESOURCE;
    }

    /* cid recycled cb registration  */
    lm_cid_recycled_cb_register(pdev, ISCSI_CONNECTION_TYPE, lm_sc_recycle_cid_cb);

    /* Sq-completion cb registration (sq that get completed internally in driver */
    lm_sq_comp_cb_register(pdev, ISCSI_CONNECTION_TYPE, lm_sc_comp_cb);

    chk_buf = (u8_t *)(&(pdev->iscsi_info.eq_addr_save));
    // Except global_buff and pdev->iscsi_info all other fileds should be zero
    for(i = 0 ;i < sizeof(pdev->iscsi_info.eq_addr_save) ;i++)
    {
        DbgBreakIf(0 != chk_buf[i]);
    }

    chk_buf = (u8_t *)(&(pdev->iscsi_info.run_time));
    // Except global_buff and pdev->iscsi_info all other fileds should be zero
    for(i = 0 ;i < sizeof(pdev->iscsi_info.run_time) ;i++)
    {
        DbgBreakIf(0 != chk_buf[i]);
    }
    return LM_STATUS_SUCCESS;
} /* lm_sc_alloc_resc */

/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
u16_t
lm_l5_eq_page_cnt(
    IN struct       _lm_device_t *pdev,
    const u32_t     max_func_cons,
    const u16_t     reserved_eq_elements,
    const u16_t     eqes_per_page,
    const u16_t     max_eq_pages
    )
{
    u16_t eq_page_cnt = 0;
    u16_t min_eq_size = 0;

    /* Init EQs - create page chains */
    min_eq_size = (u16_t)(max_func_cons + reserved_eq_elements);
    eq_page_cnt = CEIL_DIV(min_eq_size, (eqes_per_page));
    eq_page_cnt = min(eq_page_cnt, max_eq_pages);

    return eq_page_cnt;
}

/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t
lm_fc_free_init_resc(
    IN struct _lm_device_t *pdev
    )
{
    lm_status_t         lm_status   = LM_STATUS_SUCCESS;
    u16_t               eq_sb_idx   = 0;
    u16_t               eq_page_cnt = 0;

    if (CHK_NULL(pdev))
    {
        DbgBreakMsg("lm_fc_free_init_resc failed");
        return LM_STATUS_INVALID_PARAMETER;
    }

    mm_memset(&(pdev->fcoe_info.run_time), 0, sizeof(pdev->fcoe_info.run_time));
    return lm_status;
}


lm_status_t
lm_fc_clear_d0_resc(
    IN struct _lm_device_t *pdev,
    const u8_t cid
    )
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    u8_t eq_idx = 0;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    LM_FC_FOREACH_EQ_IDX(pdev, eq_idx)
    {
        lm_clear_chain_sb_cons_idx(pdev, eq_idx, &LM_FC_EQ(pdev, eq_idx).hc_sb_info, &LM_FC_EQ(pdev, eq_idx).hw_con_idx_ptr);
    }

    lm_status = lm_fc_free_init_resc(pdev);

    return lm_status;
} /* lm_fc_clear_d0_resc */

lm_status_t
lm_fc_clear_resc(
    IN struct _lm_device_t *pdev
    )
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    const u8_t cid  = FCOE_CID(pdev);

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    lm_fc_clear_d0_resc(
        pdev,
        cid);
    s_list_init(&LM_RXQ(pdev, cid).active_descq, NULL, NULL, 0);
    s_list_init(&LM_RXQ(pdev, cid).common.free_descq, NULL, NULL, 0);

    return lm_status;
} /* lm_fc_clear_resc */


/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t
lm_sc_free_init_resc(
    IN struct _lm_device_t *pdev
    )
{
    lm_status_t         lm_status   = LM_STATUS_SUCCESS;
    u16_t               eq_sb_idx   = 0;
    u16_t               eq_page_cnt = 0;

    if (CHK_NULL(pdev))
    {
        DbgBreakMsg("lm_sc_free_init_resc failed");
        return LM_STATUS_INVALID_PARAMETER;
    }

    mm_memset(&(pdev->iscsi_info.run_time), 0, sizeof(pdev->iscsi_info.run_time));
    return lm_status;
}


lm_status_t
lm_sc_clear_d0_resc(
    IN struct _lm_device_t *pdev,
    const u8_t cid
    )
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    u8_t eq_idx = 0;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    LM_SC_FOREACH_EQ_IDX(pdev, eq_idx)
    {
        lm_clear_chain_sb_cons_idx(pdev, eq_idx, &LM_SC_EQ(pdev, eq_idx).hc_sb_info, &LM_SC_EQ(pdev, eq_idx).hw_con_idx_ptr);
    }

    lm_status = lm_sc_free_init_resc(pdev);

    return lm_status;
} /* lm_sc_clear_d0_resc */

lm_status_t
lm_sc_clear_resc(
    IN struct _lm_device_t *pdev
    )
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    const u8_t cid  = ISCSI_CID(pdev);

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    lm_sc_clear_d0_resc(
        pdev,
        cid);
    s_list_init(&LM_RXQ(pdev, cid).active_descq, NULL, NULL, 0);
    s_list_init(&LM_RXQ(pdev, cid).common.free_descq, NULL, NULL, 0);

    return lm_status;
} /* lm_sc_clear_resc */



lm_status_t
lm_sc_ooo_chain_establish(
    IN struct _lm_device_t *pdev)
{
    lm_status_t             lm_status = LM_STATUS_SUCCESS;
    const u32_t             func        = FUNC_ID(pdev);

    if CHK_NULL(pdev)
    {
        lm_status = LM_STATUS_INVALID_PARAMETER;
        return lm_status;
    }
    LM_INTMEM_WRITE32(pdev,
                      TSTORM_ISCSI_L2_ISCSI_OOO_CONS_OFFSET(func),
                      0,
                      BAR_TSTRORM_INTMEM);

    LM_INTMEM_WRITE32(pdev,
                      TSTORM_ISCSI_L2_ISCSI_OOO_CID_TABLE_OFFSET(func),
                      HW_CID(pdev, OOO_CID(pdev)),
                      BAR_TSTRORM_INTMEM);

    LM_INTMEM_WRITE32(pdev,
                      TSTORM_ISCSI_L2_ISCSI_OOO_CLIENT_ID_TABLE_OFFSET(func),
                      LM_FW_CLI_ID(pdev,OOO_CID(pdev)),
                      BAR_TSTRORM_INTMEM);


    return lm_status;
}


/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t
lm_sc_init(
    IN struct _lm_device_t *pdev,
    IN struct iscsi_kwqe_init1  *req1,
    IN struct iscsi_kwqe_init2  *req2
    )
{
    lm_status_t                  lm_status;
    u16_t                        eq_page_cnt;
    u32_t                        hq_size_in_bytes;
    u32_t                        hq_pbl_entries;
    u32_t                        eq_idx;
    u16_t                        eq_sb_idx;
    u32_t                        page_size_bits;
    u8_t                         delayed_ack_en              = 0;
    const u8_t                   is_chain_mode               = TRUE;
    const u32_t                  func                        = FUNC_ID(pdev);
    struct tstorm_l5cm_tcp_flags tstorm_l5cm_tcp_flags_param = {0};

    if (CHK_NULL(req1) || CHK_NULL(req2))
    {
        return LM_STATUS_FAILURE;
    }

    DbgMessage(pdev, INFORM, "### lm_sc_init\n");

    page_size_bits = GET_FIELD(req1->flags, ISCSI_KWQE_INIT1_PAGE_SIZE);
    if (LM_PAGE_BITS - ISCSI_PAGE_BITS_SHIFT != page_size_bits)
    {
        DbgMessage(pdev, INFORM, "lm_sc_init: Illegal page size.\n");
        return LM_STATUS_FAILURE;
    }

    if(ISCSI_HSI_VERSION != req1->hsi_version)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    delayed_ack_en = GET_FIELD(req1->flags, ISCSI_KWQE_INIT1_DELAYED_ACK_ENABLE);

    pdev->iscsi_info.run_time.num_of_tasks = req1->num_tasks_per_conn;
    pdev->iscsi_info.run_time.cq_size      = req1->cq_num_wqes;
    pdev->iscsi_info.run_time.num_of_cqs   = req1->num_cqs;

    /* the number of cqs is used to determine the number of eqs */
    if (pdev->iscsi_info.run_time.num_of_cqs > MAX_EQ_CHAIN)
    {
        DbgBreakIf(pdev->iscsi_info.run_time.num_of_cqs > MAX_EQ_CHAIN);
        pdev->iscsi_info.run_time.num_of_cqs = MAX_EQ_CHAIN;
    }
    pdev->iscsi_info.run_time.l5_eq_chain_cnt     = pdev->iscsi_info.run_time.num_of_cqs;
    pdev->iscsi_info.run_time.l5_eq_max_chain_cnt = MAX_EQ_CHAIN;
    // Only one EQ chain is supported.

    if ((pdev->iscsi_info.run_time.l5_eq_chain_cnt > 1)||
        (pdev->params.sb_cnt < pdev->iscsi_info.run_time.l5_eq_chain_cnt))
    {
        DbgMessage(pdev, INFORM, "lm_sc_init: l5_eq_chain_cnt=%d\n.\n",pdev->iscsi_info.run_time.l5_eq_chain_cnt);
        DbgBreakMsg("lm_sc_init: pdev->iscsi_info.l5_eq_chain_cnt is bigger than 1.\n");
        return LM_STATUS_FAILURE;
    }
    DbgBreakIf(pdev->iscsi_info.run_time.l5_eq_chain_cnt > 1);
    DbgBreakIf(pdev->params.sb_cnt < pdev->iscsi_info.run_time.l5_eq_chain_cnt);
    /* TOE when RSS is disabled, ISCSI and FCOE will use the same NDSB.  */
    pdev->iscsi_info.run_time.l5_eq_base_chain_idx = LM_NON_RSS_SB(pdev);

//    if (!pdev->params.l4_enable_rss) {
//        RESET_FLAGS(pdev->params.sb_cpu_affinity, 1 << LM_TOE_RSS_BASE_CHAIN_INDEX(&pdev->lmdev));
//    }


    /* round up HQ size to fill an entire page */
    hq_size_in_bytes = req1->num_ccells_per_conn * sizeof(struct iscsi_hq_bd);
    hq_pbl_entries = lm_get_pbl_entries(hq_size_in_bytes);
    pdev->iscsi_info.run_time.hq_size = (u16_t)(hq_pbl_entries * (LM_PAGE_SIZE / sizeof(struct iscsi_hq_bd)));

    /* Init EQs - create page chains */
    // The size of the EQ in iSCSI is <num iscsi connections> * 2 +slowpath.
    // I.e. for each connection there should be room for 1 fastpath completion and 1 error notification.
    eq_page_cnt = lm_l5_eq_page_cnt(pdev,
                                    (u16_t)(pdev->params.max_func_iscsi_cons * 2),
                                    RESERVED_ISCSI_EQ_ELEMENTS,
                                    (ISCSI_EQES_PER_PAGE(is_chain_mode)),
                                    MAX_EQ_PAGES);// Sub the next BD page.

    LM_SC_FOREACH_EQ_IDX(pdev, eq_sb_idx)
    {
        lm_status = lm_l5_alloc_eq(pdev, &LM_SC_EQ(pdev, eq_sb_idx), &LM_EQ_ADDR_SAVE_SC(pdev, eq_sb_idx) , eq_page_cnt, LM_CLI_IDX_ISCSI);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            return lm_status;
        }

        lm_status = lm_sc_setup_eq(pdev, eq_sb_idx,is_chain_mode);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            return lm_status;
        }
    }

    SET_FLAGS( tstorm_l5cm_tcp_flags_param.flags, delayed_ack_en << TSTORM_L5CM_TCP_FLAGS_DELAYED_ACK_EN_SHIFT);

    // in case size change, we need to change LM_INTMEM_WRITEXX macro etc...
    ASSERT_STATIC( sizeof(tstorm_l5cm_tcp_flags_param) == sizeof(u16_t) );

    /* Init internal RAM */
    ASSERT_STATIC(sizeof(struct regpair_t) == sizeof(lm_address_t));

    /* init Tstorm RAM */
    LM_INTMEM_WRITE16(pdev, TSTORM_ISCSI_RQ_SIZE_OFFSET(func),           req1->rq_num_wqes, BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, TSTORM_ISCSI_PAGE_SIZE_OFFSET(func),         LM_PAGE_SIZE, BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE8 (pdev, TSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func),     LM_PAGE_BITS, BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE32(pdev, TSTORM_ISCSI_TCP_LOCAL_ADV_WND_OFFSET(func), 0x100000, BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, TSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func),      req1->num_tasks_per_conn, BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE64(pdev, TSTORM_ISCSI_ERROR_BITMAP_OFFSET(func),      *((u64_t *)&req2->error_bit_map), BAR_TSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, TSTORM_ISCSI_TCP_VARS_FLAGS_OFFSET(func),     tstorm_l5cm_tcp_flags_param.flags, BAR_TSTRORM_INTMEM);

    /* init Ustorm RAM */
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_RQ_BUFFER_SIZE_OFFSET(func), req1->rq_buffer_size, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_PAGE_SIZE_OFFSET(func), LM_PAGE_SIZE, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE8 (pdev, USTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), LM_PAGE_BITS, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), req1->num_tasks_per_conn, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_RQ_SIZE_OFFSET(func), req1->rq_num_wqes, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_CQ_SIZE_OFFSET(func), req1->cq_num_wqes, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, USTORM_ISCSI_R2TQ_SIZE_OFFSET(func), (u16_t)pdev->iscsi_info.run_time.num_of_tasks * ISCSI_MAX_NUM_OF_PENDING_R2TS, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE64(pdev, USTORM_ISCSI_GLOBAL_BUF_PHYS_ADDR_OFFSET(func), pdev->iscsi_info.bind.global_buff_base_phy.as_u64, BAR_USTRORM_INTMEM);
    LM_INTMEM_WRITE64(pdev, USTORM_ISCSI_ERROR_BITMAP_OFFSET(func), *((u64_t *)&req2->error_bit_map), BAR_USTRORM_INTMEM);

    /* init Xstorm RAM */
    LM_INTMEM_WRITE16(pdev, XSTORM_ISCSI_PAGE_SIZE_OFFSET(func), LM_PAGE_SIZE, BAR_XSTRORM_INTMEM);
    LM_INTMEM_WRITE8 (pdev, XSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), LM_PAGE_BITS, BAR_XSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, XSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), req1->num_tasks_per_conn, BAR_XSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, XSTORM_ISCSI_HQ_SIZE_OFFSET(func), pdev->iscsi_info.run_time.hq_size, BAR_XSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, XSTORM_ISCSI_SQ_SIZE_OFFSET(func), req1->num_tasks_per_conn, BAR_XSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, XSTORM_ISCSI_R2TQ_SIZE_OFFSET(func), req1->num_tasks_per_conn * ISCSI_MAX_NUM_OF_PENDING_R2TS, BAR_XSTRORM_INTMEM);

    /* init Cstorm RAM */
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_PAGE_SIZE_OFFSET(func), LM_PAGE_SIZE, BAR_CSTRORM_INTMEM);
    LM_INTMEM_WRITE8 (pdev, CSTORM_ISCSI_PAGE_SIZE_LOG_OFFSET(func), LM_PAGE_BITS, BAR_CSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_NUM_OF_TASKS_OFFSET(func), req1->num_tasks_per_conn, BAR_CSTRORM_INTMEM);
    LM_SC_FOREACH_EQ_IDX(pdev, eq_sb_idx)
    {
        eq_idx = eq_sb_idx - pdev->iscsi_info.run_time.l5_eq_base_chain_idx;
        LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_EQ_PROD_OFFSET(func, eq_idx), lm_bd_chain_prod_idx(&LM_SC_EQ(pdev, eq_sb_idx).bd_chain), BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_EQ_CONS_OFFSET(func, eq_idx), 0 , BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE32(pdev, CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, eq_idx), lm_bd_chain_phys_addr(&LM_SC_EQ(pdev, eq_sb_idx).bd_chain, 1).as_u32.low, BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE32(pdev, 4 + CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_OFFSET(func, eq_idx), lm_bd_chain_phys_addr(&LM_SC_EQ(pdev, eq_sb_idx).bd_chain, 1).as_u32.high, BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE32(pdev, CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, eq_idx), lm_bd_chain_phys_addr(&LM_SC_EQ(pdev, eq_sb_idx).bd_chain, 0).as_u32.low, BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE32(pdev, 4 + CSTORM_ISCSI_EQ_NEXT_EQE_ADDR_OFFSET(func, eq_idx), lm_bd_chain_phys_addr(&LM_SC_EQ(pdev, eq_sb_idx).bd_chain, 0).as_u32.high, BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE8 (pdev, CSTORM_ISCSI_EQ_NEXT_PAGE_ADDR_VALID_OFFSET(func, eq_idx), 1, BAR_CSTRORM_INTMEM); // maybe move to init tool
        LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_EQ_SB_NUM_OFFSET(func, eq_idx), LM_FW_SB_ID(pdev,eq_sb_idx), BAR_CSTRORM_INTMEM);
        LM_INTMEM_WRITE8 (pdev, CSTORM_ISCSI_EQ_SB_INDEX_OFFSET(func, eq_idx), HC_INDEX_ISCSI_EQ_CONS, BAR_CSTRORM_INTMEM);
    }
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_HQ_SIZE_OFFSET(func), pdev->iscsi_info.run_time.hq_size, BAR_CSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_CQ_SIZE_OFFSET(func), req1->cq_num_wqes, BAR_CSTRORM_INTMEM);
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_CQ_SQN_SIZE_OFFSET(func), req2->max_cq_sqn, BAR_CSTRORM_INTMEM);

    return LM_STATUS_SUCCESS;
} /* lm_sc_init */



/* Get dma memory for init ramrod */
STATIC lm_status_t
lm_fc_get_ramrod_phys_mem(
    IN struct _lm_device_t *pdev)
{

    if CHK_NULL(pdev->fcoe_info.bind.ramrod_mem_virt)
    {
        pdev->fcoe_info.bind.ramrod_mem_virt =
        mm_alloc_phys_mem(pdev,
                          sizeof(lm_fcoe_slow_path_phys_data_t),
                          &pdev->fcoe_info.bind.ramrod_mem_phys,
                          0,
                          LM_CLI_IDX_FCOE);

        if CHK_NULL(pdev->fcoe_info.bind.ramrod_mem_virt)
        {
            return LM_STATUS_RESOURCE;
        }
    }
    return LM_STATUS_SUCCESS;
}



lm_status_t
lm_fc_init(
    IN struct _lm_device_t          *pdev,
    IN struct fcoe_kwqe_init1       *init1,
    IN struct fcoe_kwqe_init2       *init2,
    IN struct fcoe_kwqe_init3       *init3)
{
    lm_status_t                     lm_status;
    lm_fcoe_slow_path_phys_data_t   *ramrod_params;
    u16_t                           eq_page_cnt;
    u16_t                           eq_sb_idx;
    u32_t                           func;
    u32_t                           port;
    const u8_t                      is_chain_mode = FALSE;
    if (CHK_NULL(pdev) || CHK_NULL(init1) || CHK_NULL(init2) || CHK_NULL(init3))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    func = FUNC_ID(pdev);
    port = PORT_ID(pdev);

    DbgMessage(pdev, INFORM, "### lm_fc_init\n");

    pdev->fcoe_info.run_time.num_of_cqs = 1;                 // one EQ

    // Only one EQ chain is supported.
    if ((pdev->fcoe_info.run_time.num_of_cqs > 1)||
        (pdev->params.sb_cnt < pdev->fcoe_info.run_time.num_of_cqs))
    {
        DbgMessage(pdev, INFORM, "lm_fc_init: num_of_cqs=%d\n.\n",pdev->fcoe_info.run_time.num_of_cqs);
        DbgBreakMsg("lm_fc_init: pdev->fcoe_info.run_time.num_of_cqs is bigger than 1.\n");
        return LM_STATUS_INVALID_PARAMETER;
    }
    DbgBreakIf(pdev->fcoe_info.run_time.num_of_cqs > 1);
    DbgBreakIf(pdev->params.sb_cnt < pdev->fcoe_info.run_time.num_of_cqs);
    /* TOE when RSS is disabled, ISCSI and FCOE will use the same NDSB.  */
    pdev->fcoe_info.run_time.fc_eq_base_chain_idx = LM_NON_RSS_SB(pdev);

    if(CHK_NULL(pdev->fcoe_info.bind.ramrod_mem_virt))
    {
        return LM_STATUS_RESOURCE;
    }
    ramrod_params = (lm_fcoe_slow_path_phys_data_t*)pdev->fcoe_info.bind.ramrod_mem_virt;

    // Init EQs - create page chains
    eq_page_cnt = lm_l5_eq_page_cnt(pdev,
                                    (u16_t)pdev->params.max_func_fcoe_cons,
                                    RESERVED_FCOE_EQ_ELEMENTS,
                                    FCOE_EQES_PER_PAGE(is_chain_mode),
                                    FCOE_MAX_EQ_PAGES_PER_FUNC);


    LM_FC_FOREACH_EQ_IDX(pdev, eq_sb_idx)
    {
        lm_status = lm_l5_alloc_eq(pdev, &LM_FC_EQ(pdev, eq_sb_idx),&LM_EQ_ADDR_SAVE_FC(pdev, eq_sb_idx),eq_page_cnt, LM_CLI_IDX_FCOE);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            return lm_status;
        }

        lm_status = lm_fc_alloc_eq_pbl(pdev, &LM_FC_EQ(pdev, eq_sb_idx), &LM_FC_PBL(pdev, eq_sb_idx),
                                       &LM_EQ_ADDR_SAVE_FC(pdev, eq_sb_idx));
        if (lm_status != LM_STATUS_SUCCESS)
        {
            return lm_status;
        }

        lm_status = lm_fc_setup_eq(pdev, eq_sb_idx,is_chain_mode);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            return lm_status;
        }
    }

    /* Set up the ramrod params */
    mm_memset(ramrod_params, 0, sizeof(lm_fcoe_slow_path_phys_data_t));

    memcpy(&ramrod_params->fcoe_init.init_kwqe1, init1, sizeof(struct fcoe_kwqe_init1));
    memcpy(&ramrod_params->fcoe_init.init_kwqe2, init2, sizeof(struct fcoe_kwqe_init2));
    memcpy(&ramrod_params->fcoe_init.init_kwqe3, init3, sizeof(struct fcoe_kwqe_init3));


    /* waiting for new HSI */
    ramrod_params->fcoe_init.eq_pbl_base.lo = mm_cpu_to_le32(LM_FC_PBL(pdev, pdev->fcoe_info.run_time.fc_eq_base_chain_idx).pbl_phys_table_phys.as_u32.low);
    ramrod_params->fcoe_init.eq_pbl_base.hi = mm_cpu_to_le32(LM_FC_PBL(pdev, pdev->fcoe_info.run_time.fc_eq_base_chain_idx).pbl_phys_table_phys.as_u32.high);
    ramrod_params->fcoe_init.eq_pbl_size = mm_cpu_to_le32(LM_FC_PBL(pdev, pdev->fcoe_info.run_time.fc_eq_base_chain_idx).pbl_entries);
    ramrod_params->fcoe_init.eq_prod = mm_cpu_to_le16(lm_bd_chain_prod_idx(&LM_FC_EQ(pdev, pdev->fcoe_info.run_time.fc_eq_base_chain_idx).bd_chain));
    ramrod_params->fcoe_init.sb_num = mm_cpu_to_le16(LM_FW_SB_ID(pdev,pdev->fcoe_info.run_time.fc_eq_base_chain_idx));
    ramrod_params->fcoe_init.sb_id = HC_INDEX_FCOE_EQ_CONS;

    if (IS_SD_UFP_MODE(pdev))
    {
        ramrod_params->fcoe_init.init_kwqe1.flags |= FCOE_KWQE_INIT1_CLASSIFY_FAILED_ALLOWED;
    }

    lm_status = lm_command_post(pdev,
                                LM_CLI_CID(pdev, LM_CLI_IDX_FCOE),      /* cid */
                                FCOE_RAMROD_CMD_ID_INIT_FUNC,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                pdev->fcoe_info.bind.ramrod_mem_phys.as_u64);

    if (lm_status != LM_STATUS_SUCCESS)
    {
        /* only one we know off... */
        DbgBreakIf(lm_status != LM_STATUS_REQUEST_NOT_ACCEPTED);
        /* Command wasn't posted, so we need to complete it from here. */

    }

    // completion is asynchronous

    return LM_STATUS_SUCCESS;
} /* lm_fc_init */


/** Description
 *  Callback function for cids being recylced
 */
void
lm_fc_recycle_cid_cb(
    struct _lm_device_t             *pdev,
    void                            *cookie,
    s32_t                           cid)
{
    lm_status_t         lm_status;
    lm_sp_req_common_t  *sp_req = NULL;
    lm_fcoe_state_t     *fcoe = (lm_fcoe_state_t *)cookie;

    if (CHK_NULL(pdev) || CHK_NULL(fcoe))
    {
        DbgBreakIf(1);
        return;
    }

    MM_ACQUIRE_TOE_LOCK(pdev);

    /* un-block the manager... */
    lm_set_cid_state(pdev, fcoe->cid, LM_CID_STATE_VALID);

    lm_status = lm_fc_init_fcoe_context(pdev, fcoe);

    lm_status = lm_fc_post_offload_ramrod(pdev, fcoe);

    /* we can now unblock any pending slow-paths */
    lm_sp_req_manager_unblock(pdev, cid, &sp_req);

    MM_RELEASE_TOE_LOCK(pdev);
}

void lm_fc_comp_cb(struct _lm_device_t *pdev, struct sq_pending_command *pending)
{
    struct fcoe_kcqe kcqe = {0};
    lm_fcoe_state_t *fcoe = NULL;
    u32_t            cid;
    u8_t             cmd;


    if (CHK_NULL(pdev) || CHK_NULL(pending))
    {
        return;
    }

    cmd = pending->cmd;
    cid = pending->cid;

    fcoe = lm_cid_cookie(pdev, FCOE_CONNECTION_TYPE, cid);

    if (fcoe)
    {
        kcqe.fcoe_conn_id         = fcoe->fcoe_conn_id;
        kcqe.fcoe_conn_context_id = HW_CID(pdev, cid);
    }

    kcqe.completion_status = LM_STATUS_SUCCESS; /* Fixme: do we want this?? maybe ok since l5 is aware of er... */

    switch (cmd)
    {
    case FCOE_RAMROD_CMD_ID_INIT_FUNC:
        kcqe.op_code = FCOE_KCQE_OPCODE_INIT_FUNC;
        break;

    case FCOE_RAMROD_CMD_ID_DESTROY_FUNC:
        kcqe.op_code = FCOE_KCQE_OPCODE_DESTROY_FUNC;
        break;

    case FCOE_RAMROD_CMD_ID_STAT_FUNC:
        kcqe.op_code = FCOE_KCQE_OPCODE_STAT_FUNC;
        break;

    case FCOE_RAMROD_CMD_ID_OFFLOAD_CONN:
        kcqe.op_code = FCOE_KCQE_OPCODE_OFFLOAD_CONN;
        break;

    case FCOE_RAMROD_CMD_ID_ENABLE_CONN:
        kcqe.op_code = FCOE_KCQE_OPCODE_ENABLE_CONN;
        break;

    case FCOE_RAMROD_CMD_ID_DISABLE_CONN:
        kcqe.op_code = FCOE_KCQE_OPCODE_DISABLE_CONN;
        break;

    case FCOE_RAMROD_CMD_ID_TERMINATE_CONN:
        kcqe.op_code = FCOE_RAMROD_CMD_ID_TERMINATE_CONN;
        break;
    }

    lm_fc_complete_slow_path_request(pdev, &kcqe);
}

/**
 * @description
 * Returns the max FCOE task supported.
 * In oreder to know the max task enabled refer to
 * pdev->params.max_fcoe_task
 * @param pdev
 *
 * @return u32_t
 */
u32_t
lm_fc_max_fcoe_task_sup(
    IN struct _lm_device_t          *pdev)
{
    u32_t max_fcoe_task = MAX_NUM_FCOE_TASKS_PER_ENGINE;

    /* FCOE supports a maximum of MAX_FCOE_FUNCS_PER_ENGINE per engine.
     * Incase of mf / 4-port mode it means we can have more than one fcoe function
     * on an engine - in which case we'll need to divide the number of tasks between them. 
     * However, in single function mode, on a 2-port chip (i.e. one function on the engine) 
     * the fcoe function will have all the tasks allocated to it 
     */
    if (IS_MULTI_VNIC(pdev) || (CHIP_PORT_MODE(pdev) == LM_CHIP_PORT_MODE_4))
    {
        max_fcoe_task = max_fcoe_task / MAX_FCOE_FUNCS_PER_ENGINE;
    }
    
    return max_fcoe_task;
}
/**
 *
 *
 * @description
 *
 * @param pdev
 *
 * @return STATIC void
 */
STATIC void
lm_fc_init_vars(
    IN struct _lm_device_t          *pdev)
{

    if CHK_NULL(pdev)
    {
        return ;
    }

    mm_mem_zero(&pdev->fcoe_info, sizeof(lm_fcoe_info_t));
}
/**
 *
 *
 * @description
 *
 * @param pdev
 *
 * @return lm_status_t
 */
lm_status_t
lm_fc_alloc_resc(
    IN struct _lm_device_t          *pdev)
{
    lm_status_t lm_status = LM_STATUS_SUCCESS;
    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }
    lm_fc_init_vars(pdev);
    /* cid recycled cb registration */
    lm_cid_recycled_cb_register(pdev, FCOE_CONNECTION_TYPE, lm_fc_recycle_cid_cb);

    /* Sq-completion cb registration (sq that get completed internally in driver */
    lm_sq_comp_cb_register(pdev, FCOE_CONNECTION_TYPE, lm_fc_comp_cb);
    /* Get physical memory for RAMROD commands */
    lm_status = lm_fc_get_ramrod_phys_mem(pdev);

    if (lm_status != LM_STATUS_SUCCESS)
    {
        return lm_status;
    }
    return LM_STATUS_SUCCESS;
} /* lm_fc_alloc_resc */




lm_status_t lm_sc_complete_l4_ofld_request(lm_device_t *pdev, struct iscsi_kcqe *kcqe)
{
    u32_t comp_status = 0;
    lm_tcp_state_t *tcp;
    u32_t cid;

    if (CHK_NULL(pdev) || CHK_NULL(kcqe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    cid = SW_CID(kcqe->iscsi_conn_context_id);
    tcp = lm_cid_cookie(pdev, TOE_CONNECTION_TYPE, cid);
    DbgBreakIf(!tcp);

    if (kcqe->completion_status & ISCSI_KCQE_COMPLETION_STATUS_CTX_ALLOC_FAILURE)
    {
        /* currently there is no specific completion status handling, only success / fail */
        /* but originally the flags are those of toe_initiate_offload_ramrod_data */
        comp_status = 1;
    }

    /* toe lock is taken inside */
    lm_tcp_comp_initiate_offload_request(pdev, tcp, comp_status);

    return LM_STATUS_SUCCESS;
}

lm_status_t lm_sc_complete_l4_upload_request(lm_device_t *pdev, u8_t op_code, u32_t cid)
{
    lm_status_t      lm_status = LM_STATUS_SUCCESS;
    lm_tcp_state_t * tcp       = NULL;

    tcp = lm_cid_cookie(pdev, TOE_CONNECTION_TYPE, cid);
    if (NULL == tcp)
    {
        return LM_STATUS_FAILURE;
    }

    switch (op_code)
    {
    case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
        if (mm_sc_is_omgr_enabled(pdev))
        {
            lm_empty_ramrod_eth(pdev, OOO_CID(pdev), cid, NULL, 0 /*d/c*/);
        }
        else
        {
            lm_tcp_searcher_ramrod_complete(pdev, tcp);
        }
        break;
    case RAMROD_CMD_ID_ETH_EMPTY:
        lm_tcp_searcher_ramrod_complete(pdev, tcp);
        break;
    case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
        lm_tcp_terminate_ramrod_complete(pdev, tcp);
        break;
    case L5CM_RAMROD_CMD_ID_QUERY:
        lm_tcp_query_ramrod_complete(pdev, tcp);
        break;
    default:
        DbgMessage(pdev, WARN, "lm_sc_complete_l4_upload_request: Invalid op_code 0x%x.\n", op_code);
        return LM_STATUS_INVALID_PARAMETER;
    }

    return LM_STATUS_SUCCESS;
}



lm_status_t lm_sc_complete_slow_path_request(lm_device_t *pdev, struct iscsi_kcqe *kcqe)
{
    lm_status_t lm_status = LM_STATUS_FAILURE;
    u8_t        op_code   = 0;

    if (CHK_NULL(pdev) || CHK_NULL(kcqe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    op_code = kcqe->op_code; /* Store the opcode, the function below may modify it (internal searcher), need to keep for sq_complete later on  */

    switch (kcqe->op_code)
    {
/*  case ISCSI_KCQE_OPCODE_INIT:
        lm_status = mm_sc_complete_init_request(pdev, kcqe);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            DbgMessage(pdev, WARN, "lm_sc_complete_slow_path_request: lm_sc_complete_init_request failed.\n");
        }
        break;
*/    case L5CM_RAMROD_CMD_ID_ADD_NEW_CONNECTION:
        lm_status = lm_sc_complete_l4_ofld_request(pdev, kcqe);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            DbgMessage(pdev, WARN, "lm_sc_complete_slow_path_request: lm_sc_complete_l4_ofld_request failed.\n");
        }
        break;
    case ISCSI_KCQE_OPCODE_UPDATE_CONN:
        lm_status = mm_sc_complete_update_request(pdev, kcqe);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            DbgMessage(pdev, WARN, "lm_sc_complete_slow_path_request: lm_sc_complete_update_request failed.\n");
        }
        break;
    case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
    case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
    case L5CM_RAMROD_CMD_ID_QUERY:
        lm_status = lm_sc_complete_l4_upload_request(pdev, kcqe->op_code, SW_CID(kcqe->iscsi_conn_context_id));
        break;
    default:
        DbgMessage(pdev, WARN, "lm_sc_complete_slow_path_request: Invalid op_code 0x%x.\n", kcqe->op_code);
    }

    lm_sq_complete(pdev, CMD_PRIORITY_NORMAL, op_code,
                   ISCSI_CONNECTION_TYPE, SW_CID(kcqe->iscsi_conn_context_id));

    return lm_status;
}


/* Handle FC related ramrod completions */
lm_status_t
lm_fc_complete_slow_path_request(
    IN struct _lm_device_t          *pdev,
    IN struct fcoe_kcqe             *kcqe)
{
    lm_status_t                     lm_status    = LM_STATUS_FAILURE;
    lm_fcoe_state_t                 *fcoe        = NULL;
    const u8_t                      priority     = CMD_PRIORITY_NORMAL;
    const enum connection_type      con_type     = FCOE_CONNECTION_TYPE;
    u32_t                           cid          = 0;
    u32_t                           sw_cid       = 0;
    u8_t                            fcoe_commnad = 0;
    u8_t                            b_valid      = TRUE;

    if (CHK_NULL(pdev) || CHK_NULL(kcqe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    switch (kcqe->op_code)
    {
        case FCOE_KCQE_OPCODE_INIT_FUNC:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_INIT_FUNC;
            lm_status    = mm_fc_complete_init_request(pdev, kcqe);
            cid          = LM_CLI_CID(pdev, LM_CLI_IDX_FCOE);
            break;
        }
        case FCOE_KCQE_OPCODE_OFFLOAD_CONN:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_OFFLOAD_CONN;

            DbgBreakIf(0 != mm_le32_to_cpu(kcqe->completion_status)); /* offload should never fail */

            sw_cid = SW_CID(mm_le32_to_cpu(kcqe->fcoe_conn_context_id));
            fcoe   = lm_cid_cookie(pdev, con_type, sw_cid);

            if(!fcoe)
            {
                lm_status = LM_STATUS_RESOURCE;
                DbgBreakIf(!fcoe);
                break;
            }

            cid       = fcoe->cid;
            lm_status = mm_fc_complete_ofld_request(pdev, fcoe, kcqe);
            break;
        }
        case FCOE_KCQE_OPCODE_ENABLE_CONN:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_ENABLE_CONN;

            DbgBreakIf(0 != mm_le32_to_cpu(kcqe->completion_status)); /* enable should never fail */

            sw_cid = SW_CID(mm_le32_to_cpu(kcqe->fcoe_conn_context_id));
            fcoe   = lm_cid_cookie(pdev, con_type, sw_cid);

            if(!fcoe)
            {
                lm_status = LM_STATUS_RESOURCE;
                DbgBreakIf(!fcoe);
                break;
            }
            cid    = fcoe->cid;

            lm_status = mm_fc_complete_enable_request(pdev, fcoe, kcqe);
            break;
        }
        case FCOE_KCQE_OPCODE_DISABLE_CONN:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_DISABLE_CONN;

            /* Disable is complete, now we need to send the terminate ramrod */
            DbgBreakIf(0 != mm_le32_to_cpu(kcqe->completion_status)); /* disable should never fail */

            sw_cid = SW_CID(mm_le32_to_cpu(kcqe->fcoe_conn_context_id));
            fcoe   = lm_cid_cookie(pdev, con_type, sw_cid);

            if(!fcoe)
            {
                lm_status = LM_STATUS_RESOURCE;
                DbgBreakIf(!fcoe);
                break;
            }

            cid          = fcoe->cid;
            lm_status    = mm_fc_complete_disable_request(pdev, fcoe, kcqe);
            break;
        }
        case FCOE_KCQE_OPCODE_DESTROY_FUNC:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_DESTROY_FUNC;
            lm_status    = mm_fc_complete_destroy_request(pdev, kcqe);
            cid          = LM_CLI_CID(pdev, LM_CLI_IDX_FCOE);
            break;
        }
        case FCOE_KCQE_OPCODE_STAT_FUNC:
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_STAT_FUNC;
            lm_status    = mm_fc_complete_stat_request(pdev, kcqe);
            cid          = LM_CLI_CID(pdev, LM_CLI_IDX_FCOE);
            break;
        }
        case FCOE_RAMROD_CMD_ID_TERMINATE_CONN: /* Internal VBD not passed up... */
        {
            fcoe_commnad = FCOE_RAMROD_CMD_ID_TERMINATE_CONN;

            /* Terminate is complete, now we need to send the CFC delete ramrod */
            DbgBreakIf(0 != mm_le32_to_cpu(kcqe->completion_status)); /* terminate should never fail */

            sw_cid = SW_CID(mm_le32_to_cpu(kcqe->fcoe_conn_context_id));

            fcoe = lm_cid_cookie(pdev, con_type, sw_cid);

            if(!fcoe)
            {
                lm_status = LM_STATUS_RESOURCE;
                DbgBreakIf(!fcoe);
                break;
            }

            cid = fcoe->cid;

            lm_status = mm_fc_complete_terminate_request(pdev, fcoe, kcqe);
            break;
        }
        default:
        {
            DbgMessage(pdev, WARN, "lm_fc_complete_slow_path_request: Invalid op_code 0x%x.\n", kcqe->op_code);
            b_valid = FALSE;
            break;
        }
    }

    if( b_valid )
    {
        lm_sq_complete(pdev, priority, fcoe_commnad, con_type, cid);
    }

    return lm_status;
}

u8_t lm_sc_is_eq_completion(lm_device_t *pdev, u8_t sb_idx)
{
    u8_t result = FALSE;
    lm_eq_chain_t *eq = NULL;

    DbgBreakIf(!(pdev && ARRSIZE(pdev->iscsi_info.run_time.eq_chain) > sb_idx));

    eq = &LM_SC_EQ(pdev, sb_idx);

    if (eq->hw_con_idx_ptr &&
        mm_le16_to_cpu(*eq->hw_con_idx_ptr) != lm_bd_chain_cons_idx(&eq->bd_chain) )
    {
        result = TRUE;
    }
    DbgMessage(pdev, INFORMl5, "lm_sc_is_rx_completion(): result is:%s\n", result? "TRUE" : "FALSE");

    return result;
}



u8_t
lm_fc_is_eq_completion(lm_device_t *pdev, u8_t sb_idx)
{
    u8_t result = FALSE;
    lm_eq_chain_t *eq = NULL;

    DbgBreakIf(!(pdev && ARRSIZE(pdev->fcoe_info.run_time.eq_chain) > sb_idx));

    eq = &LM_FC_EQ(pdev, sb_idx);

    if (eq->hw_con_idx_ptr &&
        mm_le16_to_cpu(*eq->hw_con_idx_ptr) != lm_bd_chain_cons_idx(&eq->bd_chain))
    {
        result = TRUE;
    }

    DbgMessage(pdev, INFORMl5, "lm_fc_is_rx_completion(): result is:%s\n", result? "TRUE" : "FALSE");

    return result;
}



lm_status_t
lm_sc_handle_tcp_event(
    IN    lm_device_t *pdev,
    IN    u32_t cid,
    IN    u32_t op_code
    )
{
    lm_tcp_state_t *tcp = NULL;

    if CHK_NULL(pdev)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    tcp = lm_cid_cookie(pdev, TOE_CONNECTION_TYPE, cid);
    if CHK_NULL(tcp)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    switch (op_code)
    {
    case ISCSI_KCQE_OPCODE_TCP_FIN:
        tcp->tcp_state_calc.fin_reception_time = mm_get_current_time(pdev);
        break;
    case ISCSI_KCQE_OPCODE_TCP_RESET:
        tcp->tcp_state_calc.con_rst_flag = TRUE;
        break;
    default:
        DbgMessage(pdev, WARN, "lm_sc_handle_tcp_event: Invalid op_code 0x%x\n", op_code);
        return LM_STATUS_INVALID_PARAMETER;
    }

    return LM_STATUS_SUCCESS;
}

lm_status_t
lm_sc_comp_l5_request(
    IN    lm_device_t *pdev,
    IN    lm_eq_chain_t *eq_chain,
    INOUT struct iscsi_kcqe **l5_kcqe_start,
    INOUT u16_t *l5_kcqe_num)
{
    lm_status_t lm_status;

    if (CHK_NULL(pdev) || CHK_NULL(eq_chain) || CHK_NULL(l5_kcqe_start) || CHK_NULL(l5_kcqe_num))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    lm_status = mm_sc_comp_l5_request(pdev, *l5_kcqe_start, *l5_kcqe_num);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        DbgMessage(pdev, WARN, "lm_sc_service_eq_intr: mm_sc_comp_l5_request failed.\n");
    }

    lm_bd_chain_bds_produced(&eq_chain->bd_chain, *l5_kcqe_num);
    *l5_kcqe_num = 0;
    *l5_kcqe_start = NULL;

    return lm_status;
}



lm_status_t
lm_fc_comp_request(
    IN    lm_device_t       *pdev,
    IN    lm_eq_chain_t     *eq_chain,
    INOUT struct fcoe_kcqe  **fcoe_kcqe_start,
    INOUT u16_t             *fcoe_kcqe_num)
{
    lm_status_t lm_status;

    if (CHK_NULL(pdev) || CHK_NULL(eq_chain) || CHK_NULL(fcoe_kcqe_start) || CHK_NULL(fcoe_kcqe_num))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    lm_status = mm_fc_comp_request(pdev, *fcoe_kcqe_start, *fcoe_kcqe_num);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        DbgMessage(pdev, WARN, "lm_fc_service_eq_intr: lm_fc_comp_request failed.\n");
    }

    lm_bd_chain_bds_produced(&eq_chain->bd_chain, *fcoe_kcqe_num);
    *fcoe_kcqe_num = 0;
    *fcoe_kcqe_start = NULL;

    return lm_status;
}




void
lm_sc_service_eq_intr(
    IN struct _lm_device_t          *pdev,
    IN u8_t                         sb_idx)
{
    lm_status_t         lm_status;
    lm_eq_chain_t *eq_chain       = NULL;
    struct iscsi_kcqe   *kcqe           = NULL;
    struct iscsi_kcqe   *l5_kcqe_start  = NULL;
    u16_t               l5_kcqe_num     = 0;
    u16_t               eq_new_idx      = 0;
    u16_t               eq_old_idx      = 0;
    u32_t               eq_num          = 0;
    u32_t               cid             = 0;


    if (CHK_NULL(pdev) || (ARRSIZE(pdev->iscsi_info.run_time.eq_chain) <= sb_idx))
    {
        DbgBreakIf(ARRSIZE(pdev->iscsi_info.run_time.eq_chain) <= sb_idx);
        DbgBreakIf(!pdev);
        return;
    }

    eq_chain = &LM_SC_EQ(pdev, sb_idx);

    eq_new_idx = mm_le16_to_cpu(*(eq_chain->hw_con_idx_ptr));
    eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);
    DbgBreakIf(S16_SUB(eq_new_idx, eq_old_idx) < 0);

    while (eq_old_idx != eq_new_idx)
    {
        DbgBreakIf(S16_SUB(eq_new_idx, eq_old_idx) <= 0);

        /* get next consumed kcqe */
        kcqe = (struct iscsi_kcqe *)lm_bd_chain_consume_bd_contiguous(&eq_chain->bd_chain);

        /* we got to the end of the page, if we have some kcqe that we need to indicate, */
        /* do it now, cause we can't assume that the memorey of the pages is contiguous */
        if (kcqe == NULL)
        {
            if (l5_kcqe_num != 0)
            {
                lm_status = lm_sc_comp_l5_request(pdev, eq_chain, &l5_kcqe_start, &l5_kcqe_num);
            }

            /* check cons index again */
            eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);

            if (eq_old_idx != eq_new_idx)
            {
                /* get next consumed cqe */
                kcqe = (struct iscsi_kcqe *)lm_bd_chain_consume_bd_contiguous(&eq_chain->bd_chain);

                if (CHK_NULL(kcqe))
                {
                    /* shouldn't have happened, got second null from the bd */
                    DbgBreakIf(!kcqe);
                    break;
                }
            }
            else
            {
                /* the new kcqe was the last one we got, break */
                break;
            }
        }

        switch (kcqe->op_code)
        {
        case ISCSI_RAMROD_CMD_ID_INIT:
        case L5CM_RAMROD_CMD_ID_ADD_NEW_CONNECTION:
        case ISCSI_RAMROD_CMD_ID_UPDATE_CONN:
        case L5CM_RAMROD_CMD_ID_SEARCHER_DELETE:
        case L5CM_RAMROD_CMD_ID_TERMINATE_OFFLOAD:
        case L5CM_RAMROD_CMD_ID_QUERY:

            /* first, complete fast path and error indication, if any */
            if (l5_kcqe_num != 0)
            {
                lm_status = lm_sc_comp_l5_request(pdev, eq_chain, &l5_kcqe_start, &l5_kcqe_num);
            }

            lm_status = lm_sc_complete_slow_path_request(pdev, kcqe);
            if (lm_status != LM_STATUS_SUCCESS)
            {
                DbgMessage(pdev, WARN, "lm_sc_service_eq_intr: mm_sc_comp_l5_request failed.\n");
            }

            lm_bd_chain_bds_produced(&eq_chain->bd_chain, 1);
            break;

        case ISCSI_KCQE_OPCODE_TCP_FIN:
        case ISCSI_KCQE_OPCODE_TCP_RESET:
            cid = SW_CID(kcqe->iscsi_conn_context_id);

            lm_sc_handle_tcp_event(pdev, cid, kcqe->op_code);
            /* FALLTHROUGH */
        default:
            if (l5_kcqe_start == NULL)
            {
                l5_kcqe_start = kcqe;
            }

            l5_kcqe_num++;
            break;
        }

        eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);
    }

    /* complete left fast path events */
    if (l5_kcqe_num != 0)
    {
        lm_status = lm_sc_comp_l5_request(pdev, eq_chain, &l5_kcqe_start, &l5_kcqe_num);
    }

    /* update EQ prod in RAM */
    eq_num = sb_idx - pdev->iscsi_info.run_time.l5_eq_base_chain_idx;
    LM_INTMEM_WRITE16(pdev, CSTORM_ISCSI_EQ_PROD_OFFSET(FUNC_ID(pdev), eq_num), lm_bd_chain_prod_idx(&eq_chain->bd_chain), BAR_CSTRORM_INTMEM);
}



void
lm_fc_service_eq_intr(lm_device_t *pdev, u8_t sb_idx)
{
    lm_status_t         lm_status;
    lm_eq_chain_t       *eq_chain       = NULL;
    struct fcoe_kcqe    *kcqe           = NULL;
    struct fcoe_kcqe    *fcoe_kcqe_start= NULL;
    u16_t               fcoe_kcqe_num   = 0;
    u16_t               eq_new_idx      = 0;
    u16_t               eq_old_idx      = 0;

    if (CHK_NULL(pdev) || (ARRSIZE(pdev->fcoe_info.run_time.eq_chain) <= sb_idx))
    {
        DbgBreakIf(ARRSIZE(pdev->fcoe_info.run_time.eq_chain) <= sb_idx);
        DbgBreakIf(!pdev);
        return;
    }

    eq_chain = &LM_FC_EQ(pdev, sb_idx);

    eq_new_idx = mm_le16_to_cpu(*(eq_chain->hw_con_idx_ptr));
    eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);
    DbgBreakIf(S16_SUB(eq_new_idx, eq_old_idx) < 0);

    while (eq_old_idx != eq_new_idx)
    {
        DbgBreakIf(S16_SUB(eq_new_idx, eq_old_idx) <= 0);

        /* get next consumed kcqe */
        kcqe = (struct fcoe_kcqe *)lm_bd_chain_consume_bd_contiguous(&eq_chain->bd_chain);

        /* we got to the end of the page, if we have some kcqe that we need to indicate, */
        /* do it now, cause we can't assume that the memorey of the pages is contiguous */
        if (kcqe == NULL)
        {
            if (fcoe_kcqe_num != 0)
            {
                lm_status = lm_fc_comp_request(pdev,
                                               eq_chain,
                                               &fcoe_kcqe_start,
                                               &fcoe_kcqe_num);
            }

            /* check cons index again */
            eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);

            if (eq_old_idx != eq_new_idx)
            {
                /* get next consumed cqe */
                kcqe = (struct fcoe_kcqe *)lm_bd_chain_consume_bd(&eq_chain->bd_chain);

                if (CHK_NULL(kcqe))
                {
                    /* shouldn't have happened, got second null from the bd */
                    DbgBreakIf(!kcqe);
                    break;
                }
            }
            else
            {
                /* the new kcqe was the last one we got, break */
                break;
            }
        }

        /* first, complete fast path completion notification and error indication, if any */
        if (fcoe_kcqe_num != 0)
        {
            lm_status = lm_fc_comp_request(pdev,
                                           eq_chain,
                                           &fcoe_kcqe_start,
                                           &fcoe_kcqe_num);
        }

        switch (kcqe->op_code)
        {
            case FCOE_KCQE_OPCODE_INIT_FUNC:
            case FCOE_KCQE_OPCODE_OFFLOAD_CONN:
            case FCOE_KCQE_OPCODE_ENABLE_CONN:
            case FCOE_KCQE_OPCODE_DISABLE_CONN:
            case FCOE_KCQE_OPCODE_DESTROY_FUNC:
            case FCOE_KCQE_OPCODE_STAT_FUNC:
            case FCOE_RAMROD_CMD_ID_TERMINATE_CONN:
            {
                lm_status = lm_fc_complete_slow_path_request(pdev, kcqe);
                if (lm_status != LM_STATUS_SUCCESS)
                {
                    DbgMessage(pdev, WARN, "lm_fc_service_eq_intr: lm_fc_complete_slow_path_request failed.\n");
                }

                lm_bd_chain_bds_produced(&eq_chain->bd_chain, 1);
                break;
            }

            default:
            {
                if (fcoe_kcqe_start == NULL)
                {
                    fcoe_kcqe_start = kcqe;
                }

                fcoe_kcqe_num++;
                break;
            }
        }

        eq_old_idx = lm_bd_chain_cons_idx(&eq_chain->bd_chain);
    }

    /* complete left fast path events */
    if (fcoe_kcqe_num != 0)
    {
        lm_status = lm_fc_comp_request(pdev, eq_chain, &fcoe_kcqe_start, &fcoe_kcqe_num);
    }

    /* update EQ prod in RAM */
    LM_INTMEM_WRITE16(pdev, USTORM_FCOE_EQ_PROD_OFFSET(FUNC_ID(pdev)), lm_bd_chain_prod_idx(&eq_chain->bd_chain), BAR_USTRORM_INTMEM);
}


lm_status_t
lm_sc_alloc_con_phys_mem(
    IN struct _lm_device_t          *pdev,
    IN lm_iscsi_state_t             *iscsi)
{
    lm_status_t lm_status  = LM_STATUS_SUCCESS;
    u32_t       mem_size   = sizeof(*iscsi->sp_req_data.virt_addr);
    u8_t        mm_cli_idx = LM_RESOURCE_ISCSI;


    /* Allocate slopwath request data */
    iscsi->sp_req_data.virt_addr = mm_rt_alloc_phys_mem(pdev,
                                                        mem_size,
                                                        &iscsi->sp_req_data.phys_addr,
                                                        0,
                                                        mm_cli_idx);
    if CHK_NULL(iscsi->sp_req_data.virt_addr)
    {   /* can't allocate task array */
        return LM_STATUS_RESOURCE;
    }

    mm_memset(iscsi->sp_req_data.virt_addr, 0, mem_size);

    /* Allocate task array */
    iscsi->task_array.base_size = pdev->iscsi_info.run_time.num_of_tasks * sizeof(struct iscsi_task_context_entry);
    iscsi->task_array.base_virt = mm_rt_alloc_phys_mem(pdev,
                                                iscsi->task_array.base_size,
                                                &iscsi->task_array.base_phy,
                                                0,
                                                mm_cli_idx);
    if CHK_NULL(iscsi->task_array.base_virt)
    {   /* can't allocate task array */
        return LM_STATUS_RESOURCE;
    }

    mm_memset(iscsi->task_array.base_virt, 0, iscsi->task_array.base_size);

    lm_status = lm_create_pbl(pdev,
                              iscsi->task_array.base_virt,
                              &iscsi->task_array.base_phy,
                              iscsi->task_array.base_size,
                              &iscsi->task_array.pbl_phys_table_virt,
                              &iscsi->task_array.pbl_phys_table_phys,
                              &iscsi->task_array.pbl_virt_table,
                              &iscsi->task_array.pbl_entries,
                              &iscsi->task_array.pbl_size,
                              TRUE,
                              LM_RESOURCE_ISCSI);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        return lm_status;
    }

    /* Allocate R2TQ */
    iscsi->r2tq.base_size = pdev->iscsi_info.run_time.num_of_tasks * ISCSI_MAX_NUM_OF_PENDING_R2TS * ISCSI_R2TQE_SIZE;
    iscsi->r2tq.base_virt = mm_rt_alloc_phys_mem(pdev,
                                                iscsi->r2tq.base_size,
                                                &iscsi->r2tq.base_phy,
                                                0,
                                                mm_cli_idx);
    if CHK_NULL(iscsi->r2tq.base_virt)
    {   /* can't allocate R2TQ */
        return LM_STATUS_RESOURCE;
    }

    mm_memset(iscsi->r2tq.base_virt, 0, iscsi->r2tq.base_size);

    lm_status = lm_create_pbl(pdev,
                              iscsi->r2tq.base_virt,
                              &iscsi->r2tq.base_phy,
                              iscsi->r2tq.base_size,
                              &iscsi->r2tq.pbl_phys_table_virt,
                              &iscsi->r2tq.pbl_phys_table_phys,
                              &iscsi->r2tq.pbl_virt_table,
                              &iscsi->r2tq.pbl_entries,
                              &iscsi->r2tq.pbl_size,
                              TRUE,
                              LM_RESOURCE_ISCSI);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        return lm_status;
    }

    /* Allocate HQ */
    iscsi->hq.base_size = pdev->iscsi_info.run_time.hq_size * sizeof(struct iscsi_hq_bd);
    iscsi->hq.base_virt = mm_rt_alloc_phys_mem(pdev,
                                                iscsi->hq.base_size,
                                                &iscsi->hq.base_phy,
                                                0,
                                                mm_cli_idx);
    if CHK_NULL(iscsi->hq.base_virt)
    {   /* can't allocate HQ */

        return LM_STATUS_RESOURCE;
    }

    mm_memset(iscsi->hq.base_virt, 0, iscsi->hq.base_size);

    lm_status = lm_create_pbl(pdev,
                              iscsi->hq.base_virt,
                              &iscsi->hq.base_phy,
                              iscsi->hq.base_size,
                              &iscsi->hq.pbl_phys_table_virt,
                              &iscsi->hq.pbl_phys_table_phys,
                              &iscsi->hq.pbl_virt_table,
                              &iscsi->hq.pbl_entries,
                              &iscsi->hq.pbl_size,
                              TRUE,
                              LM_RESOURCE_ISCSI);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        return lm_status;
    }

    return lm_status;

}
/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t
lm_sc_alloc_con_resc(
    IN struct _lm_device_t          *pdev,
    IN lm_iscsi_state_t             *iscsi,
    IN struct iscsi_kwqe_conn_offload1   *req1,
    IN struct iscsi_kwqe_conn_offload2   *req2,
    IN struct iscsi_kwqe_conn_offload3   *req3
    )
{
    lm_status_t lm_status;
    s32_t cid;

    if (CHK_NULL(pdev) || CHK_NULL(iscsi) || CHK_NULL(req1) || CHK_NULL(req2) || CHK_NULL(req3))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    DbgMessage(pdev, INFORM, "### lm_sc_alloc_con_resc\n");

    /* save the miniport's conn id */
    iscsi->iscsi_conn_id = req1->iscsi_conn_id;

    /* Boot connections physical resources are allocated during bind, and not during offload... */
    if (!iscsi->b_resources_allocated)
    {
        lm_status = lm_sc_alloc_con_phys_mem(pdev, iscsi);
        if (lm_status != LM_STATUS_SUCCESS)
        {
            lm_sc_free_con_resc(pdev, iscsi);
            return lm_status;
        }
        iscsi->b_resources_allocated = TRUE;
    }


    /* Allocate CID */
    lm_status = lm_allocate_cid(pdev, ISCSI_CONNECTION_TYPE, (void *)iscsi, &cid);
    if (lm_status == LM_STATUS_PENDING)
    {
        lm_sp_req_manager_block(pdev, (u32_t)cid);
    }
    else if (lm_status != LM_STATUS_SUCCESS)
    {
        /* failed to allocate CID */
        lm_sc_free_con_resc(pdev, iscsi);

        return lm_status;
    }

    /* save the returned cid */
    iscsi->cid = (u32_t)cid;

    /* the allocated slow path request phys data for iscsi will be used in the tcp_state.sp_data, for the query request */
    lm_status = lm_sp_req_manager_set_sp_data(pdev, iscsi->cid, iscsi->sp_req_data.virt_addr, iscsi->sp_req_data.phys_addr);
    if (lm_status != LM_STATUS_SUCCESS)
    {
        lm_sc_free_con_resc(pdev, iscsi);

        return lm_status;
    }

    if (lm_cid_state(pdev, iscsi->cid) == LM_CID_STATE_PENDING) {
        return LM_STATUS_PENDING; /* Too soon to initialize context */
    }

    return LM_STATUS_SUCCESS;
} /* lm_sc_alloc_con_resc */


void lm_sc_free_con_phys_mem(
    IN struct _lm_device_t *pdev,
    IN lm_iscsi_state_t *iscsi
    )
{
    u8_t mm_cli_idx = LM_RESOURCE_ISCSI;

    if (iscsi->sp_req_data.virt_addr)
    {
        mm_rt_free_phys_mem(pdev, sizeof(*iscsi->sp_req_data.virt_addr), iscsi->sp_req_data.virt_addr, iscsi->sp_req_data.phys_addr, mm_cli_idx);
        iscsi->sp_req_data.virt_addr = NULL;
    }
    if (iscsi->task_array.base_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->task_array.base_size, iscsi->task_array.base_virt, iscsi->task_array.base_phy, mm_cli_idx);
        iscsi->task_array.base_virt = NULL;
    }
    if (iscsi->task_array.pbl_phys_table_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->task_array.pbl_size, iscsi->task_array.pbl_phys_table_virt, iscsi->task_array.pbl_phys_table_phys, mm_cli_idx);
        iscsi->task_array.pbl_phys_table_virt = NULL;
    }
    if (iscsi->task_array.pbl_virt_table) {
        mm_rt_free_mem(pdev, iscsi->task_array.pbl_virt_table, iscsi->task_array.pbl_entries * sizeof(void *), mm_cli_idx);
        iscsi->task_array.pbl_virt_table = NULL;
    }
    if (iscsi->r2tq.base_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->r2tq.base_size, iscsi->r2tq.base_virt, iscsi->r2tq.base_phy, mm_cli_idx);
        iscsi->r2tq.base_virt = NULL;
    }
    if (iscsi->r2tq.pbl_phys_table_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->r2tq.pbl_size, iscsi->r2tq.pbl_phys_table_virt, iscsi->r2tq.pbl_phys_table_phys, mm_cli_idx);
        iscsi->r2tq.pbl_phys_table_virt = NULL;
    }
    if (iscsi->r2tq.pbl_virt_table) {
        mm_rt_free_mem(pdev, iscsi->r2tq.pbl_virt_table, iscsi->r2tq.pbl_entries * sizeof(void *), mm_cli_idx);
        iscsi->r2tq.pbl_virt_table = NULL;
    }
    if (iscsi->hq.base_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->hq.base_size, iscsi->hq.base_virt, iscsi->hq.base_phy, mm_cli_idx);
        iscsi->hq.base_virt = NULL;
    }
    if (iscsi->hq.pbl_phys_table_virt) {
        mm_rt_free_phys_mem(pdev, iscsi->hq.pbl_size, iscsi->hq.pbl_phys_table_virt, iscsi->hq.pbl_phys_table_phys, mm_cli_idx);
        iscsi->hq.pbl_phys_table_virt = NULL;
    }
    if (iscsi->hq.pbl_virt_table) {
        mm_rt_free_mem(pdev, iscsi->hq.pbl_virt_table, iscsi->hq.pbl_entries * sizeof(void *), mm_cli_idx);
        iscsi->hq.pbl_virt_table = NULL;
    }

}
/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t lm_sc_free_con_resc(
    IN struct _lm_device_t *pdev,
    IN lm_iscsi_state_t *iscsi
    )
{
    u8_t notify_fw = 1;

    if (CHK_NULL(pdev) || CHK_NULL(iscsi))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    if (iscsi->cid != 0) {
        if (iscsi->hdr.status == STATE_STATUS_INIT_OFFLOAD_ERR) {
            notify_fw = 0;
        }
        lm_free_cid_resc(pdev, ISCSI_CONNECTION_TYPE, iscsi->cid, notify_fw);
        iscsi->cid = 0;
    }

    if (!iscsi->b_keep_resources)
    {
        lm_sc_free_con_phys_mem(pdev, iscsi);
    }

    return LM_STATUS_SUCCESS;
}


/* Free the ramrod memory and the CID */
lm_status_t
lm_fc_free_con_resc(
    IN struct _lm_device_t          *pdev,
    IN lm_fcoe_state_t              *fcoe)
{
    u8_t                            notify_fw = 1;

    if (CHK_NULL(pdev) || CHK_NULL(fcoe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    if (fcoe->cid != 0)
    {
        if (fcoe->hdr.status == STATE_STATUS_INIT_OFFLOAD_ERR)
        {
            notify_fw = 0;
        }

        lm_free_cid_resc(pdev, FCOE_CONNECTION_TYPE, fcoe->cid, notify_fw);

        fcoe->hdr.state_blk = NULL;
        fcoe->cid = 0;
        fcoe->ctx_virt = NULL;
        fcoe->ctx_phys.as_u64 = 0;
    }

    return LM_STATUS_SUCCESS;
}



/*******************************************************************************
 * Description:
 *
 * Return:
 ******************************************************************************/
lm_status_t lm_sc_init_iscsi_context(
    IN struct _lm_device_t      *pdev,
    IN lm_iscsi_state_t         *iscsi,
    struct iscsi_kwqe_conn_offload1  *req1,
    struct iscsi_kwqe_conn_offload2  *req2,
    struct iscsi_kwqe_conn_offload3  *req3
    )
{
    struct iscsi_context *ctx;
    u32_t cid;
    u32_t cq_size_in_bytes;
    u32_t single_cq_pbl_entries;
    u32_t i;
    u16_t conn_id;
    lm_address_t pbl_base;

    if (CHK_NULL(pdev) || CHK_NULL(iscsi) || CHK_NULL(req1) || CHK_NULL(req2) || CHK_NULL(req3))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }


    conn_id = req1->iscsi_conn_id;
    cid = iscsi->cid;

    DbgMessage(pdev, INFORM, "### lm_sc_init_iscsi_context\n");

    if (req2->num_additional_wqes != 1)
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    /* get context */
    iscsi->ctx_virt = (struct iscsi_context *)lm_get_context(pdev, iscsi->cid);
    DbgBreakIf(!iscsi->ctx_virt);
    iscsi->ctx_phys.as_u64 = lm_get_context_phys(pdev, iscsi->cid);
    DbgBreakIf(!iscsi->ctx_phys.as_u64);
    DbgMessage(pdev, VERBOSEl5sp,
                "iscsi->ctx_virt=%p, iscsi->ctx_phys_high=%x, iscsi->ctx_phys_low=%x\n",
                iscsi->ctx_virt, iscsi->ctx_phys.as_u32.high, iscsi->ctx_phys.as_u32.low);

    ctx = iscsi->ctx_virt;

    mm_memset(ctx, 0, sizeof(struct iscsi_context));

    // init xstorm aggregative context
    ctx->xstorm_ag_context.hq_prod = 1; //this value represents actual hq_prod + 1

    // init xstorm storm context
    //iscsi context
    ctx->xstorm_st_context.iscsi.first_burst_length = ISCSI_DEFAULT_FIRST_BURST_LENGTH;
    ctx->xstorm_st_context.iscsi.max_send_pdu_length = ISCSI_DEFAULT_MAX_PDU_LENGTH;

    /* advance the SQ pbl_base cause it's pointing the SQ_DB */
    pbl_base.as_u32.low = req1->sq_page_table_addr_lo;
    pbl_base.as_u32.high = req1->sq_page_table_addr_hi;
    LM_INC64(&pbl_base, ISCSI_SQ_DB_SIZE);
    ctx->xstorm_st_context.iscsi.sq_pbl_base.lo = pbl_base.as_u32.low;
    ctx->xstorm_st_context.iscsi.sq_pbl_base.hi = pbl_base.as_u32.high;

    //!!DP
    ctx->xstorm_st_context.iscsi.sq_curr_pbe.lo = req2->sq_first_pte.lo;
    ctx->xstorm_st_context.iscsi.sq_curr_pbe.hi = req2->sq_first_pte.hi;

    ctx->xstorm_st_context.iscsi.hq_pbl_base.lo = iscsi->hq.pbl_phys_table_phys.as_u32.low;
    ctx->xstorm_st_context.iscsi.hq_pbl_base.hi = iscsi->hq.pbl_phys_table_phys.as_u32.high;
    ctx->xstorm_st_context.iscsi.hq_curr_pbe_base.lo = iscsi->hq.pbl_phys_table_virt[0].as_u32.low;
    ctx->xstorm_st_context.iscsi.hq_curr_pbe_base.hi = iscsi->hq.pbl_phys_table_virt[0].as_u32.high;

    ctx->xstorm_st_context.iscsi.r2tq_pbl_base.lo = iscsi->r2tq.pbl_phys_table_phys.as_u32.low;
    ctx->xstorm_st_context.iscsi.r2tq_pbl_base.hi = iscsi->r2tq.pbl_phys_table_phys.as_u32.high;
    ctx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.lo = iscsi->r2tq.pbl_phys_table_virt[0].as_u32.low;
    ctx->xstorm_st_context.iscsi.r2tq_curr_pbe_base.hi = iscsi->r2tq.pbl_phys_table_virt[0].as_u32.high;

    ctx->xstorm_st_context.iscsi.task_pbl_base.lo = iscsi->task_array.pbl_phys_table_phys.as_u32.low;
    ctx->xstorm_st_context.iscsi.task_pbl_base.hi = iscsi->task_array.pbl_phys_table_phys.as_u32.high;
    ctx->xstorm_st_context.iscsi.task_pbl_cache_idx = ISCSI_PBL_NOT_CACHED;
    //ctx->xstorm_st_context.iscsi.max_outstanding_r2ts = ISCSI_DEFAULT_MAX_OUTSTANDING_R2T;
    SET_FIELD(ctx->xstorm_st_context.iscsi.flags.flags, XSTORM_ISCSI_CONTEXT_FLAGS_B_IMMEDIATE_DATA, ISCSI_DEFAULT_IMMEDIATE_DATA);
    SET_FIELD(ctx->xstorm_st_context.iscsi.flags.flags, XSTORM_ISCSI_CONTEXT_FLAGS_B_INITIAL_R2T, ISCSI_DEFAULT_INITIAL_R2T);
    SET_FIELD(ctx->xstorm_st_context.iscsi.flags.flags, XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_HEADER_DIGEST, ISCSI_DEFAULT_HEADER_DIGEST);
    SET_FIELD(ctx->xstorm_st_context.iscsi.flags.flags, XSTORM_ISCSI_CONTEXT_FLAGS_B_EN_DATA_DIGEST, ISCSI_DEFAULT_DATA_DIGEST);

    // init tstorm storm context
    ctx->tstorm_st_context.iscsi.hdr_bytes_2_fetch = ISCSI_HEADER_SIZE + (ISCSI_DEFAULT_HEADER_DIGEST ? ISCSI_DIGEST_SIZE : 0);
    SET_FIELD(ctx->tstorm_st_context.iscsi.flags, TSTORM_ISCSI_ST_CONTEXT_SECTION_B_HDR_DIGEST_EN, ISCSI_DEFAULT_HEADER_DIGEST);
    SET_FIELD(ctx->tstorm_st_context.iscsi.flags, TSTORM_ISCSI_ST_CONTEXT_SECTION_B_DATA_DIGEST_EN, ISCSI_DEFAULT_DATA_DIGEST);
    ctx->tstorm_st_context.iscsi.rq_db_phy_addr.lo = req2->rq_page_table_addr_lo;
    ctx->tstorm_st_context.iscsi.rq_db_phy_addr.hi = req2->rq_page_table_addr_hi;
    ctx->tstorm_st_context.iscsi.iscsi_conn_id = conn_id;

    //To enable the timer block.
    SET_FIELD(ctx->timers_context.flags, TIMERS_BLOCK_CONTEXT_CONN_VALID_FLG, 1);

    // init ustorm storm context
    cq_size_in_bytes = pdev->iscsi_info.run_time.cq_size * ISCSI_CQE_SIZE;
    single_cq_pbl_entries = lm_get_pbl_entries(cq_size_in_bytes);

    ctx->ustorm_st_context.task_pbe_cache_index = ISCSI_PBL_NOT_CACHED;
    ctx->ustorm_st_context.task_pdu_cache_index = ISCSI_PDU_HEADER_NOT_CACHED;

    /* advance the RQ pbl_base cause it's pointing the RQ_DB  */
    pbl_base.as_u32.low = req2->rq_page_table_addr_lo;
    pbl_base.as_u32.high = req2->rq_page_table_addr_hi;
    LM_INC64(&pbl_base, ISCSI_RQ_DB_SIZE);
    ctx->ustorm_st_context.ring.rq.pbl_base.lo = pbl_base.as_u32.low;
    ctx->ustorm_st_context.ring.rq.pbl_base.hi = pbl_base.as_u32.high;

    //!!DP
    /* qp_first_pte[0] will contain the first PTE of the RQ */
    ctx->ustorm_st_context.ring.rq.curr_pbe.lo = req3->qp_first_pte[0].lo;
    ctx->ustorm_st_context.ring.rq.curr_pbe.hi = req3->qp_first_pte[0].hi;

    ctx->ustorm_st_context.ring.r2tq.pbl_base.lo = iscsi->r2tq.pbl_phys_table_phys.as_u32.low;
    ctx->ustorm_st_context.ring.r2tq.pbl_base.hi = iscsi->r2tq.pbl_phys_table_phys.as_u32.high;
    ctx->ustorm_st_context.ring.r2tq.curr_pbe.lo = iscsi->r2tq.pbl_phys_table_virt[0].as_u32.low;
    ctx->ustorm_st_context.ring.r2tq.curr_pbe.hi = iscsi->r2tq.pbl_phys_table_virt[0].as_u32.high;

    /* Set up the first CQ, the first PTE info is contained in req2 */
    pbl_base.as_u32.low = req1->cq_page_table_addr_lo;
    pbl_base.as_u32.high = req1->cq_page_table_addr_hi;
    LM_INC64(&pbl_base, ISCSI_CQ_DB_SIZE);
    ctx->ustorm_st_context.ring.cq_pbl_base.lo = pbl_base.as_u32.low;
    ctx->ustorm_st_context.ring.cq_pbl_base.hi = pbl_base.as_u32.high;
    ctx->ustorm_st_context.ring.cq[0].cq_sn = ISCSI_INITIAL_SN;
    ctx->ustorm_st_context.ring.cq[0].curr_pbe.lo = req2->cq_first_pte.lo;
    ctx->ustorm_st_context.ring.cq[0].curr_pbe.hi = req2->cq_first_pte.hi;

    if (1 != pdev->iscsi_info.run_time.num_of_cqs)
    {
        /* For now we only support a single CQ */
        return LM_STATUS_INVALID_PARAMETER;

#if 0
        /* Set up additional CQs */
        for (i = 1; i < pdev->iscsi_info.run_time.num_of_cqs; i++)   // 8 x CQ curr_pbe
        {
            ctx->ustorm_st_context.ring.cq[i].cq_sn = ISCSI_INITIAL_SN;

            curr_pbl_base.as_u32.low = pbl_base.as_u32.low;
            curr_pbl_base.as_u32.high = pbl_base.as_u32.high;

            LM_INC64(&curr_pbl_base, i * single_cq_pbl_entries * sizeof(lm_address_t));
#if 0
            fix this if we ever want to use > 1 CQ

            curr_pbe = (lm_address_t *)mm_map_io_space(curr_pbl_base, sizeof(lm_address_t));
            if CHK_NULL(curr_pbe)
            {
                return LM_STATUS_INVALID_PARAMETER;
            }
            ctx->ustorm_st_context.ring.cq[i].curr_pbe.lo = curr_pbe->as_u32.low;
            ctx->ustorm_st_context.ring.cq[i].curr_pbe.hi = curr_pbe->as_u32.high;
            mm_unmap_io_space(curr_pbe, sizeof(lm_address_t));

#endif
        }
#endif
    }

    ctx->ustorm_st_context.task_pbl_base.lo = iscsi->task_array.pbl_phys_table_phys.as_u32.low;
    ctx->ustorm_st_context.task_pbl_base.hi = iscsi->task_array.pbl_phys_table_phys.as_u32.high;
    ctx->ustorm_st_context.tce_phy_addr.lo = iscsi->task_array.pbl_phys_table_virt[0].as_u32.low;
    ctx->ustorm_st_context.tce_phy_addr.hi = iscsi->task_array.pbl_phys_table_virt[0].as_u32.high;
    ctx->ustorm_st_context.iscsi_conn_id = conn_id;
    SET_FIELD(ctx->ustorm_st_context.negotiated_rx, USTORM_ISCSI_ST_CONTEXT_MAX_RECV_PDU_LENGTH, ISCSI_DEFAULT_MAX_PDU_LENGTH);
    SET_FIELD(ctx->ustorm_st_context.negotiated_rx_and_flags, USTORM_ISCSI_ST_CONTEXT_MAX_BURST_LENGTH, ISCSI_DEFAULT_MAX_BURST_LENGTH);
    SET_FIELD(ctx->ustorm_st_context.negotiated_rx, USTORM_ISCSI_ST_CONTEXT_MAX_OUTSTANDING_R2TS, ISCSI_DEFAULT_MAX_OUTSTANDING_R2T);
    SET_FIELD(ctx->ustorm_st_context.negotiated_rx_and_flags, USTORM_ISCSI_ST_CONTEXT_B_HDR_DIGEST_EN, ISCSI_DEFAULT_HEADER_DIGEST);
    SET_FIELD(ctx->ustorm_st_context.negotiated_rx_and_flags, USTORM_ISCSI_ST_CONTEXT_B_DATA_DIGEST_EN, ISCSI_DEFAULT_DATA_DIGEST);
    ctx->ustorm_st_context.num_cqs = pdev->iscsi_info.run_time.num_of_cqs;

    // init cstorm storm context
    ctx->cstorm_st_context.hq_pbl_base.lo = iscsi->hq.pbl_phys_table_phys.as_u32.low;
    ctx->cstorm_st_context.hq_pbl_base.hi = iscsi->hq.pbl_phys_table_phys.as_u32.high;
    ctx->cstorm_st_context.hq_curr_pbe.lo = iscsi->hq.pbl_phys_table_virt[0].as_u32.low;
    ctx->cstorm_st_context.hq_curr_pbe.hi = iscsi->hq.pbl_phys_table_virt[0].as_u32.high;

    ctx->cstorm_st_context.task_pbl_base.lo = iscsi->task_array.pbl_phys_table_phys.as_u32.low;
    ctx->cstorm_st_context.task_pbl_base.hi = iscsi->task_array.pbl_phys_table_phys.as_u32.high;
    ctx->cstorm_st_context.cq_db_base.lo = req1->cq_page_table_addr_lo;
    ctx->cstorm_st_context.cq_db_base.hi = req1->cq_page_table_addr_hi;
    ctx->cstorm_st_context.iscsi_conn_id = conn_id;
    ctx->cstorm_st_context.cq_proc_en_bit_map = (1 << pdev->iscsi_info.run_time.num_of_cqs) - 1;
    SET_FIELD(ctx->cstorm_st_context.flags, CSTORM_ISCSI_ST_CONTEXT_DATA_DIGEST_EN, ISCSI_DEFAULT_HEADER_DIGEST);
    SET_FIELD(ctx->cstorm_st_context.flags, CSTORM_ISCSI_ST_CONTEXT_HDR_DIGEST_EN, ISCSI_DEFAULT_DATA_DIGEST);
    for (i = 0; i < pdev->iscsi_info.run_time.num_of_cqs; i++)
    {
        ctx->cstorm_st_context.cq_c_prod_sqn_arr.sqn[i] = ISCSI_INITIAL_SN;
        ctx->cstorm_st_context.cq_c_sqn_2_notify_arr.sqn[i] = ISCSI_INITIAL_SN;
    }

    /* now we need to configure the cdu-validation data */
    lm_set_cdu_validation_data(pdev, iscsi->cid, FALSE /* don't invalidate */);

    return LM_STATUS_SUCCESS;
}


lm_status_t
lm_fc_init_fcoe_context(
    IN struct _lm_device_t          *pdev,
    IN lm_fcoe_state_t              *fcoe)
{
    struct fcoe_context *ctx;
    u32_t cid;
    u16_t conn_id;

    if (CHK_NULL(pdev) || CHK_NULL(fcoe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    conn_id = fcoe->ofld1.fcoe_conn_id;
    cid = fcoe->cid;

    DbgMessage(pdev, INFORM, "### lm_fc_init_fcoe_context\n");

    /* get context */
    fcoe->ctx_virt = (struct fcoe_context *)lm_get_context(pdev, fcoe->cid);
    DbgBreakIf(!fcoe->ctx_virt);
    fcoe->ctx_phys.as_u64 = lm_get_context_phys(pdev, fcoe->cid);
    DbgBreakIf(!fcoe->ctx_phys.as_u64);
    DbgMessage(pdev, VERBOSEl5sp,
                "fcoe->ctx_virt=%p, fcoe->ctx_phys_high=%x, fcoe->ctx_phys_low=%x\n",
                fcoe->ctx_virt, fcoe->ctx_phys.as_u32.high, fcoe->ctx_phys.as_u32.low);

    ctx = fcoe->ctx_virt;

    mm_memset(ctx, 0, sizeof(struct fcoe_context));

    /* now we need to configure the cdu-validation data */
    lm_set_cdu_validation_data(pdev, fcoe->cid, FALSE /* don't invalidate */);

    return LM_STATUS_SUCCESS;
}



lm_status_t
lm_fc_alloc_con_resc(
    IN struct _lm_device_t          *pdev,
    IN lm_fcoe_state_t              *fcoe)
{
    lm_status_t                     lm_status = LM_STATUS_SUCCESS;
    s32_t                           cid       = 0;

    if (CHK_NULL(pdev) || CHK_NULL(fcoe))
    {
        return LM_STATUS_INVALID_PARAMETER;
    }

    DbgMessage(pdev, INFORM, "### lm_fc_alloc_con_resc\n");

    /* save the miniport's conn id */
    fcoe->fcoe_conn_id = fcoe->ofld1.fcoe_conn_id;

    /* Allocate CID */
    lm_status = lm_allocate_cid(pdev, FCOE_CONNECTION_TYPE, (void *)fcoe, &cid);
    if (lm_status == LM_STATUS_PENDING)
    {
        lm_sp_req_manager_block(pdev, (u32_t)cid);
    }
    else if (lm_status != LM_STATUS_SUCCESS)
    {
        /* failed to allocate CID */
        lm_fc_free_con_resc(pdev, fcoe);
        return lm_status;
    }

    /* save the returned cid */
    fcoe->cid = (u32_t)cid;

    if (lm_cid_state(pdev, fcoe->cid) == LM_CID_STATE_PENDING)
    {
        return LM_STATUS_PENDING; /* Too soon to initialize context */
    }

    return LM_STATUS_SUCCESS;
} /* lm_fc_alloc_con_resc */



lm_status_t
lm_fc_post_offload_ramrod(
    struct _lm_device_t             *pdev,
    lm_fcoe_state_t                 *fcoe)
{
    lm_fcoe_slow_path_phys_data_t   *ramrod_params;
    lm_status_t                     lm_status;

    ramrod_params = (lm_fcoe_slow_path_phys_data_t*)pdev->fcoe_info.bind.ramrod_mem_virt;

    mm_memset(ramrod_params, 0, sizeof(lm_fcoe_slow_path_phys_data_t));

    memcpy(&ramrod_params->fcoe_ofld.offload_kwqe1, &fcoe->ofld1, sizeof(struct fcoe_kwqe_conn_offload1));
    memcpy(&ramrod_params->fcoe_ofld.offload_kwqe2, &fcoe->ofld2, sizeof(struct fcoe_kwqe_conn_offload2));
    memcpy(&ramrod_params->fcoe_ofld.offload_kwqe3, &fcoe->ofld3, sizeof(struct fcoe_kwqe_conn_offload3));
    memcpy(&ramrod_params->fcoe_ofld.offload_kwqe4, &fcoe->ofld4, sizeof(struct fcoe_kwqe_conn_offload4));

    lm_status = lm_command_post(pdev,
                                fcoe->cid,
                                FCOE_RAMROD_CMD_ID_OFFLOAD_CONN,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                pdev->fcoe_info.bind.ramrod_mem_phys.as_u64);

    return lm_status;
}



lm_status_t
lm_fc_post_enable_ramrod(
    struct _lm_device_t                     *pdev,
    lm_fcoe_state_t                         *fcoe,
    struct fcoe_kwqe_conn_enable_disable    *enable)
{
    lm_fcoe_slow_path_phys_data_t   *ramrod_params;
    lm_status_t                     lm_status;

    ramrod_params = (lm_fcoe_slow_path_phys_data_t*)pdev->fcoe_info.bind.ramrod_mem_virt;

    mm_memset(ramrod_params, 0, sizeof(lm_fcoe_slow_path_phys_data_t));

    memcpy(&ramrod_params->fcoe_enable.enable_disable_kwqe, enable, sizeof(struct fcoe_kwqe_conn_enable_disable));

    lm_status = lm_command_post(pdev,
                                fcoe->cid,
                                FCOE_RAMROD_CMD_ID_ENABLE_CONN,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                pdev->fcoe_info.bind.ramrod_mem_phys.as_u64);

    return lm_status;
}



lm_status_t
lm_fc_post_disable_ramrod(
    struct _lm_device_t                    *pdev,
    lm_fcoe_state_t                        *fcoe,
    struct fcoe_kwqe_conn_enable_disable   *disable)
{
    lm_fcoe_slow_path_phys_data_t   *ramrod_params;
    lm_status_t                     lm_status;

    ramrod_params = (lm_fcoe_slow_path_phys_data_t*)pdev->fcoe_info.bind.ramrod_mem_virt;

    mm_memset(ramrod_params, 0, sizeof(lm_fcoe_slow_path_phys_data_t));

    memcpy(&ramrod_params->fcoe_enable.enable_disable_kwqe, disable, sizeof *disable);

    lm_status = lm_command_post(pdev,
                                fcoe->cid,
                                FCOE_RAMROD_CMD_ID_DISABLE_CONN,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                pdev->fcoe_info.bind.ramrod_mem_phys.as_u64);

    return lm_status;
}

lm_status_t
lm_fc_post_destroy_ramrod(
    struct _lm_device_t             *pdev)
{
    lm_status_t                     lm_status;

    lm_status = lm_command_post(pdev,
                                LM_CLI_CID(pdev, LM_CLI_IDX_FCOE),      /* cid */
                                FCOE_RAMROD_CMD_ID_DESTROY_FUNC,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                0);

    return lm_status;
}


lm_status_t
lm_fc_post_stat_ramrod(
    struct _lm_device_t         *pdev,
    struct fcoe_kwqe_stat       *stat)
{
    lm_status_t                     lm_status = LM_STATUS_SUCCESS;

    lm_fcoe_slow_path_phys_data_t   *ramrod_params;

    if(CHK_NULL(pdev->fcoe_info.bind.ramrod_mem_virt))
    {
        return LM_STATUS_RESOURCE;
    }
    ramrod_params = (lm_fcoe_slow_path_phys_data_t*)pdev->fcoe_info.bind.ramrod_mem_virt;

    mm_memset(ramrod_params, 0, sizeof(lm_fcoe_slow_path_phys_data_t));

    memcpy(&ramrod_params->fcoe_stat.stat_kwqe, stat, sizeof(struct fcoe_kwqe_stat));

    lm_status = lm_command_post(pdev,
                                LM_CLI_CID(pdev, LM_CLI_IDX_FCOE),      /* cid */
                                FCOE_RAMROD_CMD_ID_STAT_FUNC,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                pdev->fcoe_info.bind.ramrod_mem_phys.as_u64);

    return lm_status;
}

lm_status_t
lm_fc_post_terminate_ramrod(
    struct _lm_device_t             *pdev,
    lm_fcoe_state_t                 *fcoe)
{
    lm_status_t                     lm_status;

    lm_status = lm_command_post(pdev,
                                fcoe->cid,
                                FCOE_RAMROD_CMD_ID_TERMINATE_CONN,
                                CMD_PRIORITY_NORMAL,
                                FCOE_CONNECTION_TYPE,
                                0);

    return lm_status;
}