2406 lines
86 KiB
C
2406 lines
86 KiB
C
/******************************************************************************
|
|
*
|
|
* Copyright (C) 2003-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 file contains the GATT client action functions for the state
|
|
* machine.
|
|
*
|
|
******************************************************************************/
|
|
#define LOG_TAG "bt_bta_gattc"
|
|
|
|
#include "bt_target.h"
|
|
|
|
#include "utl.h"
|
|
#include "bta_sys.h"
|
|
|
|
#include "bta_gattc_int.h"
|
|
#include "l2c_api.h"
|
|
#include "l2c_int.h"
|
|
#include "gatt_int.h"
|
|
#include "allocator.h"
|
|
#include "mutex.h"
|
|
|
|
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
|
|
#include "bta_hh_int.h"
|
|
#endif
|
|
|
|
// #include "btif/include/btif_debug_conn.h"
|
|
|
|
#include <string.h>
|
|
|
|
// #include "osi/include/log.h"
|
|
|
|
#if GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE
|
|
|
|
/*****************************************************************************
|
|
** Constants
|
|
*****************************************************************************/
|
|
static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id,
|
|
BOOLEAN connected, tGATT_DISCONN_REASON reason,
|
|
tBT_TRANSPORT transport);
|
|
|
|
static void bta_gattc_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE *p_data);
|
|
static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op,
|
|
tBTA_GATT_STATUS status,
|
|
tGATT_CL_COMPLETE *p_data);
|
|
static void bta_gattc_pop_command_to_send(tBTA_GATTC_CLCB *p_clcb);
|
|
|
|
static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg);
|
|
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda);
|
|
static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested);
|
|
static tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_id, BD_ADDR remote_bda, BOOLEAN *need_timer);
|
|
static void bta_gattc_wait4_service_change_ccc_cback (TIMER_LIST_ENT *p_tle);
|
|
static void bta_gattc_start_service_change_ccc_timer(UINT16 conn_id, BD_ADDR bda,UINT32 timeout_ms,
|
|
UINT8 timer_cnt, UINT8 last_status, TIMER_LIST_ENT *ccc_timer);
|
|
|
|
static tGATT_CBACK bta_gattc_cl_cback = {
|
|
bta_gattc_conn_cback,
|
|
bta_gattc_cmpl_cback,
|
|
bta_gattc_disc_res_cback,
|
|
bta_gattc_disc_cmpl_cback,
|
|
NULL,
|
|
bta_gattc_enc_cmpl_cback,
|
|
bta_gattc_cong_cback
|
|
};
|
|
|
|
/* opcode(tGATTC_OPTYPE) order has to be comply with internal event order */
|
|
static UINT16 bta_gattc_opcode_to_int_evt[] = {
|
|
BTA_GATTC_API_READ_EVT,
|
|
BTA_GATTC_API_WRITE_EVT,
|
|
BTA_GATTC_API_EXEC_EVT,
|
|
BTA_GATTC_API_CFG_MTU_EVT,
|
|
BTA_GATTC_API_READ_MULTI_EVT
|
|
};
|
|
|
|
#if (BT_TRACE_VERBOSE == TRUE)
|
|
static const char *bta_gattc_op_code_name[] = {
|
|
"Unknown",
|
|
"Discovery",
|
|
"Read",
|
|
"Write",
|
|
"Exec",
|
|
"Config",
|
|
"Notification",
|
|
"Indication"
|
|
};
|
|
#endif
|
|
/*****************************************************************************
|
|
** Action Functions
|
|
*****************************************************************************/
|
|
|
|
|
|
void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status);
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_enable
|
|
**
|
|
** Description Enables GATTC module
|
|
**
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_enable(tBTA_GATTC_CB *p_cb)
|
|
{
|
|
APPL_TRACE_DEBUG("bta_gattc_enable");
|
|
|
|
if (p_cb->state == BTA_GATTC_STATE_DISABLED) {
|
|
/* initialize control block */
|
|
memset(&bta_gattc_cb, 0, sizeof(tBTA_GATTC_CB));
|
|
p_cb->state = BTA_GATTC_STATE_ENABLED;
|
|
// Create a write ccc mutex when the gatt client enable
|
|
osi_mutex_new(&bta_gattc_cb.write_ccc_mutex);
|
|
} else {
|
|
APPL_TRACE_DEBUG("GATTC is already enabled");
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_disable
|
|
**
|
|
** Description Disable GATTC module by cleaning up all active connections
|
|
** and deregister all application.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_disable(tBTA_GATTC_CB *p_cb)
|
|
{
|
|
UINT8 i;
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_disable");
|
|
|
|
if (p_cb->state != BTA_GATTC_STATE_ENABLED) {
|
|
APPL_TRACE_ERROR("not enabled or disable in pogress");
|
|
return;
|
|
}
|
|
// Free the write ccc mutex when the gatt client disable
|
|
osi_mutex_free(&bta_gattc_cb.write_ccc_mutex);
|
|
|
|
for (i = 0; i < BTA_GATTC_CL_MAX; i ++) {
|
|
if (p_cb->cl_rcb[i].in_use) {
|
|
p_cb->state = BTA_GATTC_STATE_DISABLING;
|
|
/* don't deregister HH GATT IF */
|
|
/* HH GATT IF will be deregistered by bta_hh_le_deregister when disable HH */
|
|
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
|
|
if (!bta_hh_le_is_hh_gatt_if(p_cb->cl_rcb[i].client_if)) {
|
|
#endif
|
|
bta_gattc_deregister(p_cb, &p_cb->cl_rcb[i]);
|
|
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/* no registered apps, indicate disable completed */
|
|
if (p_cb->state != BTA_GATTC_STATE_DISABLING) {
|
|
p_cb->state = BTA_GATTC_STATE_DISABLED;
|
|
memset(p_cb, 0, sizeof(tBTA_GATTC_CB));
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_register
|
|
**
|
|
** Description Register a GATT client application with BTA.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_register(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
UINT8 i;
|
|
tBT_UUID *p_app_uuid = &p_data->api_reg.app_uuid;
|
|
tBTA_GATTC_INT_START_IF *p_buf;
|
|
tBTA_GATT_STATUS status = BTA_GATT_NO_RESOURCES;
|
|
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_register state %d\n", p_cb->state);
|
|
memset(&cb_data, 0, sizeof(cb_data));
|
|
cb_data.reg_oper.status = BTA_GATT_NO_RESOURCES;
|
|
|
|
/* check if GATTC module is already enabled . Else enable */
|
|
if (p_cb->state == BTA_GATTC_STATE_DISABLED) {
|
|
bta_gattc_enable (p_cb);
|
|
}
|
|
/* todo need to check duplicate uuid */
|
|
for (i = 0; i < BTA_GATTC_CL_MAX; i ++) {
|
|
if (!p_cb->cl_rcb[i].in_use) {
|
|
if ((p_app_uuid == NULL) || (p_cb->cl_rcb[i].client_if = GATT_Register(p_app_uuid, &bta_gattc_cl_cback)) == 0) {
|
|
APPL_TRACE_ERROR("Register with GATT stack failed.\n");
|
|
status = BTA_GATT_ERROR;
|
|
} else {
|
|
p_cb->cl_rcb[i].in_use = TRUE;
|
|
p_cb->cl_rcb[i].p_cback = p_data->api_reg.p_cback;
|
|
memcpy(&p_cb->cl_rcb[i].app_uuid, p_app_uuid, sizeof(tBT_UUID));
|
|
|
|
/* BTA use the same client interface as BTE GATT statck */
|
|
cb_data.reg_oper.client_if = p_cb->cl_rcb[i].client_if;
|
|
|
|
if ((p_buf = (tBTA_GATTC_INT_START_IF *) osi_malloc(sizeof(tBTA_GATTC_INT_START_IF))) != NULL) {
|
|
p_buf->hdr.event = BTA_GATTC_INT_START_IF_EVT;
|
|
p_buf->client_if = p_cb->cl_rcb[i].client_if;
|
|
APPL_TRACE_DEBUG("GATTC getbuf sucess.\n");
|
|
bta_sys_sendmsg(p_buf);
|
|
status = BTA_GATT_OK;
|
|
} else {
|
|
GATT_Deregister(p_cb->cl_rcb[i].client_if);
|
|
|
|
status = BTA_GATT_NO_RESOURCES;
|
|
memset( &p_cb->cl_rcb[i], 0 , sizeof(tBTA_GATTC_RCB));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* callback with register event */
|
|
if (p_data->api_reg.p_cback) {
|
|
if (p_app_uuid != NULL) {
|
|
memcpy(&(cb_data.reg_oper.app_uuid), p_app_uuid, sizeof(tBT_UUID));
|
|
}
|
|
cb_data.reg_oper.status = status;
|
|
(*p_data->api_reg.p_cback)(BTA_GATTC_REG_EVT, (tBTA_GATTC *)&cb_data);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_start_if
|
|
**
|
|
** Description start an application interface.
|
|
**
|
|
** Returns none.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_start_if(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
UNUSED(p_cb);
|
|
|
|
if (bta_gattc_cl_get_regcb(p_msg->int_start_if.client_if) != NULL ) {
|
|
GATT_StartIf(p_msg->int_start_if.client_if);
|
|
} else {
|
|
APPL_TRACE_ERROR("Unable to start app.: Unknown interface =%d", p_msg->int_start_if.client_if );
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_deregister
|
|
**
|
|
** Description De-Register a GATT client application with BTA.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_deregister(tBTA_GATTC_CB *p_cb, tBTA_GATTC_RCB *p_clreg)
|
|
{
|
|
UINT8 i;
|
|
BT_HDR buf;
|
|
|
|
if (p_clreg != NULL) {
|
|
/* remove bg connection associated with this rcb */
|
|
for (i = 0; i < BTA_GATTC_KNOWN_SR_MAX; i ++) {
|
|
if (p_cb->bg_track[i].in_use) {
|
|
if (p_cb->bg_track[i].cif_mask & (1 << (p_clreg->client_if - 1))) {
|
|
bta_gattc_mark_bg_conn(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE, FALSE);
|
|
GATT_CancelConnect(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE);
|
|
}
|
|
if (p_cb->bg_track[i].cif_adv_mask & (1 << (p_clreg->client_if - 1))) {
|
|
bta_gattc_mark_bg_conn(p_clreg->client_if, p_cb->bg_track[i].remote_bda, FALSE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (p_clreg->num_clcb > 0) {
|
|
/* close all CLCB related to this app */
|
|
for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) {
|
|
if (p_cb->clcb[i].in_use && (p_cb->clcb[i].p_rcb == p_clreg)) {
|
|
p_clreg->dereg_pending = TRUE;
|
|
|
|
buf.event = BTA_GATTC_API_CLOSE_EVT;
|
|
buf.layer_specific = p_cb->clcb[i].bta_conn_id;
|
|
bta_gattc_close(&p_cb->clcb[i], (tBTA_GATTC_DATA *)&buf) ;
|
|
}
|
|
}
|
|
} else {
|
|
bta_gattc_deregister_cmpl(p_clreg);
|
|
}
|
|
} else {
|
|
APPL_TRACE_ERROR("bta_gattc_deregister Deregister Failedm unknown client cif");
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_api_open
|
|
**
|
|
** Description process connect API request.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_process_api_open (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
UINT16 event = ((BT_HDR *)p_msg)->event;
|
|
tBTA_GATTC_CLCB *p_clcb = NULL;
|
|
tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_conn.client_if);
|
|
UNUSED(p_cb);
|
|
|
|
if (p_clreg != NULL) {
|
|
if (p_msg->api_conn.is_direct) {
|
|
if ((p_clcb = bta_gattc_find_alloc_clcb(p_msg->api_conn.client_if,
|
|
p_msg->api_conn.remote_bda,
|
|
p_msg->api_conn.transport)) != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, event, p_msg);
|
|
} else {
|
|
APPL_TRACE_ERROR("No resources to open a new connection.");
|
|
|
|
bta_gattc_send_open_cback(p_clreg,
|
|
BTA_GATT_NO_RESOURCES,
|
|
p_msg->api_conn.remote_bda,
|
|
BTA_GATT_INVALID_CONN_ID,
|
|
p_msg->api_conn.transport, 0);
|
|
}
|
|
} else {
|
|
bta_gattc_init_bk_conn(&p_msg->api_conn, p_clreg);
|
|
}
|
|
} else {
|
|
APPL_TRACE_ERROR("bta_gattc_process_api_open Failed, unknown client_if: %d",
|
|
p_msg->api_conn.client_if);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_api_open_cancel
|
|
**
|
|
** Description process connect API request.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_process_api_open_cancel (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
UINT16 event = ((BT_HDR *)p_msg)->event;
|
|
tBTA_GATTC_CLCB *p_clcb = NULL;
|
|
tBTA_GATTC_RCB *p_clreg;
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_cb);
|
|
|
|
if (p_msg->api_cancel_conn.is_direct) {
|
|
if ((p_clcb = bta_gattc_find_clcb_by_cif(p_msg->api_cancel_conn.client_if,
|
|
p_msg->api_cancel_conn.remote_bda,
|
|
BTA_GATT_TRANSPORT_LE)) != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, event, p_msg);
|
|
} else {
|
|
APPL_TRACE_ERROR("No such connection need to be cancelled");
|
|
|
|
p_clreg = bta_gattc_cl_get_regcb(p_msg->api_cancel_conn.client_if);
|
|
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
cb_data.status = BTA_GATT_ERROR;
|
|
(*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
} else {
|
|
bta_gattc_cancel_bk_conn(&p_msg->api_cancel_conn);
|
|
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_enc_cmpl
|
|
**
|
|
** Description process encryption complete message.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_process_enc_cmpl(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
tBTA_GATTC_RCB *p_clreg;
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_cb);
|
|
|
|
p_clreg = bta_gattc_cl_get_regcb(p_msg->enc_cmpl.client_if);
|
|
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
|
|
cb_data.enc_cmpl.client_if = p_msg->enc_cmpl.client_if;
|
|
bdcpy(cb_data.enc_cmpl.remote_bda, p_msg->enc_cmpl.remote_bda);
|
|
|
|
(*p_clreg->p_cback)(BTA_GATTC_ENC_CMPL_CB_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cancel_open_error
|
|
**
|
|
** Description
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cancel_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_data);
|
|
|
|
cb_data.status = BTA_GATT_ERROR;
|
|
|
|
if ( p_clcb && p_clcb->p_rcb && p_clcb->p_rcb->p_cback ) {
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_open_error
|
|
**
|
|
** Description
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_open_error(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_data);
|
|
|
|
APPL_TRACE_ERROR("Connection already opened. wrong state");
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb,
|
|
BTA_GATT_OK,
|
|
p_clcb->bda,
|
|
p_clcb->bta_conn_id,
|
|
p_clcb->transport,
|
|
0);
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_open_fail
|
|
**
|
|
** Description
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_open_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_data);
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb,
|
|
BTA_GATT_ERROR,
|
|
p_clcb->bda,
|
|
p_clcb->bta_conn_id,
|
|
p_clcb->transport,
|
|
0);
|
|
/* open failure, remove clcb */
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_open
|
|
**
|
|
** Description Process API connection function.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC_DATA gattc_data;
|
|
|
|
/* open/hold a connection */
|
|
if (!GATT_Connect(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda,
|
|
TRUE, p_data->api_conn.transport)) {
|
|
APPL_TRACE_ERROR("Connection open failure");
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_OPEN_FAIL_EVT, p_data);
|
|
} else {
|
|
/* a connected remote device */
|
|
if (GATT_GetConnIdIfConnected(p_clcb->p_rcb->client_if,
|
|
p_data->api_conn.remote_bda,
|
|
&p_clcb->bta_conn_id,
|
|
p_data->api_conn.transport)) {
|
|
gattc_data.int_conn.hdr.layer_specific = p_clcb->bta_conn_id;
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data);
|
|
}
|
|
/* else wait for the callback event */
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_init_bk_conn
|
|
**
|
|
** Description Process API Open for a background connection
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_init_bk_conn(tBTA_GATTC_API_OPEN *p_data, tBTA_GATTC_RCB *p_clreg)
|
|
{
|
|
tBTA_GATT_STATUS status = BTA_GATT_NO_RESOURCES;
|
|
UINT16 conn_id;
|
|
tBTA_GATTC_CLCB *p_clcb;
|
|
tBTA_GATTC_DATA gattc_data;
|
|
|
|
if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, TRUE, FALSE)) {
|
|
/* always call open to hold a connection */
|
|
if (!GATT_Connect(p_data->client_if, p_data->remote_bda, FALSE, p_data->transport)) {
|
|
uint8_t *bda = (uint8_t *)p_data->remote_bda;
|
|
status = BTA_GATT_ERROR;
|
|
APPL_TRACE_ERROR("%s unable to connect to remote bd_addr:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
__func__, bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
|
|
|
|
} else {
|
|
status = BTA_GATT_OK;
|
|
|
|
/* if is a connected remote device */
|
|
if (GATT_GetConnIdIfConnected(p_data->client_if,
|
|
p_data->remote_bda,
|
|
&conn_id,
|
|
p_data->transport)) {
|
|
if ((p_clcb = bta_gattc_find_alloc_clcb(p_data->client_if, p_data->remote_bda,
|
|
BTA_GATT_TRANSPORT_LE)) != NULL) {
|
|
gattc_data.hdr.layer_specific = p_clcb->bta_conn_id = conn_id;
|
|
|
|
/* open connection */
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data);
|
|
status = BTA_GATT_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* open failure, report OPEN_EVT */
|
|
if (status != BTA_GATT_OK) {
|
|
bta_gattc_send_open_cback(p_clreg, status, p_data->remote_bda,
|
|
BTA_GATT_INVALID_CONN_ID, BTA_GATT_TRANSPORT_LE, 0);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cancel_bk_conn
|
|
**
|
|
** Description Process API Cancel Open for a background connection
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN *p_data)
|
|
{
|
|
tBTA_GATTC_RCB *p_clreg;
|
|
tBTA_GATTC cb_data;
|
|
cb_data.status = BTA_GATT_ERROR;
|
|
|
|
/* remove the device from the bg connection mask */
|
|
if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, FALSE, FALSE)) {
|
|
if (GATT_CancelConnect(p_data->client_if, p_data->remote_bda, FALSE)) {
|
|
cb_data.status = BTA_GATT_OK;
|
|
} else {
|
|
APPL_TRACE_ERROR("bta_gattc_cancel_bk_conn failed");
|
|
}
|
|
}
|
|
p_clreg = bta_gattc_cl_get_regcb(p_data->client_if);
|
|
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
(*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_int_cancel_open_ok
|
|
**
|
|
** Description
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cancel_open_ok(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_data);
|
|
|
|
if ( p_clcb->p_rcb->p_cback ) {
|
|
cb_data.status = BTA_GATT_OK;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cancel_open
|
|
**
|
|
** Description
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cancel_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
|
|
if (GATT_CancelConnect(p_clcb->p_rcb->client_if, p_data->api_cancel_conn.remote_bda, TRUE)) {
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CANCEL_OPEN_OK_EVT, p_data);
|
|
} else {
|
|
if ( p_clcb->p_rcb->p_cback ) {
|
|
cb_data.status = BTA_GATT_ERROR;
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_conn
|
|
**
|
|
** Description receive connection callback from stack
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC_IF gatt_if;
|
|
APPL_TRACE_DEBUG("bta_gattc_conn server cache state=%d", p_clcb->p_srcb->state);
|
|
|
|
if (p_data != NULL) {
|
|
APPL_TRACE_DEBUG("bta_gattc_conn conn_id=%d", p_data->hdr.layer_specific);
|
|
p_clcb->bta_conn_id = p_data->int_conn.hdr.layer_specific;
|
|
|
|
GATT_GetConnectionInfor(p_data->hdr.layer_specific,
|
|
&gatt_if, p_clcb->bda, &p_clcb->transport);
|
|
}
|
|
|
|
p_clcb->p_srcb->connected = TRUE;
|
|
|
|
if (p_clcb->p_srcb->mtu == 0) {
|
|
p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE;
|
|
}
|
|
|
|
/* start database cache if needed */
|
|
if (p_clcb->p_srcb->p_srvc_cache == NULL ||
|
|
p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE) {
|
|
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
|
|
if (bta_gattc_cache_load(p_clcb)) {
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, BTA_GATT_OK);
|
|
} else { /* cache is building */
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC;
|
|
/* cache load failure, start discovery */
|
|
bta_gattc_start_discover(p_clcb, NULL);
|
|
}
|
|
} else { /* cache is building */
|
|
p_clcb->state = BTA_GATTC_DISCOVER_ST;
|
|
}
|
|
} else {
|
|
/* a pending service handle change indication */
|
|
if (p_clcb->p_srcb->srvc_hdl_chg) {
|
|
p_clcb->p_srcb->srvc_hdl_chg = FALSE;
|
|
/* start discovery */
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
|
|
if (p_clcb->p_rcb) {
|
|
/* there is no RM for GATT */
|
|
if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) {
|
|
bta_sys_conn_open(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
|
|
bta_gattc_send_open_cback(p_clcb->p_rcb,
|
|
BTA_GATT_OK,
|
|
p_clcb->bda,
|
|
p_clcb->bta_conn_id,
|
|
p_clcb->transport,
|
|
p_clcb->p_srcb->mtu);
|
|
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_conncback
|
|
**
|
|
** Description receive connection callback from stack
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_conncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
if (p_rcb) {
|
|
bta_gattc_send_connect_cback(p_rcb,
|
|
p_data->int_conn.remote_bda,
|
|
p_data->int_conn.hdr.layer_specific);
|
|
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_disconncback
|
|
**
|
|
** Description receive disconnection callback from stack
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_disconncback(tBTA_GATTC_RCB *p_rcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
if (p_rcb) {
|
|
bta_gattc_send_disconnect_cback(p_rcb,
|
|
p_data->int_conn.reason,
|
|
p_data->int_conn.remote_bda,
|
|
p_data->int_conn.hdr.layer_specific);
|
|
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_close_fail
|
|
**
|
|
** Description close a connection.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_close_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
|
|
if ( p_clcb->p_rcb->p_cback ) {
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
cb_data.close.client_if = p_clcb->p_rcb->client_if;
|
|
cb_data.close.conn_id = p_data->hdr.layer_specific;
|
|
bdcpy(cb_data.close.remote_bda, p_clcb->bda);
|
|
cb_data.close.status = BTA_GATT_ERROR;
|
|
cb_data.close.reason = BTA_GATT_CONN_NONE;
|
|
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CLOSE_EVT, &cb_data);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_api_close
|
|
**
|
|
** Description close a GATTC connection.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC_CBACK *p_cback = p_clcb->p_rcb->p_cback;
|
|
tBTA_GATTC_RCB *p_clreg = p_clcb->p_rcb;
|
|
tBTA_GATTC cb_data;
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_close conn_id=%d", p_clcb->bta_conn_id);
|
|
|
|
cb_data.close.client_if = p_clcb->p_rcb->client_if;
|
|
cb_data.close.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.close.reason = p_clcb->reason;
|
|
cb_data.close.status = p_clcb->status;
|
|
bdcpy(cb_data.close.remote_bda, p_clcb->bda);
|
|
|
|
if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) {
|
|
bta_sys_conn_close( BTA_ID_GATTC , BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
|
|
bta_gattc_clcb_dealloc(p_clcb);
|
|
|
|
if (p_data->hdr.event == BTA_GATTC_API_CLOSE_EVT) {
|
|
cb_data.close.status = GATT_Disconnect(p_data->hdr.layer_specific);
|
|
} else if (p_data->hdr.event == BTA_GATTC_INT_DISCONN_EVT) {
|
|
cb_data.close.status = BTA_GATT_OK;
|
|
cb_data.close.reason = p_data->int_conn.reason;
|
|
}
|
|
|
|
if (p_cback) {
|
|
(* p_cback)(BTA_GATTC_CLOSE_EVT, (tBTA_GATTC *)&cb_data);
|
|
}
|
|
|
|
if (p_clreg->num_clcb == 0 && p_clreg->dereg_pending) {
|
|
bta_gattc_deregister_cmpl(p_clreg);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_reset_discover_st
|
|
**
|
|
** Description when a SRCB finished discovery, tell all related clcb.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_reset_discover_st(tBTA_GATTC_SERV *p_srcb, tBTA_GATT_STATUS status)
|
|
{
|
|
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) {
|
|
if (p_cb->clcb[i].p_srcb == p_srcb) {
|
|
p_cb->clcb[i].status = status;
|
|
bta_gattc_sm_execute(&p_cb->clcb[i], BTA_GATTC_DISCOVER_CMPL_EVT, NULL);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_disc_close
|
|
**
|
|
** Description close a GATTC connection while in discovery state.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_disc_close(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
APPL_TRACE_DEBUG("%s: Discovery cancel conn_id=%d", __func__,
|
|
p_clcb->bta_conn_id);
|
|
|
|
if (p_clcb->disc_active) {
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, BTA_GATT_ERROR);
|
|
} else {
|
|
p_clcb->state = BTA_GATTC_CONN_ST;
|
|
}
|
|
|
|
// This function only gets called as the result of a BTA_GATTC_API_CLOSE_EVT
|
|
// while in the BTA_GATTC_DISCOVER_ST state. Once the state changes, the
|
|
// connection itself still needs to be closed to resolve the original event.
|
|
if (p_clcb->state == BTA_GATTC_CONN_ST) {
|
|
APPL_TRACE_DEBUG("State is back to BTA_GATTC_CONN_ST. "
|
|
"Trigger connection close");
|
|
bta_gattc_close(p_clcb, p_data);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_set_discover_st
|
|
**
|
|
** Description when a SRCB start discovery, tell all related clcb and set
|
|
** the state.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_set_discover_st(tBTA_GATTC_SERV *p_srcb)
|
|
{
|
|
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
|
|
UINT8 i;
|
|
|
|
for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++) {
|
|
if (p_cb->clcb[i].p_srcb == p_srcb) {
|
|
p_cb->clcb[i].status = BTA_GATT_OK;
|
|
p_cb->clcb[i].state = BTA_GATTC_DISCOVER_ST;
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_restart_discover
|
|
**
|
|
** Description process service change in discovery state, mark up the auto
|
|
** update flag and set status to be discovery cancel for current
|
|
** discovery.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_restart_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_data);
|
|
|
|
p_clcb->status = BTA_GATT_CANCEL;
|
|
p_clcb->auto_update = BTA_GATTC_DISC_WAITING;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cfg_mtu
|
|
**
|
|
** Description Configure MTU size on the GATT connection.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATT_STATUS status;
|
|
|
|
if (bta_gattc_enqueue(p_clcb, p_data)) {
|
|
status = GATTC_ConfigureMTU (p_clcb->bta_conn_id);
|
|
|
|
/* if failed, return callback here */
|
|
if (status != GATT_SUCCESS && status != GATT_CMD_STARTED) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG, status, NULL);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_start_discover
|
|
**
|
|
** Description Start a discovery send to server.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_start_discover(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_data);
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_start_discover conn_id=%d p_clcb->p_srcb->state = %d ",
|
|
p_clcb->bta_conn_id, p_clcb->p_srcb->state);
|
|
|
|
if (((p_clcb->p_q_cmd == NULL || p_clcb->auto_update == BTA_GATTC_REQ_WAITING) &&
|
|
p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) ||
|
|
p_clcb->p_srcb->state == BTA_GATTC_SERV_DISC) {
|
|
/* no pending operation, start discovery right away */
|
|
p_clcb->auto_update = BTA_GATTC_NO_SCHEDULE;
|
|
|
|
if (p_clcb->p_srcb != NULL) {
|
|
/* clear the service change mask */
|
|
p_clcb->p_srcb->srvc_hdl_chg = FALSE;
|
|
p_clcb->p_srcb->update_count = 0;
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_DISC_ACT;
|
|
|
|
if (p_clcb->transport == BTA_TRANSPORT_LE) {
|
|
L2CA_EnableUpdateBleConnParams(p_clcb->p_srcb->server_bda, FALSE);
|
|
}
|
|
|
|
/* set all srcb related clcb into discovery ST */
|
|
bta_gattc_set_discover_st(p_clcb->p_srcb);
|
|
|
|
if ((p_clcb->status = bta_gattc_init_cache(p_clcb->p_srcb)) == BTA_GATT_OK) {
|
|
p_clcb->status = bta_gattc_discover_pri_service(p_clcb->bta_conn_id,
|
|
p_clcb->p_srcb, GATT_DISC_SRVC_ALL);
|
|
}
|
|
if (p_clcb->status != BTA_GATT_OK) {
|
|
APPL_TRACE_ERROR("discovery on server failed");
|
|
bta_gattc_reset_discover_st(p_clcb->p_srcb, p_clcb->status);
|
|
} else {
|
|
p_clcb->disc_active = TRUE;
|
|
}
|
|
} else {
|
|
APPL_TRACE_ERROR("unknown device, can not start discovery");
|
|
}
|
|
}
|
|
/* pending operation, wait until it finishes */
|
|
else {
|
|
p_clcb->auto_update = BTA_GATTC_DISC_WAITING;
|
|
|
|
if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE) {
|
|
p_clcb->state = BTA_GATTC_CONN_ST; /* set clcb state */
|
|
}
|
|
}
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_disc_cmpl
|
|
**
|
|
** Description discovery on server is finished
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_disc_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATTC_DATA *p_q_cmd = p_clcb->p_q_cmd;
|
|
UNUSED(p_data);
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_disc_cmpl conn_id=%d, status = %d", p_clcb->bta_conn_id, p_clcb->status);
|
|
|
|
p_clcb->p_srcb->state = BTA_GATTC_SERV_IDLE;
|
|
p_clcb->disc_active = FALSE;
|
|
|
|
if (p_clcb->status != GATT_SUCCESS) {
|
|
/* clean up cache */
|
|
if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) {
|
|
list_free(p_clcb->p_srcb->p_srvc_cache);
|
|
p_clcb->p_srcb->p_srvc_cache = NULL;
|
|
}
|
|
|
|
/* used to reset cache in application */
|
|
bta_gattc_cache_reset(p_clcb->p_srcb->server_bda);
|
|
}
|
|
if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_list) {
|
|
/* release pending attribute list buffer */
|
|
osi_free(p_clcb->p_srcb->p_srvc_list);
|
|
p_clcb->p_srcb->p_srvc_list = NULL;
|
|
//osi_free_and_reset((void **)&p_clcb->p_srcb->p_srvc_list);
|
|
}
|
|
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
|
|
/* start discovery again */
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
/* get any queued command to proceed */
|
|
else if (p_q_cmd != NULL) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
/* execute pending operation of link block still present */
|
|
if (l2cu_find_lcb_by_bd_addr(p_clcb->p_srcb->server_bda, BT_TRANSPORT_LE) != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
|
|
}
|
|
/* if the command executed requeued the cmd, we don't
|
|
* want to free the underlying buffer that's being
|
|
* referenced by p_clcb->p_q_cmd
|
|
*/
|
|
if (p_q_cmd != p_clcb->p_q_cmd) {
|
|
osi_free(p_q_cmd);
|
|
p_q_cmd = NULL;
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_read
|
|
**
|
|
** Description Read an attribute
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_read(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
if (!bta_gattc_enqueue(p_clcb, p_data))
|
|
return;
|
|
|
|
tGATT_READ_PARAM read_param;
|
|
memset (&read_param, 0 ,sizeof(tGATT_READ_PARAM));
|
|
read_param.by_handle.handle = p_data->api_read.handle;
|
|
read_param.by_handle.auth_req = p_data->api_read.auth_req;
|
|
|
|
tBTA_GATT_STATUS status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_BY_HANDLE, &read_param);
|
|
|
|
/* read fail */
|
|
if (status != BTA_GATT_OK) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_read_multi
|
|
**
|
|
** Description read multiple
|
|
**
|
|
** Returns None.
|
|
*********************************************************************************/
|
|
void bta_gattc_read_multi(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATT_STATUS status = BTA_GATT_OK;
|
|
tGATT_READ_PARAM read_param;
|
|
|
|
if (bta_gattc_enqueue(p_clcb, p_data)) {
|
|
memset(&read_param, 0, sizeof(tGATT_READ_PARAM));
|
|
|
|
if (status == BTA_GATT_OK) {
|
|
read_param.read_multiple.num_handles = p_data->api_read_multi.num_attr;
|
|
read_param.read_multiple.auth_req = p_data->api_read_multi.auth_req;
|
|
memcpy(&read_param.read_multiple.handles, p_data->api_read_multi.handles,
|
|
sizeof(UINT16) * p_data->api_read_multi.num_attr);
|
|
|
|
status = GATTC_Read(p_clcb->bta_conn_id, GATT_READ_MULTIPLE, &read_param);
|
|
}
|
|
|
|
/* read fail */
|
|
if (status != BTA_GATT_OK) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_READ, status, NULL);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_write
|
|
**
|
|
** Description Write an attribute
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
if (!bta_gattc_enqueue(p_clcb, p_data))
|
|
return;
|
|
|
|
tBTA_GATT_STATUS status = BTA_GATT_OK;
|
|
tGATT_VALUE attr;
|
|
|
|
attr.conn_id = p_clcb->bta_conn_id;
|
|
attr.handle = p_data->api_write.handle;
|
|
attr.offset = p_data->api_write.offset;
|
|
attr.len = p_data->api_write.len;
|
|
attr.auth_req = p_data->api_write.auth_req;
|
|
|
|
if (p_data->api_write.p_value) {
|
|
memcpy(attr.value, p_data->api_write.p_value, p_data->api_write.len);
|
|
}
|
|
|
|
status = GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &attr);
|
|
|
|
/* write fail */
|
|
if (status != BTA_GATT_OK) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_WRITE, status, NULL);
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_execute
|
|
**
|
|
** Description send execute write
|
|
**
|
|
** Returns None.
|
|
*********************************************************************************/
|
|
void bta_gattc_execute(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATT_STATUS status;
|
|
|
|
if (bta_gattc_enqueue(p_clcb, p_data)) {
|
|
status = GATTC_ExecuteWrite(p_clcb->bta_conn_id, p_data->api_exec.is_execute);
|
|
|
|
if (status != BTA_GATT_OK) {
|
|
/* Dequeue the data, if it was enqueued */
|
|
if (p_clcb->p_q_cmd == p_data) {
|
|
p_clcb->p_q_cmd = NULL;
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_EXE_WRITE, status, NULL);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_confirm
|
|
**
|
|
** Description send handle value confirmation
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_confirm(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UINT16 handle = p_data->api_confirm.handle;
|
|
|
|
if (GATTC_SendHandleValueConfirm(p_data->api_confirm.hdr.layer_specific, handle)
|
|
!= GATT_SUCCESS) {
|
|
APPL_TRACE_ERROR("bta_gattc_confirm to handle [0x%04x] failed", handle);
|
|
} else {
|
|
/* if over BR_EDR, inform PM for mode change */
|
|
if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) {
|
|
bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_read_cmpl
|
|
**
|
|
** Description read complete
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_read_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
|
|
{
|
|
UINT8 event;
|
|
tBTA_GATTC cb_data;
|
|
tBTA_GATT_UNFMT read_value;
|
|
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
memset(&read_value, 0, sizeof(tBTA_GATT_UNFMT));
|
|
|
|
cb_data.read.status = p_data->status;
|
|
|
|
if (p_data->p_cmpl != NULL && p_data->status == BTA_GATT_OK) {
|
|
cb_data.read.handle = p_data->p_cmpl->att_value.handle;
|
|
|
|
read_value.len = p_data->p_cmpl->att_value.len;
|
|
read_value.p_value = p_data->p_cmpl->att_value.value;
|
|
cb_data.read.p_value = &read_value;
|
|
} else {
|
|
cb_data.read.handle = p_clcb->p_q_cmd->api_read.handle;
|
|
}
|
|
|
|
if (p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_EVT) {
|
|
event = p_clcb->p_q_cmd->api_read.cmpl_evt;
|
|
} else {
|
|
event = p_clcb->p_q_cmd->api_read_multi.cmpl_evt;
|
|
}
|
|
cb_data.read.conn_id = p_clcb->bta_conn_id;
|
|
//free the command data store in the queue.
|
|
bta_gattc_free_command_data(p_clcb);
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
/* read complete, callback */
|
|
( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data);
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_write_cmpl
|
|
**
|
|
** Description write complete
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_write_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
|
|
{
|
|
tBTA_GATTC cb_data = {0};
|
|
UINT8 event;
|
|
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
|
|
cb_data.write.status = p_data->status;
|
|
cb_data.write.handle = p_data->p_cmpl->att_value.handle;
|
|
|
|
if (p_clcb->p_q_cmd->api_write.hdr.event == BTA_GATTC_API_WRITE_EVT &&
|
|
p_clcb->p_q_cmd->api_write.write_type == BTA_GATTC_WRITE_PREPARE) {
|
|
|
|
event = BTA_GATTC_PREP_WRITE_EVT;
|
|
} else {
|
|
event = p_clcb->p_q_cmd->api_write.cmpl_evt;
|
|
}
|
|
//free the command data store in the queue.
|
|
bta_gattc_free_command_data(p_clcb);
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
cb_data.write.conn_id = p_clcb->bta_conn_id;
|
|
/* write complete, callback */
|
|
( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data);
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_exec_cmpl
|
|
**
|
|
** Description execute write complete
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_exec_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
//free the command data store in the queue.
|
|
bta_gattc_free_command_data(p_clcb);
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
p_clcb->status = BTA_GATT_OK;
|
|
|
|
/* execute complete, callback */
|
|
cb_data.exec_cmpl.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.exec_cmpl.status = p_data->status;
|
|
|
|
( *p_clcb->p_rcb->p_cback)(BTA_GATTC_EXEC_EVT, &cb_data);
|
|
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cfg_mtu_cmpl
|
|
**
|
|
** Description configure MTU operation complete
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_cfg_mtu_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_OP_CMPL *p_data)
|
|
{
|
|
tBTA_GATTC cb_data;
|
|
//free the command data store in the queue.
|
|
bta_gattc_free_command_data(p_clcb);
|
|
bta_gattc_pop_command_to_send(p_clcb);
|
|
|
|
if (p_data->p_cmpl && p_data->status == BTA_GATT_OK) {
|
|
p_clcb->p_srcb->mtu = p_data->p_cmpl->mtu;
|
|
}
|
|
|
|
/* configure MTU complete, callback */
|
|
p_clcb->status = p_data->status;
|
|
cb_data.cfg_mtu.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.cfg_mtu.status = p_data->status;
|
|
cb_data.cfg_mtu.mtu = p_clcb->p_srcb->mtu;
|
|
|
|
(*p_clcb->p_rcb->p_cback) (BTA_GATTC_CFG_MTU_EVT, &cb_data);
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_op_cmpl
|
|
**
|
|
** Description operation completed.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UINT8 op = (UINT8)p_data->op_cmpl.op_code;
|
|
UINT8 mapped_op = 0;
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_op_cmpl op = %d", op);
|
|
|
|
if (op == GATTC_OPTYPE_INDICATION || op == GATTC_OPTYPE_NOTIFICATION) {
|
|
APPL_TRACE_ERROR("unexpected operation, ignored");
|
|
} else if (op >= GATTC_OPTYPE_READ) {
|
|
if (p_clcb->p_q_cmd == NULL) {
|
|
APPL_TRACE_ERROR("No pending command");
|
|
return;
|
|
}
|
|
if (p_clcb->p_q_cmd->hdr.event != bta_gattc_opcode_to_int_evt[op - GATTC_OPTYPE_READ]) {
|
|
if (p_clcb->p_q_cmd->hdr.event != BTA_GATTC_API_READ_MULTI_EVT) {
|
|
mapped_op = p_clcb->p_q_cmd->hdr.event - BTA_GATTC_API_READ_EVT + GATTC_OPTYPE_READ;
|
|
if ( mapped_op > GATTC_OPTYPE_INDICATION) {
|
|
mapped_op = 0;
|
|
}
|
|
|
|
#if (BT_TRACE_VERBOSE == TRUE)
|
|
APPL_TRACE_ERROR("expect op:(%s :0x%04x), receive unexpected operation (%s).",
|
|
bta_gattc_op_code_name[mapped_op] , p_clcb->p_q_cmd->hdr.event,
|
|
bta_gattc_op_code_name[op]);
|
|
#else
|
|
APPL_TRACE_ERROR("expect op:(%u :0x%04x), receive unexpected operation (%u).",
|
|
mapped_op , p_clcb->p_q_cmd->hdr.event, op);
|
|
#endif
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* discard responses if service change indication is received before operation completed */
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING && p_clcb->p_srcb->srvc_hdl_chg) {
|
|
APPL_TRACE_DEBUG("Discard all responses when service change indication is received.");
|
|
p_data->op_cmpl.status = GATT_ERROR;
|
|
}
|
|
|
|
/* service handle change void the response, discard it */
|
|
if (op == GATTC_OPTYPE_READ) {
|
|
bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl);
|
|
}
|
|
|
|
else if (op == GATTC_OPTYPE_WRITE) {
|
|
bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);
|
|
}
|
|
|
|
else if (op == GATTC_OPTYPE_EXE_WRITE) {
|
|
bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl);
|
|
}
|
|
|
|
else if (op == GATTC_OPTYPE_CONFIG) {
|
|
bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);
|
|
}
|
|
|
|
if (p_clcb->auto_update == BTA_GATTC_DISC_WAITING) {
|
|
p_clcb->auto_update = BTA_GATTC_REQ_WAITING;
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_op_cmpl
|
|
**
|
|
** Description operation completed.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_ignore_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_clcb);
|
|
|
|
/* receive op complete when discovery is started, ignore the response,
|
|
and wait for discovery finish and resent */
|
|
APPL_TRACE_DEBUG("bta_gattc_ignore_op_cmpl op = %d", p_data->hdr.layer_specific);
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_search
|
|
**
|
|
** Description start a search in the local server cache
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
tBTA_GATT_STATUS status = GATT_INTERNAL_ERROR;
|
|
tBTA_GATTC cb_data;
|
|
APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d", p_clcb->bta_conn_id);
|
|
if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache) {
|
|
status = BTA_GATT_OK;
|
|
/* search the local cache of a server device */
|
|
bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);
|
|
}
|
|
cb_data.search_cmpl.status = status;
|
|
cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id;
|
|
|
|
/* end of search or no server cache available */
|
|
( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT, &cb_data);
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_q_cmd
|
|
**
|
|
** Description enqueue a command into control block, usually because discovery
|
|
** operation is busy.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_q_cmd(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
bta_gattc_enqueue(p_clcb, p_data);
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_pop_command_to_send
|
|
**
|
|
** Description dequeue a command into control block.
|
|
** Check if there has command pending in the command queue or not,
|
|
** if there has command pending in the command queue, sent it to the state machine to decision
|
|
** should be sent it to the remote device or not.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_pop_command_to_send(tBTA_GATTC_CLCB *p_clcb)
|
|
{
|
|
if (!list_is_empty(p_clcb->p_cmd_list)) {
|
|
list_node_t *node = list_begin(p_clcb->p_cmd_list);
|
|
tBTA_GATTC_DATA *p_data = (tBTA_GATTC_DATA *)list_node(node);
|
|
if (p_data != NULL) {
|
|
/* execute pending operation of link block still present */
|
|
if (l2cu_find_lcb_by_bd_addr(p_clcb->p_srcb->server_bda, BT_TRANSPORT_LE) != NULL) {
|
|
// The data to be sent to the gattc state machine for processing
|
|
if(bta_gattc_sm_execute(p_clcb, p_data->hdr.event, p_data)) {
|
|
list_remove(p_clcb->p_cmd_list, (void *)p_data);
|
|
}
|
|
|
|
if (p_clcb->is_full) {
|
|
tBTA_GATTC cb_data = {0};
|
|
p_clcb->is_full = FALSE;
|
|
cb_data.status = GATT_SUCCESS;
|
|
cb_data.queue_full.conn_id = p_clcb->bta_conn_id;
|
|
cb_data.queue_full.is_full = FALSE;
|
|
if (p_clcb->p_rcb->p_cback != NULL) {
|
|
( *p_clcb->p_rcb->p_cback)(BTA_GATTC_QUEUE_FULL_EVT, (tBTA_GATTC *)&cb_data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_free_command_data
|
|
**
|
|
** Description free the command data into control block.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_free_command_data(tBTA_GATTC_CLCB *p_clcb)
|
|
{
|
|
assert(p_clcb->p_cmd_list);
|
|
//Check the list is empty or not.
|
|
if (!list_is_empty(p_clcb->p_cmd_list)) {
|
|
/* Traversal the command queue, check the p_q_cmd is point to the queue data or not, if the p_q_cmd point to the
|
|
command queue,should remove it from the list */
|
|
for (list_node_t *node = list_begin(p_clcb->p_cmd_list); node != list_end(p_clcb->p_cmd_list);
|
|
node = list_next(node)) {
|
|
tBTA_GATTC_DATA *p_data = (tBTA_GATTC_DATA *)list_node(node);
|
|
if (p_data == p_clcb->p_q_cmd) {
|
|
list_remove(p_clcb->p_cmd_list, (void *)p_data);
|
|
p_clcb->p_q_cmd = NULL;
|
|
return;
|
|
}
|
|
}
|
|
|
|
osi_free(p_clcb->p_q_cmd);
|
|
p_clcb->p_q_cmd = NULL;
|
|
} else {
|
|
osi_free(p_clcb->p_q_cmd);
|
|
p_clcb->p_q_cmd = NULL;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_fail
|
|
**
|
|
** Description report API call failure back to apps
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_fail(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
|
|
{
|
|
UNUSED(p_data);
|
|
|
|
if (p_clcb->status == BTA_GATT_OK) {
|
|
APPL_TRACE_ERROR("operation not supported at current state [%d]", p_clcb->state);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_deregister_cmpl
|
|
**
|
|
** Description De-Register a GATT client application with BTA completed.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB *p_clreg)
|
|
{
|
|
tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
|
|
tBTA_GATTC_IF client_if = p_clreg->client_if;
|
|
tBTA_GATTC cb_data;
|
|
tBTA_GATTC_CBACK *p_cback = p_clreg->p_cback;
|
|
|
|
memset(&cb_data, 0, sizeof(tBTA_GATTC));
|
|
|
|
GATT_Deregister(p_clreg->client_if);
|
|
memset(p_clreg, 0, sizeof(tBTA_GATTC_RCB));
|
|
|
|
cb_data.reg_oper.client_if = client_if;
|
|
cb_data.reg_oper.status = BTA_GATT_OK;
|
|
|
|
if (p_cback)
|
|
/* callback with de-register event */
|
|
{
|
|
(*p_cback)(BTA_GATTC_DEREG_EVT, (tBTA_GATTC *)&cb_data);
|
|
}
|
|
|
|
if (bta_gattc_num_reg_app() == 0 && p_cb->state == BTA_GATTC_STATE_DISABLING) {
|
|
p_cb->state = BTA_GATTC_STATE_DISABLED;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_conn_cback
|
|
**
|
|
** Description callback functions to GATT client stack.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_conn_cback(tGATT_IF gattc_if, BD_ADDR bda, UINT16 conn_id,
|
|
BOOLEAN connected, tGATT_DISCONN_REASON reason,
|
|
tBT_TRANSPORT transport)
|
|
{
|
|
tBTA_GATTC_DATA *p_buf;
|
|
BOOLEAN start_ccc_timer = FALSE;
|
|
tBTA_GATTC_CONN *p_conn = NULL;
|
|
tBTA_GATTC_FIND_SERVICE_CB result;
|
|
|
|
if (reason != 0) {
|
|
APPL_TRACE_WARNING("%s() - cif=%d connected=%d conn_id=%d reason=0x%04x",
|
|
__FUNCTION__, gattc_if, connected, conn_id, reason);
|
|
}
|
|
|
|
if (connected == TRUE){
|
|
p_conn = bta_gattc_conn_find_alloc(bda);
|
|
}
|
|
else if (connected == FALSE){
|
|
p_conn = bta_gattc_conn_find(bda);
|
|
}
|
|
|
|
if (p_conn == NULL){
|
|
APPL_TRACE_ERROR("p_conn is NULL in %s\n", __func__);
|
|
}
|
|
|
|
if ((transport == BT_TRANSPORT_LE) && (connected == TRUE) && (p_conn != NULL) \
|
|
&& (p_conn->service_change_ccc_written == FALSE) && (p_conn->ccc_timer_used == FALSE)) {
|
|
result = bta_gattc_register_service_change_notify(conn_id, bda, &start_ccc_timer);
|
|
if (start_ccc_timer == TRUE) {
|
|
TIMER_LIST_ENT *ccc_timer = &(p_conn->service_change_ccc_timer);
|
|
/* start a 1000ms timer to wait for service discovery finished */
|
|
bta_gattc_start_service_change_ccc_timer(conn_id, bda, 1000, 0, result, ccc_timer);
|
|
p_conn->ccc_timer_used = TRUE;
|
|
}
|
|
else {
|
|
/* Has written service change ccc; or service change ccc doesn't exist in remote device's gatt database */
|
|
p_conn->service_change_ccc_written = TRUE;
|
|
p_conn->ccc_timer_used = FALSE;
|
|
}
|
|
|
|
}
|
|
else if ((transport == BT_TRANSPORT_LE) && (connected == FALSE) && (p_conn != NULL)){
|
|
p_conn->service_change_ccc_written = FALSE;
|
|
if (p_conn->ccc_timer_used == TRUE){
|
|
assert(bta_gattc_cb.write_ccc_mutex != NULL);
|
|
osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
|
|
|
if (p_conn->service_change_ccc_timer.param != 0) {
|
|
osi_free((void *)p_conn->service_change_ccc_timer.param);
|
|
p_conn->service_change_ccc_timer.param = (TIMER_PARAM_TYPE)0;
|
|
}
|
|
bta_sys_stop_timer(&(p_conn->service_change_ccc_timer));
|
|
p_conn->ccc_timer_used = FALSE;
|
|
osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
|
|
}
|
|
}
|
|
|
|
bt_bdaddr_t bdaddr;
|
|
bdcpy(bdaddr.address, bda);
|
|
|
|
if ((p_buf = (tBTA_GATTC_DATA *) osi_malloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
|
|
memset(p_buf, 0, sizeof(tBTA_GATTC_DATA));
|
|
|
|
p_buf->int_conn.hdr.event = connected ? BTA_GATTC_INT_CONN_EVT :
|
|
BTA_GATTC_INT_DISCONN_EVT;
|
|
p_buf->int_conn.hdr.layer_specific = conn_id;
|
|
p_buf->int_conn.client_if = gattc_if;
|
|
p_buf->int_conn.role = L2CA_GetBleConnRole(bda);
|
|
p_buf->int_conn.reason = reason;
|
|
p_buf->int_conn.transport = transport;
|
|
bdcpy(p_buf->int_conn.remote_bda, bda);
|
|
|
|
bta_sys_sendmsg(p_buf);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_enc_cmpl_cback
|
|
**
|
|
** Description encryption complete callback function to GATT client stack.
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, BD_ADDR bda)
|
|
{
|
|
tBTA_GATTC_DATA *p_buf;
|
|
tBTA_GATTC_CLCB *p_clcb = NULL;
|
|
|
|
if ((p_clcb = bta_gattc_find_clcb_by_cif(gattc_if, bda, BTA_GATT_TRANSPORT_LE)) == NULL) {
|
|
return;
|
|
}
|
|
|
|
#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE)
|
|
/* filter this event just for BTA HH LE GATT client,
|
|
In the future, if we want to enable encryption complete event
|
|
for all GATT clients, we can remove this code */
|
|
if (!bta_hh_le_is_hh_gatt_if(gattc_if)) {
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
APPL_TRACE_DEBUG("bta_gattc_enc_cmpl_cback: cif = %d", gattc_if);
|
|
|
|
if ((p_buf = (tBTA_GATTC_DATA *) osi_calloc(sizeof(tBTA_GATTC_DATA))) != NULL) {
|
|
memset(p_buf, 0, sizeof(tBTA_GATTC_DATA));
|
|
|
|
p_buf->enc_cmpl.hdr.event = BTA_GATTC_ENC_CMPL_EVT;
|
|
p_buf->enc_cmpl.hdr.layer_specific = p_clcb->bta_conn_id;
|
|
p_buf->enc_cmpl.client_if = gattc_if;
|
|
bdcpy(p_buf->enc_cmpl.remote_bda, bda);
|
|
|
|
bta_sys_sendmsg(p_buf);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_api_refresh
|
|
**
|
|
** Description process refresh API to delete cache and start a new discovery
|
|
** if currently connected.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_process_api_refresh(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
tBTA_GATTC_SERV *p_srvc_cb = bta_gattc_find_srvr_cache(p_msg->api_conn.remote_bda);
|
|
tBTA_GATTC_CLCB *p_clcb = &bta_gattc_cb.clcb[0];
|
|
BOOLEAN found = FALSE;
|
|
UINT8 i;
|
|
UNUSED(p_cb);
|
|
|
|
if (p_srvc_cb != NULL) {
|
|
/* try to find a CLCB */
|
|
if (p_srvc_cb->connected && p_srvc_cb->num_clcb != 0) {
|
|
for (i = 0; i < BTA_GATTC_CLCB_MAX; i ++, p_clcb ++) {
|
|
if (p_clcb->in_use && p_clcb->p_srcb == p_srvc_cb) {
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (found) {
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
return;
|
|
}
|
|
}
|
|
/* in all other cases, mark it and delete the cache */
|
|
if (p_srvc_cb->p_srvc_cache != NULL) {
|
|
list_free(p_srvc_cb->p_srvc_cache);
|
|
p_srvc_cb->p_srvc_cache = NULL;
|
|
}
|
|
}
|
|
/* used to reset cache in application */
|
|
bta_gattc_cache_reset(p_msg->api_conn.remote_bda);
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_srvc_chg_ind
|
|
**
|
|
** Description process service change indication.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
BOOLEAN bta_gattc_process_srvc_chg_ind(UINT16 conn_id,
|
|
tBTA_GATTC_RCB *p_clrcb,
|
|
tBTA_GATTC_SERV *p_srcb,
|
|
tBTA_GATTC_CLCB *p_clcb,
|
|
tBTA_GATTC_NOTIFY *p_notify,
|
|
tGATT_VALUE *att_value)
|
|
{
|
|
tBT_UUID gattp_uuid, srvc_chg_uuid;
|
|
BOOLEAN processed = FALSE;
|
|
UINT8 i;
|
|
|
|
gattp_uuid.len = 2;
|
|
gattp_uuid.uu.uuid16 = UUID_SERVCLASS_GATT_SERVER;
|
|
|
|
srvc_chg_uuid.len = 2;
|
|
srvc_chg_uuid.uu.uuid16 = GATT_UUID_GATT_SRV_CHGD;
|
|
|
|
const tBTA_GATTC_CHARACTERISTIC *p_char = bta_gattc_get_characteristic_srcb(p_srcb, p_notify->handle);
|
|
if (p_char && bta_gattc_uuid_compare(&p_char->service->uuid, &gattp_uuid, TRUE) &&
|
|
bta_gattc_uuid_compare(&p_char->uuid, &srvc_chg_uuid, TRUE)) {
|
|
if (att_value->len != BTA_GATTC_SERVICE_CHANGED_LEN) {
|
|
APPL_TRACE_ERROR("%s: received malformed service changed indication, skipping", __func__);
|
|
return FALSE;
|
|
}
|
|
|
|
UINT8 *p = att_value->value;
|
|
UINT16 s_handle = ((UINT16)(*(p )) + (((UINT16)(*(p + 1))) << 8));
|
|
UINT16 e_handle = ((UINT16)(*(p + 2)) + (((UINT16)(*(p + 3))) << 8));
|
|
|
|
APPL_TRACE_ERROR("%s: service changed s_handle:0x%04x e_handle:0x%04x",
|
|
__func__, s_handle, e_handle);
|
|
|
|
processed = TRUE;
|
|
/* mark service handle change pending */
|
|
p_srcb->srvc_hdl_chg = TRUE;
|
|
/* clear up all notification/indication registration */
|
|
bta_gattc_clear_notif_registration(p_srcb, conn_id, s_handle, e_handle);
|
|
/* service change indication all received, do discovery update */
|
|
if ( ++ p_srcb->update_count == bta_gattc_num_reg_app()) {
|
|
/* not an opened connection; or connection busy */
|
|
/* search for first available clcb and start discovery */
|
|
if (p_clcb == NULL || (p_clcb && p_clcb->p_q_cmd != NULL)) {
|
|
for (i = 0 ; i < BTA_GATTC_CLCB_MAX; i ++) {
|
|
if (bta_gattc_cb.clcb[i].in_use &&
|
|
bta_gattc_cb.clcb[i].p_srcb == p_srcb &&
|
|
bta_gattc_cb.clcb[i].p_q_cmd == NULL) {
|
|
p_clcb = &bta_gattc_cb.clcb[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
/* send confirmation here if this is an indication, it should always be */
|
|
GATTC_SendHandleValueConfirm(conn_id, att_value->handle);
|
|
|
|
/* if connection available, refresh cache by doing discovery now */
|
|
if (p_clcb != NULL) {
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
|
|
}
|
|
}
|
|
/* notify applicationf or service change */
|
|
if (p_clrcb->p_cback != NULL) {
|
|
(* p_clrcb->p_cback)(BTA_GATTC_SRVC_CHG_EVT, (tBTA_GATTC *)p_srcb->server_bda);
|
|
}
|
|
|
|
}
|
|
|
|
return processed;
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_proc_other_indication
|
|
**
|
|
** Description process all non-service change indication/notification.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_proc_other_indication(tBTA_GATTC_CLCB *p_clcb, UINT8 op,
|
|
tGATT_CL_COMPLETE *p_data,
|
|
tBTA_GATTC_NOTIFY *p_notify)
|
|
{
|
|
APPL_TRACE_DEBUG("bta_gattc_proc_other_indication check p_data->att_value.handle=%d p_data->handle=%d",
|
|
p_data->att_value.handle, p_data->handle);
|
|
APPL_TRACE_DEBUG("is_notify %d", p_notify->is_notify);
|
|
|
|
p_notify->is_notify = (op == GATTC_OPTYPE_INDICATION) ? FALSE : TRUE;
|
|
p_notify->len = p_data->att_value.len;
|
|
bdcpy(p_notify->bda, p_clcb->bda);
|
|
memcpy(p_notify->value, p_data->att_value.value, p_data->att_value.len);
|
|
p_notify->conn_id = p_clcb->bta_conn_id;
|
|
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_NOTIF_EVT, (tBTA_GATTC *)p_notify);
|
|
}
|
|
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_indicate
|
|
**
|
|
** Description process indication/notification.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_process_indicate(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_CL_COMPLETE *p_data)
|
|
{
|
|
UINT16 handle = p_data->att_value.handle;
|
|
tBTA_GATTC_CLCB *p_clcb ;
|
|
tBTA_GATTC_RCB *p_clrcb = NULL;
|
|
tBTA_GATTC_SERV *p_srcb = NULL;
|
|
tBTA_GATTC_NOTIFY notify;
|
|
BD_ADDR remote_bda;
|
|
tBTA_GATTC_IF gatt_if;
|
|
tBTA_TRANSPORT transport;
|
|
|
|
if (!GATT_GetConnectionInfor(conn_id, &gatt_if, remote_bda, &transport)) {
|
|
APPL_TRACE_ERROR("%s indication/notif for unknown app", __func__);
|
|
if (op == GATTC_OPTYPE_INDICATION) {
|
|
GATTC_SendHandleValueConfirm(conn_id, handle);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((p_clrcb = bta_gattc_cl_get_regcb(gatt_if)) == NULL) {
|
|
APPL_TRACE_ERROR("%s indication/notif for unregistered app", __func__);
|
|
if (op == GATTC_OPTYPE_INDICATION) {
|
|
GATTC_SendHandleValueConfirm(conn_id, handle);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ((p_srcb = bta_gattc_find_srcb(remote_bda)) == NULL) {
|
|
APPL_TRACE_ERROR("%s indication/notif for unknown device, ignore", __func__);
|
|
if (op == GATTC_OPTYPE_INDICATION) {
|
|
GATTC_SendHandleValueConfirm(conn_id, handle);
|
|
}
|
|
return;
|
|
}
|
|
|
|
p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
|
|
|
|
notify.handle = handle;
|
|
/* if non-service change indication/notification, forward to application */
|
|
if (!bta_gattc_process_srvc_chg_ind(conn_id, p_clrcb, p_srcb, p_clcb, ¬ify, &p_data->att_value)) {
|
|
/* if app registered for the notification */
|
|
if (bta_gattc_check_notif_registry(p_clrcb, p_srcb, ¬ify)) {
|
|
/* connection not open yet */
|
|
if (p_clcb == NULL) {
|
|
p_clcb = bta_gattc_clcb_alloc(gatt_if, remote_bda, transport);
|
|
|
|
if (p_clcb == NULL) {
|
|
APPL_TRACE_ERROR("No resources");
|
|
return;
|
|
}
|
|
|
|
p_clcb->bta_conn_id = conn_id;
|
|
p_clcb->transport = transport;
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, NULL);
|
|
}
|
|
|
|
if (p_clcb != NULL) {
|
|
bta_gattc_proc_other_indication(p_clcb, op, p_data, ¬ify);
|
|
}
|
|
} else if (op == GATTC_OPTYPE_INDICATION) {
|
|
/* no one intersted and need ack? */
|
|
APPL_TRACE_DEBUG("%s no one interested, ack now", __func__);
|
|
GATTC_SendHandleValueConfirm(conn_id, handle);
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cmpl_cback
|
|
**
|
|
** Description client operation complete callback register with BTE GATT.
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_cmpl_cback(UINT16 conn_id, tGATTC_OPTYPE op, tGATT_STATUS status,
|
|
tGATT_CL_COMPLETE *p_data)
|
|
{
|
|
tBTA_GATTC_CLCB *p_clcb;
|
|
APPL_TRACE_DEBUG("bta_gattc_cmpl_cback: conn_id = %d op = %d status = %d",
|
|
conn_id, op, status);
|
|
|
|
/* notification and indication processed right away */
|
|
if (op == GATTC_OPTYPE_NOTIFICATION || op == GATTC_OPTYPE_INDICATION) {
|
|
bta_gattc_process_indicate(conn_id, op, p_data);
|
|
return;
|
|
}
|
|
/* for all other operation, not expected if w/o connection */
|
|
else if ((p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id)) == NULL) {
|
|
APPL_TRACE_ERROR("bta_gattc_cmpl_cback unknown conn_id = %d, ignore data", conn_id);
|
|
return;
|
|
}
|
|
|
|
/* if over BR_EDR, inform PM for mode change */
|
|
if (p_clcb->transport == BTA_TRANSPORT_BR_EDR) {
|
|
bta_sys_busy(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
bta_sys_idle(BTA_ID_GATTC, BTA_ALL_APP_ID, p_clcb->bda);
|
|
}
|
|
|
|
bta_gattc_cmpl_sendmsg(conn_id, op, status, p_data);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cmpl_sendmsg
|
|
**
|
|
** Description client operation complete send message
|
|
**
|
|
** Returns None.
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_cmpl_sendmsg(UINT16 conn_id, tGATTC_OPTYPE op,
|
|
tBTA_GATT_STATUS status,
|
|
tGATT_CL_COMPLETE *p_data)
|
|
{
|
|
const UINT16 len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE);
|
|
tBTA_GATTC_OP_CMPL *p_buf = (tBTA_GATTC_OP_CMPL *) osi_malloc(len);
|
|
|
|
if (p_buf != NULL) {
|
|
memset(p_buf, 0, len);
|
|
p_buf->hdr.event = BTA_GATTC_OP_CMPL_EVT;
|
|
p_buf->hdr.layer_specific = conn_id;
|
|
p_buf->status = status;
|
|
p_buf->op_code = op;
|
|
|
|
if (p_data != NULL) {
|
|
p_buf->p_cmpl = (tGATT_CL_COMPLETE *)(p_buf + 1);
|
|
memcpy(p_buf->p_cmpl, p_data, sizeof(tGATT_CL_COMPLETE));
|
|
}
|
|
|
|
bta_sys_sendmsg(p_buf);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_cong_cback
|
|
**
|
|
** Description congestion callback for BTA GATT client.
|
|
**
|
|
** Returns void
|
|
**
|
|
********************************************************************************/
|
|
static void bta_gattc_cong_cback (UINT16 conn_id, BOOLEAN congested)
|
|
{
|
|
tBTA_GATTC_CLCB *p_clcb;
|
|
tBTA_GATTC cb_data;
|
|
|
|
if ((p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id)) != NULL) {
|
|
if (p_clcb->p_rcb->p_cback) {
|
|
cb_data.congest.conn_id = conn_id;
|
|
cb_data.congest.congested = congested;
|
|
|
|
(*p_clcb->p_rcb->p_cback)(BTA_GATTC_CONGEST_EVT, &cb_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if BLE_INCLUDED == TRUE
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_init_clcb_conn
|
|
**
|
|
** Description Initaite a BTA CLCB connection
|
|
**
|
|
** Returns void
|
|
**
|
|
********************************************************************************/
|
|
void bta_gattc_init_clcb_conn(UINT8 cif, BD_ADDR remote_bda)
|
|
{
|
|
tBTA_GATTC_CLCB *p_clcb = NULL;
|
|
tBTA_GATTC_DATA gattc_data;
|
|
UINT16 conn_id;
|
|
|
|
/* should always get the connection ID */
|
|
if (GATT_GetConnIdIfConnected(cif, remote_bda, &conn_id, BTA_GATT_TRANSPORT_LE) == FALSE) {
|
|
APPL_TRACE_ERROR("bta_gattc_init_clcb_conn ERROR: not a connected device");
|
|
return;
|
|
}
|
|
|
|
/* initaite a new connection here */
|
|
if ((p_clcb = bta_gattc_clcb_alloc(cif, remote_bda, BTA_GATT_TRANSPORT_LE)) != NULL) {
|
|
gattc_data.hdr.layer_specific = p_clcb->bta_conn_id = conn_id;
|
|
|
|
gattc_data.api_conn.client_if = cif;
|
|
memcpy(gattc_data.api_conn.remote_bda, remote_bda, BD_ADDR_LEN);
|
|
gattc_data.api_conn.is_direct = TRUE;
|
|
|
|
bta_gattc_sm_execute(p_clcb, BTA_GATTC_API_OPEN_EVT, &gattc_data);
|
|
} else {
|
|
APPL_TRACE_ERROR("No resources");
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_process_listen_all
|
|
**
|
|
** Description process listen all, send open callback to application for all
|
|
** connected slave LE link.
|
|
**
|
|
** Returns void
|
|
**
|
|
********************************************************************************/
|
|
void bta_gattc_process_listen_all(UINT8 cif)
|
|
{
|
|
UINT8 i_conn = 0;
|
|
tBTA_GATTC_CONN *p_conn = &bta_gattc_cb.conn_track[0];
|
|
|
|
for (i_conn = 0; i_conn < BTA_GATTC_CONN_MAX; i_conn++, p_conn ++) {
|
|
if (p_conn->in_use ) {
|
|
if (bta_gattc_find_clcb_by_cif(cif, p_conn->remote_bda, BTA_GATT_TRANSPORT_LE) == NULL) {
|
|
bta_gattc_init_clcb_conn(cif, p_conn->remote_bda);
|
|
}
|
|
/* else already connected */
|
|
}
|
|
}
|
|
}
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_listen
|
|
**
|
|
** Description Start or stop a listen for connection
|
|
**
|
|
** Returns void
|
|
**
|
|
********************************************************************************/
|
|
void bta_gattc_listen(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_listen.client_if);
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_cb);
|
|
|
|
cb_data.reg_oper.status = BTA_GATT_ERROR;
|
|
cb_data.reg_oper.client_if = p_msg->api_listen.client_if;
|
|
|
|
if (p_clreg == NULL) {
|
|
APPL_TRACE_ERROR("bta_gattc_listen failed, unknown client_if: %d",
|
|
p_msg->api_listen.client_if);
|
|
return;
|
|
}
|
|
/* mark bg conn record */
|
|
if (bta_gattc_mark_bg_conn(p_msg->api_listen.client_if,
|
|
(BD_ADDR_PTR) p_msg->api_listen.remote_bda,
|
|
p_msg->api_listen.start,
|
|
TRUE)) {
|
|
if (!GATT_Listen(p_msg->api_listen.client_if,
|
|
p_msg->api_listen.start,
|
|
p_msg->api_listen.remote_bda)) {
|
|
APPL_TRACE_ERROR("Listen failure");
|
|
(*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data);
|
|
} else {
|
|
cb_data.status = BTA_GATT_OK;
|
|
|
|
(*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data);
|
|
|
|
if (p_msg->api_listen.start) {
|
|
/* if listen to a specific target */
|
|
if (p_msg->api_listen.remote_bda != NULL) {
|
|
|
|
/* if is a connected remote device */
|
|
if (L2CA_GetBleConnRole(p_msg->api_listen.remote_bda) == HCI_ROLE_SLAVE &&
|
|
bta_gattc_find_clcb_by_cif(p_msg->api_listen.client_if,
|
|
p_msg->api_listen.remote_bda,
|
|
BTA_GATT_TRANSPORT_LE) == NULL) {
|
|
|
|
bta_gattc_init_clcb_conn(p_msg->api_listen.client_if,
|
|
p_msg->api_listen.remote_bda);
|
|
}
|
|
}
|
|
/* if listen to all */
|
|
else {
|
|
LOG_DEBUG("Listen For All now");
|
|
/* go through all connected device and send
|
|
callback for all connected slave connection */
|
|
bta_gattc_process_listen_all(p_msg->api_listen.client_if);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_broadcast
|
|
**
|
|
** Description Start or stop broadcasting
|
|
**
|
|
** Returns void
|
|
**
|
|
********************************************************************************/
|
|
void bta_gattc_broadcast(tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA *p_msg)
|
|
{
|
|
tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_listen.client_if);
|
|
tBTA_GATTC cb_data;
|
|
UNUSED(p_cb);
|
|
|
|
cb_data.reg_oper.client_if = p_msg->api_listen.client_if;
|
|
cb_data.reg_oper.status = BTM_BleBroadcast(p_msg->api_listen.start, NULL);
|
|
//TODO need modify callback if used
|
|
if (p_clreg && p_clreg->p_cback) {
|
|
(*p_clreg->p_cback)(BTA_GATTC_LISTEN_EVT, &cb_data);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_start_service_change_ccc_timer
|
|
**
|
|
** Description start a timer to wait for service change ccc discovered
|
|
**
|
|
** Returns void
|
|
**
|
|
*******************************************************************************/
|
|
void bta_gattc_start_service_change_ccc_timer(UINT16 conn_id, BD_ADDR bda,UINT32 timeout_ms,
|
|
UINT8 timer_cnt, UINT8 last_status, TIMER_LIST_ENT *ccc_timer)
|
|
{
|
|
tBTA_GATTC_WAIT_CCC_TIMER *p_timer_param = (tBTA_GATTC_WAIT_CCC_TIMER*) osi_malloc(sizeof(tBTA_GATTC_WAIT_CCC_TIMER));
|
|
if (p_timer_param != NULL) {
|
|
p_timer_param->conn_id = conn_id;
|
|
memcpy(p_timer_param->remote_bda, bda, sizeof(BD_ADDR));
|
|
p_timer_param->count = timer_cnt;
|
|
p_timer_param->last_status = last_status;
|
|
ccc_timer->param = (UINT32)p_timer_param;
|
|
ccc_timer->p_cback = (TIMER_CBACK *)&bta_gattc_wait4_service_change_ccc_cback;
|
|
bta_sys_start_timer(ccc_timer, 0, timeout_ms);
|
|
}
|
|
else {
|
|
APPL_TRACE_ERROR("%s, allocate p_timer_param failed\n", __func__);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_register_service_change_notify
|
|
**
|
|
** Description Find remote device's gatt service change characteristic ccc's handle and write 2 to this
|
|
** this ccc. If not found, start a timer to wait for service discovery finished.
|
|
**
|
|
** Returns Return result of service change ccc service discovery result result and written operate result
|
|
**
|
|
*******************************************************************************/
|
|
tBTA_GATTC_FIND_SERVICE_CB bta_gattc_register_service_change_notify(UINT16 conn_id, BD_ADDR remote_bda, BOOLEAN *need_timer)
|
|
{
|
|
tBTA_GATTC_SERV *p_srcb = NULL;
|
|
list_t *p_cache = NULL;
|
|
tBTA_GATTC_SERVICE *p_service = NULL;
|
|
tBTA_GATTC_CHARACTERISTIC *p_char = NULL;
|
|
tBTA_GATTC_DESCRIPTOR *p_desc = NULL;
|
|
tGATT_STATUS write_status;
|
|
tGATT_VALUE ccc_value;
|
|
tBTA_GATTC_FIND_SERVICE_CB result;
|
|
BOOLEAN gatt_cache_found = FALSE;
|
|
BOOLEAN gatt_service_found = FALSE;
|
|
BOOLEAN gatt_service_change_found = FALSE;
|
|
BOOLEAN gatt_ccc_found = FALSE;
|
|
BOOLEAN start_find_ccc_timer = FALSE;
|
|
|
|
tBT_UUID gatt_service_uuid = {LEN_UUID_16, {UUID_SERVCLASS_GATT_SERVER}};
|
|
tBT_UUID gatt_service_change_uuid = {LEN_UUID_16, {GATT_UUID_GATT_SRV_CHGD}};
|
|
tBT_UUID gatt_ccc_uuid = {LEN_UUID_16, {GATT_UUID_CHAR_CLIENT_CONFIG}};
|
|
|
|
p_srcb = bta_gattc_find_srcb(remote_bda);
|
|
if ((p_srcb != NULL) && (p_srcb->p_srvc_cache != NULL)) {
|
|
p_cache = p_srcb->p_srvc_cache;
|
|
gatt_cache_found = TRUE;
|
|
}
|
|
else {
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_CACHE_NOT_FOUND;
|
|
}
|
|
/* start to find gatt service */
|
|
if (gatt_cache_found == TRUE) {
|
|
for (list_node_t *sn = list_begin(p_cache);
|
|
sn != list_end(p_cache); sn = list_next(sn)) {
|
|
p_service = list_node(sn);
|
|
if (bta_gattc_uuid_compare(&gatt_service_uuid, &p_service->uuid, TRUE)) {
|
|
gatt_service_found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_CACHE_NOT_FOUND;
|
|
}
|
|
|
|
/* start to find gatt service change characteristic */
|
|
if (gatt_service_found == TRUE) {
|
|
if (p_service->characteristics) {
|
|
for (list_node_t *cn = list_begin(p_service->characteristics);
|
|
cn != list_end(p_service->characteristics); cn = list_next(cn)) {
|
|
p_char = list_node(cn);
|
|
if (bta_gattc_uuid_compare(&gatt_service_change_uuid, &p_char->uuid, TRUE)) {
|
|
gatt_service_change_found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (gatt_cache_found == TRUE) {
|
|
/* Gatt service not found, start a timer to wait for service discovery */
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_SERVICE_NOT_FOUND;
|
|
}
|
|
/* start to find gatt service change characteristic ccc */
|
|
if (gatt_service_change_found == TRUE) {
|
|
if (p_char->descriptors) {
|
|
for (list_node_t *dn = list_begin(p_char->descriptors);
|
|
dn != list_end(p_char->descriptors); dn = list_next(dn)) {
|
|
p_desc = list_node(dn);
|
|
if (bta_gattc_uuid_compare(&gatt_ccc_uuid, &p_desc->uuid, TRUE)) {
|
|
gatt_ccc_found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (gatt_service_found ==TRUE) {
|
|
/* Gatt service found, but service change char not found,
|
|
* Case1: remote device doesn't have service change char, we don't need to start a timer here to
|
|
* wait for service discovery
|
|
* Case2: remote device exist service change char, we have found gatt service, but have not found
|
|
* service change char, we need to start a timer here*/
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_CHAR_NOT_FOUND;
|
|
}
|
|
|
|
if (gatt_ccc_found == TRUE){
|
|
ccc_value.handle = p_desc->handle;
|
|
ccc_value.len = 2;
|
|
ccc_value.value[0] = GATT_CLT_CONFIG_INDICATION;
|
|
ccc_value.auth_req = GATT_AUTH_REQ_NONE;
|
|
if (gatt_is_clcb_allocated(conn_id)) {
|
|
APPL_TRACE_DEBUG("%s, GATTC_Write GATT_BUSY conn_id = %d", __func__, conn_id);
|
|
write_status = GATT_BUSY;
|
|
} else {
|
|
write_status = GATTC_Write (conn_id, GATT_WRITE, &ccc_value);
|
|
}
|
|
if (write_status != GATT_SUCCESS) {
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_WRITE_CCC_FAILED;
|
|
}
|
|
else {
|
|
start_find_ccc_timer = FALSE;
|
|
result = SERVICE_CHANGE_CCC_WRITTEN_SUCCESS;
|
|
}
|
|
}
|
|
else if (gatt_service_change_found == TRUE) {
|
|
/* Gatt service char found, but service change char ccc not found,
|
|
* Case1: remote device doesn't have service change char ccc, we don't need to start a timer here to
|
|
* wait for service discovery
|
|
* Case2: remote device exist service change char ccc, we have found gatt service change char, but have not found
|
|
* service change char ccc, we need to start a timer here */
|
|
start_find_ccc_timer = TRUE;
|
|
result = SERVICE_CHANGE_CCC_NOT_FOUND;
|
|
}
|
|
|
|
if (need_timer != NULL) {
|
|
*need_timer = start_find_ccc_timer;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
**
|
|
** Function bta_gattc_wait4_service_change_ccc_cback
|
|
**
|
|
** Description callback function of service_change_ccc_timer
|
|
**
|
|
** Returns None
|
|
**
|
|
*******************************************************************************/
|
|
static void bta_gattc_wait4_service_change_ccc_cback (TIMER_LIST_ENT *p_tle)
|
|
{
|
|
tBTA_GATTC_FIND_SERVICE_CB result;
|
|
BOOLEAN start_ccc_timer = FALSE;
|
|
UINT32 new_timeout;
|
|
|
|
assert(bta_gattc_cb.write_ccc_mutex != NULL);
|
|
osi_mutex_lock(&bta_gattc_cb.write_ccc_mutex, OSI_MUTEX_MAX_TIMEOUT);
|
|
|
|
tBTA_GATTC_WAIT_CCC_TIMER *p_timer_param = (tBTA_GATTC_WAIT_CCC_TIMER*) p_tle->param;
|
|
p_tle->param = (TIMER_PARAM_TYPE)0;
|
|
if (p_timer_param == NULL){
|
|
APPL_TRACE_ERROR("p_timer_param is NULL in %s\n", __func__);
|
|
osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
|
|
return;
|
|
}
|
|
|
|
tBTA_GATTC_CONN *p_conn = bta_gattc_conn_find(p_timer_param->remote_bda);
|
|
if (p_conn == NULL){
|
|
APPL_TRACE_ERROR("p_conn is NULL in %s\n", __func__);
|
|
osi_free(p_timer_param);
|
|
osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
|
|
return;
|
|
}
|
|
|
|
result = bta_gattc_register_service_change_notify(p_timer_param->conn_id, p_timer_param->remote_bda, &start_ccc_timer);
|
|
/* If return SERVICE_CHANGE_CHAR_NOT_FOUND or SERVICE_CHANGE_CCC_NOT_FOUND twice, means remote device doesn't have
|
|
* service change char or ccc, stop timer */
|
|
if ((result == p_timer_param->last_status) \
|
|
&& ((result == SERVICE_CHANGE_CHAR_NOT_FOUND) || (result == SERVICE_CHANGE_CCC_NOT_FOUND))){
|
|
start_ccc_timer = FALSE;
|
|
}
|
|
|
|
if ((start_ccc_timer == TRUE) && (p_timer_param->count < 10)){
|
|
TIMER_LIST_ENT *ccc_timer = &(p_conn->service_change_ccc_timer);
|
|
if (result == SERVICE_CHANGE_WRITE_CCC_FAILED){
|
|
/* retry to write service change ccc, needn't to add counter */
|
|
new_timeout = 200;
|
|
}
|
|
else {
|
|
/* retry to find service change ccc */
|
|
new_timeout = 1000;
|
|
p_timer_param->count ++;
|
|
}
|
|
bta_gattc_start_service_change_ccc_timer(p_timer_param->conn_id, p_timer_param->remote_bda, \
|
|
new_timeout, p_timer_param->count, result, ccc_timer);
|
|
}
|
|
else {
|
|
p_conn->ccc_timer_used = FALSE;
|
|
p_conn->service_change_ccc_written = TRUE;
|
|
}
|
|
|
|
osi_free(p_timer_param);
|
|
osi_mutex_unlock(&bta_gattc_cb.write_ccc_mutex);
|
|
}
|
|
|
|
#endif
|
|
#endif ///GATTC_INCLUDED == TRUE && BLE_INCLUDED == TRUE
|