blob: 6b0c28989c3a69e7be6702a1640c3d415473605b [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010-2012 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 EE.
*
******************************************************************************/
#include <string.h>
#include "nfc_api.h"
#include "nfa_sys.h"
#include "nfa_sys_int.h"
#include "nfa_dm_int.h"
#include "nfa_ee_int.h"
extern void nfa_ee_vs_cback (tNFC_VS_EVT event, BT_HDR *p_data);
/*****************************************************************************
** Global Variables
*****************************************************************************/
/* system manager control block definition */
#if NFA_DYNAMIC_MEMORY == FALSE
tNFA_EE_CB nfa_ee_cb;
#endif
#ifndef NFA_EE_DISCV_TIMEOUT_VAL
#define NFA_EE_DISCV_TIMEOUT_VAL 2000
#endif
/*****************************************************************************
** Constants
*****************************************************************************/
static const tNFA_SYS_REG nfa_ee_sys_reg =
{
nfa_ee_sys_enable,
nfa_ee_evt_hdlr,
nfa_ee_sys_disable,
nfa_ee_proc_nfcc_power_mode
};
#define NFA_EE_NUM_ACTIONS (NFA_EE_MAX_EVT & 0x00ff)
const tNFA_EE_SM_ACT nfa_ee_actions[] =
{
/* NFA-EE action function/ internal events */
nfa_ee_api_discover , /* NFA_EE_API_DISCOVER_EVT */
nfa_ee_api_register , /* NFA_EE_API_REGISTER_EVT */
nfa_ee_api_deregister , /* NFA_EE_API_DEREGISTER_EVT */
nfa_ee_api_mode_set , /* NFA_EE_API_MODE_SET_EVT */
nfa_ee_api_set_tech_cfg , /* NFA_EE_API_SET_TECH_CFG_EVT */
nfa_ee_api_set_proto_cfg, /* NFA_EE_API_SET_PROTO_CFG_EVT */
nfa_ee_api_add_aid , /* NFA_EE_API_ADD_AID_EVT */
nfa_ee_api_remove_aid , /* NFA_EE_API_REMOVE_AID_EVT */
nfa_ee_api_update_now , /* NFA_EE_API_UPDATE_NOW_EVT */
nfa_ee_api_connect , /* NFA_EE_API_CONNECT_EVT */
nfa_ee_api_send_data , /* NFA_EE_API_SEND_DATA_EVT */
nfa_ee_api_disconnect , /* NFA_EE_API_DISCONNECT_EVT */
nfa_ee_nci_disc_rsp , /* NFA_EE_NCI_DISC_RSP_EVT */
nfa_ee_nci_disc_ntf , /* NFA_EE_NCI_DISC_NTF_EVT */
nfa_ee_nci_mode_set_rsp , /* NFA_EE_NCI_MODE_SET_RSP_EVT */
nfa_ee_nci_conn , /* NFA_EE_NCI_CONN_EVT */
nfa_ee_nci_conn , /* NFA_EE_NCI_DATA_EVT */
nfa_ee_nci_action_ntf , /* NFA_EE_NCI_ACTION_NTF_EVT */
nfa_ee_nci_disc_req_ntf , /* NFA_EE_NCI_DISC_REQ_NTF_EVT */
nfa_ee_rout_timeout , /* NFA_EE_ROUT_TIMEOUT_EVT */
nfa_ee_discv_timeout , /* NFA_EE_DISCV_TIMEOUT_EVT */
nfa_ee_lmrt_to_nfcc /* NFA_EE_CFG_TO_NFCC_EVT */
};
/*******************************************************************************
**
** Function nfa_ee_init
**
** Description Initialize NFA EE control block
** register to NFA SYS
**
** Returns None
**
*******************************************************************************/
void nfa_ee_init (void)
{
int xx;
NFA_TRACE_DEBUG0 ("nfa_ee_init ()");
/* initialize control block */
memset (&nfa_ee_cb, 0, sizeof (tNFA_EE_CB));
for (xx = 0; xx < NFA_EE_MAX_EE_SUPPORTED; xx++)
{
nfa_ee_cb.ecb[xx].nfcee_id = NFA_EE_INVALID;
nfa_ee_cb.ecb[xx].ee_status = NFC_NFCEE_STATUS_INACTIVE;
}
nfa_ee_cb.ecb[NFA_EE_CB_4_DH].ee_status = NFC_NFCEE_STATUS_ACTIVE;
nfa_ee_cb.ecb[NFA_EE_CB_4_DH].nfcee_id = NFC_DH_ID;
/* register message handler on NFA SYS */
nfa_sys_register (NFA_ID_EE, &nfa_ee_sys_reg);
}
/*******************************************************************************
**
** Function nfa_ee_sys_enable
**
** Description Enable NFA EE
**
** Returns None
**
*******************************************************************************/
void nfa_ee_sys_enable (void)
{
/* collect NFCEE information */
NFC_NfceeDiscover (TRUE);
nfa_sys_start_timer (&nfa_ee_cb.discv_timer, NFA_EE_DISCV_TIMEOUT_EVT, NFA_EE_DISCV_TIMEOUT_VAL);
}
/*******************************************************************************
**
** Function nfa_ee_restore_one_ecb
**
** Description activate the NFCEE and restore the routing when
** changing power state from low power mode to full power mode
**
** Returns None
**
*******************************************************************************/
void nfa_ee_restore_one_ecb (tNFA_EE_ECB *p_cb)
{
UINT8 mask;
tNFC_NFCEE_MODE_SET_REVT rsp;
tNFA_EE_NCI_MODE_SET ee_msg;
NFA_TRACE_DEBUG4 ("nfa_ee_restore_one_ecb () nfcee_id:0x%x, ecb_flags:0x%x ee_status:0x%x ee_old_status: 0x%x", p_cb->nfcee_id, p_cb->ecb_flags, p_cb->ee_status, p_cb->ee_old_status);
if ((p_cb->nfcee_id != NFA_EE_INVALID) && (p_cb->ee_status & NFA_EE_STATUS_RESTORING) == 0 && (p_cb->ee_old_status & NFA_EE_STATUS_RESTORING) != 0)
{
p_cb->ee_old_status &= ~NFA_EE_STATUS_RESTORING;
mask = nfa_ee_ecb_to_mask(p_cb);
if (p_cb->ee_status != p_cb->ee_old_status)
{
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_RESTORE;
if (p_cb->ee_old_status == NFC_NFCEE_STATUS_ACTIVE)
{
NFC_NfceeModeSet (p_cb->nfcee_id, NFC_MODE_ACTIVATE);
if (nfa_ee_cb.ee_cfged & mask)
{
/* if any routing is configured on this NFCEE. need to mark this NFCEE as changed
* to cause the configuration to be sent to NFCC again */
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_ROUTING;
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_VS;
}
}
else
{
NFC_NfceeModeSet (p_cb->nfcee_id, NFC_MODE_DEACTIVATE);
}
}
else if (p_cb->ee_status == NFC_NFCEE_STATUS_ACTIVE)
{
/* the initial NFCEE status after start up is the same as the current status and it's active:
* process the same as the host gets activate rsp */
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_RESTORE;
if (nfa_ee_cb.ee_cfged & mask)
{
/* if any routing is configured on this NFCEE. need to mark this NFCEE as changed
* to cause the configuration to be sent to NFCC again */
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_ROUTING;
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_VS;
}
rsp.mode = NFA_EE_MD_ACTIVATE;
rsp.nfcee_id = p_cb->nfcee_id;
rsp.status = NFA_STATUS_OK;
ee_msg.p_data = &rsp;
nfa_ee_nci_mode_set_rsp ((tNFA_EE_MSG *) &ee_msg);
}
}
}
/*******************************************************************************
**
** Function nfa_ee_proc_nfcc_power_mode
**
** Description Restore NFA EE sub-module
**
** Returns None
**
*******************************************************************************/
void nfa_ee_proc_nfcc_power_mode (UINT8 nfcc_power_mode)
{
UINT32 xx;
tNFA_EE_ECB *p_cb;
BOOLEAN proc_complete = TRUE;
NFA_TRACE_DEBUG1 ("nfa_ee_proc_nfcc_power_mode (): nfcc_power_mode=%d", nfcc_power_mode);
/* if NFCC power state is change to full power */
if (nfcc_power_mode == NFA_DM_PWR_MODE_FULL)
{
p_cb = nfa_ee_cb.ecb;
for (xx = 0; xx < NFA_EE_MAX_EE_SUPPORTED; xx++, p_cb++)
{
p_cb->ee_old_status = 0;
if (xx >= nfa_ee_cb.cur_ee)
p_cb->nfcee_id = NFA_EE_INVALID;
if ((p_cb->nfcee_id != NFA_EE_INVALID) && (p_cb->ee_interface[0] != NFC_NFCEE_INTERFACE_HCI_ACCESS) && (p_cb->ee_status != NFA_EE_STATUS_REMOVED))
{
proc_complete = FALSE;
/* NFA_EE_STATUS_RESTORING bit makes sure the ee_status restore to ee_old_status
* NFA_EE_STATUS_RESTORING bit is cleared in ee_status at NFCEE_DISCOVER NTF.
* NFA_EE_STATUS_RESTORING bit is cleared in ee_old_status at restoring the activate/inactive status after NFCEE_DISCOVER NTF */
p_cb->ee_status |= NFA_EE_STATUS_RESTORING;
p_cb->ee_old_status = p_cb->ee_status;
/* NFA_EE_FLAGS_RESTORE bit makes sure the routing/nci logical connection is restore to prior to entering low power mode */
p_cb->ecb_flags |= NFA_EE_ECB_FLAGS_RESTORE;
}
}
nfa_ee_cb.em_state = NFA_EE_EM_STATE_RESTORING;
if (nfa_sys_is_register (NFA_ID_HCI))
{
nfa_ee_cb.ee_flags |= NFA_EE_FLAG_WAIT_HCI;
nfa_ee_cb.ee_flags |= NFA_EE_FLAG_NOTIFY_HCI;
}
NFC_NfceeDiscover (TRUE);
nfa_sys_start_timer (&nfa_ee_cb.discv_timer, NFA_EE_DISCV_TIMEOUT_EVT, NFA_EE_DISCV_TIMEOUT_VAL);
}
else
{
nfa_sys_stop_timer (&nfa_ee_cb.timer);
nfa_sys_stop_timer (&nfa_ee_cb.discv_timer);
nfa_ee_cb.num_ee_expecting = 0;
}
if (proc_complete)
nfa_sys_cback_notify_nfcc_power_mode_proc_complete (NFA_ID_EE);
}
/*******************************************************************************
**
** Function nfa_ee_proc_hci_info_cback
**
** Description HCI initialization complete from power off sleep mode
**
** Returns None
**
*******************************************************************************/
void nfa_ee_proc_hci_info_cback (void)
{
UINT32 xx;
tNFA_EE_ECB *p_cb;
NFA_TRACE_DEBUG0 ("nfa_ee_proc_hci_info_cback ()");
/* if NFCC power state is change to full power */
nfa_ee_cb.ee_flags &= ~NFA_EE_FLAG_WAIT_HCI;
p_cb = nfa_ee_cb.ecb;
for (xx = 0; xx < NFA_EE_MAX_EE_SUPPORTED; xx++, p_cb++)
{
/* 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)
{
nfa_ee_restore_one_ecb (p_cb);
}
}
}
/*******************************************************************************
**
** Function nfa_ee_proc_evt
**
** Description Process NFCEE related events from NFC stack
**
**
** Returns None
**
*******************************************************************************/
void nfa_ee_proc_evt (tNFC_RESPONSE_EVT event, void *p_data)
{
tNFA_EE_INT_EVT int_event=0;
tNFA_EE_NCI_RESPONSE cbk;
BT_HDR *p_hdr;
switch (event)
{
case NFC_NFCEE_DISCOVER_REVT: /* 4 NFCEE Discover response */
int_event = NFA_EE_NCI_DISC_RSP_EVT;
break;
case NFC_NFCEE_INFO_REVT: /* 5 NFCEE Discover Notification */
int_event = NFA_EE_NCI_DISC_NTF_EVT;
break;
case NFC_NFCEE_MODE_SET_REVT: /* 6 NFCEE Mode Set response */
int_event = NFA_EE_NCI_MODE_SET_RSP_EVT;
break;
case NFC_EE_ACTION_REVT:
int_event = NFA_EE_NCI_ACTION_NTF_EVT;
break;
case NFC_EE_DISCOVER_REQ_REVT: /* 10 EE Discover Req notification */
int_event = NFA_EE_NCI_DISC_REQ_NTF_EVT;
break;
}
NFA_TRACE_DEBUG2 ("nfa_ee_proc_evt: event=0x%02x int_event:0x%x", event, int_event);
if (int_event)
{
p_hdr = (BT_HDR *) &cbk;
cbk.hdr.event = int_event;
cbk.p_data = p_data;
nfa_ee_evt_hdlr (p_hdr);
}
}
/*******************************************************************************
**
** Function nfa_ee_ecb_to_mask
**
** Description Given a ecb, return the bit mask to be used in nfa_ee_cb.ee_cfged
**
** Returns the bitmask for the given ecb.
**
*******************************************************************************/
UINT8 nfa_ee_ecb_to_mask (tNFA_EE_ECB *p_cb)
{
UINT8 mask;
UINT8 index;
index = (UINT8) (p_cb - nfa_ee_cb.ecb);
mask = 1 << index;
return mask;
}
/*******************************************************************************
**
** Function nfa_ee_find_ecb
**
** Description Return the ecb associated with the given nfcee_id
**
** Returns tNFA_EE_ECB
**
*******************************************************************************/
tNFA_EE_ECB * nfa_ee_find_ecb (UINT8 nfcee_id)
{
UINT32 xx;
tNFA_EE_ECB *p_ret = NULL, *p_cb;
NFA_TRACE_DEBUG0 ("nfa_ee_find_ecb ()");
if (nfcee_id == NFC_DH_ID)
{
p_ret = &nfa_ee_cb.ecb[NFA_EE_CB_4_DH];
}
else
{
p_cb = nfa_ee_cb.ecb;
for (xx = 0; xx < NFA_EE_MAX_EE_SUPPORTED; xx++, p_cb++)
{
if (nfcee_id == p_cb->nfcee_id)
{
p_ret = p_cb;
break;
}
}
}
return p_ret;
}
/*******************************************************************************
**
** Function nfa_ee_find_ecb_by_conn_id
**
** Description Return the ecb associated with the given connection id
**
** Returns tNFA_EE_ECB
**
*******************************************************************************/
tNFA_EE_ECB * nfa_ee_find_ecb_by_conn_id (UINT8 conn_id)
{
UINT32 xx;
tNFA_EE_ECB *p_ret = NULL, *p_cb;
NFA_TRACE_DEBUG0 ("nfa_ee_find_ecb_by_conn_id ()");
p_cb = nfa_ee_cb.ecb;
for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
{
if (conn_id == p_cb->conn_id)
{
p_ret = p_cb;
break;
}
}
return p_ret;
}
/*******************************************************************************
**
** Function nfa_ee_sys_disable
**
** Description Deregister NFA EE from NFA SYS/DM
**
**
** Returns None
**
*******************************************************************************/
void nfa_ee_sys_disable (void)
{
UINT32 xx;
tNFA_EE_ECB *p_cb;
tNFA_EE_MSG msg;
NFA_TRACE_DEBUG0 ("nfa_ee_sys_disable ()");
nfa_ee_cb.em_state = NFA_EE_EM_STATE_DISABLED;
/* report NFA_EE_DEREGISTER_EVT to all registered to EE */
for (xx = 0; xx < NFA_EE_MAX_CBACKS; xx++)
{
if (nfa_ee_cb.p_ee_cback[xx])
{
msg.deregister.index = xx;
nfa_ee_api_deregister (&msg);
}
}
p_cb = nfa_ee_cb.ecb;
for (xx = 0; xx < nfa_ee_cb.cur_ee; xx++, p_cb++)
{
if (p_cb->conn_st == NFA_EE_CONN_ST_CONN)
{
if (nfa_sys_is_graceful_disable ())
{
/* Disconnect NCI connection on graceful shutdown */
msg.disconnect.p_cb = p_cb;
nfa_ee_api_disconnect (&msg);
nfa_ee_cb.em_state = NFA_EE_EM_STATE_DISABLING;
}
else
{
/* fake NFA_EE_DISCONNECT_EVT on ungraceful shutdown */
msg.conn.conn_id = p_cb->conn_id;
msg.conn.event = NFC_CONN_CLOSE_CEVT;
nfa_ee_nci_conn (&msg);
}
}
}
nfa_sys_stop_timer (&nfa_ee_cb.timer);
nfa_sys_stop_timer (&nfa_ee_cb.discv_timer);
/* If Application initiated NFCEE discovery, fake/report the event */
nfa_ee_cb.num_ee_expecting = 0;
nfa_ee_report_disc_done (FALSE);
/* deregister message handler on NFA SYS */
if (nfa_ee_cb.em_state == NFA_EE_EM_STATE_DISABLED)
nfa_sys_deregister (NFA_ID_EE);
}
/*******************************************************************************
**
** Function nfa_ee_reg_cback_enable_done
**
** Description Allow a module to register to EE to be notified when NFA-EE
** finishes enable process
**
** Returns None
**
*******************************************************************************/
void nfa_ee_reg_cback_enable_done (tNFA_EE_ENABLE_DONE_CBACK *p_cback)
{
nfa_ee_cb.p_enable_cback = p_cback;
}
#if (BT_TRACE_VERBOSE == TRUE)
/*******************************************************************************
**
** Function nfa_ee_sm_st_2_str
**
** Description convert nfa-ee state to string
**
*******************************************************************************/
static char *nfa_ee_sm_st_2_str (UINT8 state)
{
switch (state)
{
case NFA_EE_EM_STATE_INIT:
return "INIT";
case NFA_EE_EM_STATE_INIT_DONE:
return "INIT_DONE";
case NFA_EE_EM_STATE_RESTORING:
return "RESTORING";
case NFA_EE_EM_STATE_DISABLING:
return "DISABLING";
case NFA_EE_EM_STATE_DISABLED:
return "DISABLED";
default:
return "Unknown";
}
}
/*******************************************************************************
**
** Function nfa_ee_sm_evt_2_str
**
** Description convert nfa-ee evt to string
**
*******************************************************************************/
static char *nfa_ee_sm_evt_2_str (UINT16 event)
{
switch (event)
{
case NFA_EE_API_DISCOVER_EVT:
return "API_DISCOVER";
case NFA_EE_API_REGISTER_EVT:
return "API_REGISTER";
case NFA_EE_API_DEREGISTER_EVT:
return "API_DEREGISTER";
case NFA_EE_API_MODE_SET_EVT:
return "API_MODE_SET";
case NFA_EE_API_SET_TECH_CFG_EVT:
return "API_SET_TECH_CFG";
case NFA_EE_API_SET_PROTO_CFG_EVT:
return "API_SET_PROTO_CFG";
case NFA_EE_API_ADD_AID_EVT:
return "API_ADD_AID";
case NFA_EE_API_REMOVE_AID_EVT:
return "API_REMOVE_AID";
case NFA_EE_API_UPDATE_NOW_EVT:
return "API_UPDATE_NOW";
case NFA_EE_API_CONNECT_EVT:
return "API_CONNECT";
case NFA_EE_API_SEND_DATA_EVT:
return "API_SEND_DATA";
case NFA_EE_API_DISCONNECT_EVT:
return "API_DISCONNECT";
case NFA_EE_NCI_DISC_RSP_EVT:
return "NCI_DISC_RSP";
case NFA_EE_NCI_DISC_NTF_EVT:
return "NCI_DISC_NTF";
case NFA_EE_NCI_MODE_SET_RSP_EVT:
return "NCI_MODE_SET";
case NFA_EE_NCI_CONN_EVT:
return "NCI_CONN";
case NFA_EE_NCI_DATA_EVT:
return "NCI_DATA";
case NFA_EE_NCI_ACTION_NTF_EVT:
return "NCI_ACTION";
case NFA_EE_NCI_DISC_REQ_NTF_EVT:
return "NCI_DISC_REQ";
case NFA_EE_ROUT_TIMEOUT_EVT:
return "ROUT_TIMEOUT";
case NFA_EE_DISCV_TIMEOUT_EVT:
return "NFA_EE_DISCV_TIMEOUT_EVT";
case NFA_EE_CFG_TO_NFCC_EVT:
return "CFG_TO_NFCC";
default:
return "Unknown";
}
}
#endif /* BT_TRACE_VERBOSE */
/*******************************************************************************
**
** Function nfa_ee_evt_hdlr
**
** Description Processing event for NFA EE
**
**
** Returns TRUE if p_msg needs to be deallocated
**
*******************************************************************************/
BOOLEAN nfa_ee_evt_hdlr (BT_HDR *p_msg)
{
tNFA_EE_MSG *p_evt_data = (tNFA_EE_MSG *) p_msg;
UINT16 event = p_msg->event & 0x00ff;
BOOLEAN act = FALSE;
#if (BT_TRACE_VERBOSE == TRUE)
NFA_TRACE_DEBUG4 ("nfa_ee_evt_hdlr (): Event %s(0x%02x), State: %s(%d)",
nfa_ee_sm_evt_2_str (p_evt_data->hdr.event), p_evt_data->hdr.event,
nfa_ee_sm_st_2_str (nfa_ee_cb.em_state), nfa_ee_cb.em_state);
#else
NFA_TRACE_DEBUG2 ("nfa_ee_evt_hdlr (): Event 0x%02x, State: %d", p_evt_data->hdr.event, nfa_ee_cb.em_state);
#endif
switch (nfa_ee_cb.em_state)
{
case NFA_EE_EM_STATE_INIT_DONE:
case NFA_EE_EM_STATE_RESTORING:
act = TRUE;
break;
case NFA_EE_EM_STATE_INIT:
if ((p_msg->event == NFA_EE_NCI_DISC_NTF_EVT) || (p_msg->event == NFA_EE_NCI_DISC_RSP_EVT))
act = TRUE;
break;
case NFA_EE_EM_STATE_DISABLING:
if (p_msg->event == NFA_EE_NCI_CONN_EVT)
act = TRUE;
break;
}
if (act)
{
if (event < NFA_EE_NUM_ACTIONS)
{
(*nfa_ee_actions[event]) (p_evt_data);
}
}
else
{
/* if the data event is not handled by action function, free the data packet */
if (p_msg->event == NFA_EE_NCI_DATA_EVT)
GKI_freebuf (p_evt_data->conn.p_data);
}
return TRUE;
}