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