/******************************************************************************
 *
 *  Copyright (C) 2010-2014 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 "nfa_dm_int.h"
#include "nfa_ee_api.h"
#include "nfa_ee_int.h"
#include "nfa_hci_api.h"
#include "nfa_hci_defs.h"
#include "nfa_hci_int.h"
#include "nfa_mem_co.h"
#include "nfa_nv_co.h"
#include "nfa_sys.h"
#include "nfa_sys_int.h"
#include "nfc_api.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 bool nfa_hci_evt_hdlr(NFC_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_t conn_id, tNFC_CONN_EVT event,
                               tNFC_CONN* p_data);
static void nfa_hci_set_receive_buf(uint8_t pipe);
static void nfa_hci_assemble_msg(uint8_t* p_data, uint16_t data_len);
static void nfa_hci_handle_nv_read(uint8_t 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_t 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
**
*******************************************************************************/
bool nfa_hci_is_valid_cfg(void) {
  uint8_t xx, yy, zz;
  tNFA_HANDLE reg_app[NFA_HCI_MAX_APP_CB];
  uint8_t valid_gate[NFA_HCI_MAX_GATE_CB];
  uint8_t app_count = 0;
  uint8_t gate_count = 0;
  uint32_t 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_t* 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_t 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_t num_nfcee = 2;
  uint8_t target_handle;
  uint8_t count = 0;
  bool 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_t)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);
        }
        status =
            NFC_ConnCreate(NCI_DEST_TYPE_NFCEE, target_handle,
                           NFA_EE_INTERFACE_HCI_ACCESS, nfa_hci_conn_cback);
        if (status == 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_t*)&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_t conn_id, tNFC_CONN_EVT event,
                               tNFC_CONN* p_data) {
  uint8_t* p;
  NFC_HDR* p_pkt = (NFC_HDR*)p_data->data.p_data;
  uint8_t chaining_bit;
  uint8_t pipe;
  uint16_t 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_t*)(p_pkt + 1) + p_pkt->offset;
  pkt_len = p_pkt->len;

#if (BT_TRACE_PROTOCOL == TRUE)
  DispHcp(p, pkt_len, true, (bool)!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_t)pipe, nfa_hciu_get_type_inst_names(pipe, nfa_hci_cb.type,
                                                  nfa_hci_cb.inst, buff),
      (uint8_t)chaining_bit, (uint8_t)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_t)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_t block, tNFA_STATUS status) {
  uint8_t session_id[NFA_HCI_SESSION_ID_LEN];
  uint8_t default_session[NFA_HCI_SESSION_ID_LEN] = {0xFF, 0xFF, 0xFF, 0xFF,
                                                     0xFF, 0xFF, 0xFF, 0xFF};
  uint8_t reset_session[NFA_HCI_SESSION_ID_LEN] = {0x00, 0x00, 0x00, 0x00,
                                                   0x00, 0x00, 0x00, 0x00};
  uint32_t 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_t*)&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_t 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_t 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_t* p_data, uint16_t 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 bool nfa_hci_evt_hdlr(NFC_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_t*)&nfa_hci_cb.cfg, sizeof(nfa_hci_cb.cfg),
                    DH_NV_BLOCK);
  }

  return false;
}