blob: 607786a71b4c5245816fe4fa9dce1d0caaf4ec86 [file] [log] [blame]
/******************************************************************************
*
* Copyright (C) 2010-2014 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 file contains the LLCP Service Discovery
*
******************************************************************************/
#include <string.h>
#include <android-base/stringprintf.h>
#include <base/logging.h>
#include "bt_types.h"
#include "gki.h"
#include "llcp_api.h"
#include "llcp_int.h"
#include "nfa_dm_int.h"
using android::base::StringPrintf;
extern bool nfc_debug_enabled;
/*******************************************************************************
**
** Function llcp_sdp_proc_data
**
** Description Do nothing
**
**
** Returns void
**
*******************************************************************************/
void llcp_sdp_proc_data(__attribute__((unused)) tLLCP_SAP_CBACK_DATA* p_data) {
/*
** Do nothing
** llcp_sdp_proc_SNL () is called by link layer
*/
}
/*******************************************************************************
**
** Function llcp_sdp_check_send_snl
**
** Description Enqueue Service Name Lookup PDU into sig_xmit_q for
** transmitting
**
**
** Returns void
**
*******************************************************************************/
void llcp_sdp_check_send_snl(void) {
uint8_t* p;
if (llcp_cb.sdp_cb.p_snl) {
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
llcp_cb.sdp_cb.p_snl->len += LLCP_PDU_HEADER_SIZE;
llcp_cb.sdp_cb.p_snl->offset -= LLCP_PDU_HEADER_SIZE;
p = (uint8_t*)(llcp_cb.sdp_cb.p_snl + 1) + llcp_cb.sdp_cb.p_snl->offset;
UINT16_TO_BE_STREAM(
p, LLCP_GET_PDU_HEADER(LLCP_SAP_SDP, LLCP_PDU_SNL_TYPE, LLCP_SAP_SDP));
GKI_enqueue(&llcp_cb.lcb.sig_xmit_q, llcp_cb.sdp_cb.p_snl);
llcp_cb.sdp_cb.p_snl = nullptr;
} else {
/* Notify DTA after sending out SNL with SDRES not to send SNLs in AGF PDU
*/
if (llcp_cb.p_dta_cback && llcp_cb.dta_snl_resp) {
llcp_cb.dta_snl_resp = false;
(*llcp_cb.p_dta_cback)();
}
}
}
/*******************************************************************************
**
** Function llcp_sdp_add_sdreq
**
** Description Add Service Discovery Request into SNL PDU
**
**
** Returns void
**
*******************************************************************************/
static void llcp_sdp_add_sdreq(uint8_t tid, char* p_name) {
uint8_t* p;
uint16_t name_len = (uint16_t)strlen(p_name);
p = (uint8_t*)(llcp_cb.sdp_cb.p_snl + 1) + llcp_cb.sdp_cb.p_snl->offset +
llcp_cb.sdp_cb.p_snl->len;
UINT8_TO_BE_STREAM(p, LLCP_SDREQ_TYPE);
UINT8_TO_BE_STREAM(p, (1 + name_len));
UINT8_TO_BE_STREAM(p, tid);
ARRAY_TO_BE_STREAM(p, p_name, name_len);
llcp_cb.sdp_cb.p_snl->len += LLCP_SDREQ_MIN_LEN + name_len;
}
/*******************************************************************************
**
** Function llcp_sdp_send_sdreq
**
** Description Send Service Discovery Request
**
**
** Returns LLCP_STATUS
**
*******************************************************************************/
tLLCP_STATUS llcp_sdp_send_sdreq(uint8_t tid, char* p_name) {
tLLCP_STATUS status;
uint16_t name_len;
uint16_t available_bytes;
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("tid=0x%x, ServiceName=%s", tid, p_name);
/* if there is no pending SNL */
if (!llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID);
if (llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl->offset =
NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE + LLCP_PDU_HEADER_SIZE;
llcp_cb.sdp_cb.p_snl->len = 0;
}
}
if (llcp_cb.sdp_cb.p_snl) {
available_bytes = GKI_get_buf_size(llcp_cb.sdp_cb.p_snl) - NFC_HDR_SIZE -
llcp_cb.sdp_cb.p_snl->offset - llcp_cb.sdp_cb.p_snl->len;
name_len = (uint16_t)strlen(p_name);
/* if SDREQ parameter can be added in SNL */
if ((available_bytes >= LLCP_SDREQ_MIN_LEN + name_len) &&
(llcp_cb.sdp_cb.p_snl->len + LLCP_SDREQ_MIN_LEN + name_len <=
llcp_cb.lcb.effective_miu)) {
llcp_sdp_add_sdreq(tid, p_name);
status = LLCP_STATUS_SUCCESS;
} else {
/* send pending SNL PDU to LM */
llcp_sdp_check_send_snl();
llcp_cb.sdp_cb.p_snl = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID);
if (llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl->offset =
NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE + LLCP_PDU_HEADER_SIZE;
llcp_cb.sdp_cb.p_snl->len = 0;
llcp_sdp_add_sdreq(tid, p_name);
status = LLCP_STATUS_SUCCESS;
} else {
status = LLCP_STATUS_FAIL;
}
}
} else {
status = LLCP_STATUS_FAIL;
}
/* if LM is waiting for PDUs from upper layer */
if ((status == LLCP_STATUS_SUCCESS) &&
(llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_LOCAL_XMIT_NEXT)) {
llcp_link_check_send_data();
}
return status;
}
/*******************************************************************************
**
** Function llcp_sdp_add_sdres
**
** Description Add Service Discovery Response into SNL PDU
**
**
** Returns void
**
*******************************************************************************/
static void llcp_sdp_add_sdres(uint8_t tid, uint8_t sap) {
uint8_t* p;
p = (uint8_t*)(llcp_cb.sdp_cb.p_snl + 1) + llcp_cb.sdp_cb.p_snl->offset +
llcp_cb.sdp_cb.p_snl->len;
UINT8_TO_BE_STREAM(p, LLCP_SDRES_TYPE);
UINT8_TO_BE_STREAM(p, LLCP_SDRES_LEN);
UINT8_TO_BE_STREAM(p, tid);
UINT8_TO_BE_STREAM(p, sap);
llcp_cb.sdp_cb.p_snl->len += 2 + LLCP_SDRES_LEN; /* type and length */
}
/*******************************************************************************
**
** Function llcp_sdp_send_sdres
**
** Description Send Service Discovery Response
**
**
** Returns LLCP_STATUS
**
*******************************************************************************/
static tLLCP_STATUS llcp_sdp_send_sdres(uint8_t tid, uint8_t sap) {
tLLCP_STATUS status;
uint16_t available_bytes;
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("tid=0x%x, SAP=0x%x", tid, sap);
/* if there is no pending SNL */
if (!llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID);
if (llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl->offset =
NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE + LLCP_PDU_HEADER_SIZE;
llcp_cb.sdp_cb.p_snl->len = 0;
}
}
if (llcp_cb.sdp_cb.p_snl) {
available_bytes = GKI_get_buf_size(llcp_cb.sdp_cb.p_snl) - NFC_HDR_SIZE -
llcp_cb.sdp_cb.p_snl->offset - llcp_cb.sdp_cb.p_snl->len;
/* if SDRES parameter can be added in SNL */
if ((available_bytes >= 2 + LLCP_SDRES_LEN) &&
(llcp_cb.sdp_cb.p_snl->len + 2 + LLCP_SDRES_LEN <=
llcp_cb.lcb.effective_miu)) {
llcp_sdp_add_sdres(tid, sap);
status = LLCP_STATUS_SUCCESS;
} else {
/* send pending SNL PDU to LM */
llcp_sdp_check_send_snl();
llcp_cb.sdp_cb.p_snl = (NFC_HDR*)GKI_getpoolbuf(LLCP_POOL_ID);
if (llcp_cb.sdp_cb.p_snl) {
llcp_cb.sdp_cb.p_snl->offset =
NCI_MSG_OFFSET_SIZE + NCI_DATA_HDR_SIZE + LLCP_PDU_HEADER_SIZE;
llcp_cb.sdp_cb.p_snl->len = 0;
llcp_sdp_add_sdres(tid, sap);
status = LLCP_STATUS_SUCCESS;
} else {
status = LLCP_STATUS_FAIL;
}
}
} else {
status = LLCP_STATUS_FAIL;
}
/* if LM is waiting for PDUs from upper layer */
if ((status == LLCP_STATUS_SUCCESS) &&
(llcp_cb.lcb.symm_state == LLCP_LINK_SYMM_LOCAL_XMIT_NEXT)) {
llcp_link_check_send_data();
}
return status;
}
/*******************************************************************************
**
** Function llcp_sdp_get_sap_by_name
**
** Description Search SAP by service name
**
**
** Returns SAP if success
**
*******************************************************************************/
uint8_t llcp_sdp_get_sap_by_name(char* p_name, uint8_t length) {
uint8_t sap;
tLLCP_APP_CB* p_app_cb;
for (sap = LLCP_SAP_SDP; sap <= LLCP_UPPER_BOUND_SDP_SAP; sap++) {
p_app_cb = llcp_util_get_app_cb(sap);
if ((p_app_cb) && (p_app_cb->p_app_cback) &&
(strlen((char*)p_app_cb->p_service_name) == length) &&
(!strncmp((char*)p_app_cb->p_service_name, p_name, length))) {
/* if device is under LLCP DTA testing */
if (llcp_cb.p_dta_cback && (!strncmp((char*)p_app_cb->p_service_name,
"urn:nfc:sn:cl-echo-in", length))) {
llcp_cb.dta_snl_resp = true;
}
return (sap);
}
}
return 0;
}
/*******************************************************************************
**
** Function llcp_sdp_return_sap
**
** Description Report TID and SAP to requester
**
**
** Returns void
**
*******************************************************************************/
static void llcp_sdp_return_sap(uint8_t tid, uint8_t sap) {
uint8_t i;
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("tid=0x%x, SAP=0x%x", tid, sap);
for (i = 0; i < LLCP_MAX_SDP_TRANSAC; i++) {
if ((llcp_cb.sdp_cb.transac[i].p_cback) &&
(llcp_cb.sdp_cb.transac[i].tid == tid)) {
(*llcp_cb.sdp_cb.transac[i].p_cback)(tid, sap);
llcp_cb.sdp_cb.transac[i].p_cback = nullptr;
}
}
}
/*******************************************************************************
**
** Function llcp_sdp_proc_deactivation
**
** Description Report SDP failure for any pending request because of
** deactivation
**
**
** Returns void
**
*******************************************************************************/
void llcp_sdp_proc_deactivation(void) {
uint8_t i;
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
for (i = 0; i < LLCP_MAX_SDP_TRANSAC; i++) {
if (llcp_cb.sdp_cb.transac[i].p_cback) {
(*llcp_cb.sdp_cb.transac[i].p_cback)(llcp_cb.sdp_cb.transac[i].tid, 0x00);
llcp_cb.sdp_cb.transac[i].p_cback = nullptr;
}
}
/* free any pending SNL PDU */
if (llcp_cb.sdp_cb.p_snl) {
GKI_freebuf(llcp_cb.sdp_cb.p_snl);
llcp_cb.sdp_cb.p_snl = nullptr;
}
llcp_cb.sdp_cb.next_tid = 0;
llcp_cb.dta_snl_resp = false;
}
/*******************************************************************************
**
** Function llcp_sdp_proc_snl
**
** Description Process SDREQ and SDRES in SNL
**
**
** Returns LLCP_STATUS
**
*******************************************************************************/
tLLCP_STATUS llcp_sdp_proc_snl(uint16_t sdu_length, uint8_t* p) {
uint8_t type, length, tid, sap, *p_value;
DLOG_IF(INFO, nfc_debug_enabled) << __func__;
if ((llcp_cb.lcb.agreed_major_version < LLCP_MIN_SNL_MAJOR_VERSION) ||
((llcp_cb.lcb.agreed_major_version == LLCP_MIN_SNL_MAJOR_VERSION) &&
(llcp_cb.lcb.agreed_minor_version < LLCP_MIN_SNL_MINOR_VERSION))) {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"version number less than 1.1, SNL not "
"supported.");
return LLCP_STATUS_FAIL;
}
while (sdu_length >= 2) /* at least type and length */
{
BE_STREAM_TO_UINT8(type, p);
BE_STREAM_TO_UINT8(length, p);
switch (type) {
case LLCP_SDREQ_TYPE:
if ((length > 1) /* TID and sevice name */
&&
(sdu_length >= 2 + length)) /* type, length, TID and service name */
{
p_value = p;
BE_STREAM_TO_UINT8(tid, p_value);
sap = llcp_sdp_get_sap_by_name((char*)p_value, (uint8_t)(length - 1));
/* fix to pass TC_CTO_TAR_BI_03_x (x=5) test case
* As per the LLCP test specification v1.2.00 by receiving erroneous
* SNL PDU i'e with improper service name "urn:nfc:sn:dta-co-echo-in",
* the IUT should not send any PDU except SYMM PDU */
if (appl_dta_mode_flag == 1 && sap == 0x00) {
DLOG_IF(INFO, nfc_debug_enabled) << StringPrintf(
"%s: In dta mode sap == 0x00 p_value = %s", __func__, p_value);
if ((length - 1) == strlen((const char*)p_value)) {
DLOG_IF(INFO, nfc_debug_enabled)
<< StringPrintf("%s: Strings are not equal", __func__);
llcp_sdp_send_sdres(tid, sap);
}
} else {
llcp_sdp_send_sdres(tid, sap);
}
} else {
/*For P2P in LLCP mode TC_CTO_TAR_BI_03_x(x=3) fix*/
if (appl_dta_mode_flag == 1 &&
((nfa_dm_cb.eDtaMode & 0x0F) == NFA_DTA_LLCP_MODE)) {
LOG(ERROR) << StringPrintf("%s: Calling llcp_sdp_send_sdres",
__func__);
tid = 0x01;
sap = 0x00;
llcp_sdp_send_sdres(tid, sap);
}
LOG(ERROR) << StringPrintf("bad length (%d) in LLCP_SDREQ_TYPE",
length);
}
break;
case LLCP_SDRES_TYPE:
if ((length == LLCP_SDRES_LEN) /* TID and SAP */
&& (sdu_length >= 2 + length)) /* type, length, TID and SAP */
{
p_value = p;
BE_STREAM_TO_UINT8(tid, p_value);
BE_STREAM_TO_UINT8(sap, p_value);
llcp_sdp_return_sap(tid, sap);
} else {
LOG(ERROR) << StringPrintf("bad length (%d) in LLCP_SDRES_TYPE",
length);
}
break;
default:
LOG(WARNING) << StringPrintf("Unknown type (0x%x) is ignored", type);
break;
}
if (sdu_length >= 2 + length) /* type, length, value */
{
sdu_length -= 2 + length;
p += length;
} else {
break;
}
}
if (sdu_length) {
LOG(ERROR) << StringPrintf("Bad format of SNL");
return LLCP_STATUS_FAIL;
} else {
return LLCP_STATUS_SUCCESS;
}
}