C++程序  |  1023行  |  34.23 KB

/******************************************************************************
 *
 *  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 */