blob: d0c8308c37c25330e1a4605d3126ae222e548ff8 [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 state implementation file for the NFA Connection Handover.
*
******************************************************************************/
#include <string.h>
#include "nfc_api.h"
#include "llcp_api.h"
#include "llcp_defs.h"
#include "nfa_sys.h"
#include "nfa_sys_int.h"
#include "nfa_cho_api.h"
#include "nfa_cho_int.h"
#include "nfa_mem_co.h"
/*****************************************************************************
** Global Variables
*****************************************************************************/
/*****************************************************************************
** Static Functions
*****************************************************************************/
static void nfa_cho_sm_disabled (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data);
static void nfa_cho_sm_idle (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data);
static void nfa_cho_sm_w4_cc (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data);
static void nfa_cho_sm_connected (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data);
static void nfa_cho_proc_rx_handover_msg (void);
/* debug functions type */
#if (BT_TRACE_VERBOSE == TRUE)
static char *nfa_cho_state_code (tNFA_CHO_STATE state_code);
static char *nfa_cho_evt_code (tNFA_CHO_INT_EVT evt_code);
#endif
/*****************************************************************************
** Constants
*****************************************************************************/
/*******************************************************************************
**
** Function nfa_cho_sm_llcp_cback
**
** Description Processing event from LLCP
**
**
** Returns None
**
*******************************************************************************/
void nfa_cho_sm_llcp_cback (tLLCP_SAP_CBACK_DATA *p_data)
{
tNFA_CHO_RX_NDEF_STATUS rx_status;
CHO_TRACE_DEBUG2 ("nfa_cho_sm_llcp_cback (): event:0x%02X, local_sap:0x%02X",
p_data->hdr.event, p_data->hdr.local_sap);
switch (p_data->hdr.event)
{
case LLCP_SAP_EVT_DATA_IND:
/* check if we received complete Handover Message */
rx_status = nfa_cho_reassemble_ho_msg (p_data->data_ind.local_sap,
p_data->data_ind.remote_sap);
if (rx_status == NFA_CHO_RX_NDEF_COMPLETE)
{
nfa_cho_sm_execute (NFA_CHO_RX_HANDOVER_MSG_EVT, NULL);
}
break;
case LLCP_SAP_EVT_CONNECT_IND:
nfa_cho_sm_execute (NFA_CHO_LLCP_CONNECT_IND_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
case LLCP_SAP_EVT_CONNECT_RESP:
nfa_cho_sm_execute (NFA_CHO_LLCP_CONNECT_RESP_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
case LLCP_SAP_EVT_DISCONNECT_IND:
nfa_cho_sm_execute (NFA_CHO_LLCP_DISCONNECT_IND_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
case LLCP_SAP_EVT_DISCONNECT_RESP:
nfa_cho_sm_execute (NFA_CHO_LLCP_DISCONNECT_RESP_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
case LLCP_SAP_EVT_CONGEST:
nfa_cho_sm_execute (NFA_CHO_LLCP_CONGEST_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
case LLCP_SAP_EVT_LINK_STATUS:
nfa_cho_sm_execute (NFA_CHO_LLCP_LINK_STATUS_EVT, (tNFA_CHO_INT_EVENT_DATA *) p_data);
break;
default:
CHO_TRACE_ERROR1 ("Unknown event:0x%02X", p_data->hdr.event);
return;
}
}
/*******************************************************************************
**
** Function nfa_cho_sm_disabled
**
** Description Process event in disabled state
**
** Returns None
**
*******************************************************************************/
static void nfa_cho_sm_disabled (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data)
{
tNFA_CHO_EVT_DATA evt_data;
UINT16 remote_link_miu;
switch (event)
{
case NFA_CHO_API_REG_EVT:
evt_data.status = nfa_cho_proc_api_reg (p_data);
if (evt_data.status == NFA_STATUS_OK)
{
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
}
p_data->api_reg.p_cback (NFA_CHO_REG_EVT, &evt_data);
if (evt_data.status == NFA_STATUS_OK)
{
/* check if LLCP is already activated */
LLCP_GetLinkMIU (&nfa_cho_cb.local_link_miu, &remote_link_miu);
if (nfa_cho_cb.local_link_miu > 0)
{
nfa_cho_cb.flags |= NFA_CHO_FLAGS_LLCP_ACTIVATED;
/* Notify application LLCP link activated */
evt_data.activated.is_initiator = FALSE;
nfa_cho_cb.p_cback (NFA_CHO_ACTIVATED_EVT, &evt_data);
}
}
break;
default:
CHO_TRACE_ERROR0 ("Unknown event");
break;
}
}
/*******************************************************************************
**
** Function nfa_cho_sm_idle
**
** Description Process event in idle state
**
** Returns None
**
*******************************************************************************/
static void nfa_cho_sm_idle (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data)
{
UINT16 remote_link_miu;
tNFA_CHO_EVT_DATA evt_data;
tLLCP_CONNECTION_PARAMS params;
switch (event)
{
case NFA_CHO_API_REG_EVT:
evt_data.status = NFA_STATUS_FAILED;
p_data->api_reg.p_cback (NFA_CHO_REG_EVT, &evt_data);
break;
case NFA_CHO_API_DEREG_EVT:
nfa_cho_proc_api_dereg ();
nfa_cho_cb.state = NFA_CHO_ST_DISABLED;
break;
case NFA_CHO_API_CONNECT_EVT:
/* if LLCP activated then create data link connection */
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_LLCP_ACTIVATED)
{
if (nfa_cho_create_connection () == NFA_STATUS_OK)
{
/* waiting for connection confirm */
nfa_cho_cb.state = NFA_CHO_ST_W4_CC;
}
else
{
evt_data.disconnected.reason = NFA_CHO_DISC_REASON_CONNECTION_FAIL;
nfa_cho_cb.p_cback (NFA_CHO_DISCONNECTED_EVT, &evt_data);
}
}
else
{
evt_data.disconnected.reason = NFA_CHO_DISC_REASON_LINK_DEACTIVATED;
nfa_cho_cb.p_cback (NFA_CHO_DISCONNECTED_EVT, &evt_data);
}
break;
case NFA_CHO_API_DISCONNECT_EVT:
/* Nothing to disconnect */
nfa_cho_process_disconnection (NFA_CHO_DISC_REASON_API_REQUEST);
break;
case NFA_CHO_LLCP_CONNECT_IND_EVT:
/* accept connection request */
params.miu = (UINT16) (nfa_cho_cb.local_link_miu >= NFA_CHO_MIU ? NFA_CHO_MIU : nfa_cho_cb.local_link_miu);
params.rw = NFA_CHO_RW;
params.sn[0] = 0;
LLCP_ConnectCfm (p_data->llcp_cback_data.connect_ind.local_sap,
p_data->llcp_cback_data.connect_ind.remote_sap,
&params);
nfa_cho_cb.remote_miu = p_data->llcp_cback_data.connect_ind.miu;
nfa_cho_cb.remote_sap = p_data->llcp_cback_data.connect_ind.remote_sap;
nfa_cho_cb.local_sap = p_data->llcp_cback_data.connect_ind.local_sap;
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HR;
nfa_cho_cb.state = NFA_CHO_ST_CONNECTED;
nfa_cho_cb.congested = FALSE;
evt_data.connected.initial_role = NFA_CHO_ROLE_SELECTOR;
nfa_cho_cb.p_cback (NFA_CHO_CONNECTED_EVT, &evt_data);
break;
case NFA_CHO_LLCP_LINK_STATUS_EVT:
/*
** LLCP sends NFA_CHO_LLCP_DISCONNECT_IND_EVT for all data link connection
** before sending NFA_CHO_LLCP_LINK_STATUS_EVT for deactivation.
** This event can be received only in this state.
*/
if (p_data->llcp_cback_data.link_status.is_activated == TRUE)
{
nfa_cho_cb.flags |= NFA_CHO_FLAGS_LLCP_ACTIVATED;
/* store local link MIU to decide MIU of data link connection later */
LLCP_GetLinkMIU (&nfa_cho_cb.local_link_miu, &remote_link_miu);
/* Notify application LLCP link activated */
evt_data.activated.is_initiator = p_data->llcp_cback_data.link_status.is_initiator;
nfa_cho_cb.p_cback (NFA_CHO_ACTIVATED_EVT, &evt_data);
}
else
{
/* the other flags had been cleared by NFA_CHO_LLCP_DISCONNECT_IND_EVT */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_LLCP_ACTIVATED;
/* Notify application LLCP link deactivated */
evt_data.status = NFA_STATUS_OK;
nfa_cho_cb.p_cback (NFA_CHO_DEACTIVATED_EVT, &evt_data);
}
break;
case NFA_CHO_API_SEND_HR_EVT:
GKI_freebuf (p_data->api_send_hr.p_ndef);
break;
case NFA_CHO_API_SEND_HS_EVT:
GKI_freebuf (p_data->api_send_hs.p_ndef);
break;
case NFA_CHO_NDEF_TYPE_HANDLER_EVT:
nfa_cho_proc_ndef_type_handler_evt (p_data);
break;
default:
CHO_TRACE_ERROR0 ("Unknown event");
break;
}
}
/*******************************************************************************
**
** Function nfa_cho_sm_w4_cc
**
** Description Process event in waiting for connection confirm state
**
** Returns None
**
*******************************************************************************/
static void nfa_cho_sm_w4_cc (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data)
{
tNFA_CHO_EVT_DATA evt_data;
tLLCP_CONNECTION_PARAMS params;
switch (event)
{
case NFA_CHO_API_REG_EVT:
evt_data.status = NFA_STATUS_FAILED;
p_data->api_reg.p_cback (NFA_CHO_REG_EVT, &evt_data);
break;
case NFA_CHO_API_DEREG_EVT:
nfa_cho_proc_api_dereg ();
nfa_cho_cb.state = NFA_CHO_ST_DISABLED;
break;
case NFA_CHO_API_CONNECT_EVT:
evt_data.disconnected.reason = NFA_CHO_DISC_REASON_ALEADY_CONNECTED;
nfa_cho_cb.p_cback (NFA_CHO_DISCONNECTED_EVT, &evt_data);
break;
case NFA_CHO_API_DISCONNECT_EVT:
/* disconnect collision connection accepted by local device */
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
LLCP_DisconnectReq (nfa_cho_cb.collision_local_sap,
nfa_cho_cb.collision_remote_sap,
FALSE);
/* clear collision flag */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
}
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
/* we cannot send DISC because we don't know remote SAP */
nfa_cho_process_disconnection (NFA_CHO_DISC_REASON_API_REQUEST);
break;
case NFA_CHO_LLCP_CONNECT_RESP_EVT:
/* peer accepted connection request */
nfa_cho_cb.state = NFA_CHO_ST_CONNECTED;
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_LOCAL_HR;
nfa_cho_cb.congested = FALSE;
/* store data link connection parameters */
nfa_cho_cb.remote_miu = p_data->llcp_cback_data.connect_resp.miu;
nfa_cho_cb.remote_sap = p_data->llcp_cback_data.connect_resp.remote_sap;
nfa_cho_cb.local_sap = nfa_cho_cb.client_sap;
evt_data.connected.initial_role = NFA_CHO_ROLE_REQUESTER;
nfa_cho_cb.p_cback (NFA_CHO_CONNECTED_EVT, &evt_data);
break;
case NFA_CHO_LLCP_CONNECT_IND_EVT:
/* if already collision of connection */
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
LLCP_ConnectReject (p_data->llcp_cback_data.connect_ind.local_sap,
p_data->llcp_cback_data.connect_ind.remote_sap,
LLCP_SAP_DM_REASON_TEMP_REJECT_THIS);
}
else
{
/*
** accept connection request and set collision flag
** wait for accepting connection request from peer or Hr message
*/
params.miu = (UINT16) (nfa_cho_cb.local_link_miu >= NFA_CHO_MIU ? NFA_CHO_MIU : nfa_cho_cb.local_link_miu);
params.rw = NFA_CHO_RW;
params.sn[0] = 0;
LLCP_ConnectCfm (p_data->llcp_cback_data.connect_ind.local_sap,
p_data->llcp_cback_data.connect_ind.remote_sap,
&params);
nfa_cho_cb.flags |= NFA_CHO_FLAGS_CONN_COLLISION;
nfa_cho_cb.collision_remote_miu = p_data->llcp_cback_data.connect_ind.miu;
nfa_cho_cb.collision_remote_sap = p_data->llcp_cback_data.connect_ind.remote_sap;
nfa_cho_cb.collision_local_sap = p_data->llcp_cback_data.connect_ind.local_sap;
nfa_cho_cb.collision_congested = FALSE;
}
break;
case NFA_CHO_RX_HANDOVER_MSG_EVT:
/* peer device sent handover message before accepting connection */
/* clear collision flag */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
nfa_cho_cb.remote_miu = nfa_cho_cb.collision_remote_miu;
nfa_cho_cb.remote_sap = nfa_cho_cb.collision_remote_sap;
nfa_cho_cb.local_sap = nfa_cho_cb.collision_local_sap;
nfa_cho_cb.congested = nfa_cho_cb.collision_congested;
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HR;
nfa_cho_cb.state = NFA_CHO_ST_CONNECTED;
evt_data.connected.initial_role = NFA_CHO_ROLE_SELECTOR;
nfa_cho_cb.p_cback (NFA_CHO_CONNECTED_EVT, &evt_data);
/* process handover message in nfa_cho_cb.p_rx_ndef_msg */
nfa_cho_proc_rx_handover_msg ();
break;
case NFA_CHO_LLCP_DISCONNECT_RESP_EVT:
/*
** if peer rejected our connection request or there is no handover service in peer
** but we already accepted connection from peer
*/
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
/* clear collision flag */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
nfa_cho_cb.remote_miu = nfa_cho_cb.collision_remote_miu;
nfa_cho_cb.remote_sap = nfa_cho_cb.collision_remote_sap;
nfa_cho_cb.local_sap = nfa_cho_cb.collision_local_sap;
nfa_cho_cb.congested = nfa_cho_cb.collision_congested;
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HR;
nfa_cho_cb.state = NFA_CHO_ST_CONNECTED;
evt_data.connected.initial_role = NFA_CHO_ROLE_SELECTOR;
nfa_cho_cb.p_cback (NFA_CHO_CONNECTED_EVT, &evt_data);
}
else
{
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
nfa_cho_process_disconnection (NFA_CHO_DISC_REASON_CONNECTION_FAIL);
}
break;
case NFA_CHO_LLCP_DISCONNECT_IND_EVT:
/* if peer disconnects collision connection */
if ( (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
&&(p_data->llcp_cback_data.disconnect_ind.local_sap == nfa_cho_cb.collision_local_sap)
&&(p_data->llcp_cback_data.disconnect_ind.remote_sap == nfa_cho_cb.collision_remote_sap) )
{
/* clear collision flag */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
}
else /* Link failure before peer accepts or rejects connection request */
{
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
nfa_cho_process_disconnection (NFA_CHO_DISC_REASON_CONNECTION_FAIL);
}
break;
case NFA_CHO_LLCP_CONGEST_EVT:
/* if collision connection is congested */
if ( (p_data->llcp_cback_data.congest.link_type == LLCP_LINK_TYPE_DATA_LINK_CONNECTION)
&&(nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION))
{
nfa_cho_cb.collision_congested = p_data->llcp_cback_data.congest.is_congested;
}
break;
case NFA_CHO_API_SEND_HR_EVT:
GKI_freebuf (p_data->api_send_hr.p_ndef);
break;
case NFA_CHO_API_SEND_HS_EVT:
GKI_freebuf (p_data->api_send_hs.p_ndef);
break;
case NFA_CHO_NDEF_TYPE_HANDLER_EVT:
nfa_cho_proc_ndef_type_handler_evt (p_data);
break;
default:
CHO_TRACE_ERROR0 ("Unknown event");
break;
}
}
/*******************************************************************************
**
** Function nfa_cho_sm_connected
**
** Description Process event in connected state
**
** Returns None
**
*******************************************************************************/
static void nfa_cho_sm_connected (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data)
{
tNFA_CHO_EVT_DATA evt_data;
tNFA_STATUS status;
switch (event)
{
case NFA_CHO_API_REG_EVT:
evt_data.status = NFA_STATUS_FAILED;
p_data->api_reg.p_cback (NFA_CHO_REG_EVT, &evt_data);
break;
case NFA_CHO_API_DEREG_EVT:
nfa_cho_proc_api_dereg ();
nfa_cho_cb.state = NFA_CHO_ST_DISABLED;
break;
case NFA_CHO_API_CONNECT_EVT:
/* it could be race condition, let app know outgoing connection failed */
evt_data.disconnected.reason = NFA_CHO_DISC_REASON_ALEADY_CONNECTED;
nfa_cho_cb.p_cback (NFA_CHO_DISCONNECTED_EVT, &evt_data);
break;
case NFA_CHO_API_DISCONNECT_EVT:
/* disconnect collision connection accepted by local device */
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
LLCP_DisconnectReq (nfa_cho_cb.collision_local_sap,
nfa_cho_cb.collision_remote_sap,
FALSE);
/* clear collision flag */
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
}
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
/* store disconnect reason */
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_API_REQUEST;
break;
case NFA_CHO_API_SEND_HR_EVT:
if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_LOCAL_HR)
{
/* Send Handover Request Message */
status = nfa_cho_send_hr (&p_data->api_send_hr);
if (status == NFA_STATUS_OK)
{
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HS;
/* start timer for Handover Select Message */
nfa_sys_start_timer (&nfa_cho_cb.timer, 0, NFA_CHO_TIMEOUT_FOR_HS);
}
else
{
CHO_TRACE_ERROR0 ("NFA CHO failed to send Hr");
nfa_cho_notify_tx_fail_evt (status);
}
}
else
{
CHO_TRACE_ERROR0 ("NFA CHO got unexpected NFA_CHO_API_SEND_HR_EVT");
nfa_cho_notify_tx_fail_evt (NFA_STATUS_SEMANTIC_ERROR);
}
GKI_freebuf (p_data->api_send_hr.p_ndef);
break;
case NFA_CHO_API_SEND_HS_EVT:
if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_LOCAL_HS)
{
/* send Handover Select Message */
status = nfa_cho_send_hs (&p_data->api_send_hs);
if (status == NFA_STATUS_OK)
{
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HR;
}
else
{
CHO_TRACE_ERROR0 ("NFA CHO failed to send Hs");
nfa_cho_notify_tx_fail_evt (status);
}
}
else
{
CHO_TRACE_ERROR0 ("NFA CHO got unexpected NFA_CHO_API_SEND_HS_EVT");
nfa_cho_notify_tx_fail_evt (NFA_STATUS_SEMANTIC_ERROR);
}
GKI_freebuf (p_data->api_send_hs.p_ndef);
break;
case NFA_CHO_API_SEL_ERR_EVT:
/* application detected error */
if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_LOCAL_HS)
{
/* Send Handover Select Error record */
status = nfa_cho_send_hs_error (p_data->api_sel_err.error_reason,
p_data->api_sel_err.error_data);
if (status == NFA_STATUS_OK)
{
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_REMOTE_HR;
}
else
{
CHO_TRACE_ERROR0 ("Failed to send Hs Error record");
nfa_cho_notify_tx_fail_evt (status);
}
}
else
{
CHO_TRACE_ERROR0 ("NFA CHO got unexpected NFA_CHO_API_SEL_ERR_EVT");
nfa_cho_notify_tx_fail_evt (NFA_STATUS_SEMANTIC_ERROR);
}
break;
case NFA_CHO_LLCP_CONNECT_RESP_EVT:
/* peer accepted connection after we accepted and received Hr */
/* disconnect data link connection created by local device */
LLCP_DisconnectReq (p_data->llcp_cback_data.connect_resp.local_sap,
p_data->llcp_cback_data.connect_resp.remote_sap,
FALSE);
break;
case NFA_CHO_LLCP_CONNECT_IND_EVT:
LLCP_ConnectReject (p_data->llcp_cback_data.connect_ind.local_sap,
p_data->llcp_cback_data.connect_ind.remote_sap,
LLCP_SAP_DM_REASON_TEMP_REJECT_THIS);
break;
case NFA_CHO_RX_HANDOVER_MSG_EVT:
/* process handover message in nfa_cho_cb.p_rx_ndef_msg */
nfa_cho_proc_rx_handover_msg ();
break;
case NFA_CHO_LLCP_DISCONNECT_IND_EVT:
if ( (p_data->llcp_cback_data.disconnect_ind.local_sap == nfa_cho_cb.local_sap)
&&(p_data->llcp_cback_data.disconnect_ind.remote_sap == nfa_cho_cb.remote_sap) )
{
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
nfa_cho_process_disconnection (NFA_CHO_DISC_REASON_PEER_REQUEST);
}
else /* if disconnection of collision conneciton */
{
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
}
break;
case NFA_CHO_LLCP_DISCONNECT_RESP_EVT:
if ( (p_data->llcp_cback_data.disconnect_ind.local_sap == nfa_cho_cb.local_sap)
&&(p_data->llcp_cback_data.disconnect_ind.remote_sap == nfa_cho_cb.remote_sap) )
{
nfa_cho_cb.state = NFA_CHO_ST_IDLE;
nfa_cho_process_disconnection (nfa_cho_cb.disc_reason);
}
else /* if disconnection of collision conneciton */
{
nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;
}
break;
case NFA_CHO_LLCP_CONGEST_EVT:
/* if data link connection is congested */
if ( (p_data->llcp_cback_data.congest.link_type == LLCP_LINK_TYPE_DATA_LINK_CONNECTION)
&&(p_data->llcp_cback_data.congest.local_sap == nfa_cho_cb.local_sap)
&&(p_data->llcp_cback_data.congest.remote_sap == nfa_cho_cb.remote_sap) )
{
nfa_cho_cb.congested = p_data->llcp_cback_data.congest.is_congested;
if (!nfa_cho_cb.congested)
{
/* send remaining message if any */
if ( (nfa_cho_cb.p_tx_ndef_msg)
&&(nfa_cho_cb.tx_ndef_sent_size < nfa_cho_cb.tx_ndef_cur_size) )
{
nfa_cho_send_handover_msg ();
}
}
}
break;
case NFA_CHO_TIMEOUT_EVT:
if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HS)
{
CHO_TRACE_ERROR0 ("Failed to receive Hs message");
}
else if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
{
/* we didn't get complete Hr, don't need to notify application */
CHO_TRACE_ERROR0 ("Failed to receive Hr message");
}
/* store disconnect reason and disconnect */
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_TIMEOUT;
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
break;
case NFA_CHO_NDEF_TYPE_HANDLER_EVT:
nfa_cho_proc_ndef_type_handler_evt (p_data);
break;
default:
CHO_TRACE_ERROR0 ("Unknown event");
break;
}
}
/*******************************************************************************
**
** Function nfa_cho_sm_execute
**
** Description Process event in state machine
**
** Returns None
**
*******************************************************************************/
void nfa_cho_sm_execute (tNFA_CHO_INT_EVT event, tNFA_CHO_INT_EVENT_DATA *p_data)
{
#if (BT_TRACE_VERBOSE == TRUE)
CHO_TRACE_DEBUG2 ("nfa_cho_sm_execute (): State[%s], Event[%s]",
nfa_cho_state_code (nfa_cho_cb.state),
nfa_cho_evt_code (event));
#else
CHO_TRACE_DEBUG2 ("nfa_cho_sm_execute (): State[%d], Event[%d]",
nfa_cho_cb.state, event);
#endif
switch (nfa_cho_cb.state)
{
case NFA_CHO_ST_DISABLED:
nfa_cho_sm_disabled (event, p_data);
break;
case NFA_CHO_ST_IDLE:
nfa_cho_sm_idle (event, p_data);
break;
case NFA_CHO_ST_W4_CC:
nfa_cho_sm_w4_cc (event, p_data);
break;
case NFA_CHO_ST_CONNECTED:
nfa_cho_sm_connected (event, p_data);
break;
default:
CHO_TRACE_ERROR0 ("Unknown state");
break;
}
}
/*******************************************************************************
**
** Function nfa_cho_resolve_collision
**
** Description Resolve collision by random number in Hr
**
** Returns None
**
*******************************************************************************/
void nfa_cho_resolve_collision (BOOLEAN *p_free_hr)
{
tNFA_CHO_ROLE_TYPE role;
tNFA_STATUS status;
/* resolve collistion by random number */
role = nfa_cho_get_local_device_role (nfa_cho_cb.rx_ndef_cur_size,
nfa_cho_cb.p_rx_ndef_msg);
/* if local device becomes selector */
if (role == NFA_CHO_ROLE_SELECTOR)
{
/* peer device is winner so clean up any collision */
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
/* disconnect data link connection created by local device */
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
nfa_cho_cb.remote_miu = nfa_cho_cb.collision_remote_miu;
nfa_cho_cb.remote_sap = nfa_cho_cb.collision_remote_sap;
nfa_cho_cb.local_sap = nfa_cho_cb.collision_local_sap;
nfa_cho_cb.congested = nfa_cho_cb.collision_congested;
}
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_LOCAL_HS;
nfa_cho_proc_hr (nfa_cho_cb.rx_ndef_cur_size,
nfa_cho_cb.p_rx_ndef_msg);
*p_free_hr = TRUE;
}
/* if both random numbers are equal */
else if (role == NFA_CHO_ROLE_UNDECIDED)
{
/* send Hr with new random number */
if (nfa_cho_cb.p_tx_ndef_msg)
{
status = nfa_cho_update_random_number (nfa_cho_cb.p_tx_ndef_msg);
if (status == NFA_STATUS_OK)
{
nfa_cho_cb.tx_ndef_sent_size = 0;
status = nfa_cho_send_handover_msg ();
}
}
else
{
status = NFA_STATUS_FAILED;
}
if (status == NFA_STATUS_FAILED)
{
CHO_TRACE_ERROR0 ("Failed to send Hr record with new random number");
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_INTERNAL_ERROR;
/* disconnect and notify application */
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
}
else
{
/* restart timer */
nfa_sys_start_timer (&nfa_cho_cb.timer, 0, NFA_CHO_TIMEOUT_FOR_HS);
/* Don't free previous tx NDEF message because we are reusing it */
*p_free_hr = FALSE;
}
}
else /* if (role == NFA_CHO_ROLE_REQUESTER) */
{
/* wait for "Hs" record */
*p_free_hr = TRUE;
}
}
/*******************************************************************************
**
** Function nfa_cho_check_disconnect_collision
**
** Description Disconnect any collision connection
**
** Returns None
**
*******************************************************************************/
void nfa_cho_check_disconnect_collision (void)
{
if (nfa_cho_cb.flags & NFA_CHO_FLAGS_CONN_COLLISION)
{
/* disconnect collision connection */
LLCP_DisconnectReq (nfa_cho_cb.collision_local_sap,
nfa_cho_cb.collision_remote_sap,
FALSE);
}
}
/*******************************************************************************
**
** Function nfa_cho_proc_rx_handover_msg
**
** Description Process received Handover Message
**
** Returns None
**
*******************************************************************************/
void nfa_cho_proc_rx_handover_msg (void)
{
tNFA_CHO_MSG_TYPE msg_type;
BOOLEAN free_tx_ndef_msg = TRUE;
/* get message type before processing to check collision */
msg_type = nfa_cho_get_msg_type (nfa_cho_cb.rx_ndef_cur_size,
nfa_cho_cb.p_rx_ndef_msg);
if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HS)
{
/* if we sent "Hr" but received "Hr", collision */
if (msg_type == NFA_CHO_MSG_HR)
{
nfa_cho_resolve_collision (&free_tx_ndef_msg);
}
else if (msg_type == NFA_CHO_MSG_HS)
{
/* parse and report application */
nfa_cho_proc_hs (nfa_cho_cb.rx_ndef_cur_size,
nfa_cho_cb.p_rx_ndef_msg);
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_LOCAL_HR;
}
else
{
CHO_TRACE_ERROR0 ("nfa_cho_proc_rx_handover_msg (): Unknown Message Type");
nfa_cho_check_disconnect_collision ();
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_UNKNOWN_MSG;
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
}
}
else if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
{
if (msg_type == NFA_CHO_MSG_HR)
{
/* parse and notify NFA_CHO_REQ_EVT to application */
nfa_cho_proc_hr (nfa_cho_cb.rx_ndef_cur_size,
nfa_cho_cb.p_rx_ndef_msg);
/* In case of parsing error, let peer got timeout (1 sec) */
/* wait for application selection */
nfa_cho_cb.substate = NFA_CHO_SUBSTATE_W4_LOCAL_HS;
}
else
{
CHO_TRACE_ERROR0 ("nfa_cho_proc_rx_handover_msg (): Expecting Handover Request");
nfa_cho_check_disconnect_collision ();
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_SEMANTIC_ERROR;
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
}
}
else
{
CHO_TRACE_ERROR1 ("nfa_cho_proc_rx_handover_msg (): Unexpected data in substate (0x%x)", nfa_cho_cb.substate);
nfa_cho_check_disconnect_collision ();
nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_SEMANTIC_ERROR;
LLCP_DisconnectReq (nfa_cho_cb.local_sap,
nfa_cho_cb.remote_sap,
FALSE);
}
if ((free_tx_ndef_msg) && (nfa_cho_cb.p_tx_ndef_msg))
{
GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
nfa_cho_cb.p_tx_ndef_msg = NULL;
}
/* processing rx message is done, free buffer for rx handover message */
if (nfa_cho_cb.p_rx_ndef_msg)
{
GKI_freebuf (nfa_cho_cb.p_rx_ndef_msg);
nfa_cho_cb.p_rx_ndef_msg = NULL;
}
}
#if (BT_TRACE_VERBOSE == TRUE)
/*******************************************************************************
**
** Function nfa_cho_state_code
**
** Description
**
** Returns string of state
**
*******************************************************************************/
static char *nfa_cho_state_code (tNFA_CHO_STATE state_code)
{
switch (state_code)
{
case NFA_CHO_ST_DISABLED:
return "DISABLED";
case NFA_CHO_ST_IDLE:
return "IDLE";
case NFA_CHO_ST_CONNECTED:
return "CONNECTED";
default:
return "unknown state";
}
}
/*******************************************************************************
**
** Function nfa_cho_evt_code
**
** Description
**
** Returns string of event
**
*******************************************************************************/
char *nfa_cho_evt_code (tNFA_CHO_INT_EVT evt_code)
{
switch (evt_code)
{
case NFA_CHO_API_REG_EVT:
return "API_REG";
case NFA_CHO_API_DEREG_EVT:
return "API_DEREG";
case NFA_CHO_API_CONNECT_EVT:
return "API_CONNECT";
case NFA_CHO_API_DISCONNECT_EVT:
return "API_DISCONNECT";
case NFA_CHO_API_SEND_HR_EVT:
return "API_SEND_HR";
case NFA_CHO_API_SEND_HS_EVT:
return "API_SEND_HS";
case NFA_CHO_API_SEL_ERR_EVT:
return "API_SEL_ERR";
case NFA_CHO_RX_HANDOVER_MSG_EVT:
return "RX_HANDOVER_MSG";
case NFA_CHO_LLCP_CONNECT_IND_EVT:
return "LLCP_CONNECT_IND";
case NFA_CHO_LLCP_CONNECT_RESP_EVT:
return "LLCP_CONNECT_RESP";
case NFA_CHO_LLCP_DISCONNECT_IND_EVT:
return "LLCP_DISCONNECT_IND";
case NFA_CHO_LLCP_DISCONNECT_RESP_EVT:
return "LLCP_DISCONNECT_RESP";
case NFA_CHO_LLCP_CONGEST_EVT:
return "LLCP_CONGEST";
case NFA_CHO_LLCP_LINK_STATUS_EVT:
return "LLCP_LINK_STATUS";
case NFA_CHO_NDEF_TYPE_HANDLER_EVT:
return "NDEF_TYPE_HANDLER";
case NFA_CHO_TIMEOUT_EVT:
return "TIMEOUT";
default:
return "unknown event";
}
}
#endif /* Debug Functions */