/******************************************************************************
 *
 *  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 file contains the action functions for NFA-EE
 *
 ******************************************************************************/
#include <string.h>
#include "nfa_sys.h"
#include "nfa_api.h"
#include "nfa_dm_int.h"
#include "nfa_sys_int.h"
#include "nfc_api.h"
#include "nfa_ee_int.h"


/* the de-bounce timer:
 * The NFA-EE API functions are called to set the routing and VS configuration.
 * When this timer expires, the configuration is sent to NFCC all at once.
 * This is the timeout value for the de-bounce timer. */
#ifndef NFA_EE_ROUT_TIMEOUT_VAL
#define NFA_EE_ROUT_TIMEOUT_VAL         1000
#endif

#define NFA_EE_ROUT_BUF_SIZE            540
#define NFA_EE_ROUT_MAX_TLV_SIZE        0xFD


/* the following 2 tables convert the technology mask in API and control block to the command for NFCC */
#define NFA_EE_NUM_TECH     3
const UINT8 nfa_ee_tech_mask_list[NFA_EE_NUM_TECH] =
{
    NFA_TECHNOLOGY_MASK_A,
    NFA_TECHNOLOGY_MASK_B,
    NFA_TECHNOLOGY_MASK_F
};

const UINT8 nfa_ee_tech_list[NFA_EE_NUM_TECH] =
{
    NFC_RF_TECHNOLOGY_A,
    NFC_RF_TECHNOLOGY_B,
    NFC_RF_TECHNOLOGY_F
};

/* the following 2 tables convert the protocol mask in API and control block to the command for NFCC */
#define NFA_EE_NUM_PROTO     5
const UINT8 nfa_ee_proto_mask_list[NFA_EE_NUM_PROTO] =
{
    NFA_PROTOCOL_MASK_T1T,
    NFA_PROTOCOL_MASK_T2T,
    NFA_PROTOCOL_MASK_T3T,
    NFA_PROTOCOL_MASK_ISO_DEP,
    NFA_PROTOCOL_MASK_NFC_DEP
};

const UINT8 nfa_ee_proto_list[NFA_EE_NUM_PROTO] =
{
    NFC_PROTOCOL_T1T,
    NFC_PROTOCOL_T2T,
    NFC_PROTOCOL_T3T,
    NFC_PROTOCOL_ISO_DEP,
    NFC_PROTOCOL_NFC_DEP
};

static void nfa_ee_report_discover_req_evt(void);
static void nfa_ee_build_discover_req_evt (tNFA_EE_DISCOVER_REQ *p_evt_data);
/*******************************************************************************
**
** Function         nfa_ee_trace_aid
**
** Description      trace AID
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_trace_aid (char *p_str, UINT8 id,  UINT8 aid_len, UINT8 *p)
{
    int     len = aid_len;
    int     xx, yy = 0;
    char    buff[100];

    buff[0] = 0;
    if (aid_len > NFA_MAX_AID_LEN)
    {
        NFA_TRACE_ERROR2 ("aid_len: %d exceeds max(%d)", aid_len, NFA_MAX_AID_LEN);
        len = NFA_MAX_AID_LEN;
    }
    for (xx = 0; xx < len; xx++)
    {
        yy += sprintf (&buff[yy], "%02x ", *p);
        p++;
    }
    NFA_TRACE_DEBUG4 ("%s id:0x%x len=%d aid:%s", p_str, id, aid_len, buff);

}

/*******************************************************************************
**
** Function         nfa_ee_update_route_size
**
** Description      Update the size required for technology and protocol routing
**                  of the given NFCEE ID.
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_update_route_size(tNFA_EE_ECB *p_cb)
{
    int     xx;
    UINT8   power_cfg = 0;

    p_cb->size_mask = 0;
    /* add the Technology based routing */
    for (xx = 0; xx < NFA_EE_NUM_TECH; xx++)
    {
        power_cfg = 0;
        if (p_cb->tech_switch_on & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_ON;
        if (p_cb->tech_switch_off & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_SWITCH_OFF;
        if (p_cb->tech_battery_off & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_BATT_OFF;
        if (power_cfg)
        {
            /* 5 = 1 (tag) + 1 (len) + 1(nfcee_id) + 1(power cfg) + 1 (techonogy) */
            p_cb->size_mask += 5;
        }
    }

    /* add the Protocol based routing */
    for (xx = 0; xx < NFA_EE_NUM_PROTO; xx++)
    {
        power_cfg = 0;
        if (p_cb->proto_switch_on & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_ON;
        if (p_cb->proto_switch_off & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_SWITCH_OFF;
        if (p_cb->proto_battery_off & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_BATT_OFF;
        if (power_cfg)
        {
            /* 5 = 1 (tag) + 1 (len) + 1(nfcee_id) + 1(power cfg) + 1 (protocol) */
            p_cb->size_mask += 5;
        }
    }
    NFA_TRACE_DEBUG2 ("nfa_ee_update_route_size nfcee_id:0x%x size_mask:%d", p_cb->nfcee_id, p_cb->size_mask);
}

/*******************************************************************************
**
** Function         nfa_ee_update_route_aid_size
**
** Description      Update the size required for AID routing
**                  of the given NFCEE ID.
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_update_route_aid_size(tNFA_EE_ECB *p_cb)
{
    UINT8   *pa, len;
    int     start_offset;
    int     xx;

    p_cb->size_aid  = 0;
    if (p_cb->aid_entries)
    {
        start_offset = 0;
        for (xx = 0; xx < p_cb->aid_entries; xx++)
        {
            /* add one AID entry */
            if (p_cb->aid_rt_info[xx] & NFA_EE_AE_ROUTE)
            {
                pa      = &p_cb->aid_cfg[start_offset];
                pa ++; /* EMV tag */
                len     = *pa++; /* aid_len */
                /* 4 = 1 (tag) + 1 (len) + 1(nfcee_id) + 1(power cfg) */
                p_cb->size_aid  += 4;
                p_cb->size_aid  += len;
            }
            start_offset += p_cb->aid_len[xx];
        }
    }
    NFA_TRACE_DEBUG2 ("nfa_ee_update_route_aid_size nfcee_id:0x%x size_aid:%d", p_cb->nfcee_id, p_cb->size_aid);
}

/*******************************************************************************
**
** Function         nfa_ee_total_lmrt_size
**
** Description      the total listen mode routing table size
**
** Returns          UINT16
**
*******************************************************************************/
static UINT16 nfa_ee_total_lmrt_size(void)
{
    int xx;
    UINT16 lmrt_size = 0;
    tNFA_EE_ECB          *p_cb;

    p_cb = &nfa_ee_cb.ecb[NFA_EE_CB_4_DH];
    lmrt_size += p_cb->size_mask;
    lmrt_size += p_cb->size_aid;
    p_cb = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee - 1];
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb--)
    {
        if (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE)
        {
            lmrt_size += p_cb->size_mask;
            lmrt_size += p_cb->size_aid;
        }
    }
    NFA_TRACE_DEBUG1 ("nfa_ee_total_lmrt_size size:%d", lmrt_size);
    return lmrt_size;
}

/*******************************************************************************
**
** Function         nfa_ee_conn_cback
**
** Description      process connection callback event from stack
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_conn_cback (UINT8 conn_id, tNFC_CONN_EVT event, tNFC_CONN *p_data)
{
    BT_HDR             *p_msg;
    tNFA_EE_NCI_CONN    cbk;

    NFA_TRACE_DEBUG2("nfa_ee_conn_cback: conn_id: %d, event=0x%02x", conn_id, event);

    cbk.hdr.event   = NFA_EE_NCI_CONN_EVT;
    if (event == NFC_DATA_CEVT)
    {
        /* Treat data event specially to avoid potential memory leak */
        cbk.hdr.event   = NFA_EE_NCI_DATA_EVT;
    }
    cbk.conn_id     = conn_id;
    cbk.event       = event;
    cbk.p_data      = p_data;
    p_msg           = (BT_HDR *)&cbk;

    nfa_ee_evt_hdlr (p_msg);
}


/*******************************************************************************
**
** Function         nfa_ee_find_total_aid_len
**
** Description      Find the total len in aid_cfg from start_entry to the last
**
** Returns          void
**
*******************************************************************************/
int nfa_ee_find_total_aid_len(tNFA_EE_ECB *p_cb, int start_entry)
{
    int len = 0, xx;

    if (p_cb->aid_entries > start_entry)
    {
        for (xx = start_entry; xx < p_cb->aid_entries; xx++)
        {
            len += p_cb->aid_len[xx];
        }
    }
    return len;
}




/*******************************************************************************
**
** Function         nfa_ee_find_aid_offset
**
** Description      Given the AID, find the associated tNFA_EE_ECB and the
**                  offset in aid_cfg[]. *p_entry is the index.
**
** Returns          void
**
*******************************************************************************/
tNFA_EE_ECB * nfa_ee_find_aid_offset(UINT8 aid_len, UINT8 *p_aid, int *p_offset, int *p_entry)
{
    int  xx, yy, aid_len_offset, offset;
    tNFA_EE_ECB *p_ret = NULL, *p_ecb;

    p_ecb = &nfa_ee_cb.ecb[NFA_EE_CB_4_DH];
    aid_len_offset = 1; /* skip the tag */
    for (yy = 0; yy < nfa_ee_cb.cur_ee; yy++, p_ecb++)
    {
        if (p_ecb->aid_entries)
        {
            offset = 0;
            for (xx = 0; xx < p_ecb->aid_entries; xx++)
            {
                if (  (p_ecb->aid_cfg[offset + aid_len_offset] == aid_len)
                    &&(memcmp(&p_ecb->aid_cfg[offset + aid_len_offset + 1], p_aid, aid_len) == 0)  )
                {
                    p_ret = p_ecb;
                    if (p_offset)
                        *p_offset = offset;
                    if (p_entry)
                        *p_entry  = xx;
                    break;
                }
                offset += p_ecb->aid_len[xx];
            }

            if (p_ret)
            {
                /* found the entry already */
                break;
            }
        }
        p_ecb = &nfa_ee_cb.ecb[yy];
    }

    return p_ret;
}

/*******************************************************************************
**
** Function         nfa_ee_report_event
**
** Description      report the given event to the callback
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_report_event(tNFA_EE_CBACK *p_cback, tNFA_EE_EVT event, tNFA_EE_CBACK_DATA *p_data)
{
    int xx;

    /* use the given callback, if not NULL */
    if (p_cback)
    {
        (*p_cback)(event, p_data);
        return;
    }
    /* if the given is NULL, report to all registered ones */
    for (xx = 0; xx < NFA_EE_MAX_CBACKS; xx++)
    {
        if (nfa_ee_cb.p_ee_cback[xx] != NULL)
        {
            (*nfa_ee_cb.p_ee_cback[xx])(event, p_data);
        }
    }
}
/*******************************************************************************
**
** Function         nfa_ee_start_timer
**
** Description      start the de-bounce timer
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_start_timer(void)
{
    if (nfa_dm_is_active())
        nfa_sys_start_timer(&nfa_ee_cb.timer, NFA_EE_ROUT_TIMEOUT_EVT, NFA_EE_ROUT_TIMEOUT_VAL);
}

/*******************************************************************************
**
** Function         nfa_ee_api_discover
**
** Description      process discover command from user
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_discover(tNFA_EE_MSG *p_data)
{
    tNFA_EE_CBACK *p_cback = p_data->ee_discover.p_cback;
    tNFA_EE_CBACK_DATA  evt_data = {0};

    NFA_TRACE_DEBUG1 ("nfa_ee_api_discover() in_use:%d", nfa_ee_cb.discv_timer.in_use);
    if (nfa_ee_cb.discv_timer.in_use)
    {
        nfa_sys_stop_timer(&nfa_ee_cb.discv_timer);
        NFC_NfceeDiscover(FALSE);
    }
    if (nfa_ee_cb.p_ee_disc_cback == NULL && NFC_NfceeDiscover(TRUE) == NFC_STATUS_OK)
    {
        nfa_ee_cb.p_ee_disc_cback   = p_cback;
    }
    else
    {
        evt_data.status = NFA_STATUS_FAILED;
        nfa_ee_report_event (p_cback, NFA_EE_DISCOVER_EVT, &evt_data);
    }
}

/*******************************************************************************
**
** Function         nfa_ee_api_register
**
** Description      process register command from user
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_register(tNFA_EE_MSG *p_data)
{
    int xx;
    tNFA_EE_CBACK *p_cback = p_data->ee_register.p_cback;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    BOOLEAN found = FALSE;

    evt_data.ee_register = NFA_STATUS_FAILED;
    /* loop through all entries to see if there's a matching callback */
    for (xx = 0; xx < NFA_EE_MAX_CBACKS; xx++)
    {
        if (nfa_ee_cb.p_ee_cback[xx] == p_cback)
        {
            evt_data.ee_register        = NFA_STATUS_OK;
            found                       = TRUE;
            break;
        }
    }

    /* If no matching callback, allocated an entry */
    if (!found)
    {
        for (xx = 0; xx < NFA_EE_MAX_CBACKS; xx++)
        {
            if (nfa_ee_cb.p_ee_cback[xx] == NULL)
            {
                nfa_ee_cb.p_ee_cback[xx]    = p_cback;
                evt_data.ee_register        = NFA_STATUS_OK;
                break;
            }
        }
    }
    /* This callback is verified (not NULL) in NFA_EeRegister() */
    (*p_cback)(NFA_EE_REGISTER_EVT, &evt_data);

    /* report NFCEE Discovery Request collected during booting up */
    nfa_ee_build_discover_req_evt (&evt_data.discover_req);
    (*p_cback)(NFA_EE_DISCOVER_REQ_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_deregister
**
** Description      process de-register command from user
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_deregister(tNFA_EE_MSG *p_data)
{
    tNFA_EE_CBACK *p_cback = NULL;
    int                 index  = p_data->deregister.index;
    tNFA_EE_CBACK_DATA  evt_data = {0};

    NFA_TRACE_DEBUG0 ("nfa_ee_api_deregister");
    p_cback = nfa_ee_cb.p_ee_cback[index];
    nfa_ee_cb.p_ee_cback[index] = NULL;
    if (p_cback)
        (*p_cback)(NFA_EE_DEREGISTER_EVT, &evt_data);
}


/*******************************************************************************
**
** Function         nfa_ee_api_mode_set
**
** Description      process mode set command from user
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_mode_set(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB *p_cb= p_data->cfg_hdr.p_cb;

    NFA_TRACE_DEBUG2 ("nfa_ee_api_mode_set() handle:0x%02x mode:%d", p_cb->nfcee_id, p_data->mode_set.mode);
    NFC_NfceeModeSet (p_cb->nfcee_id, p_data->mode_set.mode);
    /* set the NFA_EE_STATUS_PENDING bit to indicate the status is not exactly active */
    if (p_data->mode_set.mode == NFC_MODE_ACTIVATE)
        p_cb->ee_status = NFA_EE_STATUS_PENDING | NFA_EE_STATUS_ACTIVE;
    else
    {
        p_cb->ee_status = NFA_EE_STATUS_INACTIVE;
        /* DH should release the NCI connection before deactivate the NFCEE */
        if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
        {
            p_cb->conn_st = NFA_EE_CONN_ST_DISC;
            NFC_ConnClose(p_cb->conn_id);
        }
    }
    /* report the NFA_EE_MODE_SET_EVT status on the response from NFCC */
}



/*******************************************************************************
**
** Function         nfa_ee_api_set_tech_cfg
**
** Description      process set technology routing configuration from user
**                  start a 1 second timer. When the timer expires,
**                  the configuration collected in control block is sent to NFCC
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_set_tech_cfg(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB *p_cb = p_data->cfg_hdr.p_cb;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    tNFA_TECHNOLOGY_MASK    old_tech_switch_on   = p_cb->tech_switch_on;
    tNFA_TECHNOLOGY_MASK    old_tech_switch_off  = p_cb->tech_switch_off;
    tNFA_TECHNOLOGY_MASK    old_tech_battery_off = p_cb->tech_battery_off;
    UINT8                   old_size_mask        = p_cb->size_mask;

    if (   (p_cb->tech_switch_on == p_data->set_tech.technologies_switch_on)
        && (p_cb->tech_switch_off == p_data->set_tech.technologies_switch_off)
        && (p_cb->tech_battery_off == p_data->set_tech.technologies_battery_off))
    {
        /* nothing to change */
        evt_data.status = NFA_STATUS_OK;
        nfa_ee_report_event (p_cb->p_ee_cback, NFA_EE_SET_TECH_CFG_EVT, &evt_data);
        return;
    }

    p_cb->tech_switch_on   = p_data->set_tech.technologies_switch_on;
    p_cb->tech_switch_off  = p_data->set_tech.technologies_switch_off;
    p_cb->tech_battery_off = p_data->set_tech.technologies_battery_off;
    nfa_ee_update_route_size(p_cb);
    if (nfa_ee_total_lmrt_size() > NFC_GetLmrtSize())
    {
        NFA_TRACE_ERROR0 ("nfa_ee_api_set_tech_cfg Exceed LMRT size");
        evt_data.status        = NFA_STATUS_BUFFER_FULL;
        p_cb->tech_switch_on   = old_tech_switch_on;
        p_cb->tech_switch_off  = old_tech_switch_off;
        p_cb->tech_battery_off = old_tech_battery_off;
        p_cb->size_mask        = old_size_mask;
    }
    else
    {
        p_cb->ecb_flags       |= NFA_EE_ECB_FLAGS_TECH;
        if (p_cb->tech_switch_on | p_cb->tech_switch_off | p_cb->tech_battery_off)
        {
            /* if any technology in any power mode is configured, mark this entry as configured */
            nfa_ee_cb.ee_cfged    |= nfa_ee_ecb_to_mask(p_cb);
        }
        nfa_ee_start_timer();
    }
    nfa_ee_report_event (p_cb->p_ee_cback, NFA_EE_SET_TECH_CFG_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_set_proto_cfg
**
** Description      process set protocol routing configuration from user
**                  start a 1 second timer. When the timer expires,
**                  the configuration collected in control block is sent to NFCC
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_set_proto_cfg(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB *p_cb = p_data->cfg_hdr.p_cb;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    tNFA_PROTOCOL_MASK    old_proto_switch_on   = p_cb->proto_switch_on;
    tNFA_PROTOCOL_MASK    old_proto_switch_off  = p_cb->proto_switch_off;
    tNFA_PROTOCOL_MASK    old_proto_battery_off = p_cb->proto_battery_off;
    UINT8                   old_size_mask        = p_cb->size_mask;

    if (   (p_cb->proto_switch_on == p_data->set_proto.protocols_switch_on)
        && (p_cb->proto_switch_off == p_data->set_proto.protocols_switch_off)
        && (p_cb->proto_battery_off == p_data->set_proto.protocols_battery_off)  )
    {
        /* nothing to change */
        evt_data.status = NFA_STATUS_OK;
        nfa_ee_report_event (p_cb->p_ee_cback, NFA_EE_SET_PROTO_CFG_EVT, &evt_data);
        return;
    }

    p_cb->proto_switch_on       = p_data->set_proto.protocols_switch_on;
    p_cb->proto_switch_off      = p_data->set_proto.protocols_switch_off;
    p_cb->proto_battery_off     = p_data->set_proto.protocols_battery_off;
    nfa_ee_update_route_size(p_cb);
    if (nfa_ee_total_lmrt_size() > NFC_GetLmrtSize())
    {
        NFA_TRACE_ERROR0 ("nfa_ee_api_set_proto_cfg Exceed LMRT size");
        evt_data.status         = NFA_STATUS_BUFFER_FULL;
        p_cb->proto_switch_on   = old_proto_switch_on;
        p_cb->proto_switch_off  = old_proto_switch_off;
        p_cb->proto_battery_off = old_proto_battery_off;
        p_cb->size_mask         = old_size_mask;
    }
    else
    {
        p_cb->ecb_flags            |= NFA_EE_ECB_FLAGS_PROTO;
        if (p_cb->proto_switch_on | p_cb->proto_switch_off | p_cb->proto_battery_off)
        {
            /* if any protocol in any power mode is configured, mark this entry as configured */
            nfa_ee_cb.ee_cfged         |= nfa_ee_ecb_to_mask(p_cb);
        }
        nfa_ee_start_timer();
    }
    nfa_ee_report_event (p_cb->p_ee_cback, NFA_EE_SET_PROTO_CFG_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_add_aid
**
** Description      process add an AID routing configuration from user
**                  start a 1 second timer. When the timer expires,
**                  the configuration collected in control block is sent to NFCC
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_add_aid(tNFA_EE_MSG *p_data)
{
    tNFA_EE_API_ADD_AID *p_add = &p_data->add_aid;
    tNFA_EE_ECB *p_cb = p_data->cfg_hdr.p_cb;
    tNFA_EE_ECB *p_chk_cb;
    UINT8   *p, *p_start;
    int     len, len_needed;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    int offset = 0, entry = 0;
    UINT16  new_size;

    nfa_ee_trace_aid ("nfa_ee_api_add_aid", p_cb->nfcee_id, p_add->aid_len, p_add->p_aid);
    p_chk_cb = nfa_ee_find_aid_offset(p_add->aid_len, p_add->p_aid, &offset, &entry);
    if (p_chk_cb)
    {
        NFA_TRACE_DEBUG0 ("nfa_ee_api_add_aid The AID entry is already in the database");
        if (p_chk_cb == p_cb)
        {
            p_cb->aid_rt_info[entry]    |= NFA_EE_AE_ROUTE;
            new_size = nfa_ee_total_lmrt_size();
            if (new_size > NFC_GetLmrtSize())
            {
                NFA_TRACE_ERROR1 ("Exceed LMRT size:%d (add ROUTE)", new_size);
                evt_data.status             = NFA_STATUS_BUFFER_FULL;
                p_cb->aid_rt_info[entry]    &= ~NFA_EE_AE_ROUTE;
            }
            else
            {
                p_cb->aid_pwr_cfg[entry]     = p_add->power_state;
            }
        }
        else
        {
            NFA_TRACE_ERROR1 ("The AID entry is already in the database for different NFCEE ID:0x%02x", p_chk_cb->nfcee_id);
            evt_data.status = NFA_STATUS_SEMANTIC_ERROR;
        }
    }
    else
    {
        /* Find the total length so far */
        len = nfa_ee_find_total_aid_len(p_cb, 0);

        /* make sure the control block has enough room to hold this entry */
        len_needed  = p_add->aid_len + 2; /* tag/len */

        if ((len_needed + len) > NFA_EE_MAX_AID_CFG_LEN)
        {
            NFA_TRACE_ERROR3 ("Exceed capacity: (len_needed:%d + len:%d) > NFA_EE_MAX_AID_CFG_LEN:%d", len_needed, len, NFA_EE_MAX_AID_CFG_LEN);
            evt_data.status = NFA_STATUS_BUFFER_FULL;
        }
        else if (p_cb->aid_entries < NFA_EE_MAX_AID_ENTRIES)
        {
            new_size = nfa_ee_total_lmrt_size() + 4 + p_add->aid_len; /* 4 = 1 (tag) + 1 (len) + 1(nfcee_id) + 1(power cfg) */
            if (new_size > NFC_GetLmrtSize())
            {
                NFA_TRACE_ERROR1 ("Exceed LMRT size:%d", new_size);
                evt_data.status        = NFA_STATUS_BUFFER_FULL;
            }
            else
            {
                /* add AID */
                p_cb->aid_pwr_cfg[p_cb->aid_entries]    = p_add->power_state;
                p_cb->aid_rt_info[p_cb->aid_entries]    = NFA_EE_AE_ROUTE;
                p       = p_cb->aid_cfg + len;
                p_start = p;
                *p++    = NFA_EE_AID_CFG_TAG_NAME;
                *p++    = p_add->aid_len;
                memcpy(p, p_add->p_aid, p_add->aid_len);
                p      += p_add->aid_len;

                p_cb->aid_len[p_cb->aid_entries++]     = (UINT8)(p - p_start);
            }
        }
        else
        {
            NFA_TRACE_ERROR1 ("Exceed NFA_EE_MAX_AID_ENTRIES:%d", NFA_EE_MAX_AID_ENTRIES);
            evt_data.status = NFA_STATUS_BUFFER_FULL;
        }
    }

    if (evt_data.status == NFA_STATUS_OK)
    {
        /* mark AID changed */
        p_cb->ecb_flags                       |= NFA_EE_ECB_FLAGS_AID;
        nfa_ee_cb.ee_cfged                    |= nfa_ee_ecb_to_mask(p_cb);
        nfa_ee_update_route_aid_size(p_cb);
        nfa_ee_start_timer();
    }
    NFA_TRACE_DEBUG2 ("status:%d ee_cfged:0x%02x ",evt_data.status, nfa_ee_cb.ee_cfged);
    /* report the status of this operation */
    nfa_ee_report_event (p_cb->p_ee_cback, NFA_EE_ADD_AID_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_remove_aid
**
** Description      process remove an AID routing configuration from user
**                  start a 1 second timer. When the timer expires,
**                  the configuration collected in control block is sent to NFCC
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_remove_aid(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB  *p_cb;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    int offset = 0, entry = 0, len;
    int rest_len;
    tNFA_EE_CBACK *p_cback = NULL;

    nfa_ee_trace_aid ("nfa_ee_api_remove_aid", 0, p_data->rm_aid.aid_len, p_data->rm_aid.p_aid);
    p_cb = nfa_ee_find_aid_offset(p_data->rm_aid.aid_len, p_data->rm_aid.p_aid, &offset, &entry);
    if (p_cb && p_cb->aid_entries)
    {
        NFA_TRACE_DEBUG2 ("aid_rt_info[%d]: 0x%02x", entry, p_cb->aid_rt_info[entry]);
        /* mark routing and VS changed */
        if (p_cb->aid_rt_info[entry] & NFA_EE_AE_ROUTE)
            p_cb->ecb_flags         |= NFA_EE_ECB_FLAGS_AID;

        if (p_cb->aid_rt_info[entry] & NFA_EE_AE_VS)
            p_cb->ecb_flags         |= NFA_EE_ECB_FLAGS_VS;

        /* remove the aid */
        if ((entry+1) < p_cb->aid_entries)
        {
            /* not the last entry, move the aid entries in control block */
            /* Find the total len from the next entry to the last one */
            rest_len = nfa_ee_find_total_aid_len(p_cb, entry + 1);

            len = p_cb->aid_len[entry];
            NFA_TRACE_DEBUG2 ("nfa_ee_api_remove_aid len:%d, rest_len:%d", len, rest_len);
            GKI_shiftup (&p_cb->aid_cfg[offset], &p_cb->aid_cfg[offset+ len], rest_len);
            rest_len = p_cb->aid_entries - entry;
            GKI_shiftup (&p_cb->aid_len[entry], &p_cb->aid_len[entry + 1], rest_len);
            GKI_shiftup (&p_cb->aid_pwr_cfg[entry], &p_cb->aid_pwr_cfg[entry + 1], rest_len);
            GKI_shiftup (&p_cb->aid_rt_info[entry], &p_cb->aid_rt_info[entry + 1], rest_len);
        }
        /* else the last entry, just reduce the aid_entries by 1 */
        p_cb->aid_entries--;
        nfa_ee_cb.ee_cfged      |= nfa_ee_ecb_to_mask(p_cb);
        nfa_ee_update_route_aid_size(p_cb);
        nfa_ee_start_timer();
        /* report NFA_EE_REMOVE_AID_EVT to the callback associated the NFCEE */
        p_cback = p_cb->p_ee_cback;
    }
    else
    {
        NFA_TRACE_ERROR0 ("nfa_ee_api_remove_aid The AID entry is not in the database");
        evt_data.status = NFA_STATUS_INVALID_PARAM;
    }
    nfa_ee_report_event (p_cback, NFA_EE_REMOVE_AID_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_lmrt_size
**
** Description      Reports the remaining size in the Listen Mode Routing Table
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_lmrt_size(tNFA_EE_MSG *p_data)
{
    tNFA_EE_CBACK_DATA  evt_data = {0};
    UINT16 total_size = NFC_GetLmrtSize();

    evt_data.size       = total_size - nfa_ee_total_lmrt_size();
    NFA_TRACE_DEBUG2 ("nfa_ee_api_lmrt_size total size:%d remaining size:%d", total_size, evt_data.size);

    nfa_ee_report_event (NULL, NFA_EE_REMAINING_SIZE_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_update_now
**
** Description      Initiates connection creation process to the given NFCEE
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_update_now(tNFA_EE_MSG *p_data)
{
    tNFA_EE_CBACK_DATA  evt_data;

    if (nfa_ee_cb.ee_wait_evt & NFA_EE_WAIT_UPDATE_ALL)
    {
        NFA_TRACE_ERROR2 ("nfa_ee_api_update_now still waiting for update complete ee_wait_evt:0x%x wait_rsp:%d", nfa_ee_cb.ee_wait_evt, nfa_ee_cb.wait_rsp);
        evt_data.status       = NFA_STATUS_SEMANTIC_ERROR;
        nfa_ee_report_event (NULL, NFA_EE_UPDATED_EVT, &evt_data);
        return;
    }
    nfa_sys_stop_timer(&nfa_ee_cb.timer);
    nfa_ee_cb.ee_cfged  |= NFA_EE_CFGED_UPDATE_NOW;
    nfa_ee_rout_timeout(p_data);
}

/*******************************************************************************
**
** Function         nfa_ee_api_connect
**
** Description      Initiates connection creation process to the given NFCEE
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_connect(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB  *p_cb = p_data->connect.p_cb;
    int xx;
    tNFA_EE_CBACK_DATA  evt_data = {0};

    evt_data.connect.status       = NFA_STATUS_FAILED;
    if (p_cb->conn_st == NFA_EE_CONN_ST_NONE)
    {
        for (xx = 0; xx < p_cb->num_interface; xx++)
        {
            if (p_data->connect.ee_interface == p_cb->ee_interface[xx])
            {
                p_cb->p_ee_cback        = p_data->connect.p_cback;
                p_cb->conn_st           = NFA_EE_CONN_ST_WAIT;
                p_cb->use_interface     = p_data->connect.ee_interface;
                evt_data.connect.status = NFC_ConnCreate(NCI_DEST_TYPE_NFCEE, p_data->connect.nfcee_id,
                    p_data->connect.ee_interface, nfa_ee_conn_cback);
                /* report the NFA_EE_CONNECT_EVT status on the response from NFCC */
                break;
            }
        }
    }

    if (evt_data.connect.status != NCI_STATUS_OK)
    {
        evt_data.connect.ee_handle    = (tNFA_HANDLE)p_data->connect.nfcee_id | NFA_HANDLE_GROUP_EE;
        evt_data.connect.status       = NFA_STATUS_INVALID_PARAM;
        evt_data.connect.ee_interface = p_data->connect.ee_interface;
        nfa_ee_report_event (p_data->connect.p_cback, NFA_EE_CONNECT_EVT, &evt_data);
    }
}

/*******************************************************************************
**
** Function         nfa_ee_api_send_data
**
** Description      Send the given data packet to the given NFCEE
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_send_data(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB  *p_cb = p_data->send_data.p_cb;
    BT_HDR *p_pkt;
    UINT16 size = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE + p_data->send_data.data_len + BT_HDR_SIZE;
    UINT8  *p;
    tNFA_STATUS status = NFA_STATUS_FAILED;

    if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
    {
        p_pkt = (BT_HDR *)GKI_getbuf(size);
        if (p_pkt)
        {
            p_pkt->offset   = NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE;
            p_pkt->len      = p_data->send_data.data_len;
            p               = (UINT8 *)(p_pkt+1) + p_pkt->offset;
            memcpy(p, p_data->send_data.p_data, p_pkt->len);
            NFC_SendData (p_cb->conn_id, p_pkt);
        }
        else
        {
            nfa_ee_report_event( p_cb->p_ee_cback, NFA_EE_NO_MEM_ERR_EVT, (tNFA_EE_CBACK_DATA *)&status);
        }
    }
    else
    {
        nfa_ee_report_event( p_cb->p_ee_cback, NFA_EE_NO_CB_ERR_EVT, (tNFA_EE_CBACK_DATA *)&status);
    }
}

/*******************************************************************************
**
** Function         nfa_ee_api_disconnect
**
** Description      Initiates closing of the connection to the given NFCEE
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_api_disconnect(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB  *p_cb = p_data->disconnect.p_cb;
    tNFA_EE_CBACK_DATA  evt_data = {0};

    if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
    {
        p_cb->conn_st = NFA_EE_CONN_ST_DISC;
        NFC_ConnClose(p_cb->conn_id);
    }
    evt_data.handle = (tNFA_HANDLE)p_cb->nfcee_id | NFA_HANDLE_GROUP_EE;
    nfa_ee_report_event(p_cb->p_ee_cback, NFA_EE_DISCONNECT_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_report_disc_done
**
** Description      Process the callback for NFCEE discovery response
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_report_disc_done(BOOLEAN notify_enable_done)
{
    tNFA_EE_CBACK           *p_cback;
    tNFA_EE_CBACK_DATA      evt_data = {0};

    NFA_TRACE_DEBUG3("nfa_ee_report_disc_done() em_state:%d num_ee_expecting:%d notify_enable_done:%d", nfa_ee_cb.em_state, nfa_ee_cb.num_ee_expecting, notify_enable_done);
    if (nfa_ee_cb.num_ee_expecting == 0)
    {
        if (notify_enable_done)
        {
            if (nfa_ee_cb.em_state == NFA_EE_EM_STATE_INIT_DONE)
            {
                nfa_sys_cback_notify_enable_complete (NFA_ID_EE);
                if (nfa_ee_cb.p_enable_cback)
                    (*nfa_ee_cb.p_enable_cback)(NFA_EE_DISC_STS_ON);
            }
            else if ((nfa_ee_cb.em_state == NFA_EE_EM_STATE_RESTORING) && (nfa_ee_cb.ee_flags & NFA_EE_FLAG_NOTIFY_HCI) )
            {
                nfa_ee_cb.ee_flags   &= ~NFA_EE_FLAG_NOTIFY_HCI;
                if (nfa_ee_cb.p_enable_cback)
                    (*nfa_ee_cb.p_enable_cback)(NFA_EE_DISC_STS_ON);
            }
        }


        if (nfa_ee_cb.p_ee_disc_cback)
        {
            /* notify API callback */
            p_cback                         = nfa_ee_cb.p_ee_disc_cback;
            nfa_ee_cb.p_ee_disc_cback       = NULL;
            evt_data.status                         = NFA_STATUS_OK;
            evt_data.ee_discover.num_ee             = NFA_EE_MAX_EE_SUPPORTED;
            NFA_EeGetInfo(&evt_data.ee_discover.num_ee, evt_data.ee_discover.ee_info);
            nfa_ee_report_event (p_cback, NFA_EE_DISCOVER_EVT, &evt_data);
        }
    }
}

/*******************************************************************************
**
** Function         nfa_ee_restore_ntf_done
**
** Description      check if any ee_status still has NFA_EE_STATUS_PENDING bit
**
** Returns          TRUE, if all NFA_EE_STATUS_PENDING bits are removed
**
*******************************************************************************/
BOOLEAN nfa_ee_restore_ntf_done(void)
{
    tNFA_EE_ECB     *p_cb;
    BOOLEAN         is_done = TRUE;
    int             xx;

    p_cb = nfa_ee_cb.ecb;
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
    {
        if ((p_cb->nfcee_id != NFA_EE_INVALID) && (p_cb->ee_old_status & NFA_EE_STATUS_RESTORING))
        {
            is_done = FALSE;
            break;
        }
    }
    return is_done;
}

/*******************************************************************************
**
** Function         nfa_ee_remove_pending
**
** Description      check if any ee_status still has NFA_EE_STATUS_RESTORING bit
**
** Returns          TRUE, if all NFA_EE_STATUS_RESTORING bits are removed
**
*******************************************************************************/
static void nfa_ee_remove_pending(void)
{
    tNFA_EE_ECB     *p_cb;
    tNFA_EE_ECB     *p_cb_n, *p_cb_end;
    int             xx, num_removed = 0;
    int             first_removed = NFA_EE_MAX_EE_SUPPORTED;

    p_cb = nfa_ee_cb.ecb;
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
    {
        if ((p_cb->nfcee_id != NFA_EE_INVALID) && (p_cb->ee_status & NFA_EE_STATUS_RESTORING))
        {
            p_cb->nfcee_id  = NFA_EE_INVALID;
            num_removed ++;
            if (first_removed == NFA_EE_MAX_EE_SUPPORTED)
                first_removed   = xx;
        }
    }

    NFA_TRACE_DEBUG3("nfa_ee_remove_pending() cur_ee:%d, num_removed:%d first_removed:%d", nfa_ee_cb.cur_ee, num_removed, first_removed);
    if (num_removed && (first_removed != (nfa_ee_cb.cur_ee - num_removed)))
    {
        /* if the removes ECB entried are not at the end, move the entries up */
        p_cb_end = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee - 1];
        p_cb = &nfa_ee_cb.ecb[first_removed];
        for (p_cb_n = p_cb + 1; p_cb_n <= p_cb_end;)
        {
            while ((p_cb_n->nfcee_id == NFA_EE_INVALID) && (p_cb_n <= p_cb_end))
            {
                p_cb_n++;
            }

            if (p_cb_n <= p_cb_end)
            {
                memcpy(p_cb, p_cb_n, sizeof(tNFA_EE_ECB));
                p_cb_n->nfcee_id = NFA_EE_INVALID;
            }
            p_cb++;
            p_cb_n++;
        }
    }
    nfa_ee_cb.cur_ee -= (UINT8)num_removed;
}


/*******************************************************************************
**
** Function         nfa_ee_nci_disc_rsp
**
** Description      Process the callback for NFCEE discovery response
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_disc_rsp(tNFA_EE_MSG *p_data)
{
    tNFC_NFCEE_DISCOVER_REVT    *p_evt = p_data->disc_rsp.p_data;
    tNFA_EE_ECB              *p_cb;
    UINT8   xx;
    UINT8   num_nfcee = p_evt->num_nfcee;
    BOOLEAN notify_enable_done = FALSE;

    NFA_TRACE_DEBUG3("nfa_ee_nci_disc_rsp() em_state:%d cur_ee:%d, num_nfcee:%d", nfa_ee_cb.em_state, nfa_ee_cb.cur_ee, num_nfcee);
    switch (nfa_ee_cb.em_state)
    {
    case NFA_EE_EM_STATE_INIT:
        nfa_ee_cb.cur_ee            = 0;
        nfa_ee_cb.num_ee_expecting  = 0;
        if (num_nfcee == 0)
        {
            nfa_ee_cb.em_state = NFA_EE_EM_STATE_INIT_DONE;
            notify_enable_done = TRUE;
            if (p_evt->status != NFC_STATUS_OK)
            {
                nfa_sys_stop_timer(&nfa_ee_cb.discv_timer);
            }
        }
        break;

    case NFA_EE_EM_STATE_INIT_DONE:
        if (num_nfcee)
        {
            /* if this is initiated by api function,
             * check if the number of NFCEE expected is more than what's currently in CB */
            if (num_nfcee > NFA_EE_MAX_EE_SUPPORTED)
                num_nfcee = NFA_EE_MAX_EE_SUPPORTED;
            if (nfa_ee_cb.cur_ee < num_nfcee)
            {
                p_cb = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee];
                for (xx = nfa_ee_cb.cur_ee; xx < num_nfcee; xx++, p_cb++)
                {
                    /* mark the new entries as a new one */
                    p_cb->nfcee_id = NFA_EE_INVALID;
                }
            }
            nfa_ee_cb.cur_ee = num_nfcee;
        }
        break;

    case NFA_EE_EM_STATE_RESTORING:
        if (num_nfcee == 0)
        {
            nfa_ee_cb.em_state = NFA_EE_EM_STATE_INIT_DONE;
            nfa_ee_remove_pending();
            nfa_ee_check_restore_complete();
            if (p_evt->status != NFC_STATUS_OK)
            {
                nfa_sys_stop_timer(&nfa_ee_cb.discv_timer);
            }
        }
        break;
    }

    if (p_evt->status == NFC_STATUS_OK)
    {
        nfa_ee_cb.num_ee_expecting = p_evt->num_nfcee;
        if (nfa_ee_cb.num_ee_expecting > NFA_EE_MAX_EE_SUPPORTED)
        {
            NFA_TRACE_ERROR2 ("NFA-EE num_ee_expecting:%d > max:%d", nfa_ee_cb.num_ee_expecting, NFA_EE_MAX_EE_SUPPORTED);
        }
    }
    nfa_ee_report_disc_done(notify_enable_done);
    NFA_TRACE_DEBUG3("nfa_ee_nci_disc_rsp() em_state:%d cur_ee:%d num_ee_expecting:%d", nfa_ee_cb.em_state, nfa_ee_cb.cur_ee, nfa_ee_cb.num_ee_expecting);
}

/*******************************************************************************
**
** Function         nfa_ee_nci_disc_ntf
**
** Description      Process the callback for NFCEE discovery notification
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_disc_ntf(tNFA_EE_MSG *p_data)
{
    tNFC_NFCEE_INFO_REVT    *p_ee = p_data->disc_ntf.p_data;
    tNFA_EE_ECB             *p_cb = NULL;
    BOOLEAN                 notify_enable_done = FALSE;
    BOOLEAN                 notify_new_ee = FALSE;
    tNFA_EE_CBACK_DATA      evt_data = {0};
    tNFA_EE_INFO            *p_info;
    tNFA_EE_EM_STATE        new_em_state = NFA_EE_EM_STATE_MAX;

    NFA_TRACE_DEBUG4("nfa_ee_nci_disc_ntf() em_state:%d ee_flags:0x%x cur_ee:%d num_ee_expecting:%d", nfa_ee_cb.em_state, nfa_ee_cb.ee_flags, nfa_ee_cb.cur_ee, nfa_ee_cb.num_ee_expecting);
    if (nfa_ee_cb.num_ee_expecting)
    {
        nfa_ee_cb.num_ee_expecting--;
        if ((nfa_ee_cb.num_ee_expecting == 0) && (nfa_ee_cb.p_ee_disc_cback != NULL))
        {
            /* Discovery triggered by API function */
            NFC_NfceeDiscover(FALSE);
        }
    }
    switch (nfa_ee_cb.em_state)
    {
    case NFA_EE_EM_STATE_INIT:
        if (nfa_ee_cb.cur_ee < NFA_EE_MAX_EE_SUPPORTED)
        {
            /* the cb can collect up to NFA_EE_MAX_EE_SUPPORTED ee_info */
            p_cb = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee++];
        }

        if (nfa_ee_cb.num_ee_expecting == 0)
        {
            /* notify init_done callback */
            nfa_ee_cb.em_state = NFA_EE_EM_STATE_INIT_DONE;
            notify_enable_done = TRUE;
        }
        break;

    case NFA_EE_EM_STATE_INIT_DONE:
        p_cb = nfa_ee_find_ecb (p_ee->nfcee_id);
        if (p_cb == NULL)
        {
            /* the NFCEE ID is not in the last NFCEE discovery
             * maybe it's a new one */
            p_cb = nfa_ee_find_ecb (NFA_EE_INVALID);
            if (p_cb)
            {
                nfa_ee_cb.cur_ee++;
                notify_new_ee = TRUE;
            }
        }
        else if (p_cb->ecb_flags & NFA_EE_ECB_FLAGS_ORDER)
        {
            nfa_ee_cb.cur_ee++;
            notify_new_ee = TRUE;
        }
        else
        {
            NFA_TRACE_DEBUG3 ("cur_ee:%d ecb_flags=0x%02x  ee_status=0x%x", nfa_ee_cb.cur_ee, p_cb->ecb_flags, p_cb->ee_status);
        }
        break;

    case NFA_EE_EM_STATE_RESTORING:
        p_cb = nfa_ee_find_ecb (p_ee->nfcee_id);
        if (p_cb == NULL)
        {
            /* the NFCEE ID is not in the last NFCEE discovery
             * maybe it's a new one */
            p_cb = nfa_ee_find_ecb (NFA_EE_INVALID);
            if (p_cb)
            {
                nfa_ee_cb.cur_ee++;
                notify_new_ee = TRUE;
            }
        }
        if (nfa_ee_cb.num_ee_expecting == 0)
        {
            /* notify init_done callback */
            notify_enable_done = TRUE;
            if (nfa_ee_restore_ntf_done())
            {
                new_em_state       = NFA_EE_EM_STATE_INIT_DONE;
            }
        }
        break;
    }
    NFA_TRACE_DEBUG1 ("nfa_ee_nci_disc_ntf cur_ee:%d", nfa_ee_cb.cur_ee);

    if (p_cb)
    {
        p_cb->nfcee_id      = p_ee->nfcee_id;
        p_cb->ee_status     = p_ee->ee_status;
        p_cb->num_interface = p_ee->num_interface;
        memcpy(p_cb->ee_interface, p_ee->ee_interface, p_ee->num_interface);
        p_cb->num_tlvs      = p_ee->num_tlvs;
        memcpy(p_cb->ee_tlv, p_ee->ee_tlv, p_ee->num_tlvs * sizeof(tNFA_EE_TLV));

        if (nfa_ee_cb.em_state == NFA_EE_EM_STATE_RESTORING)
        {
            /* NCI spec says: An NFCEE_DISCOVER_NTF that contains a Protocol type of "HCI Access"
             * SHALL NOT contain any other additional Protocol
             * i.e. check only first supported NFCEE interface is HCI access */
            /* NFA_HCI module handles restoring configurations for HCI access */
            if (p_cb->ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS)
            {
                if ((nfa_ee_cb.ee_flags & NFA_EE_FLAG_WAIT_HCI) == 0)
                {
                    nfa_ee_restore_one_ecb (p_cb);
                }
                /* else wait for NFA-HCI module to restore the HCI network information before enabling the NFCEE */
            }
        }

        if ((nfa_ee_cb.p_ee_disc_cback == NULL) && (notify_new_ee == TRUE))
        {
            if (nfa_dm_is_active() && (p_cb->ee_status != NFA_EE_STATUS_REMOVED))
            {
                /* report this NFA_EE_NEW_EE_EVT only after NFA_DM_ENABLE_EVT is reported */
                p_info                  = &evt_data.new_ee;
                p_info->ee_handle       = NFA_HANDLE_GROUP_EE | (tNFA_HANDLE)p_cb->nfcee_id;
                p_info->ee_status       = p_cb->ee_status;
                p_info->num_interface   = p_cb->num_interface;
                p_info->num_tlvs        = p_cb->num_tlvs;
                memcpy(p_info->ee_interface, p_cb->ee_interface, p_cb->num_interface);
                memcpy(p_info->ee_tlv, p_cb->ee_tlv, p_cb->num_tlvs * sizeof(tNFA_EE_TLV));
                nfa_ee_report_event (NULL, NFA_EE_NEW_EE_EVT, &evt_data);
            }
        }
        else
            nfa_ee_report_disc_done(notify_enable_done);

        if (p_cb->ecb_flags & NFA_EE_ECB_FLAGS_ORDER)
        {
            NFA_TRACE_DEBUG0 ("NFA_EE_ECB_FLAGS_ORDER");
            p_cb->ecb_flags &= ~NFA_EE_ECB_FLAGS_ORDER;
            nfa_ee_report_discover_req_evt();
        }

    }

    if (new_em_state != NFA_EE_EM_STATE_MAX)
    {
        nfa_ee_cb.em_state = new_em_state;
        nfa_ee_check_restore_complete();
    }

    if ((nfa_ee_cb.cur_ee == nfa_ee_max_ee_cfg) && (nfa_ee_cb.em_state == NFA_EE_EM_STATE_INIT_DONE) )
    {
        if (nfa_ee_cb.discv_timer.in_use)
        {
            nfa_sys_stop_timer (&nfa_ee_cb.discv_timer);
            p_data->hdr.event = NFA_EE_DISCV_TIMEOUT_EVT;
            nfa_ee_evt_hdlr((BT_HDR *)p_data);
        }
    }
}

/*******************************************************************************
**
** Function         nfa_ee_check_restore_complete
**
** Description      Check if restore the NFA-EE related configuration to the
**                  state prior to low power mode is complete.
**                  If complete, notify sys.
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_check_restore_complete(void)
{
    UINT32  xx;
    tNFA_EE_ECB     *p_cb;
    BOOLEAN         proc_complete = TRUE;

    p_cb = nfa_ee_cb.ecb;
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
    {
        if (p_cb->ecb_flags & NFA_EE_ECB_FLAGS_RESTORE)
        {
            /* NFA_HCI module handles restoring configurations for HCI access.
             * ignore the restoring status for HCI Access */
            if (p_cb->ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS)
            {
                proc_complete = FALSE;
                break;
            }
        }
    }

    NFA_TRACE_DEBUG2 ("nfa_ee_check_restore_complete nfa_ee_cb.ee_cfg_sts:0x%02x proc_complete:%d", nfa_ee_cb.ee_cfg_sts, proc_complete);
    if (proc_complete)
    {
        /* update routing table when NFA_EE_ROUT_TIMEOUT_EVT is received */
        if (nfa_ee_cb.ee_cfg_sts & NFA_EE_STS_PREV_ROUTING)
            nfa_ee_api_update_now(NULL);

        nfa_ee_cb.em_state = NFA_EE_EM_STATE_INIT_DONE;
        nfa_sys_cback_notify_nfcc_power_mode_proc_complete (NFA_ID_EE);
    }
}

/*******************************************************************************
**
** Function         nfa_ee_build_discover_req_evt
**
** Description      Build NFA_EE_DISCOVER_REQ_EVT for all active NFCEE
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_build_discover_req_evt (tNFA_EE_DISCOVER_REQ *p_evt_data)
{
    tNFA_EE_ECB           *p_cb;
    tNFA_EE_DISCOVER_INFO *p_info;
    UINT8                 xx;

    if (!p_evt_data)
        return;

    p_evt_data->num_ee = 0;
    p_cb               = nfa_ee_cb.ecb;
    p_info             = p_evt_data->ee_disc_info;

    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
    {
        if (  (p_cb->ee_status & NFA_EE_STATUS_INT_MASK)
            ||(p_cb->ee_status != NFA_EE_STATUS_ACTIVE)
            ||((p_cb->ecb_flags & NFA_EE_ECB_FLAGS_DISC_REQ) == 0)  )
        {
            continue;
        }
        p_info->ee_handle       = (tNFA_HANDLE)p_cb->nfcee_id | NFA_HANDLE_GROUP_EE;
        p_info->la_protocol     = p_cb->la_protocol;
        p_info->lb_protocol     = p_cb->lb_protocol;
        p_info->lf_protocol     = p_cb->lf_protocol;
        p_info->lbp_protocol    = p_cb->lbp_protocol;
        p_evt_data->num_ee++;
        p_info++;

        NFA_TRACE_DEBUG6 ("[%d] ee_handle:0x%x, listen protocol A:%d, B:%d, F:%d, BP:%d",
                          p_evt_data->num_ee, p_cb->nfcee_id,
                          p_cb->la_protocol, p_cb->lb_protocol, p_cb->lf_protocol, p_cb->lbp_protocol);
    }

    p_evt_data->status     = NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_ee_report_discover_req_evt
**
** Description      Report NFA_EE_DISCOVER_REQ_EVT for all active NFCEE
**
** Returns          void
**
*******************************************************************************/
static void nfa_ee_report_discover_req_evt(void)
{
    tNFA_EE_DISCOVER_REQ    evt_data;

    if (nfa_ee_cb.p_enable_cback)
        (*nfa_ee_cb.p_enable_cback) (NFA_EE_DISC_STS_REQ);


    /* if this is restoring NFCC */
    if (!nfa_dm_is_active ())
    {
        NFA_TRACE_DEBUG0 ("nfa_ee_report_discover_req_evt DM is not active");
        return;
    }

    nfa_ee_build_discover_req_evt (&evt_data);
    nfa_ee_report_event(NULL, NFA_EE_DISCOVER_REQ_EVT, (tNFA_EE_CBACK_DATA *)&evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_nci_mode_set_rsp
**
** Description      Process the result for NFCEE ModeSet response
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_mode_set_rsp(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB *p_cb;
    tNFA_EE_MODE_SET    mode_set;
    tNFC_NFCEE_MODE_SET_REVT    *p_rsp = p_data->mode_set_rsp.p_data;

    NFA_TRACE_DEBUG2 ("nfa_ee_nci_mode_set_rsp() handle:0x%02x mode:%d", p_rsp->nfcee_id, p_rsp->mode);
    p_cb = nfa_ee_find_ecb (p_rsp->nfcee_id);
    if (p_cb == NULL)
    {
        NFA_TRACE_ERROR1 ("nfa_ee_nci_mode_set_rsp() Can not find cb for handle:0x%02x", p_rsp->nfcee_id);
        return;
    }

    /* update routing table and vs on mode change */
    nfa_ee_start_timer();

    if (p_rsp->status == NFA_STATUS_OK)
    {

        if (p_rsp->mode == NFA_EE_MD_ACTIVATE)
        {
            p_cb->ee_status = NFC_NFCEE_STATUS_ACTIVE;
        }
        else
        {
            if (p_cb->tech_switch_on | p_cb->tech_switch_off | p_cb->tech_battery_off |
                p_cb->proto_switch_on| p_cb->proto_switch_off| p_cb->proto_battery_off |
                p_cb->aid_entries)
            {
                /* this NFCEE still has configuration when deactivated. clear the configuration */
                nfa_ee_cb.ee_cfged  &= ~nfa_ee_ecb_to_mask(p_cb);
                nfa_ee_cb.ee_cfg_sts|= NFA_EE_STS_CHANGED_ROUTING;
                NFA_TRACE_DEBUG0("deactivating/still configured. Force update");
            }
            p_cb->tech_switch_on    = p_cb->tech_switch_off = p_cb->tech_battery_off    = 0;
            p_cb->proto_switch_on   = p_cb->proto_switch_off= p_cb->proto_battery_off   = 0;
            p_cb->aid_entries       = 0;
            p_cb->ee_status = NFC_NFCEE_STATUS_INACTIVE;
        }
    }
    NFA_TRACE_DEBUG4 ("status:%d ecb_flags  :0x%02x ee_cfged:0x%02x ee_status:%d",
        p_rsp->status, p_cb->ecb_flags  , nfa_ee_cb.ee_cfged, p_cb->ee_status);
    if (p_cb->ecb_flags   & NFA_EE_ECB_FLAGS_RESTORE)
    {
        if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
        {
            /* NFA_HCI module handles restoring configurations for HCI access */
            if (p_cb->ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS)
            {
                NFC_ConnCreate(NCI_DEST_TYPE_NFCEE, p_cb->nfcee_id,  p_cb->use_interface, nfa_ee_conn_cback);
            }
        }
        else
        {
            p_cb->ecb_flags   &= ~NFA_EE_ECB_FLAGS_RESTORE;
            nfa_ee_check_restore_complete();
        }
    }
    else
    {
        mode_set.status     = p_rsp->status;
        mode_set.ee_handle  = (tNFA_HANDLE)p_rsp->nfcee_id | NFA_HANDLE_GROUP_EE;
        mode_set.ee_status  = p_cb->ee_status;

        nfa_ee_report_event(p_cb->p_ee_cback, NFA_EE_MODE_SET_EVT, (tNFA_EE_CBACK_DATA *)&mode_set);

        if ((p_cb->ee_status == NFC_NFCEE_STATUS_INACTIVE)
            || (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE))
        {
            /* Report NFA_EE_DISCOVER_REQ_EVT for all active NFCEE */
            nfa_ee_report_discover_req_evt();
        }
    }
}

/*******************************************************************************
**
** Function         nfa_ee_report_update_evt
**
** Description      Check if need to report NFA_EE_UPDATED_EVT
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_report_update_evt (void)
{
    tNFA_EE_CBACK_DATA  evt_data;

    NFA_TRACE_DEBUG2 ("nfa_ee_report_update_evt ee_wait_evt:0x%x wait_rsp:%d", nfa_ee_cb.ee_wait_evt, nfa_ee_cb.wait_rsp);
    if (nfa_ee_cb.wait_rsp == 0)
    {
        nfa_ee_cb.ee_wait_evt &= ~NFA_EE_WAIT_UPDATE_RSP;

        if (nfa_ee_cb.ee_wait_evt & NFA_EE_WAIT_UPDATE)
        {
            nfa_ee_cb.ee_wait_evt &= ~NFA_EE_WAIT_UPDATE;
            /* finished updating NFCC; report NFA_EE_UPDATED_EVT now */
            evt_data.status       = NFA_STATUS_OK;
            nfa_ee_report_event (NULL, NFA_EE_UPDATED_EVT, &evt_data);
        }
    }
}

/*******************************************************************************
**
** Function         nfa_ee_nci_wait_rsp
**
** Description      Process the result for NCI response
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_wait_rsp(tNFA_EE_MSG *p_data)
{
    tNFA_EE_NCI_WAIT_RSP *p_rsp = &p_data->wait_rsp;

    NFA_TRACE_DEBUG2 ("nfa_ee_nci_wait_rsp() ee_wait_evt:0x%x wait_rsp:%d", nfa_ee_cb.ee_wait_evt, nfa_ee_cb.wait_rsp);
    if (nfa_ee_cb.wait_rsp)
    {
        if (p_rsp->opcode == NCI_MSG_RF_SET_ROUTING)
            nfa_ee_cb.wait_rsp--;
    }
    nfa_ee_report_update_evt ();
}

/*******************************************************************************
**
** Function         nfa_ee_nci_conn
**
** Description      process the connection callback events
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_conn(tNFA_EE_MSG *p_data)
{
    tNFA_EE_ECB      *p_cb;
    tNFA_EE_NCI_CONN    *p_cbk   = &p_data->conn;
    tNFC_CONN           *p_conn  = p_data->conn.p_data;
    BT_HDR              *p_pkt   = NULL;
    tNFA_EE_CBACK_DATA  evt_data = {0};
    tNFA_EE_EVT         event    = NFA_EE_INVALID;
    tNFA_EE_CBACK       *p_cback = NULL;

    if (p_cbk->event == NFC_CONN_CREATE_CEVT)
    {
        p_cb = nfa_ee_find_ecb (p_cbk->p_data->conn_create.id);
    }
    else
    {
        p_cb = nfa_ee_find_ecb_by_conn_id (p_cbk->conn_id);
        if (p_cbk->event == NFC_DATA_CEVT)
            p_pkt = p_conn->data.p_data;
    }

    if (p_cb)
    {
        p_cback         = p_cb->p_ee_cback;
        evt_data.handle = (tNFA_HANDLE)p_cb->nfcee_id | NFA_HANDLE_GROUP_EE;
        switch (p_cbk->event)
        {
        case NFC_CONN_CREATE_CEVT:
            if (p_conn->conn_create.status == NFC_STATUS_OK)
            {
                p_cb->conn_id = p_cbk->conn_id;
                p_cb->conn_st = NFA_EE_CONN_ST_CONN;
            }
            else
            {
                p_cb->conn_st = NFA_EE_CONN_ST_NONE;
            }
            if (p_cb->ecb_flags & NFA_EE_ECB_FLAGS_RESTORE)
            {
                p_cb->ecb_flags &= ~NFA_EE_ECB_FLAGS_RESTORE;
                nfa_ee_check_restore_complete();
            }
            else
            {
                evt_data.connect.status       = p_conn->conn_create.status;
                evt_data.connect.ee_interface = p_cb->use_interface;
                event = NFA_EE_CONNECT_EVT;
            }
            break;

        case NFC_CONN_CLOSE_CEVT:
            if (p_cb->conn_st != NFA_EE_CONN_ST_DISC)
                event = NFA_EE_DISCONNECT_EVT;
            p_cb->conn_st    = NFA_EE_CONN_ST_NONE;
            p_cb->p_ee_cback = NULL;
            p_cb->conn_id    = 0;
            if (nfa_ee_cb.em_state == NFA_EE_EM_STATE_DISABLING)
            {
                if (nfa_ee_cb.ee_flags & NFA_EE_FLAG_WAIT_DISCONN)
                {
                    if (nfa_ee_cb.num_ee_expecting)
                    {
                        nfa_ee_cb.num_ee_expecting--;
                    }
                }
                if (nfa_ee_cb.num_ee_expecting == 0)
                {
                    nfa_ee_cb.ee_flags &= ~NFA_EE_FLAG_WAIT_DISCONN;
                    nfa_ee_check_disable();
                }
            }
            break;

        case NFC_DATA_CEVT:
            if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
            {
                /* report data event only in connected state */
                if (p_cb->p_ee_cback && p_pkt)
                {
                    evt_data.data.len   = p_pkt->len;
                    evt_data.data.p_buf = (UINT8 *)(p_pkt+1) + p_pkt->offset;
                    event               = NFA_EE_DATA_EVT;
                    p_pkt               = NULL; /* so this function does not free this GKI buffer */
                }
            }
            break;
        }

        if ((event != NFA_EE_INVALID) && (p_cback))
            (*p_cback)(event, &evt_data);
    }
    if (p_pkt)
        GKI_freebuf (p_pkt);
}


/*******************************************************************************
**
** Function         nfa_ee_nci_action_ntf
**
** Description      process the NFCEE action callback event
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_action_ntf(tNFA_EE_MSG *p_data)
{
    tNFC_EE_ACTION_REVT *p_cbk = p_data->act.p_data;
    tNFA_EE_ACTION      evt_data;

    evt_data.ee_handle  = (tNFA_HANDLE)p_cbk->nfcee_id | NFA_HANDLE_GROUP_EE;
    evt_data.trigger    = p_cbk->act_data.trigger;
    memcpy (&(evt_data.param), &(p_cbk->act_data.param), sizeof (tNFA_EE_ACTION_PARAM));
    nfa_ee_report_event(NULL, NFA_EE_ACTION_EVT, (tNFA_EE_CBACK_DATA *)&evt_data);
}

/*******************************************************************************
**
** Function         nfa_ee_nci_disc_req_ntf
**
** Description      process the NFCEE discover request callback event
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_nci_disc_req_ntf(tNFA_EE_MSG *p_data)
{
    tNFC_EE_DISCOVER_REQ_REVT   *p_cbk = p_data->disc_req.p_data;
    tNFA_HANDLE         ee_handle;
    tNFA_EE_ECB         *p_cb = NULL;
    UINT8               report_ntf = 0;
    UINT8 xx;

    NFA_TRACE_DEBUG2 ("nfa_ee_nci_disc_req_ntf () num_info: %d cur_ee:%d", p_cbk->num_info, nfa_ee_cb.cur_ee );

    for (xx = 0; xx < p_cbk->num_info; xx++)
    {
        ee_handle = NFA_HANDLE_GROUP_EE|p_cbk->info[xx].nfcee_id;

        p_cb = nfa_ee_find_ecb (p_cbk->info[xx].nfcee_id);
        if (!p_cb)
        {
            NFA_TRACE_DEBUG1 ("Cannot find cb for NFCEE: 0x%x", p_cbk->info[xx].nfcee_id);
            p_cb = nfa_ee_find_ecb (NFA_EE_INVALID);
            if (p_cb)
            {
                p_cb->nfcee_id   = p_cbk->info[xx].nfcee_id;
                p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_ORDER;
            }
            else
            {
                NFA_TRACE_ERROR1 ("Cannot allocate cb for NFCEE: 0x%x", p_cbk->info[xx].nfcee_id);
                continue;
            }
        }
        else
        {
            report_ntf  |= nfa_ee_ecb_to_mask (p_cb);
        }

        p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_DISC_REQ;
        if (p_cbk->info[xx].op == NFC_EE_DISC_OP_ADD)
        {
            if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_A)
            {
                p_cb->la_protocol = p_cbk->info[xx].protocol;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_B)
            {
                p_cb->lb_protocol = p_cbk->info[xx].protocol;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_F)
            {
                p_cb->lf_protocol = p_cbk->info[xx].protocol;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_B_PRIME)
            {
                p_cb->lbp_protocol = p_cbk->info[xx].protocol;
            }
            NFA_TRACE_DEBUG6 ("nfcee_id=0x%x ee_status=0x%x ecb_flags=0x%x la_protocol=0x%x la_protocol=0x%x la_protocol=0x%x",
                p_cb->nfcee_id, p_cb->ee_status, p_cb->ecb_flags,
                p_cb->la_protocol, p_cb->lb_protocol, p_cb->lf_protocol);
        }
        else
        {
            if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_A)
            {
                p_cb->la_protocol = 0;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_B)
            {
                p_cb->lb_protocol = 0;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_F)
            {
                p_cb->lf_protocol = 0;
            }
            else if (p_cbk->info[xx].tech_n_mode == NFC_DISCOVERY_TYPE_LISTEN_B_PRIME)
            {
                p_cb->lbp_protocol = 0;
            }
        }
    }


    /* Report NFA_EE_DISCOVER_REQ_EVT for all active NFCEE */
    if (report_ntf)
        nfa_ee_report_discover_req_evt();

}

/*******************************************************************************
**
** Function         nfa_ee_is_active
**
** Description      Check if the given NFCEE is active
**
** Returns          TRUE if the given NFCEE is active
**
*******************************************************************************/
BOOLEAN nfa_ee_is_active (tNFA_HANDLE nfcee_id)
{
    BOOLEAN is_active = FALSE;
    int     xx;
    tNFA_EE_ECB  *p_cb = nfa_ee_cb.ecb;

    if ((NFA_HANDLE_GROUP_MASK & nfcee_id) == NFA_HANDLE_GROUP_EE)
        nfcee_id    &= NFA_HANDLE_MASK;

    /* compose output */
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
    {
        if ((tNFA_HANDLE)p_cb->nfcee_id == nfcee_id)
        {
            if (p_cb->ee_status == NFA_EE_STATUS_ACTIVE)
            {
                is_active = TRUE;
            }
            break;
        }
    }
    return is_active;
}

/*******************************************************************************
**
** Function         nfa_ee_get_tech_route
**
** Description      Given a power state, find the technology routing destination.
**                  The result is filled in the given p_handles
**                  in the order of A, B, F, Bprime
**
** Returns          None
**
*******************************************************************************/
void nfa_ee_get_tech_route (UINT8 power_state, UINT8 *p_handles)
{
    int     xx, yy;
    tNFA_EE_ECB *p_cb;
    UINT8   tech_mask_list[NFA_EE_MAX_TECH_ROUTE] =
    {
        NFA_TECHNOLOGY_MASK_A,
        NFA_TECHNOLOGY_MASK_B,
        NFA_TECHNOLOGY_MASK_F,
        NFA_TECHNOLOGY_MASK_B_PRIME
    };

    NFA_TRACE_DEBUG1("nfa_ee_get_tech_route(): %d", power_state);

    for (xx = 0; xx < NFA_EE_MAX_TECH_ROUTE; xx++)
    {
        p_handles[xx] = NFC_DH_ID;
        p_cb = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee - 1];
        for (yy = 0; yy < nfa_ee_cb.cur_ee; yy++, p_cb--)
        {
            if (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE)
            {
                switch (power_state)
                {
                case NFA_EE_PWR_STATE_ON:
                    if (p_cb->tech_switch_on & tech_mask_list[xx])
                        p_handles[xx] = p_cb->nfcee_id;
                    break;
                case NFA_EE_PWR_STATE_SWITCH_OFF:
                    if (p_cb->tech_switch_off & tech_mask_list[xx])
                        p_handles[xx] = p_cb->nfcee_id;
                    break;
                case NFA_EE_PWR_STATE_BATT_OFF:
                    if (p_cb->tech_battery_off & tech_mask_list[xx])
                        p_handles[xx] = p_cb->nfcee_id;
                    break;
                }
            }
        }
    }
    NFA_TRACE_DEBUG4("0x%x, 0x%x, 0x%x, 0x%x", p_handles[0], p_handles[1], p_handles[2], p_handles[3]);
}

/*******************************************************************************
**
** Function         nfa_ee_check_set_routing
**
** Description      If the new size exceeds the capacity of next block,
**                  send the routing command now and reset the related parameters
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_check_set_routing(UINT16 new_size, int *p_max_len, UINT8 *p, int *p_cur_offset)
{
    UINT8   max_tlv = (UINT8)((*p_max_len > NFA_EE_ROUT_MAX_TLV_SIZE)?NFA_EE_ROUT_MAX_TLV_SIZE:*p_max_len);
    tNFA_STATUS status = NFA_STATUS_OK;

    if (new_size + *p_cur_offset > max_tlv)
    {
        if (NFC_SetRouting(TRUE, *p, *p_cur_offset, p + 1) == NFA_STATUS_OK)
        {
            nfa_ee_cb.wait_rsp++;
        }
        /* after the routing command is sent, re-use the same buffer to send the next routing command.
         * reset the related parameters */
        if (*p_max_len > *p_cur_offset)
            *p_max_len     -= *p_cur_offset;/* the max is reduced */
        else
            *p_max_len      = 0;
        *p_cur_offset   = 0;                /* nothing is in queue any more */
        *p              = 0;                /* num_tlv=0 */
    }
}

/*******************************************************************************
**
** Function         nfa_ee_route_add_one_ecb
**
** Description      Add the routing entries for one NFCEE/DH
**
** Returns          NFA_STATUS_OK, if ok to continue
**
*******************************************************************************/
tNFA_STATUS nfa_ee_route_add_one_ecb(tNFA_EE_ECB *p_cb, int *p_max_len, BOOLEAN more, UINT8 *ps, int *p_cur_offset)
{
    UINT8   *p, *pa;
    UINT16  tlv_size;
    UINT8   num_tlv, len;
    int     xx;
    int     start_offset;
    UINT8   power_cfg = 0;
    UINT8   *pp = ps + *p_cur_offset;
    UINT8   entry_size;
    UINT8   max_tlv;
    UINT8   *p_start;
    UINT8   new_size;
    tNFA_STATUS status = NFA_STATUS_OK;

    nfa_ee_check_set_routing (p_cb->size_mask, p_max_len, ps, p_cur_offset);
    max_tlv = (UINT8)((*p_max_len > NFA_EE_ROUT_MAX_TLV_SIZE)?NFA_EE_ROUT_MAX_TLV_SIZE:*p_max_len);
    /* use the first byte of the buffer (ps) to keep the num_tlv */
    num_tlv  = *ps;
    NFA_TRACE_DEBUG5 ("nfa_ee_route_add_one_ecb max_len:%d, max_tlv:%d, cur_offset:%d, more:%d, num_tlv:%d",
        *p_max_len, max_tlv, *p_cur_offset, more, num_tlv);
    pp       = ps + 1 + *p_cur_offset;
    p        = pp;
    tlv_size = (UINT8)*p_cur_offset;
    /* add the Technology based routing */
    for (xx = 0; xx < NFA_EE_NUM_TECH; xx++)
    {
        power_cfg = 0;
        if (p_cb->tech_switch_on & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_ON;
        if (p_cb->tech_switch_off & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_SWITCH_OFF;
        if (p_cb->tech_battery_off & nfa_ee_tech_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_BATT_OFF;
        if (power_cfg)
        {
            *pp++   = NFC_ROUTE_TAG_TECH;
            *pp++   = 3;
            *pp++   = p_cb->nfcee_id;
            *pp++   = power_cfg;
            *pp++   = nfa_ee_tech_list[xx];
            num_tlv++;
            if (power_cfg != NCI_ROUTE_PWR_STATE_ON)
                nfa_ee_cb.ee_cfged  |= NFA_EE_CFGED_OFF_ROUTING;
        }
    }

    /* add the Protocol based routing */
    for (xx = 0; xx < NFA_EE_NUM_PROTO; xx++)
    {
        power_cfg = 0;
        if (p_cb->proto_switch_on & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_ON;
        if (p_cb->proto_switch_off & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_SWITCH_OFF;
        if (p_cb->proto_battery_off & nfa_ee_proto_mask_list[xx])
            power_cfg |= NCI_ROUTE_PWR_STATE_BATT_OFF;
        if (power_cfg)
        {
            *pp++   = NFC_ROUTE_TAG_PROTO;
            *pp++   = 3;
            *pp++   = p_cb->nfcee_id;
            *pp++   = power_cfg;
            *pp++   = nfa_ee_proto_list[xx];
            num_tlv++;
            if (power_cfg != NCI_ROUTE_PWR_STATE_ON)
                nfa_ee_cb.ee_cfged  |= NFA_EE_CFGED_OFF_ROUTING;
        }
    }

    /* add NFC-DEP routing to HOST */
    if (p_cb->nfcee_id == NFC_DH_ID)
    {
        *pp++   = NFC_ROUTE_TAG_PROTO;
        *pp++   = 3;
        *pp++   = NFC_DH_ID;
        *pp++   = NCI_ROUTE_PWR_STATE_ON;
        *pp++   = NFC_PROTOCOL_NFC_DEP;
        num_tlv++;
    }

    /* update the num_tlv and current offset */
    entry_size       = (UINT8)(pp - p);
    *p_cur_offset   += entry_size;
    *ps              = num_tlv;
    /* add the AID routing */
    if (p_cb->aid_entries)
    {
        start_offset = 0;
        for (xx = 0; xx < p_cb->aid_entries; xx++)
        {
            p_start     = pp; /* rememebr the beginning of this AID routing entry, just in case we need to put it in next command */
            /* add one AID entry */
            if (p_cb->aid_rt_info[xx] & NFA_EE_AE_ROUTE)
            {
                num_tlv++;
                pa      = &p_cb->aid_cfg[start_offset];
                pa ++; /* EMV tag */
                len     = *pa++; /* aid_len */
                *pp++   = NFC_ROUTE_TAG_AID;
                *pp++   = len + 2;
                *pp++   = p_cb->nfcee_id;
                *pp++   = p_cb->aid_pwr_cfg[xx];
                /* copy the AID */
                memcpy(pp, pa, len);
                pp     += len;
            }
            start_offset += p_cb->aid_len[xx];
            new_size        = (UINT8)(pp - p_start);
            nfa_ee_check_set_routing(new_size, p_max_len, ps, p_cur_offset);
            if (*ps == 0)
            {
                /* just sent routing command, update local */
                *ps      = 1;
                num_tlv  = *ps;
                *p_cur_offset = new_size;
                pp       = ps + 1;
                p        = pp;
                tlv_size = (UINT8)*p_cur_offset;
                max_tlv  = (UINT8)((*p_max_len > NFA_EE_ROUT_MAX_TLV_SIZE)?NFA_EE_ROUT_MAX_TLV_SIZE:*p_max_len);
                memcpy (p, p_start, new_size);
                pp      += new_size;
            }
            else
            {
                /* add the new entry */
                *ps              = num_tlv;
                *p_cur_offset   += new_size;
            }
        }
    }

    tlv_size   = nfa_ee_total_lmrt_size();
    if (tlv_size)
    {
        nfa_ee_cb.ee_cfged |= nfa_ee_ecb_to_mask(p_cb);
    }
    if (p_cb->ecb_flags   & NFA_EE_ECB_FLAGS_ROUTING)
    {
        nfa_ee_cb.ee_cfg_sts   |= NFA_EE_STS_CHANGED_ROUTING;
    }
    NFA_TRACE_DEBUG2 ("ee_cfg_sts:0x%02x lmrt_size:%d", nfa_ee_cb.ee_cfg_sts, tlv_size);

    if (more == FALSE)
    {
        /* last entry. update routing table now */
        if (nfa_ee_cb.ee_cfg_sts & NFA_EE_STS_CHANGED_ROUTING)
        {
            if (tlv_size)
            {
                nfa_ee_cb.ee_cfg_sts       |= NFA_EE_STS_PREV_ROUTING;
            }
            else
            {
                nfa_ee_cb.ee_cfg_sts       &= ~NFA_EE_STS_PREV_ROUTING;
            }
            NFA_TRACE_DEBUG2 ("nfa_ee_route_add_one_ecb: set routing num_tlv:%d tlv_size:%d", num_tlv, tlv_size);
            if (NFC_SetRouting(more, num_tlv, (UINT8)(*p_cur_offset), ps + 1) == NFA_STATUS_OK)
            {
                nfa_ee_cb.wait_rsp++;
            }
        }
        else if (nfa_ee_cb.ee_cfg_sts & NFA_EE_STS_PREV_ROUTING)
        {
            if (tlv_size == 0)
            {
                nfa_ee_cb.ee_cfg_sts       &= ~NFA_EE_STS_PREV_ROUTING;
                /* indicated routing is configured to NFCC */
                nfa_ee_cb.ee_cfg_sts       |= NFA_EE_STS_CHANGED_ROUTING;
                if (NFC_SetRouting(more, 0, 0, ps + 1) == NFA_STATUS_OK)
                {
                    nfa_ee_cb.wait_rsp++;
                }
            }
        }
    }

    return status;
}


/*******************************************************************************
**
** Function         nfa_ee_need_recfg
**
** Description      Check if any API function to configure the routing table or
**                  VS is called since last update
**
**                  The algorithm for the NFCEE configuration handling is as follows:
**
**                  Each NFCEE_ID/DH has its own control block - tNFA_EE_ECB
**                  Each control block uses ecb_flags to keep track if an API
**                  that changes routing/VS is invoked.
**                  This ecb_flags is cleared at the end of nfa_ee_update_rout().
**
**                  nfa_ee_cb.ee_cfged is the bitmask of the control blocks with
**                  routing/VS configuration and NFA_EE_CFGED_UPDATE_NOW.
**                  nfa_ee_cb.ee_cfged is cleared and re-calculated at the end of
**                  nfa_ee_update_rout().
**
**                  nfa_ee_cb.ee_cfg_sts is used to check is any status is changed
**                  and the associated command is issued to NFCC.
**                  nfa_ee_cb.ee_cfg_sts is AND with NFA_EE_STS_PREV at the end of
**                  nfa_ee_update_rout() to clear the NFA_EE_STS_CHANGED bits
**                  (except NFA_EE_STS_CHANGED_CANNED_VS is cleared in nfa_ee_vs_cback)
**
** Returns          TRUE if any configuration is changed
**
*******************************************************************************/
static BOOLEAN nfa_ee_need_recfg(void)
{
    BOOLEAN needed = FALSE;
    UINT32  xx;
    tNFA_EE_ECB  *p_cb;
    UINT8   mask;

    NFA_TRACE_DEBUG2("nfa_ee_need_recfg() ee_cfged: 0x%02x ee_cfg_sts: 0x%02x", nfa_ee_cb.ee_cfged, nfa_ee_cb.ee_cfg_sts);
    /* if no routing/vs is configured, do not need to send the info to NFCC */
    if (nfa_ee_cb.ee_cfged || nfa_ee_cb.ee_cfg_sts)
    {
        if (nfa_ee_cb.ee_cfg_sts & NFA_EE_STS_CHANGED)
        {
            needed = TRUE;
        }
        else
        {
            p_cb = &nfa_ee_cb.ecb[NFA_EE_CB_4_DH];
            mask = 1 << NFA_EE_CB_4_DH;
            for (xx = 0; xx <= nfa_ee_cb.cur_ee; xx++)
            {
                NFA_TRACE_DEBUG3("%d: ecb_flags  : 0x%02x, mask: 0x%02x", xx, p_cb->ecb_flags  , mask);
                if ((p_cb->ecb_flags  ) && (nfa_ee_cb.ee_cfged & mask))
                {
                    needed = TRUE;
                    break;
                }
                p_cb = &nfa_ee_cb.ecb[xx];
                mask = 1 << xx;
            }
        }
    }

    return needed;
}

/*******************************************************************************
**
** Function         nfa_ee_rout_timeout
**
** Description      Anytime VS or routing entries are changed,
**                  a 1 second timer is started. This function is called when
**                  the timer expires or NFA_EeUpdateNow() is called.
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_rout_timeout(tNFA_EE_MSG *p_data)
{
    UINT8               ee_cfged = nfa_ee_cb.ee_cfged;

    NFA_TRACE_DEBUG0("nfa_ee_rout_timeout()");
    if (nfa_ee_need_recfg())
    {
        /* discovery is not started */
        nfa_ee_update_rout();
    }

    if (nfa_ee_cb.wait_rsp)
        nfa_ee_cb.ee_wait_evt   |= NFA_EE_WAIT_UPDATE_RSP;
    if (ee_cfged & NFA_EE_CFGED_UPDATE_NOW)
    {
        /* need to report NFA_EE_UPDATED_EVT when done updating NFCC */
        nfa_ee_cb.ee_wait_evt   |= NFA_EE_WAIT_UPDATE;
        if (!nfa_ee_cb.wait_rsp)
        {
            nfa_ee_report_update_evt();
        }
    }
}

/*******************************************************************************
**
** Function         nfa_ee_discv_timeout
**
** Description
**
**
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_discv_timeout(tNFA_EE_MSG *p_data)
{
    NFC_NfceeDiscover(FALSE);
    if (nfa_ee_cb.p_enable_cback)
        (*nfa_ee_cb.p_enable_cback)(NFA_EE_DISC_STS_OFF);
}

/*******************************************************************************
**
** Function         nfa_ee_lmrt_to_nfcc
**
** Description      This function would set the listen mode routing table
**                  to NFCC.
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_lmrt_to_nfcc(tNFA_EE_MSG *p_data)
{
    int xx;
    tNFA_EE_ECB          *p_cb;
    UINT8   *p = NULL;
    BOOLEAN more = TRUE;
    UINT8   last_active = NFA_EE_INVALID;
    int     max_len, len;
    tNFA_STATUS status = NFA_STATUS_FAILED;
    int     cur_offset;
    UINT8   max_tlv;

    /* update routing table: DH and the activated NFCEEs */
    p = (UINT8 *)GKI_getbuf(NFA_EE_ROUT_BUF_SIZE);
    if (p == NULL)
    {
        NFA_TRACE_ERROR0 ("nfa_ee_lmrt_to_nfcc() no buffer to send routing info.");
        nfa_ee_report_event( NULL, NFA_EE_NO_MEM_ERR_EVT, (tNFA_EE_CBACK_DATA *)&status);
        return;
    }

    /* find the last active NFCEE. */
    p_cb = &nfa_ee_cb.ecb[nfa_ee_cb.cur_ee - 1];
    for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb--)
    {
        if (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE)
        {
            if (last_active == NFA_EE_INVALID)
            {
                last_active = p_cb->nfcee_id;
                NFA_TRACE_DEBUG1 ("last_active: 0x%x", last_active);
            }
        }
    }
    if (last_active == NFA_EE_INVALID)
    {
        more = FALSE;
    }

    /* add the routing for DH first */
    status  = NFA_STATUS_OK;
    max_len = NFC_GetLmrtSize();
    max_tlv = (UINT8)((max_len > NFA_EE_ROUT_MAX_TLV_SIZE)?NFA_EE_ROUT_MAX_TLV_SIZE:max_len);
    cur_offset  = 0;
    /* use the first byte of the buffer (p) to keep the num_tlv */
    *p          = 0;
    status = nfa_ee_route_add_one_ecb(&nfa_ee_cb.ecb[NFA_EE_CB_4_DH], &max_len, more, p, &cur_offset);

    /* add only what is supported by NFCC. report overflow */
    if (status == NFA_STATUS_OK)
    {
        /* add the routing for NFCEEs */
        p_cb = &nfa_ee_cb.ecb[0];
        for (xx = 0; (xx < nfa_ee_cb.cur_ee) && more; xx++, p_cb++)
        {
            len = 0;
            if (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE)
            {
                NFA_TRACE_DEBUG2 ("nfcee_id:0x%x, last_active: 0x%x", p_cb->nfcee_id, last_active);
                if (last_active == p_cb->nfcee_id)
                    more = FALSE;
                status = nfa_ee_route_add_one_ecb(p_cb, &max_len, more, p, &cur_offset);
                if (status != NFA_STATUS_OK)
                {
                    more    = FALSE;
                }
            }
        }
    }
    if (status != NFA_STATUS_OK)
    {
        nfa_ee_report_event( NULL, NFA_EE_ROUT_ERR_EVT, (tNFA_EE_CBACK_DATA *)&status);
    }
    GKI_freebuf(p);
}

/*******************************************************************************
**
** Function         nfa_ee_update_rout
**
** Description      This function would set the VS and listen mode routing table
**                  to NFCC.
**
** Returns          void
**
*******************************************************************************/
void nfa_ee_update_rout(void)
{
    int xx;
    tNFA_EE_ECB          *p_cb;
    UINT8   mask;
    BT_HDR  msg;

    NFA_TRACE_DEBUG1 ("nfa_ee_update_rout ee_cfg_sts:0x%02x", nfa_ee_cb.ee_cfg_sts);

    /* use action function to send routing and VS configuration to NFCC */
    msg.event = NFA_EE_CFG_TO_NFCC_EVT;
    nfa_ee_evt_hdlr (&msg);

    /* all configuration is updated to NFCC, clear the status mask */
    nfa_ee_cb.ee_cfg_sts   &= NFA_EE_STS_PREV;
    nfa_ee_cb.ee_cfged  = 0;
    p_cb                = &nfa_ee_cb.ecb[0];
    for (xx = 0; xx < NFA_EE_NUM_ECBS; xx++, p_cb++)
    {
        p_cb->ecb_flags     = 0;
        mask                = (1 << xx);
        if (p_cb->tech_switch_on | p_cb->tech_switch_off | p_cb->tech_battery_off |
            p_cb->proto_switch_on| p_cb->proto_switch_off| p_cb->proto_battery_off |
            p_cb->aid_entries)
        {
            /* this entry has routing configuration. mark it configured */
            nfa_ee_cb.ee_cfged  |= mask;
        }
    }
    NFA_TRACE_DEBUG2 ("nfa_ee_update_rout ee_cfg_sts:0x%02x ee_cfged:0x%02x", nfa_ee_cb.ee_cfg_sts, nfa_ee_cb.ee_cfged);
}