/****************************************************************************** * * 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 #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, ¶ms); 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, ¶ms); 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 */