C++程序  |  1178行  |  42.69 KB

/******************************************************************************
 *
 *  Copyright (C) 2010-2013 Broadcom Corporation
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at:
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 ******************************************************************************/


/******************************************************************************
 *
 *  This is the main implementation file for the NFA HCI.
 *
 ******************************************************************************/
#include <string.h>
#include "nfc_api.h"
#include "nfa_sys.h"
#include "nfa_sys_int.h"
#include "nfa_dm_int.h"
#include "nfa_hci_api.h"
#include "nfa_hci_int.h"
#include "nfa_ee_api.h"
#include "nfa_ee_int.h"
#include "nfa_nv_co.h"
#include "nfa_mem_co.h"
#include "nfa_hci_defs.h"
#include "trace_api.h"


/*****************************************************************************
**  Global Variables
*****************************************************************************/

tNFA_HCI_CB nfa_hci_cb;

#ifndef NFA_HCI_NV_READ_TIMEOUT_VAL
#define NFA_HCI_NV_READ_TIMEOUT_VAL    1000
#endif

#ifndef NFA_HCI_CON_CREATE_TIMEOUT_VAL
#define NFA_HCI_CON_CREATE_TIMEOUT_VAL 1000
#endif

/*****************************************************************************
**  Static Functions
*****************************************************************************/

/* event handler function type */
static BOOLEAN nfa_hci_evt_hdlr (BT_HDR *p_msg);

static void nfa_hci_sys_enable (void);
static void nfa_hci_sys_disable (void);
static void nfa_hci_rsp_timeout (tNFA_HCI_EVENT_DATA *p_evt_data);
static void nfa_hci_conn_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data);
static void nfa_hci_set_receive_buf (UINT8 pipe);
static void nfa_hci_assemble_msg (UINT8 *p_data, UINT16 data_len);
static void nfa_hci_handle_nv_read (UINT8 block, tNFA_STATUS status);

/*****************************************************************************
**  Constants
*****************************************************************************/
static const tNFA_SYS_REG nfa_hci_sys_reg =
{
    nfa_hci_sys_enable,
    nfa_hci_evt_hdlr,
    nfa_hci_sys_disable,
    nfa_hci_proc_nfcc_power_mode
};

/*******************************************************************************
**
** Function         nfa_hci_ee_info_cback
**
** Description      Callback function
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_ee_info_cback (tNFA_EE_DISC_STS status)
{
    UINT8           num_nfcee = 3;
    tNFA_EE_INFO    ee_info[3];

    NFA_TRACE_DEBUG1 ("nfa_hci_ee_info_cback (): %d", status);

    switch (status)
    {
    case NFA_EE_DISC_STS_ON:
        if (  (!nfa_hci_cb.ee_disc_cmplt)
            &&((nfa_hci_cb.hci_state == NFA_HCI_STATE_STARTUP) || (nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE))  )
        {
            /* NFCEE Discovery is in progress */
            nfa_hci_cb.ee_disc_cmplt      = TRUE;
            nfa_hci_cb.num_ee_dis_req_ntf = 0;
            nfa_hci_cb.num_hot_plug_evts  = 0;
            nfa_hci_cb.conn_id            = 0;
            nfa_hci_startup ();
        }
        break;

    case NFA_EE_DISC_STS_OFF:
        if (nfa_hci_cb.ee_disable_disc)
            break;
        nfa_hci_cb.ee_disable_disc  = TRUE;
        /* Discovery operation is complete, retrieve discovery result */
        NFA_EeGetInfo (&num_nfcee, ee_info);
        nfa_hci_cb.num_nfcee        = num_nfcee;

        if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_WAIT_NETWK_ENABLE)
            ||(nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE_NETWK_ENABLE)  )
        {
            if (  (nfa_hci_cb.num_nfcee <= 1)
                ||(nfa_hci_cb.num_ee_dis_req_ntf == (nfa_hci_cb.num_nfcee - 1))
                ||(nfa_hci_cb.num_hot_plug_evts  == (nfa_hci_cb.num_nfcee - 1))  )
            {
                /* No UICC Host is detected or
                 * HOT_PLUG_EVT(s) and or EE DISC REQ Ntf(s) are already received
                 * Get Host list and notify SYS on Initialization complete */
                nfa_sys_stop_timer (&nfa_hci_cb.timer);
                if (  (nfa_hci_cb.num_nfcee > 1)
                    &&(nfa_hci_cb.num_ee_dis_req_ntf != (nfa_hci_cb.num_nfcee - 1))  )
                {
                    /* Received HOT PLUG EVT, we will also wait for EE DISC REQ Ntf(s) */
                    nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, p_nfa_hci_cfg->hci_netwk_enable_timeout);
                }
                else
                {
                    nfa_hci_cb.w4_hci_netwk_init = FALSE;
                    nfa_hciu_send_get_param_cmd (NFA_HCI_ADMIN_PIPE, NFA_HCI_HOST_LIST_INDEX);
                }
            }
        }
        else if (nfa_hci_cb.num_nfcee <= 1)
        {
            /* No UICC Host is detected, HCI NETWORK is enabled */
            nfa_hci_cb.w4_hci_netwk_init = FALSE;
        }
        break;

    case NFA_EE_DISC_STS_REQ:
        nfa_hci_cb.num_ee_dis_req_ntf++;

        if (nfa_hci_cb.ee_disable_disc)
        {
            /* Already received Discovery Ntf */
            if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_WAIT_NETWK_ENABLE)
                ||(nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE_NETWK_ENABLE)  )
            {
                /* Received DISC REQ Ntf while waiting for other Host in the network to bootup after DH host bootup is complete */
                if (nfa_hci_cb.num_ee_dis_req_ntf == (nfa_hci_cb.num_nfcee - 1))
                {
                    /* Received expected number of EE DISC REQ Ntf(s) */
                    nfa_sys_stop_timer (&nfa_hci_cb.timer);
                    nfa_hci_cb.w4_hci_netwk_init = FALSE;
                    nfa_hciu_send_get_param_cmd (NFA_HCI_ADMIN_PIPE, NFA_HCI_HOST_LIST_INDEX);
                }
            }
            else if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_STARTUP)
                     ||(nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE)  )
            {
                /* Received DISC REQ Ntf during DH host bootup */
                if (nfa_hci_cb.num_ee_dis_req_ntf == (nfa_hci_cb.num_nfcee - 1))
                {
                    /* Received expected number of EE DISC REQ Ntf(s) */
                    nfa_hci_cb.w4_hci_netwk_init = FALSE;
                }
            }
        }
        break;
    }
}

/*******************************************************************************
**
** Function         nfa_hci_init
**
** Description      Initialize NFA HCI
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_init (void)
{
    NFA_TRACE_DEBUG0 ("nfa_hci_init ()");

    /* initialize control block */
    memset (&nfa_hci_cb, 0, sizeof (tNFA_HCI_CB));

    nfa_hci_cb.hci_state = NFA_HCI_STATE_STARTUP;

    /* register message handler on NFA SYS */
    nfa_sys_register (NFA_ID_HCI, &nfa_hci_sys_reg);
}

/*******************************************************************************
**
** Function         nfa_hci_is_valid_cfg
**
** Description      Validate hci control block config parameters
**
** Returns          None
**
*******************************************************************************/
BOOLEAN nfa_hci_is_valid_cfg (void)
{
    UINT8       xx,yy,zz;
    tNFA_HANDLE reg_app[NFA_HCI_MAX_APP_CB];
    UINT8       valid_gate[NFA_HCI_MAX_GATE_CB];
    UINT8       app_count       = 0;
    UINT8       gate_count      = 0;
    UINT32      pipe_inx_mask   = 0;

    /* First, see if valid values are stored in app names, send connectivity events flag */
    for (xx = 0; xx < NFA_HCI_MAX_APP_CB; xx++)
    {
        /* Check if app name is valid with null terminated string */
        if (strlen (&nfa_hci_cb.cfg.reg_app_names[xx][0]) > NFA_MAX_HCI_APP_NAME_LEN)
            return FALSE;

        /* Send Connectivity event flag can be either TRUE or FALSE */
        if (  (nfa_hci_cb.cfg.b_send_conn_evts[xx] != TRUE)
            &&(nfa_hci_cb.cfg.b_send_conn_evts[xx] != FALSE))
            return FALSE;

        if (nfa_hci_cb.cfg.reg_app_names[xx][0] != 0)
        {
            /* Check if the app name is present more than one time in the control block */
            for (yy = xx + 1; yy < NFA_HCI_MAX_APP_CB; yy++)
            {
                if (  (nfa_hci_cb.cfg.reg_app_names[yy][0] != 0)
                    &&(!strncmp (&nfa_hci_cb.cfg.reg_app_names[xx][0], &nfa_hci_cb.cfg.reg_app_names[yy][0], strlen (nfa_hci_cb.cfg.reg_app_names[xx]))) )
                {
                    /* Two app cannot have the same name , NVRAM is corrupted */
                    NFA_TRACE_EVENT2 ("nfa_hci_is_valid_cfg (%s)  Reusing: %u", &nfa_hci_cb.cfg.reg_app_names[xx][0], xx);
                    return FALSE;
                }
            }
            /* Collect list of hci handle */
            reg_app[app_count++] = (tNFA_HANDLE) (xx | NFA_HANDLE_GROUP_HCI);
        }
    }

    /* Validate Gate Control block */
    for (xx = 0; xx < NFA_HCI_MAX_GATE_CB; xx++)
    {
        if (nfa_hci_cb.cfg.dyn_gates[xx].gate_id != 0)
        {
            if (  (  (nfa_hci_cb.cfg.dyn_gates[xx].gate_id != NFA_HCI_LOOP_BACK_GATE)
                   &&(nfa_hci_cb.cfg.dyn_gates[xx].gate_id != NFA_HCI_IDENTITY_MANAGEMENT_GATE)
                   &&(nfa_hci_cb.cfg.dyn_gates[xx].gate_id < NFA_HCI_FIRST_HOST_SPECIFIC_GENERIC_GATE))
                ||(nfa_hci_cb.cfg.dyn_gates[xx].gate_id > NFA_HCI_LAST_PROP_GATE))
                return FALSE;

            /* Check if the same gate id is present more than once in the control block */
            for (yy = xx + 1; yy < NFA_HCI_MAX_GATE_CB; yy++)
            {
                if (  (nfa_hci_cb.cfg.dyn_gates[yy].gate_id != 0)
                    &&(nfa_hci_cb.cfg.dyn_gates[xx].gate_id == nfa_hci_cb.cfg.dyn_gates[yy].gate_id) )
                {
                    NFA_TRACE_EVENT1 ("nfa_hci_is_valid_cfg  Reusing: %u", nfa_hci_cb.cfg.dyn_gates[xx].gate_id);
                    return FALSE;
                }
            }
            if ((nfa_hci_cb.cfg.dyn_gates[xx].gate_owner & (~NFA_HANDLE_GROUP_HCI)) >= NFA_HCI_MAX_APP_CB)
            {
                NFA_TRACE_EVENT1 ("nfa_hci_is_valid_cfg  Invalid Gate owner: %u", nfa_hci_cb.cfg.dyn_gates[xx].gate_owner);
                return FALSE;
            }
            if (nfa_hci_cb.cfg.dyn_gates[xx].gate_id != NFA_HCI_CONNECTIVITY_GATE)
            {
                /* The gate owner should be one of the registered application */
                for (zz = 0; zz < app_count; zz++)
                {
                    if (nfa_hci_cb.cfg.dyn_gates[xx].gate_owner == reg_app[zz])
                        break;
                }
                if (zz == app_count)
                {
                    NFA_TRACE_EVENT1 ("nfa_hci_is_valid_cfg  Invalid Gate owner: %u", nfa_hci_cb.cfg.dyn_gates[xx].gate_owner);
                    return FALSE;
                }
            }
            /* Collect list of allocated gates */
            valid_gate[gate_count++] = nfa_hci_cb.cfg.dyn_gates[xx].gate_id;

            /* No two gates can own a same pipe */
            if ((pipe_inx_mask & nfa_hci_cb.cfg.dyn_gates[xx].pipe_inx_mask) != 0)
                return FALSE;
            /* Collect the list of pipes on this gate */
            pipe_inx_mask |= nfa_hci_cb.cfg.dyn_gates[xx].pipe_inx_mask;
        }
    }

    for (xx = 0; (pipe_inx_mask && (xx < NFA_HCI_MAX_PIPE_CB)); xx++,pipe_inx_mask >>= 1)
    {
        /* Every bit set in pipe increment mask indicates a valid pipe */
        if (pipe_inx_mask & 1)
        {
            /* Check if the pipe is valid one */
            if (nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id < NFA_HCI_FIRST_DYNAMIC_PIPE)
                return FALSE;
        }
    }

    if (xx == NFA_HCI_MAX_PIPE_CB)
        return FALSE;

    /* Validate Gate Control block */
    for (xx = 0; xx < NFA_HCI_MAX_PIPE_CB; xx++)
    {
        if (nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id != 0)
        {
            /* Check if pipe id is valid */
            if (nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id < NFA_HCI_FIRST_DYNAMIC_PIPE)
                return FALSE;

            /* Check if pipe state is valid */
            if (  (nfa_hci_cb.cfg.dyn_pipes[xx].pipe_state != NFA_HCI_PIPE_OPENED)
                &&(nfa_hci_cb.cfg.dyn_pipes[xx].pipe_state != NFA_HCI_PIPE_CLOSED))
                return FALSE;

            /* Check if local gate on which the pipe is created is valid */
            if (  (((nfa_hci_cb.cfg.dyn_pipes[xx].local_gate != NFA_HCI_LOOP_BACK_GATE) && (nfa_hci_cb.cfg.dyn_pipes[xx].local_gate != NFA_HCI_IDENTITY_MANAGEMENT_GATE)) && (nfa_hci_cb.cfg.dyn_pipes[xx].local_gate < NFA_HCI_FIRST_HOST_SPECIFIC_GENERIC_GATE))
                ||(nfa_hci_cb.cfg.dyn_pipes[xx].local_gate > NFA_HCI_LAST_PROP_GATE))
                return FALSE;

            /* Check if the peer gate on which the pipe is created is valid */
            if (  (((nfa_hci_cb.cfg.dyn_pipes[xx].dest_gate != NFA_HCI_LOOP_BACK_GATE) && (nfa_hci_cb.cfg.dyn_pipes[xx].dest_gate != NFA_HCI_IDENTITY_MANAGEMENT_GATE)) && (nfa_hci_cb.cfg.dyn_pipes[xx].dest_gate < NFA_HCI_FIRST_HOST_SPECIFIC_GENERIC_GATE))
                ||(nfa_hci_cb.cfg.dyn_pipes[xx].dest_gate > NFA_HCI_LAST_PROP_GATE))
                return FALSE;

            /* Check if the same pipe is present more than once in the control block */
            for (yy = xx + 1; yy < NFA_HCI_MAX_PIPE_CB; yy++)
            {
                if (  (nfa_hci_cb.cfg.dyn_pipes[yy].pipe_id != 0)
                    &&(nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id == nfa_hci_cb.cfg.dyn_pipes[yy].pipe_id) )
                {
                    NFA_TRACE_EVENT1 ("nfa_hci_is_valid_cfg  Reusing: %u", nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id);
                    return FALSE;
                }
            }
            /* The local gate should be one of the element in gate control block */
            for (zz = 0; zz < gate_count; zz++)
            {
                if (nfa_hci_cb.cfg.dyn_pipes[xx].local_gate == valid_gate[zz])
                    break;
            }
            if (zz == gate_count)
            {
                NFA_TRACE_EVENT1 ("nfa_hci_is_valid_cfg  Invalid Gate: %u", nfa_hci_cb.cfg.dyn_pipes[xx].local_gate);
                return FALSE;
            }
        }
    }

    /* Check if admin pipe state is valid */
    if (  (nfa_hci_cb.cfg.admin_gate.pipe01_state != NFA_HCI_PIPE_OPENED)
        &&(nfa_hci_cb.cfg.admin_gate.pipe01_state != NFA_HCI_PIPE_CLOSED))
        return FALSE;

    /* Check if link management pipe state is valid */
    if (  (nfa_hci_cb.cfg.link_mgmt_gate.pipe00_state != NFA_HCI_PIPE_OPENED)
        &&(nfa_hci_cb.cfg.link_mgmt_gate.pipe00_state != NFA_HCI_PIPE_CLOSED))
        return FALSE;

    pipe_inx_mask = nfa_hci_cb.cfg.id_mgmt_gate.pipe_inx_mask;
    for (xx = 0; (pipe_inx_mask && (xx < NFA_HCI_MAX_PIPE_CB)); xx++,pipe_inx_mask >>= 1)
    {
        /* Every bit set in pipe increment mask indicates a valid pipe */
        if (pipe_inx_mask & 1)
        {
            /* Check if the pipe is valid one */
            if (nfa_hci_cb.cfg.dyn_pipes[xx].pipe_id < NFA_HCI_FIRST_DYNAMIC_PIPE)
                return FALSE;
            /* Check if the pipe is connected to Identity management gate */
            if (nfa_hci_cb.cfg.dyn_pipes[xx].local_gate != NFA_HCI_IDENTITY_MANAGEMENT_GATE)
                return FALSE;
        }
    }
    if (xx == NFA_HCI_MAX_PIPE_CB)
        return FALSE;

    return TRUE;
}

/*******************************************************************************
**
** Function         nfa_hci_cfg_default
**
** Description      Configure default values for hci control block
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_restore_default_config (UINT8 *p_session_id)
{
    memset (&nfa_hci_cb.cfg, 0, sizeof (nfa_hci_cb.cfg));
    memcpy (nfa_hci_cb.cfg.admin_gate.session_id, p_session_id, NFA_HCI_SESSION_ID_LEN);
    nfa_hci_cb.nv_write_needed = TRUE;
}

/*******************************************************************************
**
** Function         nfa_hci_proc_nfcc_power_mode
**
** Description      Restore NFA HCI sub-module
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_proc_nfcc_power_mode (UINT8 nfcc_power_mode)
{
    NFA_TRACE_DEBUG1 ("nfa_hci_proc_nfcc_power_mode () nfcc_power_mode=%d", nfcc_power_mode);

    /* if NFCC power mode is change to full power */
    if (nfcc_power_mode == NFA_DM_PWR_MODE_FULL)
    {
        nfa_hci_cb.b_low_power_mode = FALSE;
        if (nfa_hci_cb.hci_state == NFA_HCI_STATE_IDLE)
        {
            nfa_hci_cb.hci_state          = NFA_HCI_STATE_RESTORE;
            nfa_hci_cb.ee_disc_cmplt      = FALSE;
            nfa_hci_cb.ee_disable_disc    = TRUE;
            if (nfa_hci_cb.num_nfcee > 1)
                nfa_hci_cb.w4_hci_netwk_init  = TRUE;
            else
                nfa_hci_cb.w4_hci_netwk_init  = FALSE;
            nfa_hci_cb.conn_id            = 0;
            nfa_hci_cb.num_ee_dis_req_ntf = 0;
            nfa_hci_cb.num_hot_plug_evts  = 0;
        }
        else
        {
            NFA_TRACE_ERROR0 ("nfa_hci_proc_nfcc_power_mode (): Cannot restore now");
            nfa_sys_cback_notify_nfcc_power_mode_proc_complete (NFA_ID_HCI);
        }
    }
    else
    {
        nfa_hci_cb.hci_state     = NFA_HCI_STATE_IDLE;
        nfa_hci_cb.w4_rsp_evt    = FALSE;
        nfa_hci_cb.conn_id       = 0;
        nfa_sys_stop_timer (&nfa_hci_cb.timer);
        nfa_hci_cb.b_low_power_mode = TRUE;
        nfa_sys_cback_notify_nfcc_power_mode_proc_complete (NFA_ID_HCI);
    }
}

/*******************************************************************************
**
** Function         nfa_hci_dh_startup_complete
**
** Description      Initialization of terminal host in HCI Network is completed
**                  Wait for other host in the network to initialize
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_dh_startup_complete (void)
{
    if (nfa_hci_cb.w4_hci_netwk_init)
    {
        if (nfa_hci_cb.hci_state == NFA_HCI_STATE_STARTUP)
        {
            nfa_hci_cb.hci_state = NFA_HCI_STATE_WAIT_NETWK_ENABLE;
            /* Wait for EE Discovery to complete */
            nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, NFA_EE_DISCV_TIMEOUT_VAL);
        }
        else if (nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE)
        {
            nfa_hci_cb.hci_state = NFA_HCI_STATE_RESTORE_NETWK_ENABLE;
            /* No HCP packet to DH for a specified period of time indicates all host in the network is initialized */
            nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, p_nfa_hci_cfg->hci_netwk_enable_timeout);
        }
    }
    else if (  (nfa_hci_cb.num_nfcee > 1)
             &&(nfa_hci_cb.num_ee_dis_req_ntf != (nfa_hci_cb.num_nfcee - 1))  )
    {
        if (nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE)
            nfa_hci_cb.ee_disable_disc  = TRUE;
        /* Received HOT PLUG EVT, we will also wait for EE DISC REQ Ntf(s) */
        nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, p_nfa_hci_cfg->hci_netwk_enable_timeout);
    }
    else
    {
        /* Received EE DISC REQ Ntf(s) */
        nfa_hciu_send_get_param_cmd (NFA_HCI_ADMIN_PIPE, NFA_HCI_HOST_LIST_INDEX);
    }
}

/*******************************************************************************
**
** Function         nfa_hci_startup_complete
**
** Description      HCI network initialization is completed
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_startup_complete (tNFA_STATUS status)
{
    tNFA_HCI_EVT_DATA   evt_data;

    NFA_TRACE_EVENT1 ("nfa_hci_startup_complete (): Status: %u", status);

    nfa_sys_stop_timer (&nfa_hci_cb.timer);

    if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE)
        ||(nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE_NETWK_ENABLE)  )
    {
        nfa_ee_proc_hci_info_cback ();
        nfa_sys_cback_notify_nfcc_power_mode_proc_complete (NFA_ID_HCI);
    }
    else
    {
        evt_data.hci_init.status = status;

        nfa_hciu_send_to_all_apps (NFA_HCI_INIT_EVT, &evt_data);
        nfa_sys_cback_notify_enable_complete (NFA_ID_HCI);
    }

    if (status == NFA_STATUS_OK)
        nfa_hci_cb.hci_state = NFA_HCI_STATE_IDLE;

    else
        nfa_hci_cb.hci_state = NFA_HCI_STATE_DISABLED;
}

/*******************************************************************************
**
** Function         nfa_hci_startup
**
** Description      Perform HCI startup
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_startup (void)
{
    tNFA_STATUS     status = NFA_STATUS_FAILED;
    tNFA_EE_INFO    ee_info[2];
    UINT8           num_nfcee = 2;
    UINT8           target_handle;
    UINT8           count = 0;
    BOOLEAN         found = FALSE;

    if (HCI_LOOPBACK_DEBUG)
    {
        /* First step in initialization is to open the admin pipe */
        nfa_hciu_send_open_pipe_cmd (NFA_HCI_ADMIN_PIPE);
        return;
    }

    /* We can only start up if NV Ram is read and EE discovery is complete */
    if (nfa_hci_cb.nv_read_cmplt && nfa_hci_cb.ee_disc_cmplt && (nfa_hci_cb.conn_id == 0))
    {
        NFA_EeGetInfo (&num_nfcee, ee_info);

        while ((count < num_nfcee) && (!found))
        {
            target_handle = (UINT8) ee_info[count].ee_handle;

            if(ee_info[count].ee_interface[0] == NFA_EE_INTERFACE_HCI_ACCESS)
            {
                found = TRUE;

                if (ee_info[count].ee_status == NFA_EE_STATUS_INACTIVE)
                {
                    NFC_NfceeModeSet (target_handle, NFC_MODE_ACTIVATE);
                }
                if ((status = NFC_ConnCreate (NCI_DEST_TYPE_NFCEE, target_handle, NFA_EE_INTERFACE_HCI_ACCESS, nfa_hci_conn_cback)) == NFA_STATUS_OK)
                    nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, NFA_HCI_CON_CREATE_TIMEOUT_VAL);
                else
                {
                    nfa_hci_cb.hci_state = NFA_HCI_STATE_DISABLED;
                    NFA_TRACE_ERROR0 ("nfa_hci_startup - Failed to Create Logical connection. HCI Initialization/Restore failed");
                    nfa_hci_startup_complete (NFA_STATUS_FAILED);
                }
            }
            count++;
        }
        if (!found)
        {
            NFA_TRACE_ERROR0 ("nfa_hci_startup - HCI ACCESS Interface not discovered. HCI Initialization/Restore failed");
            nfa_hci_startup_complete (NFA_STATUS_FAILED);
        }
    }
}

/*******************************************************************************
**
** Function         nfa_hci_sys_enable
**
** Description      Enable NFA HCI
**
** Returns          None
**
*******************************************************************************/
static void nfa_hci_sys_enable (void)
{
    NFA_TRACE_DEBUG0 ("nfa_hci_sys_enable ()");
    nfa_ee_reg_cback_enable_done (&nfa_hci_ee_info_cback);

    nfa_nv_co_read ((UINT8 *)&nfa_hci_cb.cfg, sizeof (nfa_hci_cb.cfg),DH_NV_BLOCK);
    nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, NFA_HCI_NV_READ_TIMEOUT_VAL);
}

/*******************************************************************************
**
** Function         nfa_hci_sys_disable
**
** Description      Disable NFA HCI
**
** Returns          None
**
*******************************************************************************/
static void nfa_hci_sys_disable (void)
{
    tNFA_HCI_EVT_DATA   evt_data;

    nfa_sys_stop_timer (&nfa_hci_cb.timer);

    if (nfa_hci_cb.conn_id)
    {
        if (nfa_sys_is_graceful_disable ())
        {
            /* Tell all applications stack is down */
            nfa_hciu_send_to_all_apps (NFA_HCI_EXIT_EVT, &evt_data);
            NFC_ConnClose (nfa_hci_cb.conn_id);
            return;
        }
        nfa_hci_cb.conn_id = 0;
    }

    nfa_hci_cb.hci_state = NFA_HCI_STATE_DISABLED;
    /* deregister message handler on NFA SYS */
    nfa_sys_deregister (NFA_ID_HCI);
}

/*******************************************************************************
**
** Function         nfa_hci_conn_cback
**
** Description      This function Process event from NCI
**
** Returns          None
**
*******************************************************************************/
static void nfa_hci_conn_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data)
{
    UINT8   *p;
    BT_HDR  *p_pkt = (BT_HDR *) p_data->data.p_data;
    UINT8   chaining_bit;
    UINT8   pipe;
    UINT16  pkt_len;
#if (BT_TRACE_VERBOSE == TRUE)
    char    buff[100];
#endif

    if (event == NFC_CONN_CREATE_CEVT)
    {
        nfa_hci_cb.conn_id   = conn_id;
        nfa_hci_cb.buff_size = p_data->conn_create.buff_size;

        if (nfa_hci_cb.hci_state == NFA_HCI_STATE_STARTUP)
        {
            nfa_hci_cb.w4_hci_netwk_init = TRUE;
            nfa_hciu_alloc_gate (NFA_HCI_CONNECTIVITY_GATE,0);
        }

        if (nfa_hci_cb.cfg.admin_gate.pipe01_state == NFA_HCI_PIPE_CLOSED)
        {
            /* First step in initialization/restore is to open the admin pipe */
            nfa_hciu_send_open_pipe_cmd (NFA_HCI_ADMIN_PIPE);
        }
        else
        {
            /* Read session id, to know DH session id is correct */
            nfa_hciu_send_get_param_cmd (NFA_HCI_ADMIN_PIPE, NFA_HCI_SESSION_IDENTITY_INDEX);
        }
    }
    else if (event == NFC_CONN_CLOSE_CEVT)
    {
        nfa_hci_cb.conn_id   = 0;
        nfa_hci_cb.hci_state = NFA_HCI_STATE_DISABLED;
        /* deregister message handler on NFA SYS */
        nfa_sys_deregister (NFA_ID_HCI);
    }

    if ((event != NFC_DATA_CEVT) || (p_pkt == NULL))
            return;

    if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_WAIT_NETWK_ENABLE)
        ||(nfa_hci_cb.hci_state == NFA_HCI_STATE_RESTORE_NETWK_ENABLE)  )
    {
        /* Received HCP Packet before timeout, Other Host initialization is not complete */
        nfa_sys_stop_timer (&nfa_hci_cb.timer);
        if (nfa_hci_cb.w4_hci_netwk_init)
            nfa_sys_start_timer (&nfa_hci_cb.timer, NFA_HCI_RSP_TIMEOUT_EVT, p_nfa_hci_cfg->hci_netwk_enable_timeout);
    }

    p       = (UINT8 *) (p_pkt + 1) + p_pkt->offset;
    pkt_len = p_pkt->len;

#if (BT_TRACE_PROTOCOL == TRUE)
    DispHcp (p, pkt_len, TRUE, (BOOLEAN) !nfa_hci_cb.assembling);
#endif

    chaining_bit = ((*p) >> 0x07) & 0x01;
    pipe = (*p++) & 0x7F;
    if (pkt_len != 0)
        pkt_len--;

    if (nfa_hci_cb.assembling == FALSE)
    {
        /* First Segment of a packet */
        nfa_hci_cb.type            = ((*p) >> 0x06) & 0x03;
        nfa_hci_cb.inst            = (*p++ & 0x3F);
        if (pkt_len != 0)
            pkt_len--;
        nfa_hci_cb.assembly_failed = FALSE;
        nfa_hci_cb.msg_len         = 0;

        if (chaining_bit == NFA_HCI_MESSAGE_FRAGMENTATION)
        {
            nfa_hci_cb.assembling = TRUE;
            nfa_hci_set_receive_buf (pipe);
            nfa_hci_assemble_msg (p, pkt_len);
        }
        else
        {
            if ((pipe >= NFA_HCI_FIRST_DYNAMIC_PIPE) && (nfa_hci_cb.type == NFA_HCI_EVENT_TYPE))
            {
                nfa_hci_set_receive_buf (pipe);
                nfa_hci_assemble_msg (p, pkt_len);
                p = nfa_hci_cb.p_msg_data;
            }
        }
    }
    else
    {
        if (nfa_hci_cb.assembly_failed)
        {
            /* If Reassembly failed because of insufficient buffer, just drop the new segmented packets */
            NFA_TRACE_ERROR1 ("nfa_hci_conn_cback (): Insufficient buffer to Reassemble HCP packet! Dropping :%u bytes", pkt_len);
        }
        else
        {
            /* Reassemble the packet */
            nfa_hci_assemble_msg (p, pkt_len);
        }

        if (chaining_bit == NFA_HCI_NO_MESSAGE_FRAGMENTATION)
        {
            /* Just added the last segment in the chain. Reset pointers */
            nfa_hci_cb.assembling = FALSE;
            p                     = nfa_hci_cb.p_msg_data;
            pkt_len               = nfa_hci_cb.msg_len;
        }
    }

#if (BT_TRACE_VERBOSE == TRUE)
    NFA_TRACE_EVENT5 ("nfa_hci_conn_cback Recvd data pipe:%d  %s  chain:%d  assmbl:%d  len:%d",
                      (UINT8)pipe, nfa_hciu_get_type_inst_names (pipe, nfa_hci_cb.type, nfa_hci_cb.inst, buff),
                      (UINT8)chaining_bit, (UINT8)nfa_hci_cb.assembling, p_pkt->len);
#else
    NFA_TRACE_EVENT6 ("nfa_hci_conn_cback Recvd data pipe:%d  Type: %u  Inst: %u  chain:%d reassm:%d len:%d",
                      pipe, nfa_hci_cb.type, nfa_hci_cb.inst, chaining_bit, nfa_hci_cb.assembling, p_pkt->len);
#endif


    /* If still reassembling fragments, just return */
    if (nfa_hci_cb.assembling)
    {
        /* if not last packet, release GKI buffer */
        GKI_freebuf (p_pkt);
        return;
    }

    /* If we got a response, cancel the response timer. Also, if waiting for */
    /* a single response, we can go back to idle state                       */
    if (  (nfa_hci_cb.hci_state == NFA_HCI_STATE_WAIT_RSP)
        &&((nfa_hci_cb.type == NFA_HCI_RESPONSE_TYPE) || (nfa_hci_cb.w4_rsp_evt && (nfa_hci_cb.type == NFA_HCI_EVENT_TYPE)))  )
    {
        nfa_sys_stop_timer (&nfa_hci_cb.timer);
        nfa_hci_cb.hci_state  = NFA_HCI_STATE_IDLE;
    }

    switch (pipe)
    {
    case NFA_HCI_ADMIN_PIPE:
        /* Check if data packet is a command, response or event */
        if (nfa_hci_cb.type == NFA_HCI_COMMAND_TYPE)
        {
            nfa_hci_handle_admin_gate_cmd (p);
        }
            else if (nfa_hci_cb.type == NFA_HCI_RESPONSE_TYPE)
        {
            nfa_hci_handle_admin_gate_rsp (p, (UINT8) pkt_len);
        }
        else if (nfa_hci_cb.type == NFA_HCI_EVENT_TYPE)
        {
            nfa_hci_handle_admin_gate_evt (p);
        }
        break;

    case NFA_HCI_LINK_MANAGEMENT_PIPE:
        /* We don't send Link Management commands, we only get them */
        if (nfa_hci_cb.type == NFA_HCI_COMMAND_TYPE)
            nfa_hci_handle_link_mgm_gate_cmd (p);
        break;

    default:
        if (pipe >= NFA_HCI_FIRST_DYNAMIC_PIPE)
            nfa_hci_handle_dyn_pipe_pkt (pipe, p, pkt_len);
        break;
    }

    if ((nfa_hci_cb.type == NFA_HCI_RESPONSE_TYPE) || (nfa_hci_cb.w4_rsp_evt && (nfa_hci_cb.type == NFA_HCI_EVENT_TYPE)))
    {
        nfa_hci_cb.w4_rsp_evt = FALSE;
    }

    /* Send a message to ouselves to check for anything to do */
    p_pkt->event = NFA_HCI_CHECK_QUEUE_EVT;
    p_pkt->len   = 0;
    nfa_sys_sendmsg (p_pkt);
}

/*******************************************************************************
**
** Function         nfa_hci_handle_nv_read
**
** Description      handler function for nv read complete event
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_handle_nv_read (UINT8 block, tNFA_STATUS status)
{
    UINT8   session_id[NFA_HCI_SESSION_ID_LEN];
    UINT8   default_session[NFA_HCI_SESSION_ID_LEN] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
    UINT8   reset_session[NFA_HCI_SESSION_ID_LEN]   = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    UINT32  os_tick;

    if (block == DH_NV_BLOCK)
    {
        /* Stop timer as NVDATA Read Completed */
        nfa_sys_stop_timer (&nfa_hci_cb.timer);
        nfa_hci_cb.nv_read_cmplt = TRUE;
        if (  (status != NFA_STATUS_OK)
            ||(!nfa_hci_is_valid_cfg ())
            ||(!(memcmp (nfa_hci_cb.cfg.admin_gate.session_id, default_session, NFA_HCI_SESSION_ID_LEN)))
            ||(!(memcmp (nfa_hci_cb.cfg.admin_gate.session_id, reset_session, NFA_HCI_SESSION_ID_LEN)))  )
        {
            nfa_hci_cb.b_hci_netwk_reset = TRUE;
            /* Set a new session id so that we clear all pipes later after seeing a difference with the HC Session ID */
            memcpy (&session_id[(NFA_HCI_SESSION_ID_LEN / 2)], nfa_hci_cb.cfg.admin_gate.session_id, (NFA_HCI_SESSION_ID_LEN / 2));
            os_tick = GKI_get_os_tick_count ();
            memcpy (session_id, (UINT8 *)&os_tick, (NFA_HCI_SESSION_ID_LEN / 2));
            nfa_hci_restore_default_config (session_id);
        }
        nfa_hci_startup ();
    }
}

/*******************************************************************************
**
** Function         nfa_hci_rsp_timeout
**
** Description      action function to process timeout
**
** Returns          None
**
*******************************************************************************/
void nfa_hci_rsp_timeout (tNFA_HCI_EVENT_DATA *p_evt_data)
{
    tNFA_HCI_EVT        evt = 0;
    tNFA_HCI_EVT_DATA   evt_data;
    UINT8               delete_pipe;

    NFA_TRACE_EVENT2 ("nfa_hci_rsp_timeout () State: %u  Cmd: %u", nfa_hci_cb.hci_state, nfa_hci_cb.cmd_sent);

    evt_data.status      = NFA_STATUS_FAILED;

    switch (nfa_hci_cb.hci_state)
    {
    case NFA_HCI_STATE_STARTUP:
    case NFA_HCI_STATE_RESTORE:
        NFA_TRACE_ERROR0 ("nfa_hci_rsp_timeout - Initialization failed!");
        nfa_hci_startup_complete (NFA_STATUS_TIMEOUT);
        break;

    case NFA_HCI_STATE_WAIT_NETWK_ENABLE:
    case NFA_HCI_STATE_RESTORE_NETWK_ENABLE:

        if (nfa_hci_cb.w4_hci_netwk_init)
        {
            /* HCI Network is enabled */
            nfa_hci_cb.w4_hci_netwk_init = FALSE;
            nfa_hciu_send_get_param_cmd (NFA_HCI_ADMIN_PIPE, NFA_HCI_HOST_LIST_INDEX);
        }
        else
        {
            nfa_hci_startup_complete (NFA_STATUS_FAILED);
        }
        break;

    case NFA_HCI_STATE_REMOVE_GATE:
        /* Something wrong, NVRAM data could be corrupt */
        if (nfa_hci_cb.cmd_sent == NFA_HCI_ADM_DELETE_PIPE)
        {
            nfa_hciu_send_clear_all_pipe_cmd ();
        }
        else
        {
            nfa_hciu_remove_all_pipes_from_host (0);
            nfa_hci_api_dealloc_gate (NULL);
        }
        break;

    case NFA_HCI_STATE_APP_DEREGISTER:
        /* Something wrong, NVRAM data could be corrupt */
        if (nfa_hci_cb.cmd_sent == NFA_HCI_ADM_DELETE_PIPE)
        {
            nfa_hciu_send_clear_all_pipe_cmd ();
        }
        else
        {
            nfa_hciu_remove_all_pipes_from_host (0);
            nfa_hci_api_deregister (NULL);
        }
        break;

    case NFA_HCI_STATE_WAIT_RSP:
        nfa_hci_cb.hci_state = NFA_HCI_STATE_IDLE;

        if (nfa_hci_cb.w4_rsp_evt)
        {
            nfa_hci_cb.w4_rsp_evt       = FALSE;
            evt                         = NFA_HCI_EVENT_RCVD_EVT;
            evt_data.rcvd_evt.pipe      = nfa_hci_cb.pipe_in_use;
            evt_data.rcvd_evt.evt_code  = 0;
            evt_data.rcvd_evt.evt_len   = 0;
            evt_data.rcvd_evt.p_evt_buf = NULL;
            nfa_hci_cb.rsp_buf_size     = 0;
            nfa_hci_cb.p_rsp_buf        = NULL;

            break;
        }

        delete_pipe          = 0;
        switch (nfa_hci_cb.cmd_sent)
        {
        case NFA_HCI_ANY_SET_PARAMETER:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already and release the pipe. But still send delete pipe command to be safe.
             */
            delete_pipe                = nfa_hci_cb.pipe_in_use;
            evt_data.registry.pipe     = nfa_hci_cb.pipe_in_use;
            evt_data.registry.data_len = 0;
            evt_data.registry.index    = nfa_hci_cb.param_in_use;
            evt                        = NFA_HCI_SET_REG_RSP_EVT;
            break;

        case NFA_HCI_ANY_GET_PARAMETER:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already and release the pipe. But still send delete pipe command to be safe.
             */
            delete_pipe                = nfa_hci_cb.pipe_in_use;
            evt_data.registry.pipe     = nfa_hci_cb.pipe_in_use;
            evt_data.registry.data_len = 0;
            evt_data.registry.index    = nfa_hci_cb.param_in_use;
            evt                        = NFA_HCI_GET_REG_RSP_EVT;
            break;

        case NFA_HCI_ANY_OPEN_PIPE:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already and release the pipe. But still send delete pipe command to be safe.
             */
            delete_pipe          = nfa_hci_cb.pipe_in_use;
            evt_data.opened.pipe = nfa_hci_cb.pipe_in_use;
            evt                  = NFA_HCI_OPEN_PIPE_EVT;
            break;

        case NFA_HCI_ANY_CLOSE_PIPE:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already and release the pipe. But still send delete pipe command to be safe.
             */
            delete_pipe          = nfa_hci_cb.pipe_in_use;
            evt_data.closed.pipe = nfa_hci_cb.pipe_in_use;
            evt                  = NFA_HCI_CLOSE_PIPE_EVT;
            break;

        case NFA_HCI_ADM_CREATE_PIPE:
            evt_data.created.pipe        = nfa_hci_cb.pipe_in_use;
            evt_data.created.source_gate = nfa_hci_cb.local_gate_in_use;
            evt_data.created.dest_host   = nfa_hci_cb.remote_host_in_use;
            evt_data.created.dest_gate   = nfa_hci_cb.remote_gate_in_use;
            evt                          = NFA_HCI_CREATE_PIPE_EVT;
            break;

        case NFA_HCI_ADM_DELETE_PIPE:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already. Just release the pipe.
             */
            if (nfa_hci_cb.pipe_in_use <= NFA_HCI_LAST_DYNAMIC_PIPE)
                nfa_hciu_release_pipe (nfa_hci_cb.pipe_in_use);
            evt_data.deleted.pipe = nfa_hci_cb.pipe_in_use;
            evt                   = NFA_HCI_DELETE_PIPE_EVT;
            break;

        default:
            /*
             * As no response to the command sent on this pipe, we may assume the pipe is
             * deleted already and release the pipe. But still send delete pipe command to be safe.
             */
            delete_pipe                = nfa_hci_cb.pipe_in_use;
            break;
        }
        if (delete_pipe && (delete_pipe <= NFA_HCI_LAST_DYNAMIC_PIPE))
        {
            nfa_hciu_send_delete_pipe_cmd (delete_pipe);
            nfa_hciu_release_pipe (delete_pipe);
        }
        break;
    case NFA_HCI_STATE_DISABLED:
    default:
        NFA_TRACE_DEBUG0 ("nfa_hci_rsp_timeout () Timeout in DISABLED/ Invalid state");
        break;
    }
    if (evt != 0)
        nfa_hciu_send_to_app (evt, &evt_data, nfa_hci_cb.app_in_use);
}

/*******************************************************************************
**
** Function         nfa_hci_set_receive_buf
**
** Description      Set reassembly buffer for incoming message
**
** Returns          status
**
*******************************************************************************/
static void nfa_hci_set_receive_buf (UINT8 pipe)
{
    if (  (pipe >= NFA_HCI_FIRST_DYNAMIC_PIPE)
        &&(nfa_hci_cb.type == NFA_HCI_EVENT_TYPE)  )
    {
        if (  (nfa_hci_cb.rsp_buf_size)
            &&(nfa_hci_cb.p_rsp_buf != NULL)  )
        {
            nfa_hci_cb.p_msg_data  = nfa_hci_cb.p_rsp_buf;
            nfa_hci_cb.max_msg_len = nfa_hci_cb.rsp_buf_size;
            return;
        }
    }
    nfa_hci_cb.p_msg_data  = nfa_hci_cb.msg_data;
    nfa_hci_cb.max_msg_len = NFA_MAX_HCI_EVENT_LEN;
}

/*******************************************************************************
**
** Function         nfa_hci_assemble_msg
**
** Description      Reassemble the incoming message
**
** Returns          None
**
*******************************************************************************/
static void nfa_hci_assemble_msg (UINT8 *p_data, UINT16 data_len)
{
    if ((nfa_hci_cb.msg_len + data_len) > nfa_hci_cb.max_msg_len)
    {
        /* Fill the buffer as much it can hold */
        memcpy (&nfa_hci_cb.p_msg_data[nfa_hci_cb.msg_len], p_data, (nfa_hci_cb.max_msg_len - nfa_hci_cb.msg_len));
        nfa_hci_cb.msg_len         = nfa_hci_cb.max_msg_len;
        /* Set Reassembly failed */
        nfa_hci_cb.assembly_failed = TRUE;
        NFA_TRACE_ERROR1 ("nfa_hci_assemble_msg (): Insufficient buffer to Reassemble HCP packet! Dropping :%u bytes", ((nfa_hci_cb.msg_len + data_len) - nfa_hci_cb.max_msg_len));
    }
    else
    {
        memcpy (&nfa_hci_cb.p_msg_data[nfa_hci_cb.msg_len], p_data, data_len);
        nfa_hci_cb.msg_len += data_len;
    }
}

/*******************************************************************************
**
** Function         nfa_hci_evt_hdlr
**
** Description      Processing all event for NFA HCI
**
** Returns          TRUE if p_msg needs to be deallocated
**
*******************************************************************************/
static BOOLEAN nfa_hci_evt_hdlr (BT_HDR *p_msg)
{
    tNFA_HCI_EVENT_DATA *p_evt_data = (tNFA_HCI_EVENT_DATA *)p_msg;

#if (BT_TRACE_VERBOSE == TRUE)
    NFA_TRACE_EVENT4 ("nfa_hci_evt_hdlr state: %s (%d) event: %s (0x%04x)",
                      nfa_hciu_get_state_name (nfa_hci_cb.hci_state), nfa_hci_cb.hci_state,
                      nfa_hciu_get_event_name (p_evt_data->hdr.event), p_evt_data->hdr.event);
#else
    NFA_TRACE_EVENT2 ("nfa_hci_evt_hdlr state: %d event: 0x%04x", nfa_hci_cb.hci_state, p_evt_data->hdr.event);
#endif

    /* If this is an API request, queue it up */
    if ((p_msg->event >= NFA_HCI_FIRST_API_EVENT) && (p_msg->event <= NFA_HCI_LAST_API_EVENT))
    {
        GKI_enqueue (&nfa_hci_cb.hci_api_q, p_msg);
    }
    else
    {
        switch (p_msg->event)
        {
        case NFA_HCI_RSP_NV_READ_EVT:
            nfa_hci_handle_nv_read (p_evt_data->nv_read.block, p_evt_data->nv_read.status);
            break;

        case NFA_HCI_RSP_NV_WRITE_EVT:
            /* NV Ram write completed - nothing to do... */
            break;

        case NFA_HCI_RSP_TIMEOUT_EVT:
            nfa_hci_rsp_timeout ((tNFA_HCI_EVENT_DATA *)p_msg);
            break;

        case NFA_HCI_CHECK_QUEUE_EVT:
            if (HCI_LOOPBACK_DEBUG)
            {
                if (p_msg->len != 0)
                {
                    tNFC_DATA_CEVT   xx;
                    xx.p_data = p_msg;
                    nfa_hci_conn_cback (0, NFC_DATA_CEVT, (tNFC_CONN *)&xx);
                    return FALSE;
                }
            }
            break;
        }
    }

    if ((p_msg->event > NFA_HCI_LAST_API_EVENT))
        GKI_freebuf (p_msg);

    nfa_hci_check_api_requests ();

    if (nfa_hciu_is_no_host_resetting ())
        nfa_hci_check_pending_api_requests ();

    if ((nfa_hci_cb.hci_state == NFA_HCI_STATE_IDLE) && (nfa_hci_cb.nv_write_needed))
    {
        nfa_hci_cb.nv_write_needed = FALSE;
        nfa_nv_co_write ((UINT8 *)&nfa_hci_cb.cfg, sizeof (nfa_hci_cb.cfg),DH_NV_BLOCK);
    }

    return FALSE;
}