C++程序  |  2006行  |  64.2 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 utilities implementation file for the NFA Connection
 *  Handover.
 *
 ******************************************************************************/

#include "string.h"
#include "nfa_sys.h"
#include "llcp_api.h"
#include "llcp_defs.h"
#include "nfa_p2p_int.h"
#include "nfa_cho_api.h"
#include "nfa_cho_int.h"
#include "trace_api.h"
#include "nfa_mem_co.h"

/*****************************************************************************
**  Constants
*****************************************************************************/
/* Handover server name on LLCP */
static char *p_cho_service_name = "urn:nfc:sn:handover";

/* Handover Request Record Type */
static UINT8 hr_rec_type[HR_REC_TYPE_LEN] = { 0x48, 0x72 }; /* "Hr" */

/* Handover Select Record Type */
static UINT8 hs_rec_type[HS_REC_TYPE_LEN] = { 0x48, 0x73 }; /* "Hs" */

/* Handover Carrier recrod Type */
/* static UINT8 hc_rec_type[HC_REC_TYPE_LEN] = { 0x48, 0x63 }; "Hc" */

/* Collision Resolution Record Type */
static UINT8 cr_rec_type[CR_REC_TYPE_LEN] = { 0x63, 0x72 }; /* "cr" */

/* Alternative Carrier Record Type */
static UINT8 ac_rec_type[AC_REC_TYPE_LEN] = { 0x61, 0x63 }; /* "ac" */

/* Error Record Type */
static UINT8 err_rec_type[ERR_REC_TYPE_LEN] = { 0x65, 0x72, 0x72 }; /* "err" */

/* Bluetooth OOB Data Type */
static UINT8 *p_bt_oob_rec_type = (UINT8 *) "application/vnd.bluetooth.ep.oob";

/* WiFi Data Type */
static UINT8 *p_wifi_rec_type = (UINT8 *) "application/vnd.wfa.wsc";

/*****************************************************************************
**  Global Variables
*****************************************************************************/

/*****************************************************************************
**  Static Functions
*****************************************************************************/
static void nfa_cho_ndef_cback (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *p_data);

/*******************************************************************************
**
** Function         nfa_cho_ndef_cback
**
** Description      callback function from NDEF handler
**                  Post NDEF handler callback event to NFA Connection Handover module
**
** Returns          None
**
*******************************************************************************/
static void nfa_cho_ndef_cback (tNFA_NDEF_EVT event, tNFA_NDEF_EVT_DATA *p_data)
{
    tNFA_CHO_NDEF_TYPE_HDLR_EVT *p_msg;
    tNFA_CHO_MSG_TYPE           msg_type;

    CHO_TRACE_DEBUG1 ("nfa_cho_ndef_cback () event=%d", event);

    if ((p_msg = (tNFA_CHO_NDEF_TYPE_HDLR_EVT *) GKI_getbuf (sizeof (tNFA_CHO_NDEF_TYPE_HDLR_EVT))) != NULL)
    {
        p_msg->hdr.event = NFA_CHO_NDEF_TYPE_HANDLER_EVT;

        /* copy NDEF handler callback event and data */
        p_msg->event = event;
        memcpy (&(p_msg->data), p_data, sizeof (tNFA_NDEF_EVT_DATA));

        /* if it has NDEF message */
        if (event == NFA_NDEF_DATA_EVT)
        {
            if (p_data->ndef_data.ndef_type_handle == nfa_cho_cb.bt_ndef_type_handle )
            {
                msg_type = nfa_cho_get_msg_type (p_data->ndef_data.len,
                                                 p_data->ndef_data.p_data);
                if (msg_type != NFA_CHO_MSG_BT_OOB)
                {
                    /* This is not simplified BT OOB Message. It contains BT OOB Message. */
                    GKI_freebuf (p_msg);
                    return;
                }
            }
            else if (p_data->ndef_data.ndef_type_handle == nfa_cho_cb.wifi_ndef_type_handle )
            {
                msg_type = nfa_cho_get_msg_type (p_data->ndef_data.len,
                                                 p_data->ndef_data.p_data);
                if (msg_type != NFA_CHO_MSG_WIFI)
                {
                    /* This is not simplified WiFi Message. It contains WiFi Message. */
                    GKI_freebuf (p_msg);
                    return;
                }
            }

            /*
            ** NDEF message could be bigger than max GKI buffer
            ** so allocate memory from platform.
            */
            p_msg->data.ndef_data.p_data = (UINT8 *) nfa_mem_co_alloc (p_msg->data.ndef_data.len);

            if (p_msg->data.ndef_data.p_data)
            {
                memcpy (p_msg->data.ndef_data.p_data,
                        p_data->ndef_data.p_data,
                        p_msg->data.ndef_data.len);
            }
            else
            {
                CHO_TRACE_ERROR1 ("Failed nfa_mem_co_alloc () for %d bytes", p_msg->data.ndef_data.len);
                GKI_freebuf (p_msg);
                return;
            }
        }

        nfa_sys_sendmsg (p_msg);
    }
}

/*******************************************************************************
**
** Function         nfa_cho_proc_ndef_type_handler_evt
**
** Description      Process events (registration and NDEF data) from NFA NDEF
**                  Type Handler
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
void nfa_cho_proc_ndef_type_handler_evt (tNFA_CHO_INT_EVENT_DATA *p_evt_data)
{
    tNFA_CHO_MSG_TYPE msg_type;

    if (p_evt_data->ndef_type_hdlr.event == NFA_NDEF_REGISTER_EVT)
    {
        if (p_evt_data->ndef_type_hdlr.data.ndef_reg.status == NFA_STATUS_OK)
        {
            /* store handle for deregistration */
            if (nfa_cho_cb.hs_ndef_type_handle == NFA_HANDLE_INVALID)
            {
                nfa_cho_cb.hs_ndef_type_handle = p_evt_data->ndef_type_hdlr.data.ndef_reg.ndef_type_handle;
            }
            else if (nfa_cho_cb.bt_ndef_type_handle == NFA_HANDLE_INVALID)
            {
                nfa_cho_cb.bt_ndef_type_handle = p_evt_data->ndef_type_hdlr.data.ndef_reg.ndef_type_handle;
            }
            else if (nfa_cho_cb.wifi_ndef_type_handle == NFA_HANDLE_INVALID)
            {
                nfa_cho_cb.wifi_ndef_type_handle = p_evt_data->ndef_type_hdlr.data.ndef_reg.ndef_type_handle;
            }
        }
    }
    else if (p_evt_data->ndef_type_hdlr.event == NFA_NDEF_DATA_EVT)
    {
        /* if negotiated handover is on going, then ignore static handover */
        if (nfa_cho_cb.state != NFA_CHO_ST_CONNECTED)
        {
#if (BT_TRACE_PROTOCOL == TRUE)
            DispNDEFMsg (p_evt_data->ndef_type_hdlr.data.ndef_data.p_data,
                         p_evt_data->ndef_type_hdlr.data.ndef_data.len, TRUE);
#endif
            msg_type = nfa_cho_get_msg_type (p_evt_data->ndef_type_hdlr.data.ndef_data.len,
                                             p_evt_data->ndef_type_hdlr.data.ndef_data.p_data);

            if (msg_type == NFA_CHO_MSG_HS)
            {
                nfa_cho_proc_hs (p_evt_data->ndef_type_hdlr.data.ndef_data.len,
                                 p_evt_data->ndef_type_hdlr.data.ndef_data.p_data);
            }
            else if (  (msg_type == NFA_CHO_MSG_BT_OOB)
                     ||(msg_type == NFA_CHO_MSG_WIFI)  )
            {
                /* simplified BT OOB/Wifi Message */
                nfa_cho_proc_simplified_format (p_evt_data->ndef_type_hdlr.data.ndef_data.len,
                                                p_evt_data->ndef_type_hdlr.data.ndef_data.p_data);
            }
            else
            {
                CHO_TRACE_ERROR0 ("Unexpected CHO Message Type");
            }
        }

        nfa_mem_co_free (p_evt_data->ndef_type_hdlr.data.ndef_data.p_data);
    }
}

/*******************************************************************************
**
** Function         nfa_cho_proc_api_reg
**
** Description      Process registeration request from application
**                  Register Handover server on LLCP for negotiated handover
**                  Register handover select records on NDEF handler for static handover
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_proc_api_reg (tNFA_CHO_INT_EVENT_DATA *p_evt_data)
{
    CHO_TRACE_DEBUG1 ("nfa_cho_proc_api_reg (): enable_server=%d",
                      p_evt_data->api_reg.enable_server);

    if (p_evt_data->api_reg.enable_server == TRUE)
    {
        /* Register Handover server on LLCP for negotiated handover */
        nfa_cho_cb.server_sap = LLCP_RegisterServer (LLCP_INVALID_SAP,
                                                     LLCP_LINK_TYPE_DATA_LINK_CONNECTION,
                                                     p_cho_service_name,
                                                     nfa_cho_sm_llcp_cback);
        if (nfa_cho_cb.server_sap == LLCP_INVALID_SAP)
        {
            CHO_TRACE_ERROR0 ("Cannot register CHO server");
            return NFA_STATUS_FAILED;
        }
        else
        {
            nfa_p2p_enable_listening (NFA_ID_CHO, FALSE);
        }
    }
    else
    {
        /*
        ** Register Handover client on LLCP for negotiated handover
        ** LLCP will notify link status through callback
        */
        nfa_cho_cb.client_sap = LLCP_RegisterClient (LLCP_LINK_TYPE_DATA_LINK_CONNECTION,
                                                     nfa_cho_sm_llcp_cback);

        if (nfa_cho_cb.client_sap == LLCP_INVALID_SAP)
        {
            CHO_TRACE_ERROR0 ("Cannot register CHO client");
            return NFA_STATUS_FAILED;
        }

        /* set flag not to deregister client when disconnected */
        nfa_cho_cb.flags |= NFA_CHO_FLAGS_CLIENT_ONLY;
    }

    /* Register handover select record on NDEF handler for static handover */
    if (nfa_cho_cb.hs_ndef_type_handle == NFA_HANDLE_INVALID)
    {
        NFA_RegisterNDefTypeHandler (TRUE, NFA_TNF_WKT, hs_rec_type, HS_REC_TYPE_LEN,
                                     nfa_cho_ndef_cback);
    }
    if (nfa_cho_cb.bt_ndef_type_handle == NFA_HANDLE_INVALID)
    {
        NFA_RegisterNDefTypeHandler (TRUE, NFA_TNF_RFC2046_MEDIA,
                                     p_bt_oob_rec_type, (UINT8) strlen ((char *) p_bt_oob_rec_type),
                                     nfa_cho_ndef_cback);
    }
    if (nfa_cho_cb.wifi_ndef_type_handle == NFA_HANDLE_INVALID)
    {
        NFA_RegisterNDefTypeHandler (TRUE, NFA_TNF_RFC2046_MEDIA,
                                     p_wifi_rec_type, (UINT8) strlen ((char *) p_wifi_rec_type),
                                     nfa_cho_ndef_cback);
    }

    nfa_cho_cb.p_cback = p_evt_data->api_reg.p_cback;

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_proc_api_dereg
**
** Description      Process deregisteration request from application
**                  Disconnect LLCP connection if any
**                  Deregister callback from NDEF handler and NFA P2P
**
** Returns          None
**
*******************************************************************************/
void nfa_cho_proc_api_dereg (void)
{
    CHO_TRACE_DEBUG0 ("nfa_cho_proc_api_dereg ()");

    /* Deregister outgoing connection, data link will be disconnected if any */
    if (nfa_cho_cb.client_sap != LLCP_INVALID_SAP)
    {
        LLCP_Deregister (nfa_cho_cb.client_sap);
        nfa_cho_cb.client_sap = LLCP_INVALID_SAP;
    }

    /* Close Connection Handover server in LLCP, data link will be disconnected if any */
    if (nfa_cho_cb.server_sap != LLCP_INVALID_SAP)
    {
        LLCP_Deregister (nfa_cho_cb.server_sap);
        nfa_cho_cb.server_sap = LLCP_INVALID_SAP;
    }

    /* Deregister type handler if any */
    if (nfa_cho_cb.hs_ndef_type_handle != NFA_HANDLE_INVALID)
    {
        NFA_DeregisterNDefTypeHandler (nfa_cho_cb.hs_ndef_type_handle);
        nfa_cho_cb.hs_ndef_type_handle = NFA_HANDLE_INVALID;
    }

    if (nfa_cho_cb.bt_ndef_type_handle != NFA_HANDLE_INVALID)
    {
        NFA_DeregisterNDefTypeHandler (nfa_cho_cb.bt_ndef_type_handle);
        nfa_cho_cb.bt_ndef_type_handle = NFA_HANDLE_INVALID;
    }

    if (nfa_cho_cb.wifi_ndef_type_handle != NFA_HANDLE_INVALID)
    {
        NFA_DeregisterNDefTypeHandler (nfa_cho_cb.wifi_ndef_type_handle);
        nfa_cho_cb.wifi_ndef_type_handle = NFA_HANDLE_INVALID;
    }

    nfa_sys_stop_timer (&nfa_cho_cb.timer);
    nfa_cho_cb.p_cback = NULL;
    nfa_cho_cb.flags   = 0;

    nfa_p2p_disable_listening (NFA_ID_CHO, FALSE);
}

/*******************************************************************************
**
** Function         nfa_cho_create_connection
**
** Description      Create data link connection with handover server in remote
**
**
** Returns          None
**
*******************************************************************************/
tNFA_STATUS nfa_cho_create_connection (void)
{
    tLLCP_CONNECTION_PARAMS conn_params;
    tNFA_STATUS             status = NFA_STATUS_FAILED;

    CHO_TRACE_DEBUG0 ("nfa_cho_create_connection ()");

    if (nfa_cho_cb.client_sap == LLCP_INVALID_SAP)
    {
        nfa_cho_cb.client_sap = LLCP_RegisterClient (LLCP_LINK_TYPE_DATA_LINK_CONNECTION,
                                                     nfa_cho_sm_llcp_cback);
    }

    if (nfa_cho_cb.client_sap == LLCP_INVALID_SAP)
    {
        CHO_TRACE_ERROR0 ("Cannot register CHO client");
    }
    else
    {
        /* create data link connection with server name */
        conn_params.miu = (UINT16) (nfa_cho_cb.local_link_miu >= NFA_CHO_MIU ? NFA_CHO_MIU : nfa_cho_cb.local_link_miu);
        conn_params.rw  = NFA_CHO_RW;
        BCM_STRNCPY_S (conn_params.sn, sizeof (conn_params.sn),
                       p_cho_service_name, LLCP_MAX_SN_LEN);
        conn_params.sn[LLCP_MAX_SN_LEN] = 0;

        if (LLCP_ConnectReq (nfa_cho_cb.client_sap, LLCP_SAP_SDP, &conn_params) == LLCP_STATUS_SUCCESS)
            status = NFA_STATUS_OK;
    }

    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_process_disconnection
**
** Description      Clean up buffers and notify disconnection to application
**
**
** Returns          None
**
*******************************************************************************/
void nfa_cho_process_disconnection (tNFA_CHO_DISC_REASON disc_reason)
{
    tNFA_CHO_EVT_DATA evt_data;

    nfa_sys_stop_timer (&nfa_cho_cb.timer);

    /* free buffer for Tx/Rx NDEF message */
    if (nfa_cho_cb.p_tx_ndef_msg)
    {
        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;
    }
    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 no server is registered on LLCP, do not deregister client to get link statue from LLCP */
    if (!(nfa_cho_cb.flags & NFA_CHO_FLAGS_CLIENT_ONLY))
    {
        if (nfa_cho_cb.client_sap != LLCP_INVALID_SAP)
        {
            LLCP_Deregister (nfa_cho_cb.client_sap);
            nfa_cho_cb.client_sap = LLCP_INVALID_SAP;
        }
    }

    nfa_cho_cb.flags &= ~NFA_CHO_FLAGS_CONN_COLLISION;

    evt_data.disconnected.reason = disc_reason;
    nfa_cho_cb.p_cback (NFA_CHO_DISCONNECTED_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_cho_notify_tx_fail_evt
**
** Description      Notify application of NFA_CHO_TX_FAIL_EVT
**
**
** Returns          None
**
*******************************************************************************/
void nfa_cho_notify_tx_fail_evt (tNFA_STATUS status)
{
    tNFA_CHO_EVT_DATA evt_data;

    CHO_TRACE_DEBUG0 ("nfa_cho_notify_tx_fail_evt ()");

    evt_data.status = status;

    if (nfa_cho_cb.p_cback)
        nfa_cho_cb.p_cback (NFA_CHO_TX_FAIL_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_cho_reassemble_ho_msg
**
** Description      Reassemble received data for handover message
**
**
** Returns          tNFA_CHO_RX_NDEF_STATUS
**
*******************************************************************************/
tNFA_CHO_RX_NDEF_STATUS nfa_cho_reassemble_ho_msg (UINT8 local_sap, UINT8 remote_sap)
{
    tNFA_CHO_RX_NDEF_STATUS rx_status;

    nfa_sys_stop_timer (&nfa_cho_cb.timer);

    /*
    ** allocate memory for NDEF message for the first segment
    ** validate NDEF message to check if received complete message
    */
    rx_status = nfa_cho_read_ndef_msg (local_sap, remote_sap);

    /* if Temporary Memory Constraint */
    if (rx_status == NFA_CHO_RX_NDEF_TEMP_MEM)
    {
        CHO_TRACE_ERROR0 ("Failed due to Temporary Memory Constraint");

        /* if we are expecting Hr then send Hs Error record */
        if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
        {
            /* ask retry later, handover request will disconnect */
            nfa_cho_send_hs_error (NFA_CHO_ERROR_TEMP_MEM, NFA_CHO_TIMEOUT_FOR_RETRY);
        }
        else
        {
            /* we cannot send error record, so disconnect */
            nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_INTERNAL_ERROR;
            LLCP_DisconnectReq (nfa_cho_cb.local_sap, nfa_cho_cb.remote_sap, FALSE);
        }
    }
    /* Permanent Memory Constraint */
    else if (rx_status == NFA_CHO_RX_NDEF_PERM_MEM)
    {
        CHO_TRACE_ERROR0 ("Failed due to Permanent Memory Constraint");

        /* if we are expecting Hr then send Hs Error record */
        if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
        {
            /*
            ** notify our buffer size and ask retry with modified message later
            ** handover request will disconnect
            */
            nfa_cho_send_hs_error (NFA_CHO_ERROR_PERM_MEM, nfa_cho_cb.rx_ndef_buf_size);
        }
        else
        {
            /* we cannot send error record, so disconnect */
            nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_INTERNAL_ERROR;
            LLCP_DisconnectReq (nfa_cho_cb.local_sap, nfa_cho_cb.remote_sap, FALSE);
        }
    }
    /* Invalid NDEF message */
    else if (rx_status == NFA_CHO_RX_NDEF_INVALID)
    {
        CHO_TRACE_ERROR0 ("Failed due to invalid NDEF message");

        if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
        {
            /* let Handover Requester got timeout */
        }
        else
        {
            /* we cannot send error record, so disconnect */
            nfa_cho_cb.disc_reason = NFA_CHO_DISC_REASON_INVALID_MSG;
            LLCP_DisconnectReq (nfa_cho_cb.local_sap, nfa_cho_cb.remote_sap, FALSE);
        }
    }
    /* need more segment */
    else if (rx_status == NFA_CHO_RX_NDEF_INCOMPLTE)
    {
        /* wait for next segment */
        if (nfa_cho_cb.substate == NFA_CHO_SUBSTATE_W4_REMOTE_HR)
        {
            nfa_sys_start_timer (&nfa_cho_cb.timer, 0, NFA_CHO_TIMEOUT_SEGMENTED_HR);
        }
        /* don't update running timer if we are waiting Hs */
    }
    else /* NFA_CHO_RX_NDEF_COMPLETE */
    {
        /* Received complete NDEF message */
    }

    return rx_status;
}

/*******************************************************************************
**
** Function         nfa_cho_send_handover_msg
**
** Description      Send segmented or whole Handover Message on LLCP
**                  if congested then wait for uncongested event from LLCP
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_send_handover_msg (void)
{
    tNFA_STATUS  status = NFA_STATUS_FAILED;
    tLLCP_STATUS llcp_status;
    UINT16       tx_size;
    BT_HDR       *p_msg;
    UINT8        *p_src, *p_dst;

    CHO_TRACE_DEBUG2 ("nfa_cho_send_handover_msg () size=%d, sent=%d",
                      nfa_cho_cb.tx_ndef_cur_size, nfa_cho_cb.tx_ndef_sent_size);

    /* while data link connection is not congested */
    while ((!nfa_cho_cb.congested) && (nfa_cho_cb.tx_ndef_sent_size < nfa_cho_cb.tx_ndef_cur_size))
    {
        /* select segment size as min (MIU of remote, remaining NDEF size) */
        if (nfa_cho_cb.tx_ndef_cur_size - nfa_cho_cb.tx_ndef_sent_size > nfa_cho_cb.remote_miu)
        {
            tx_size = nfa_cho_cb.remote_miu;
        }
        else
        {
            tx_size = (UINT16) (nfa_cho_cb.tx_ndef_cur_size - nfa_cho_cb.tx_ndef_sent_size);
        }

        /* transmit a segment on LLCP */
        if ((p_msg = (BT_HDR *) GKI_getpoolbuf (LLCP_POOL_ID)) != NULL)
        {
            p_msg->len    = (UINT16) tx_size;
            p_msg->offset = LLCP_MIN_OFFSET;

            p_dst = (UINT8*) (p_msg + 1) + p_msg->offset;
            p_src = nfa_cho_cb.p_tx_ndef_msg + nfa_cho_cb.tx_ndef_sent_size;

            memcpy (p_dst, p_src, tx_size);

            llcp_status = LLCP_SendData (nfa_cho_cb.local_sap, nfa_cho_cb.remote_sap, p_msg);

            nfa_cho_cb.tx_ndef_sent_size += tx_size;
        }
        else
        {
            llcp_status = LLCP_STATUS_FAIL;
        }

        if (llcp_status == LLCP_STATUS_SUCCESS)
        {
            status = NFA_STATUS_OK;
        }
        else if (llcp_status == LLCP_STATUS_CONGESTED)
        {
            status = NFA_STATUS_CONGESTED;
            CHO_TRACE_DEBUG0 ("Data link connection is congested");
            /* wait for uncongested event */
            nfa_cho_cb.congested = TRUE;
            break;
        }
        else
        {
            status = NFA_STATUS_FAILED;
            GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
            nfa_cho_cb.p_tx_ndef_msg = NULL;
            break;
        }
    }

    /*
    ** free buffer when receiving response or disconnected because we may need to send
    ** Hr message again due to collision
    */

    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_read_ndef_msg
**
** Description      allocate memory for NDEF message for the first segment
**                  validate NDEF message to check if received complete message
**
** Returns          None
**
*******************************************************************************/
tNFA_CHO_RX_NDEF_STATUS nfa_cho_read_ndef_msg (UINT8 local_sap, UINT8 remote_sap)
{
    tNDEF_STATUS            ndef_status;
    tNFA_CHO_RX_NDEF_STATUS rx_status;
    BOOLEAN                 more;
    UINT32                  length;

    CHO_TRACE_DEBUG2 ("nfa_cho_read_ndef_msg () local_sap=0x%x, remote_sap=0x%x",
                      local_sap, remote_sap);

    /* if this is the first segment */
    if (!nfa_cho_cb.p_rx_ndef_msg)
    {
        nfa_cho_cb.p_rx_ndef_msg = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);

        if (!nfa_cho_cb.p_rx_ndef_msg)
        {
            CHO_TRACE_ERROR0 ("Failed to allocate buffer");
            return NFA_CHO_RX_NDEF_TEMP_MEM;
        }

        nfa_cho_cb.rx_ndef_buf_size = LLCP_POOL_BUF_SIZE;
        nfa_cho_cb.rx_ndef_cur_size = 0;
    }

    more = TRUE;
    while (more)
    {
        more = LLCP_ReadDataLinkData (local_sap,
                                      remote_sap,
                                      (UINT16)(nfa_cho_cb.rx_ndef_buf_size - nfa_cho_cb.rx_ndef_cur_size),
                                      &length,
                                      nfa_cho_cb.p_rx_ndef_msg + nfa_cho_cb.rx_ndef_cur_size);

        nfa_cho_cb.rx_ndef_cur_size += length;

        /* if it doesn't fit into allocated memory */
        if ((nfa_cho_cb.rx_ndef_cur_size >= nfa_cho_cb.rx_ndef_buf_size)
          &&(more))
        {
            CHO_TRACE_ERROR0 ("Failed to store too much data");

            LLCP_FlushDataLinkRxData (local_sap, remote_sap);

            GKI_freebuf (nfa_cho_cb.p_rx_ndef_msg);
            nfa_cho_cb.p_rx_ndef_msg = NULL;

            return NFA_CHO_RX_NDEF_PERM_MEM;
        }
    }

    /* check NDEF message */
    ndef_status = NDEF_MsgValidate (nfa_cho_cb.p_rx_ndef_msg, nfa_cho_cb.rx_ndef_cur_size, FALSE);

    switch (ndef_status)
    {
    case NDEF_OK:
        rx_status = NFA_CHO_RX_NDEF_COMPLETE;
        break;

    case NDEF_MSG_TOO_SHORT:
    case NDEF_MSG_NO_MSG_END:
    case NDEF_MSG_LENGTH_MISMATCH:
        rx_status = NFA_CHO_RX_NDEF_INCOMPLTE;
        break;

    default:
        rx_status = NFA_CHO_RX_NDEF_INVALID;
        break;
    }

    if (rx_status == NFA_CHO_RX_NDEF_COMPLETE)
    {
#if (BT_TRACE_PROTOCOL == TRUE)
        DispCHO (nfa_cho_cb.p_rx_ndef_msg, nfa_cho_cb.rx_ndef_cur_size, TRUE);
#endif
    }
    else if (rx_status == NFA_CHO_RX_NDEF_INCOMPLTE)
    {
        CHO_TRACE_DEBUG0 ("Need more data to complete NDEF message");
    }
    else /* if (rx_status == NFA_CHO_RX_NDEF_INVALID) */
    {
        CHO_TRACE_ERROR1 ("Failed to validate NDEF message error=0x%x", ndef_status);
        GKI_freebuf (nfa_cho_cb.p_rx_ndef_msg);
        nfa_cho_cb.p_rx_ndef_msg = NULL;
    }

    return rx_status;
}

/*******************************************************************************
**
** Function         nfa_cho_add_cr_record
**
** Description      Adding Collision Resolution record
**
**
** Returns          NDEF_OK if success
**
*******************************************************************************/
tNDEF_STATUS nfa_cho_add_cr_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size)
{
    tNDEF_STATUS status;
    UINT32       temp32;

    CHO_TRACE_DEBUG1 ("nfa_cho_add_cr_record () cur_size = %d", *p_cur_size);

    /* Get random number from timer */
    temp32 = GKI_get_tick_count ();
    nfa_cho_cb.tx_random_number = (UINT16) ((temp32 >> 16) ^ (temp32));

#if (defined (NFA_CHO_TEST_INCLUDED) && (NFA_CHO_TEST_INCLUDED == TRUE))
    if (nfa_cho_cb.test_enabled & NFA_CHO_TEST_RANDOM)
    {
        nfa_cho_cb.tx_random_number = nfa_cho_cb.test_random_number;
    }
#endif

    CHO_TRACE_DEBUG1 ("tx_random_number = 0x%04x", nfa_cho_cb.tx_random_number);

    /* Add Well-Known Type:Collistion Resolution Record */
    status = NDEF_MsgAddWktCr (p_msg, max_size, p_cur_size,
                               nfa_cho_cb.tx_random_number);

    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_add_ac_record
**
** Description      Adding Alternative Carrier record
**
**
** Returns          NDEF_OK if success
**
*******************************************************************************/
tNDEF_STATUS nfa_cho_add_ac_record (UINT8 *p_msg, UINT32 max_size, UINT32 *p_cur_size,
                                    UINT8 num_ac_info, tNFA_CHO_AC_INFO *p_ac_info,
                                    UINT8 *p_ndef, UINT32 max_ndef_size, UINT32 *p_cur_ndef_size)
{
    tNDEF_STATUS status = NDEF_OK;
    UINT8        xx, yy;
    UINT8       *p_rec, *p_id, id_len;
    char         carrier_data_ref_str[NFA_CHO_MAX_REF_NAME_LEN];
    char        *aux_data_ref[NFA_CHO_MAX_AUX_DATA_COUNT];
    char         aux_data_ref_str[NFA_CHO_MAX_AUX_DATA_COUNT][NFA_CHO_MAX_REF_NAME_LEN];

    CHO_TRACE_DEBUG1 ("nfa_cho_add_ac_record (): num_ac_info = %d", num_ac_info);

    /* initialize auxilary data reference */
    for (xx = 0; xx < NFA_CHO_MAX_AUX_DATA_COUNT; xx++)
    {
        aux_data_ref[xx] = aux_data_ref_str[xx];
    }

    p_rec = p_ndef;

    /* Alternative Carrier Records */
    for (xx = 0; (xx < num_ac_info) && (status == NDEF_OK); xx++)
    {
        if (!p_rec)
        {
            status = NDEF_REC_NOT_FOUND;
            break;
        }

        p_id = NDEF_RecGetId (p_rec, &id_len);

        if ((p_id) && (id_len > 0) && (id_len <= NFA_CHO_MAX_REF_NAME_LEN))
        {
            memcpy (carrier_data_ref_str, p_id, id_len);
            carrier_data_ref_str[id_len] = 0x00;
        }
        else
        {
            CHO_TRACE_ERROR1 ("nfa_cho_add_ac_record ():id_len=%d", id_len);
            status = NDEF_REC_NOT_FOUND;
            break;
        }

        p_rec = NDEF_MsgGetNextRec (p_rec);

        /* auxilary data reference */
        for (yy = 0; yy < p_ac_info[xx].num_aux_data; yy++)
        {
            if (!p_rec)
            {
                status = NDEF_REC_NOT_FOUND;
                break;
            }

            p_id = NDEF_RecGetId (p_rec, &id_len);

            if ((p_id) && (id_len > 0) && (id_len <= NFA_CHO_MAX_REF_NAME_LEN))
            {
                memcpy (aux_data_ref_str[yy], p_id, id_len);
                aux_data_ref_str[yy][id_len] = 0x00;
            }
            else
            {
                CHO_TRACE_ERROR1 ("nfa_cho_add_ac_record ():id_len=%d", id_len);
                status = NDEF_REC_NOT_FOUND;
                break;
            }

            p_rec = NDEF_MsgGetNextRec (p_rec);
        }

        if (status == NDEF_OK)
        {
            /* Add Well-Known Type:Alternative Carrier Record */
            status = NDEF_MsgAddWktAc (p_msg, max_size, p_cur_size,
                                       p_ac_info[xx].cps, carrier_data_ref_str,
                                       p_ac_info[xx].num_aux_data, aux_data_ref);
        }

        if (status != NDEF_OK)
        {
            break;
        }
    }

    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_send_hr
**
** Description      Sending Handover Request Message
**                  It may send one from AC list to select a specific AC.
**
** Returns          NFA_STATUS_OK if success
**
*******************************************************************************/
tNFA_STATUS nfa_cho_send_hr (tNFA_CHO_API_SEND_HR *p_api_send_hr)
{
    tNDEF_STATUS    status;
    UINT8          *p_msg_cr_ac;
    UINT32          cur_size_cr_ac, max_size;
    UINT8           version;

    CHO_TRACE_DEBUG0 ("nfa_cho_send_hr ()");

    /* Collistion Resolution Record and Alternative Carrier Records */

    p_msg_cr_ac = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);
    if (!p_msg_cr_ac)
    {
        CHO_TRACE_ERROR0 ("Failed to allocate buffer");
        return NFA_STATUS_NO_BUFFERS;
    }

    max_size = LLCP_POOL_BUF_SIZE;
    NDEF_MsgInit (p_msg_cr_ac, max_size, &cur_size_cr_ac);

    /* Collistion Resolution Record */
    if (NDEF_OK != nfa_cho_add_cr_record (p_msg_cr_ac, max_size, &cur_size_cr_ac))
    {
        CHO_TRACE_ERROR0 ("Failed to add cr record");
        GKI_freebuf (p_msg_cr_ac);
        return NFA_STATUS_FAILED;
    }

    /* Alternative Carrier Records */
    if (NDEF_OK != nfa_cho_add_ac_record (p_msg_cr_ac, max_size, &cur_size_cr_ac,
                                          p_api_send_hr->num_ac_info, p_api_send_hr->p_ac_info,
                                          p_api_send_hr->p_ndef, p_api_send_hr->max_ndef_size,
                                          &(p_api_send_hr->cur_ndef_size)))
    {
        CHO_TRACE_ERROR0 ("Failed to add ac record");
        GKI_freebuf (p_msg_cr_ac);
        return NFA_STATUS_FAILED;
    }

    /* Handover Request Message */

    nfa_cho_cb.p_tx_ndef_msg = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);
    if (!nfa_cho_cb.p_tx_ndef_msg)
    {
        CHO_TRACE_ERROR0 ("Failed to allocate buffer");
        GKI_freebuf (p_msg_cr_ac);
        return NFA_STATUS_FAILED;
    }

    max_size = LLCP_POOL_BUF_SIZE;

    /* Handover Request Record */
    version = NFA_CHO_VERSION;

#if (defined (NFA_CHO_TEST_INCLUDED) && (NFA_CHO_TEST_INCLUDED == TRUE))
    if (nfa_cho_cb.test_enabled & NFA_CHO_TEST_VERSION)
    {
        version = nfa_cho_cb.test_version;
    }
#endif

    status = NDEF_MsgCreateWktHr (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                  version);
    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to create Hr");
        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;
        GKI_freebuf (p_msg_cr_ac);
        return NFA_STATUS_FAILED;
    }

    /* Append Collistion Resolution Record and Alternative Carrier Records */
    status = NDEF_MsgAppendPayload (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                    nfa_cho_cb.p_tx_ndef_msg, p_msg_cr_ac, cur_size_cr_ac);

    GKI_freebuf (p_msg_cr_ac);

    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to add cr/ac record");
        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;
        return NFA_STATUS_FAILED;
    }


    /* Append Alternative Carrier Reference Data or Handover Carrier Record */
    status = NDEF_MsgAppendRec (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                p_api_send_hr->p_ndef, p_api_send_hr->cur_ndef_size);

    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to add ac reference data or Hc record");
        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;
        return NFA_STATUS_FAILED;
    }

#if (BT_TRACE_PROTOCOL == TRUE)
    DispCHO (nfa_cho_cb.p_tx_ndef_msg, nfa_cho_cb.tx_ndef_cur_size, FALSE);
#endif

    /* Send it to peer */
    nfa_cho_cb.tx_ndef_sent_size = 0;

    status = nfa_cho_send_handover_msg ();

    if (status == NFA_STATUS_CONGESTED)
    {
        status = NFA_STATUS_OK;
    }

    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_send_hs
**
** Description      Send Handover Select Message
**
**
** Returns          NFA_STATUS_OK if success
**
*******************************************************************************/
tNFA_STATUS nfa_cho_send_hs (tNFA_CHO_API_SEND_HS *p_api_select)
{
    tNDEF_STATUS    status;
    UINT8          *p_msg_ac;
    UINT32          cur_size_ac = 0, max_size;
    UINT8           version;

    CHO_TRACE_DEBUG1 ("nfa_cho_send_hs () num_ac_info=%d", p_api_select->num_ac_info);

    if (p_api_select->num_ac_info > 0)
    {
        /* Alternative Carrier Records */

        p_msg_ac = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);

        if (!p_msg_ac)
        {
            CHO_TRACE_ERROR0 ("Failed to allocate buffer");
            return NFA_STATUS_FAILED;
        }

        max_size = LLCP_POOL_BUF_SIZE;
        NDEF_MsgInit (p_msg_ac, max_size, &cur_size_ac);

        if (NDEF_OK != nfa_cho_add_ac_record (p_msg_ac, max_size, &cur_size_ac,
                                              p_api_select->num_ac_info, p_api_select->p_ac_info,
                                              p_api_select->p_ndef, p_api_select->max_ndef_size,
                                              &(p_api_select->cur_ndef_size)))
        {
            CHO_TRACE_ERROR0 ("Failed to add ac record");
            GKI_freebuf (p_msg_ac);
            return NFA_STATUS_FAILED;
        }
    }
    else
    {
        p_msg_ac = NULL;
    }

    /* Handover Select Message */
    nfa_cho_cb.p_tx_ndef_msg = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);

    if (!nfa_cho_cb.p_tx_ndef_msg)
    {
        CHO_TRACE_ERROR0 ("Failed to allocate buffer");

        if (p_msg_ac)
            GKI_freebuf (p_msg_ac);

        return NFA_STATUS_FAILED;
    }
    max_size = LLCP_POOL_BUF_SIZE;

    /* Handover Select Record */
    version = NFA_CHO_VERSION;

#if (defined (NFA_CHO_TEST_INCLUDED) && (NFA_CHO_TEST_INCLUDED == TRUE))
    if (nfa_cho_cb.test_enabled & NFA_CHO_TEST_VERSION)
    {
        version = nfa_cho_cb.test_version;
    }
#endif
    status = NDEF_MsgCreateWktHs (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                  version);

    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to create Hs");

        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;

        if (p_msg_ac)
            GKI_freebuf (p_msg_ac);

        return NFA_STATUS_FAILED;
    }

    if (p_api_select->num_ac_info > 0)
    {
        /* Append Alternative Carrier Records */
        status = NDEF_MsgAppendPayload (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                        nfa_cho_cb.p_tx_ndef_msg, p_msg_ac, cur_size_ac);

        if (p_msg_ac)
            GKI_freebuf (p_msg_ac);

        if (status != NDEF_OK)
        {
            CHO_TRACE_ERROR0 ("Failed to add cr record");
            GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
            nfa_cho_cb.p_tx_ndef_msg = NULL;
            return NFA_STATUS_FAILED;
        }

        /* Append Alternative Carrier Reference Data */
        status = NDEF_MsgAppendRec (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                    p_api_select->p_ndef, p_api_select->cur_ndef_size);

        if (status != NDEF_OK)
        {
            CHO_TRACE_ERROR0 ("Failed to add ac reference data record");
            GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
            nfa_cho_cb.p_tx_ndef_msg = NULL;
            return NFA_STATUS_FAILED;
        }
    }

#if (BT_TRACE_PROTOCOL == TRUE)
    DispCHO (nfa_cho_cb.p_tx_ndef_msg, nfa_cho_cb.tx_ndef_cur_size, FALSE);
#endif

    /* Send it to peer */
    nfa_cho_cb.tx_ndef_sent_size = 0;

    status = nfa_cho_send_handover_msg ();

    if (status == NFA_STATUS_CONGESTED)
    {
        status = NFA_STATUS_OK;
    }
    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_send_hs_error
**
** Description      Sending Handover Select Message with error record
**
**
** Returns          NFA_STATUS_OK if success
**
*******************************************************************************/
tNFA_STATUS nfa_cho_send_hs_error (UINT8 error_reason, UINT32 error_data)
{
    tNDEF_STATUS    status;
    UINT8           version;
    UINT32          max_size;

    CHO_TRACE_DEBUG2 ("nfa_cho_send_hs_error () error_reason=0x%x, error_data=0x%x",
                       error_reason, error_data);

    /* Handover Select Message */
    nfa_cho_cb.p_tx_ndef_msg = (UINT8 *) GKI_getpoolbuf (LLCP_POOL_ID);

    if (!nfa_cho_cb.p_tx_ndef_msg)
    {
        CHO_TRACE_ERROR0 ("Failed to allocate buffer");
        return NFA_STATUS_FAILED;
    }

    max_size = LLCP_POOL_BUF_SIZE;

    /* Handover Select Record with Version */
    version = NFA_CHO_VERSION;

#if (defined (NFA_CHO_TEST_INCLUDED) && (NFA_CHO_TEST_INCLUDED == TRUE))
    if (nfa_cho_cb.test_enabled & NFA_CHO_TEST_VERSION)
    {
        version = nfa_cho_cb.test_version;
    }
#endif
    status = NDEF_MsgCreateWktHs (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                  version);

    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to create Hs");

        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;

        return NFA_STATUS_FAILED;
    }

    /* Add Error Records */
    status = NDEF_MsgAddWktErr (nfa_cho_cb.p_tx_ndef_msg, max_size, &nfa_cho_cb.tx_ndef_cur_size,
                                error_reason, error_data);

    if (status != NDEF_OK)
    {
        CHO_TRACE_ERROR0 ("Failed to add err record");

        GKI_freebuf (nfa_cho_cb.p_tx_ndef_msg);
        nfa_cho_cb.p_tx_ndef_msg = NULL;

        return NFA_STATUS_FAILED;
    }

#if (BT_TRACE_PROTOCOL == TRUE)
    DispCHO (nfa_cho_cb.p_tx_ndef_msg, nfa_cho_cb.tx_ndef_cur_size, FALSE);
#endif

    /* Send it to peer */
    nfa_cho_cb.tx_ndef_sent_size = 0;

    status = nfa_cho_send_handover_msg ();

    if (status == NFA_STATUS_CONGESTED)
    {
        status = NFA_STATUS_OK;
    }
    return status;
}

/*******************************************************************************
**
** Function         nfa_cho_get_random_number
**
** Description      Return random number in Handover Request message
**
**
** Returns          random number in "cr" record
**
*******************************************************************************/
UINT16 nfa_cho_get_random_number (UINT8 *p_ndef_msg)
{
    UINT16 random_number;
    UINT8 *p_cr_record, *p_cr_payload;
    UINT32 cr_payload_len;

    CHO_TRACE_DEBUG0 ("nfa_cho_get_random_number ()");

    /* find Collision Resolution record */
    p_cr_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, cr_rec_type, CR_REC_TYPE_LEN);

    if (!p_cr_record)
    {
        CHO_TRACE_ERROR0 ("Failed to find cr record");
        return 0;
    }

    /* get start of payload in Collision Resolution record */
    p_cr_payload = NDEF_RecGetPayload (p_cr_record, &cr_payload_len);

    if ((!p_cr_payload) || (cr_payload_len != 2))
    {
        CHO_TRACE_ERROR0 ("Failed to get cr payload (random number)");
        return 0;
    }

    /* get random number from payload */
    BE_STREAM_TO_UINT16 (random_number, p_cr_payload);

    return random_number;
}

/*******************************************************************************
**
** Function         nfa_cho_parse_ac_records
**
** Description      Parsing NDEF message to retrieve Alternative Carrier records
**                  and store into tNFA_CHO_AC_REC
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_parse_ac_records (UINT8 *p_ndef_msg, UINT8 *p_num_ac_rec, tNFA_CHO_AC_REC *p_ac_rec)
{
    UINT8 *p_ac_record, *p_ac_payload;
    UINT32 ac_payload_len;

    UINT8  xx, yy;

    CHO_TRACE_DEBUG0 ("nfa_cho_parse_ac_records ()");

    /* get Alternative Carrier record */
    p_ac_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, ac_rec_type, AC_REC_TYPE_LEN);

    xx = 0;

    while ((p_ac_record) && (xx < NFA_CHO_MAX_AC_INFO))
    {
        /* get payload */
        p_ac_payload = NDEF_RecGetPayload (p_ac_record, &ac_payload_len);

        if ((!p_ac_payload) || (ac_payload_len < 3))
        {
            CHO_TRACE_ERROR0 ("Failed to get ac payload");
            return NFA_STATUS_FAILED;
        }

        /* Carrier Power State */
        BE_STREAM_TO_UINT8 (p_ac_rec->cps, p_ac_payload);

        /* Carrier Data Reference Length and Characters */
        BE_STREAM_TO_UINT8 (p_ac_rec->carrier_data_ref.ref_len, p_ac_payload);

        ac_payload_len -= 2;

        /* remaining must have carrier data ref and Auxiliary Data Reference Count at least */
        if (ac_payload_len > p_ac_rec->carrier_data_ref.ref_len)
        {
            if (p_ac_rec->carrier_data_ref.ref_len > NFA_CHO_MAX_REF_NAME_LEN)
            {
                CHO_TRACE_ERROR1 ("Too many bytes for carrier_data_ref, ref_len = %d",
                                   p_ac_rec->carrier_data_ref.ref_len);
                return NFA_STATUS_FAILED;
            }

            BE_STREAM_TO_ARRAY (p_ac_payload,
                                p_ac_rec->carrier_data_ref.ref_name,
                                p_ac_rec->carrier_data_ref.ref_len);
            ac_payload_len -= p_ac_rec->carrier_data_ref.ref_len;
        }
        else
        {
            CHO_TRACE_ERROR0 ("Failed to parse carrier_data_ref.ref_len");
            return NFA_STATUS_FAILED;
        }

        /* Auxiliary Data Reference Count */
        BE_STREAM_TO_UINT8 (p_ac_rec->aux_data_ref_count, p_ac_payload);
        ac_payload_len--;

        /* Auxiliary Data Reference Length and Characters */
        for (yy = 0; (yy < p_ac_rec->aux_data_ref_count) && (yy < NFA_CHO_MAX_AUX_DATA_COUNT); yy++)
        {
            if (ac_payload_len > 0)
            {
                BE_STREAM_TO_UINT8 (p_ac_rec->aux_data_ref[yy].ref_len, p_ac_payload);
                ac_payload_len--;

                if (ac_payload_len >= p_ac_rec->aux_data_ref[yy].ref_len)
                {
                    if (p_ac_rec->aux_data_ref[yy].ref_len > NFA_CHO_MAX_REF_NAME_LEN)
                    {
                        CHO_TRACE_ERROR2 ("Too many bytes for aux_data_ref[%d], ref_len=%d",
                                           yy, p_ac_rec->aux_data_ref[yy].ref_len);
                        return NFA_STATUS_FAILED;
                    }

                    BE_STREAM_TO_ARRAY (p_ac_payload,
                                        p_ac_rec->aux_data_ref[yy].ref_name,
                                        p_ac_rec->aux_data_ref[yy].ref_len);
                    ac_payload_len -= p_ac_rec->aux_data_ref[yy].ref_len;
                }
                else
                {
                    CHO_TRACE_ERROR1 ("Failed to parse ref_name for aux_data_ref[%d]", yy);
                    return NFA_STATUS_FAILED;
                }
            }
            else
            {
                CHO_TRACE_ERROR1 ("Failed to parse ref_len for aux_data_ref[%d]", yy);
                return NFA_STATUS_FAILED;
            }
        }

        if (ac_payload_len != 0)
        {
            CHO_TRACE_WARNING1 ("Found extra data in AC record[%d]", xx);
        }

        xx++;
        p_ac_rec++;

        /* get next Alternative Carrier record */
        p_ac_record = NDEF_MsgGetNextRecByType (p_ac_record, NDEF_TNF_WKT, ac_rec_type, AC_REC_TYPE_LEN);
    }

    *p_num_ac_rec = xx;

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_parse_hr_record
**
** Description      Parsing Handover Request message to retrieve version, random number,
**                  Alternative Carrier records and store into tNFA_CHO_AC_REC
**
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_parse_hr_record (UINT8  *p_ndef_msg,
                                     UINT8  *p_version,
                                     UINT16 *p_random_number,
                                     UINT8  *p_num_ac_rec,
                                     tNFA_CHO_AC_REC *p_ac_rec)
{
    UINT8 *p_hr_record, *p_hr_payload;
    UINT32 hr_payload_len;

    CHO_TRACE_DEBUG0 ("nfa_cho_parse_hr_record ()");

    /* get Handover Request record */
    p_hr_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, hr_rec_type, HR_REC_TYPE_LEN);

    if (!p_hr_record)
    {
        CHO_TRACE_ERROR0 ("Failed to find Hr record");
        return NFA_STATUS_FAILED;
    }

    p_hr_payload = NDEF_RecGetPayload (p_hr_record, &hr_payload_len);

    if ((!p_hr_payload) || (hr_payload_len < 7))
    {
        CHO_TRACE_ERROR0 ("Failed to get Hr payload (version, cr/ac record)");
        return NFA_STATUS_FAILED;
    }

    /* Version */
    STREAM_TO_UINT8 ((*p_version), p_hr_payload);
    hr_payload_len--;

    /* NDEF message for Collision Resolution record and Alternative Carrier records */

    if (NDEF_OK != NDEF_MsgValidate (p_hr_payload, hr_payload_len, FALSE))
    {
        CHO_TRACE_ERROR0 ("Failed to validate NDEF message for cr/ac records");
        return NFA_STATUS_FAILED;
    }

    /* Collision Resolution record */
    if (p_random_number)
    {
        *p_random_number = nfa_cho_get_random_number (p_hr_payload);
    }

    /* Alternative Carrier records */
    if (p_ac_rec)
    {
        return (nfa_cho_parse_ac_records (p_hr_payload, p_num_ac_rec, p_ac_rec));
    }

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_parse_carrier_config
**
** Description      Parse Alternative Carrier Configuration and Aux Data
**
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_parse_carrier_config (UINT8 *p_ndef_msg, UINT8 num_ac_rec, tNFA_CHO_AC_REC *p_ac_rec)
{
    UINT8  *p_record;
    UINT8   xx, yy;

    CHO_TRACE_DEBUG1 ("nfa_cho_parse_carrier_config () num_ac_rec = %d", num_ac_rec);

    /* Parse Alternative Carrier Configuration and Aux Data */
    for (xx = 0; xx < num_ac_rec; xx++)
    {
        p_record = NDEF_MsgGetFirstRecById (p_ndef_msg,
                                            p_ac_rec->carrier_data_ref.ref_name,
                                            p_ac_rec->carrier_data_ref.ref_len);

        if (!p_record)
        {
            CHO_TRACE_ERROR2 ("Failed to find Payload ID: len=%d, [0x%x ...]",
                              p_ac_rec->carrier_data_ref.ref_len,
                              p_ac_rec->carrier_data_ref.ref_name[0]);
            return NFA_STATUS_FAILED;
        }

        for (yy = 0; yy < p_ac_rec->aux_data_ref_count; yy++)
        {
            /* Get aux data record by Payload ID */
            p_record = NDEF_MsgGetFirstRecById (p_ndef_msg,
                                                p_ac_rec->aux_data_ref[yy].ref_name,
                                                p_ac_rec->aux_data_ref[yy].ref_len);

            if (!p_record)
            {
                CHO_TRACE_ERROR2 ("Failed to find Payload ID for Aux: len=%d, [0x%x ...]",
                                  p_ac_rec->aux_data_ref[yy].ref_len,
                                  p_ac_rec->aux_data_ref[yy].ref_name[0]);
                return NFA_STATUS_FAILED;
            }
        }

        p_ac_rec++;
    }

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_proc_hr
**
** Description      Parse Handover Request Message
**                  In case of parsing error, let peer got timeout (1 sec).
**
**
** Returns          None
**
*******************************************************************************/
void nfa_cho_proc_hr (UINT32 length, UINT8 *p_ndef_msg)
{
    tNFA_CHO_EVT_DATA    evt_data;
    tNFA_CHO_API_SEND_HS select;
    UINT8  version;
    UINT16 random_number;

    CHO_TRACE_DEBUG1 ("nfa_cho_proc_hr () length=%d", length);

    /* Parse Handover Request record */
    if (NDEF_OK != nfa_cho_parse_hr_record (p_ndef_msg, &version, &random_number,
                                            &evt_data.request.num_ac_rec,
                                            &evt_data.request.ac_rec[0]))
    {
        CHO_TRACE_ERROR0 ("Failed to parse hr record");
        return;
    }

    if (version != NFA_CHO_VERSION)
    {
        CHO_TRACE_DEBUG1 ("Version (0x%02x) not matched", version);
        /* For the future, */
        /* if we have higher major then support peer's version */
        /* if we have lower major then send empty handover select message */
        if (NFA_CHO_GET_MAJOR_VERSION (version) > NFA_CHO_GET_MAJOR_VERSION (NFA_CHO_VERSION))
        {
            select.num_ac_info = 0;
            nfa_cho_send_hs (&select);
            return;
        }
    }

    if (NFA_STATUS_OK != nfa_cho_parse_carrier_config (p_ndef_msg,
                                                       evt_data.request.num_ac_rec,
                                                       &evt_data.request.ac_rec[0]))
    {
        CHO_TRACE_ERROR0 ("Failed to parse carrier configuration");

        evt_data.request.status       = NFA_STATUS_FAILED;
        evt_data.request.num_ac_rec   = 0;
        evt_data.request.p_ref_ndef   = NULL;
        evt_data.request.ref_ndef_len = 0;

        nfa_cho_cb.p_cback (NFA_CHO_REQUEST_EVT, &evt_data);
        return;
    }

    if (evt_data.request.num_ac_rec)
    {
        /* passing alternative carrier references */
        evt_data.request.p_ref_ndef   = NDEF_MsgGetNextRec (p_ndef_msg);
        *evt_data.request.p_ref_ndef |= NDEF_MB_MASK;
        evt_data.request.ref_ndef_len = (UINT32)(length - (evt_data.request.p_ref_ndef - p_ndef_msg));
    }
    else
    {
        evt_data.request.p_ref_ndef   = NULL;
        evt_data.request.ref_ndef_len = 0;
    }

    evt_data.request.status = NFA_STATUS_OK;

    nfa_cho_cb.p_cback (NFA_CHO_REQUEST_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_cho_get_error
**
** Description      Search Error record and parse it
**
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_get_error (UINT8 *p_ndef_msg, UINT8 *p_error_reason, UINT32 *p_error_data)
{
    UINT8 *p_err_record, *p_err_payload, u8;
    UINT32 err_payload_len;

    CHO_TRACE_DEBUG0 ("nfa_cho_get_error ()");

    p_err_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, err_rec_type, ERR_REC_TYPE_LEN);

    if (!p_err_record)
    {
        CHO_TRACE_DEBUG0 ("Found no err record");
        return NFA_STATUS_FAILED;
    }

    p_err_payload = NDEF_RecGetPayload (p_err_record, &err_payload_len);

    if (!p_err_payload)
    {
        CHO_TRACE_ERROR0 ("Failed to get err payload");
        return NFA_STATUS_SYNTAX_ERROR;
    }

    BE_STREAM_TO_UINT8 (*p_error_reason, p_err_payload);

    if (  (err_payload_len == 2)
        &&(  (*p_error_reason == NFA_CHO_ERROR_TEMP_MEM)
           ||(*p_error_reason == NFA_CHO_ERROR_CARRIER)  )  )
    {
        BE_STREAM_TO_UINT8 (u8, p_err_payload);
        *p_error_data = (UINT32)u8;
    }
    else if (  (err_payload_len == 5)
             &&(*p_error_reason == NFA_CHO_ERROR_PERM_MEM)  )
    {
        BE_STREAM_TO_UINT32 (*p_error_data, p_err_payload);
    }
    else
    {
        CHO_TRACE_ERROR2 ("Unknown error reason = %d, err_payload_len = %d",
                          *p_error_reason, err_payload_len);
        return NFA_STATUS_SYNTAX_ERROR;
    }

    CHO_TRACE_DEBUG2 ("error_reason=0x%x, error_data=0x%x", *p_error_reason, *p_error_data);

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_parse_hs_record
**
** Description      Parse Handover Select record
**
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_parse_hs_record (UINT8  *p_ndef_msg,
                                     UINT8  *p_version,
                                     UINT8  *p_num_ac_rec,
                                     tNFA_CHO_AC_REC *p_ac_rec,
                                     UINT8  *p_error_reason,
                                     UINT32 *p_error_data)
{
    tNFA_STATUS status;
    UINT8 *p_hs_record, *p_hs_payload;
    UINT32 hs_payload_len;

    CHO_TRACE_DEBUG0 ("nfa_cho_parse_hs_record ()");

    /* get Handover Select record */
    p_hs_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, hs_rec_type, HS_REC_TYPE_LEN);

    if (!p_hs_record)
    {
        CHO_TRACE_ERROR0 ("Failed to find Hs record");
        return NFA_STATUS_FAILED;
    }

    p_hs_payload = NDEF_RecGetPayload (p_hs_record, &hs_payload_len);

    if ((!p_hs_payload) || (hs_payload_len < 1))  /* at least version */
    {
        CHO_TRACE_ERROR0 ("Failed to get Hs payload (version, ac record)");
        return NFA_STATUS_FAILED;
    }

    STREAM_TO_UINT8 ((*p_version), p_hs_payload);
    hs_payload_len--;

    /* Check if error record is sent */
    status = nfa_cho_get_error (p_ndef_msg, p_error_reason, p_error_data);

    if (status == NFA_STATUS_SYNTAX_ERROR)
    {
        return NFA_STATUS_FAILED;
    }
    else if (status == NFA_STATUS_OK)
    {
        return NFA_STATUS_OK;
    }

    if (hs_payload_len >= 3 )
    {
        /* NDEF message for Alternative Carrier records */
        if (NDEF_OK != NDEF_MsgValidate (p_hs_payload, hs_payload_len, FALSE))
        {
            CHO_TRACE_ERROR0 ("Failed to validate NDEF message for ac records");
            return NFA_STATUS_FAILED;
        }

        /* Alternative Carrier records */
        if (p_ac_rec)
        {
            if (NFA_STATUS_OK != nfa_cho_parse_ac_records (p_hs_payload, p_num_ac_rec, p_ac_rec))
            {
                return NFA_STATUS_FAILED;
            }

            CHO_TRACE_DEBUG1 ("Found %d ac record", *p_num_ac_rec);
        }
    }
    else
    {
        CHO_TRACE_DEBUG0 ("Empty Handover Select Message");
        *p_num_ac_rec = 0;
    }

    return NFA_STATUS_OK;
}

/*******************************************************************************
**
** Function         nfa_cho_proc_hs
**
** Description      Parse Handover Select Message
**
**
** Returns          FALSE if we need to select one from inactive ACs
**
*******************************************************************************/
void nfa_cho_proc_hs (UINT32 length, UINT8 *p_ndef_msg)
{
    tNFA_CHO_EVT_DATA evt_data;
    UINT8  version, error_reason = 0;
    UINT32 error_data;

    CHO_TRACE_DEBUG0 ("nfa_cho_proc_hs ()");

    evt_data.select.status = NFA_STATUS_OK;

    /* Parse Handover Select record */
    if (NFA_STATUS_OK != nfa_cho_parse_hs_record (p_ndef_msg, &version,
                                                  &evt_data.select.num_ac_rec,
                                                  &evt_data.select.ac_rec[0],
                                                  &error_reason, &error_data))
    {
        CHO_TRACE_ERROR0 ("Failed to parse hs record");

        evt_data.select.status = NFA_STATUS_FAILED;
    }

    if (  (evt_data.select.status == NFA_STATUS_OK)
        &&(error_reason != 0)  )
    {
        /* We got error records */
        evt_data.sel_err.error_reason = error_reason;
        evt_data.sel_err.error_data   = error_data;

        nfa_cho_cb.p_cback (NFA_CHO_SEL_ERR_EVT, &evt_data);
        return;
    }

    if (  (evt_data.select.status == NFA_STATUS_OK)
        &&(version != NFA_CHO_VERSION)  )
    {
        CHO_TRACE_ERROR1 ("Version (0x%02x) not matched", version);

        evt_data.select.status = NFA_STATUS_FAILED;
    }

    /* parse Alternative Carrier information */

    if (  (evt_data.select.status == NFA_STATUS_OK)
        &&(NFA_STATUS_OK != nfa_cho_parse_carrier_config (p_ndef_msg,
                                                          evt_data.select.num_ac_rec,
                                                          &evt_data.select.ac_rec[0]))  )
    {
        CHO_TRACE_ERROR0 ("Failed to parse carrier configuration");

        evt_data.select.status = NFA_STATUS_FAILED;
    }

    if (evt_data.select.status == NFA_STATUS_OK)
    {
        if (evt_data.select.num_ac_rec)
        {
            /* passing alternative carrier references */
            evt_data.select.p_ref_ndef   = NDEF_MsgGetNextRec (p_ndef_msg);
            *evt_data.select.p_ref_ndef |= NDEF_MB_MASK;
            evt_data.select.ref_ndef_len = (UINT32)(length - (evt_data.select.p_ref_ndef - p_ndef_msg));
        }
        else
        {
            evt_data.select.p_ref_ndef   = NULL;
            evt_data.select.ref_ndef_len = 0;
        }
    }
    else
    {
        evt_data.select.num_ac_rec   = 0;
        evt_data.select.p_ref_ndef   = NULL;
        evt_data.select.ref_ndef_len = 0;
    }

    nfa_cho_cb.p_cback (NFA_CHO_SELECT_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_cho_proc_simplified_format
**
** Description      Parse simplified BT OOB/Wifi Message
**
**
** Returns          void
**
*******************************************************************************/
void nfa_cho_proc_simplified_format (UINT32 length, UINT8 *p_ndef_msg)
{
    tNFA_CHO_EVT_DATA evt_data;

    CHO_TRACE_DEBUG0 ("nfa_cho_proc_simplified_format ()");

    evt_data.select.status = NFA_STATUS_OK;

    evt_data.select.num_ac_rec = 1;

    evt_data.select.ac_rec[0].cps = NFA_CHO_CPS_UNKNOWN;
    evt_data.select.ac_rec[0].carrier_data_ref.ref_len = 0;
    evt_data.select.ac_rec[0].aux_data_ref_count       = 0;

    evt_data.select.p_ref_ndef   = p_ndef_msg;
    evt_data.select.ref_ndef_len = length;

    nfa_cho_cb.p_cback (NFA_CHO_SELECT_EVT, &evt_data);
}

/*******************************************************************************
**
** Function         nfa_cho_get_msg_type
**
** Description      Get handover message type to check collision
**
**
** Returns          NFA_CHO_MSG_HR if it has Handover Request record
**                  NFA_CHO_MSG_HS if it has Handover Select record
**                  NFA_CHO_MSG_BT_OOB if it has simplified BT OOB record
**                  NFA_CHO_MSG_WIFI if it has simplified WiFi record
**                  NFA_CHO_MSG_UNKNOWN, otherwise
**
*******************************************************************************/
tNFA_CHO_MSG_TYPE nfa_cho_get_msg_type (UINT32 length, UINT8 *p_ndef_msg)
{
    UINT8 *p_record;

    CHO_TRACE_DEBUG1 ("nfa_cho_get_msg_type () length=%d", length);

    p_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, hr_rec_type, HR_REC_TYPE_LEN);

    if (p_record)
    {
        CHO_TRACE_DEBUG0 ("Found Hr record");
        return NFA_CHO_MSG_HR;
    }

    p_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, hs_rec_type, HS_REC_TYPE_LEN);

    if (p_record)
    {
        CHO_TRACE_DEBUG0 ("Found Hs record");
        return NFA_CHO_MSG_HS;
    }

    p_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_MEDIA,
                                          p_bt_oob_rec_type, BT_OOB_REC_TYPE_LEN);

    if (p_record)
    {
        CHO_TRACE_DEBUG0 ("Found simplified BT OOB record");
        return NFA_CHO_MSG_BT_OOB;
    }

    p_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_MEDIA,
                                          p_wifi_rec_type, (UINT8) strlen ((char *) p_wifi_rec_type));

    if (p_record)
    {
        CHO_TRACE_DEBUG0 ("Found simplified WiFi record");
        return NFA_CHO_MSG_WIFI;
    }

    CHO_TRACE_ERROR0 ("Failed to find Hr/Hs record");

    return NFA_CHO_MSG_UNKNOWN;
}

/*******************************************************************************
**
** Function         nfa_cho_get_local_device_role
**
** Description      Resolve collision and get role of local device
**
**
** Returns          tNFA_CHO_ROLE_TYPE
**
*******************************************************************************/
tNFA_CHO_ROLE_TYPE nfa_cho_get_local_device_role (UINT32 length, UINT8 *p_ndef_msg)
{
    UINT16 rx_random_number;
    UINT8  version;

    CHO_TRACE_DEBUG1 ("nfa_cho_get_local_device_role () length=%d", length);

    /* Get random number in Handover Request record */
    if (NDEF_OK != nfa_cho_parse_hr_record (p_ndef_msg, &version, &rx_random_number, NULL, NULL))
    {
        CHO_TRACE_ERROR0 ("Failed to parse hr record");
        return NFA_CHO_ROLE_UNDECIDED;
    }

    CHO_TRACE_DEBUG2 ("tx_random_number=0x%x, rx_random_number=0x%x",
                       nfa_cho_cb.tx_random_number, rx_random_number);

    if (nfa_cho_cb.tx_random_number == rx_random_number)
    {
        return NFA_CHO_ROLE_UNDECIDED;
    }
    /* if the least significant bits are same */
    else if (((nfa_cho_cb.tx_random_number ^ rx_random_number) & 0x0001) == 0)
    {
        if (nfa_cho_cb.tx_random_number > rx_random_number)
            return NFA_CHO_ROLE_SELECTOR;
        else
            return NFA_CHO_ROLE_REQUESTER;
    }
    else
    {
        if (nfa_cho_cb.tx_random_number > rx_random_number)
            return NFA_CHO_ROLE_REQUESTER;
        else
            return NFA_CHO_ROLE_SELECTOR;
    }
}

/*******************************************************************************
**
** Function         nfa_cho_update_random_number
**
** Description      Replace random number
**
**
** Returns          tNFA_STATUS
**
*******************************************************************************/
tNFA_STATUS nfa_cho_update_random_number (UINT8 *p_ndef_msg)
{
    UINT8 *p_hr_record, *p_hr_payload;
    UINT8 *p_cr_record, *p_cr_payload;
    UINT32 hr_payload_len, cr_payload_len;
    UINT32 temp32;

    CHO_TRACE_DEBUG0 ("nfa_cho_update_random_number ()");

    /* get Handover Request record */
    p_hr_record = NDEF_MsgGetFirstRecByType (p_ndef_msg, NDEF_TNF_WKT, hr_rec_type, HR_REC_TYPE_LEN);

    if (!p_hr_record)
    {
        CHO_TRACE_ERROR0 ("Failed to find Hr record");
        return NFA_STATUS_FAILED;
    }

    p_hr_payload = NDEF_RecGetPayload (p_hr_record, &hr_payload_len);

    /* Skip Version */
    p_hr_payload++;
    hr_payload_len--;

    /* NDEF message for Collision Resolution record and Alternative Carrier records */

    /* find Collision Resolution record */
    p_cr_record = NDEF_MsgGetFirstRecByType (p_hr_payload, NDEF_TNF_WKT, cr_rec_type, CR_REC_TYPE_LEN);

    if (!p_cr_record)
    {
        CHO_TRACE_ERROR0 ("Failed to find cr record");
        return NFA_STATUS_FAILED;
    }

    /* get start of payload in Collision Resolution record */
    p_cr_payload = NDEF_RecGetPayload (p_cr_record, &cr_payload_len);

    /* Get random number from timer */
    temp32 = GKI_get_tick_count ();
    nfa_cho_cb.tx_random_number = (UINT16) ((temp32 >> 16) ^ (temp32));

#if (defined (NFA_CHO_TEST_INCLUDED) && (NFA_CHO_TEST_INCLUDED == TRUE))
    if (nfa_cho_cb.test_enabled & NFA_CHO_TEST_RANDOM)
    {
        nfa_cho_cb.tx_random_number = nfa_cho_cb.test_random_number;
    }
#endif

    CHO_TRACE_DEBUG1 ("tx_random_number = 0x%04x", nfa_cho_cb.tx_random_number);

    /* update random number in payload */
    UINT16_TO_BE_STREAM (p_cr_payload, nfa_cho_cb.tx_random_number);

    return NFA_STATUS_OK;
}