You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2369 lines
80 KiB
2369 lines
80 KiB
/****************************************************************************** |
|
* |
|
* Copyright (C) 2009-2013 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. |
|
* |
|
******************************************************************************/ |
|
|
|
#define LOG_TAG "bt_bta_hh" |
|
|
|
#include "bta_api.h" |
|
#include "bta_hh_int.h" |
|
#include "mediatek/bta/hh/bta_hh_int.h" |
|
#include "osi/include/osi.h" |
|
|
|
#if (BTA_HH_LE_INCLUDED == TRUE) |
|
|
|
#include <string.h> |
|
|
|
#include <base/bind.h> |
|
#include <base/callback.h> |
|
#include <list> |
|
#include <unordered_map> |
|
#include <unordered_set> |
|
#include <vector> |
|
|
|
#include "bta_gatt_api.h" |
|
#include "bta_hh_co.h" |
|
#include "btm_api.h" |
|
#include "btm_ble_api.h" |
|
#include "btm_int.h" |
|
#include "device/include/interop.h" |
|
#include "osi/include/log.h" |
|
#include "srvc_api.h" |
|
#include "stack/include/l2c_api.h" |
|
#include "utl.h" |
|
|
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
#include "mediatek/include/interop_mtk.h" |
|
#endif |
|
|
|
using std::vector; |
|
|
|
#ifndef BTA_HH_LE_RECONN |
|
#define BTA_HH_LE_RECONN TRUE |
|
#endif |
|
|
|
#define BTA_HH_APP_ID_LE 0xff |
|
|
|
#define BTA_HH_LE_RPT_TYPE_VALID(x) \ |
|
((x) <= BTA_LE_HID_RPT_FEATURE && (x) >= BTA_LE_HID_RPT_INPUT) |
|
|
|
#define BTA_HH_LE_PROTO_BOOT_MODE 0x00 |
|
#define BTA_HH_LE_PROTO_REPORT_MODE 0x01 |
|
|
|
#define BTA_LE_HID_RTP_UUID_MAX 5 |
|
static const uint16_t bta_hh_uuid_to_rtp_type[BTA_LE_HID_RTP_UUID_MAX][2] = { |
|
{GATT_UUID_HID_REPORT, BTA_HH_RPTT_INPUT}, |
|
{GATT_UUID_HID_BT_KB_INPUT, BTA_HH_RPTT_INPUT}, |
|
{GATT_UUID_HID_BT_KB_OUTPUT, BTA_HH_RPTT_OUTPUT}, |
|
{GATT_UUID_HID_BT_MOUSE_INPUT, BTA_HH_RPTT_INPUT}, |
|
{GATT_UUID_BATTERY_LEVEL, BTA_HH_RPTT_INPUT}}; |
|
|
|
static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data); |
|
static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB* p_cb, bool check_bond); |
|
// TODO(jpawlowski): uncomment when fixed |
|
// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb, |
|
// tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache, |
|
// uint8_t num_rpt); |
|
|
|
#define GATT_READ_CHAR 0 |
|
#define GATT_READ_DESC 1 |
|
#define GATT_WRITE_CHAR 2 |
|
#define GATT_WRITE_DESC 3 |
|
|
|
/* Holds pending GATT operations */ |
|
struct gatt_operation { |
|
uint8_t type; |
|
uint16_t handle; |
|
GATT_READ_OP_CB read_cb; |
|
void* read_cb_data; |
|
GATT_WRITE_OP_CB write_cb; |
|
void* write_cb_data; |
|
|
|
/* write-specific fields */ |
|
tBTA_GATTC_WRITE_TYPE write_type; |
|
vector<uint8_t> value; |
|
}; |
|
|
|
// maps connection id to operations waiting for execution |
|
static std::unordered_map<uint16_t, std::list<gatt_operation>> gatt_op_queue; |
|
// contain connection ids that currently execute operations |
|
static std::unordered_set<uint16_t> gatt_op_queue_executing; |
|
|
|
static void mark_as_not_executing(uint16_t conn_id) { |
|
gatt_op_queue_executing.erase(conn_id); |
|
} |
|
|
|
static void gatt_op_queue_clean(uint16_t conn_id) { |
|
gatt_op_queue.erase(conn_id); |
|
gatt_op_queue_executing.erase(conn_id); |
|
} |
|
|
|
static void gatt_execute_next_op(uint16_t conn_id); |
|
GATT_READ_OP_CB act_read_cb = NULL; |
|
void* act_read_cb_data = NULL; |
|
static void gatt_read_op_finished(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, uint8_t* value, |
|
void* data) { |
|
GATT_READ_OP_CB tmp_cb = act_read_cb; |
|
void* tmp_cb_data = act_read_cb_data; |
|
|
|
act_read_cb = NULL; |
|
act_read_cb_data = NULL; |
|
|
|
mark_as_not_executing(conn_id); |
|
gatt_execute_next_op(conn_id); |
|
|
|
if (tmp_cb) { |
|
tmp_cb(conn_id, status, handle, len, value, tmp_cb_data); |
|
return; |
|
} |
|
} |
|
|
|
GATT_WRITE_OP_CB act_write_cb = NULL; |
|
void* act_write_cb_data = NULL; |
|
static void gatt_write_op_finished(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, void* data) { |
|
GATT_WRITE_OP_CB tmp_cb = act_write_cb; |
|
void* tmp_cb_data = act_write_cb_data; |
|
act_write_cb = NULL; |
|
act_write_cb_data = NULL; |
|
|
|
mark_as_not_executing(conn_id); |
|
gatt_execute_next_op(conn_id); |
|
|
|
if (tmp_cb) { |
|
tmp_cb(conn_id, status, handle, tmp_cb_data); |
|
return; |
|
} |
|
} |
|
|
|
static void gatt_execute_next_op(uint16_t conn_id) { |
|
APPL_TRACE_DEBUG("%s:", __func__, conn_id); |
|
if (gatt_op_queue.empty()) { |
|
APPL_TRACE_DEBUG("%s: op queue is empty", __func__); |
|
return; |
|
} |
|
|
|
auto map_ptr = gatt_op_queue.find(conn_id); |
|
if (map_ptr == gatt_op_queue.end() || map_ptr->second.empty()) { |
|
APPL_TRACE_DEBUG("%s: no more operations queued for conn_id %d", __func__, |
|
conn_id); |
|
return; |
|
} |
|
|
|
if (gatt_op_queue_executing.count(conn_id)) { |
|
APPL_TRACE_DEBUG("%s: can't enqueue next op, already executing", __func__); |
|
return; |
|
} |
|
|
|
gatt_op_queue_executing.insert(conn_id); |
|
|
|
std::list<gatt_operation>& gatt_ops = map_ptr->second; |
|
|
|
gatt_operation& op = gatt_ops.front(); |
|
|
|
if (op.type == GATT_READ_CHAR) { |
|
act_read_cb = op.read_cb; |
|
act_read_cb_data = op.read_cb_data; |
|
BTA_GATTC_ReadCharacteristic(conn_id, op.handle, BTA_GATT_AUTH_REQ_NONE, |
|
gatt_read_op_finished, NULL); |
|
|
|
} else if (op.type == GATT_READ_DESC) { |
|
act_read_cb = op.read_cb; |
|
act_read_cb_data = op.read_cb_data; |
|
BTA_GATTC_ReadCharDescr(conn_id, op.handle, BTA_GATT_AUTH_REQ_NONE, |
|
gatt_read_op_finished, NULL); |
|
|
|
} else if (op.type == GATT_WRITE_CHAR) { |
|
act_write_cb = op.write_cb; |
|
act_write_cb_data = op.write_cb_data; |
|
BTA_GATTC_WriteCharValue(conn_id, op.handle, op.write_type, |
|
std::move(op.value), BTA_GATT_AUTH_REQ_NONE, |
|
gatt_write_op_finished, NULL); |
|
|
|
} else if (op.type == GATT_WRITE_DESC) { |
|
act_write_cb = op.write_cb; |
|
act_write_cb_data = op.write_cb_data; |
|
BTA_GATTC_WriteCharDescr(conn_id, op.handle, std::move(op.value), |
|
BTA_GATT_AUTH_REQ_NONE, gatt_write_op_finished, |
|
NULL); |
|
} |
|
|
|
gatt_ops.pop_front(); |
|
} |
|
|
|
static void gatt_queue_read_op(uint8_t op_type, uint16_t conn_id, |
|
uint16_t handle, GATT_READ_OP_CB cb, |
|
void* cb_data) { |
|
gatt_operation op; |
|
op.type = op_type; |
|
op.handle = handle; |
|
op.read_cb = cb; |
|
op.read_cb_data = cb_data; |
|
gatt_op_queue[conn_id].push_back(op); |
|
gatt_execute_next_op(conn_id); |
|
} |
|
|
|
static void gatt_queue_write_op(uint8_t op_type, uint16_t conn_id, |
|
uint16_t handle, vector<uint8_t> value, |
|
tBTA_GATTC_WRITE_TYPE write_type, |
|
GATT_WRITE_OP_CB cb, void* cb_data) { |
|
gatt_operation op; |
|
op.type = op_type; |
|
op.handle = handle; |
|
op.write_type = write_type; |
|
op.write_cb = cb; |
|
op.write_cb_data = cb_data; |
|
op.value = std::move(value); |
|
|
|
gatt_op_queue[conn_id].push_back(op); |
|
gatt_execute_next_op(conn_id); |
|
} |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
static const char* bta_hh_le_rpt_name[4] = {"UNKNOWN", "INPUT", "OUTPUT", |
|
"FEATURE"}; |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_hid_report_dbg |
|
* |
|
* Description debug function to print out all HID report available on |
|
* remote device. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_hh_le_hid_report_dbg(tBTA_HH_DEV_CB* p_cb) { |
|
APPL_TRACE_DEBUG("%s: HID Report DB", __func__); |
|
|
|
if (!p_cb->hid_srvc.in_use) return; |
|
|
|
tBTA_HH_LE_RPT* p_rpt = &p_cb->hid_srvc.report[0]; |
|
|
|
for (int j = 0; j < BTA_HH_LE_RPT_MAX; j++, p_rpt++) { |
|
const char* rpt_name = "Unknown"; |
|
|
|
if (!p_rpt->in_use) break; |
|
|
|
if (p_rpt->uuid == GATT_UUID_HID_REPORT) rpt_name = "Report"; |
|
if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT) rpt_name = "Boot KB Input"; |
|
if (p_rpt->uuid == GATT_UUID_HID_BT_KB_OUTPUT) rpt_name = "Boot KB Output"; |
|
if (p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) rpt_name = "Boot MI Input"; |
|
|
|
APPL_TRACE_DEBUG( |
|
"\t\t [%s- 0x%04x] [Type: %s], [ReportID: %d] [srvc_inst_id: %d] " |
|
"[char_inst_id: %d] [Clt_cfg: %d]", |
|
rpt_name, p_rpt->uuid, |
|
((p_rpt->rpt_type < 4) ? bta_hh_le_rpt_name[p_rpt->rpt_type] |
|
: "UNKNOWN"), |
|
p_rpt->rpt_id, p_rpt->srvc_inst_id, p_rpt->char_inst_id, |
|
p_rpt->client_cfg_value); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_uuid_to_str |
|
* |
|
* Description |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static const char* bta_hh_uuid_to_str(uint16_t uuid) { |
|
switch (uuid) { |
|
case GATT_UUID_HID_INFORMATION: |
|
return "GATT_UUID_HID_INFORMATION"; |
|
case GATT_UUID_HID_REPORT_MAP: |
|
return "GATT_UUID_HID_REPORT_MAP"; |
|
case GATT_UUID_HID_CONTROL_POINT: |
|
return "GATT_UUID_HID_CONTROL_POINT"; |
|
case GATT_UUID_HID_REPORT: |
|
return "GATT_UUID_HID_REPORT"; |
|
case GATT_UUID_HID_PROTO_MODE: |
|
return "GATT_UUID_HID_PROTO_MODE"; |
|
case GATT_UUID_HID_BT_KB_INPUT: |
|
return "GATT_UUID_HID_BT_KB_INPUT"; |
|
case GATT_UUID_HID_BT_KB_OUTPUT: |
|
return "GATT_UUID_HID_BT_KB_OUTPUT"; |
|
case GATT_UUID_HID_BT_MOUSE_INPUT: |
|
return "GATT_UUID_HID_BT_MOUSE_INPUT"; |
|
case GATT_UUID_CHAR_CLIENT_CONFIG: |
|
return "GATT_UUID_CHAR_CLIENT_CONFIG"; |
|
case GATT_UUID_EXT_RPT_REF_DESCR: |
|
return "GATT_UUID_EXT_RPT_REF_DESCR"; |
|
case GATT_UUID_RPT_REF_DESCR: |
|
return "GATT_UUID_RPT_REF_DESCR"; |
|
default: |
|
return "Unknown UUID"; |
|
} |
|
} |
|
|
|
#endif |
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_enable |
|
* |
|
* Description initialize LE HID related functionality |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_enable(void) { |
|
uint8_t xx; |
|
|
|
bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; |
|
|
|
for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx++) |
|
bta_hh_cb.le_cb_index[xx] = BTA_HH_IDX_INVALID; |
|
|
|
BTA_GATTC_AppRegister(bta_hh_gattc_callback, |
|
base::Bind([](uint8_t client_id, uint8_t r_status) { |
|
tBTA_HH_STATUS status = BTA_HH_ERR; |
|
|
|
if (r_status == BTA_GATT_OK) { |
|
bta_hh_cb.gatt_if = client_id; |
|
status = BTA_HH_OK; |
|
} else |
|
bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF; |
|
|
|
/* signal BTA call back event */ |
|
(*bta_hh_cb.p_cback)(BTA_HH_ENABLE_EVT, |
|
(tBTA_HH*)&status); |
|
})); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_is_hh_gatt_if |
|
* |
|
* Description Check to see if client_if is BTA HH LE GATT interface |
|
* |
|
* |
|
* Returns whether it is HH GATT IF |
|
* |
|
******************************************************************************/ |
|
bool bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if) { |
|
return (bta_hh_cb.gatt_if == client_if); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_deregister |
|
* |
|
* Description De-register BTA HH from BTA GATTC |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_deregister(void) { BTA_GATTC_AppDeregister(bta_hh_cb.gatt_if); } |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_is_le_device |
|
* |
|
* Description Check to see if the remote device is a LE only device |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
bool bta_hh_is_le_device(tBTA_HH_DEV_CB* p_cb, const RawAddress& remote_bda) { |
|
p_cb->is_le_device = BTM_UseLeLink(remote_bda); |
|
|
|
return p_cb->is_le_device; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_open_conn |
|
* |
|
* Description open a GATT connection first. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_open_conn(tBTA_HH_DEV_CB* p_cb, const RawAddress& remote_bda) { |
|
/* update cb_index[] map */ |
|
p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); |
|
p_cb->addr = remote_bda; |
|
bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; |
|
p_cb->in_use = true; |
|
|
|
BTA_GATTC_Open(bta_hh_cb.gatt_if, remote_bda, true, BTA_GATT_TRANSPORT_LE, |
|
false); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_dev_cb_by_conn_id |
|
* |
|
* Description Utility function find a device control block by connection |
|
* ID. |
|
* |
|
******************************************************************************/ |
|
tBTA_HH_DEV_CB* bta_hh_le_find_dev_cb_by_conn_id(uint16_t conn_id) { |
|
uint8_t i; |
|
tBTA_HH_DEV_CB* p_dev_cb = &bta_hh_cb.kdev[0]; |
|
|
|
for (i = 0; i < BTA_HH_MAX_DEVICE; i++, p_dev_cb++) { |
|
if (p_dev_cb->in_use && p_dev_cb->conn_id == conn_id) return p_dev_cb; |
|
} |
|
return NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_dev_cb_by_bda |
|
* |
|
* Description Utility function find a device control block by BD address. |
|
* |
|
******************************************************************************/ |
|
tBTA_HH_DEV_CB* bta_hh_le_find_dev_cb_by_bda(const RawAddress& bda) { |
|
uint8_t i; |
|
tBTA_HH_DEV_CB* p_dev_cb = &bta_hh_cb.kdev[0]; |
|
|
|
for (i = 0; i < BTA_HH_MAX_DEVICE; i++, p_dev_cb++) { |
|
if (p_dev_cb->in_use && p_dev_cb->addr == bda) return p_dev_cb; |
|
} |
|
return NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_service_inst_by_battery_inst_id |
|
* |
|
* Description find HID service instance ID by battery service instance ID |
|
* |
|
******************************************************************************/ |
|
uint8_t bta_hh_le_find_service_inst_by_battery_inst_id(tBTA_HH_DEV_CB* p_cb, |
|
uint8_t ba_inst_id) { |
|
if (p_cb->hid_srvc.in_use && p_cb->hid_srvc.incl_srvc_inst == ba_inst_id) { |
|
return p_cb->hid_srvc.srvc_inst_id; |
|
} |
|
return BTA_HH_IDX_INVALID; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_report_entry |
|
* |
|
* Description find the report entry by service instance and report UUID |
|
* and instance ID |
|
* |
|
******************************************************************************/ |
|
tBTA_HH_LE_RPT* bta_hh_le_find_report_entry( |
|
tBTA_HH_DEV_CB* p_cb, uint8_t srvc_inst_id, /* service instance ID */ |
|
uint16_t rpt_uuid, uint8_t char_inst_id) { |
|
uint8_t i; |
|
uint8_t hid_inst_id = srvc_inst_id; |
|
tBTA_HH_LE_RPT* p_rpt; |
|
|
|
if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) { |
|
hid_inst_id = |
|
bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); |
|
|
|
if (hid_inst_id == BTA_HH_IDX_INVALID) return NULL; |
|
} |
|
|
|
p_rpt = &p_cb->hid_srvc.report[0]; |
|
|
|
for (i = 0; i < BTA_HH_LE_RPT_MAX; i++, p_rpt++) { |
|
if (p_rpt->uuid == rpt_uuid && p_rpt->srvc_inst_id == srvc_inst_id && |
|
p_rpt->char_inst_id == char_inst_id) { |
|
return p_rpt; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_rpt_by_idtype |
|
* |
|
* Description find a report entry by report ID and protocol mode |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
tBTA_HH_LE_RPT* bta_hh_le_find_rpt_by_idtype(tBTA_HH_LE_RPT* p_head, |
|
uint8_t mode, |
|
tBTA_HH_RPT_TYPE r_type, |
|
uint8_t rpt_id) { |
|
tBTA_HH_LE_RPT* p_rpt = p_head; |
|
uint8_t i; |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("bta_hh_le_find_rpt_by_idtype: r_type: %d rpt_id: %d", |
|
r_type, rpt_id); |
|
#endif |
|
|
|
for (i = 0; i < BTA_HH_LE_RPT_MAX; i++, p_rpt++) { |
|
if (p_rpt->in_use && p_rpt->rpt_id == rpt_id && r_type == p_rpt->rpt_type) { |
|
/* return battery report w/o condition */ |
|
if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) return p_rpt; |
|
|
|
if (mode == BTA_HH_PROTO_RPT_MODE && p_rpt->uuid == GATT_UUID_HID_REPORT) |
|
return p_rpt; |
|
|
|
if (mode == BTA_HH_PROTO_BOOT_MODE && |
|
(p_rpt->uuid >= GATT_UUID_HID_BT_KB_INPUT && |
|
p_rpt->uuid <= GATT_UUID_HID_BT_MOUSE_INPUT)) |
|
return p_rpt; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_find_alloc_report_entry |
|
* |
|
* Description find or allocate a report entry in the HID service report |
|
* list. |
|
* |
|
******************************************************************************/ |
|
tBTA_HH_LE_RPT* bta_hh_le_find_alloc_report_entry(tBTA_HH_DEV_CB* p_cb, |
|
uint8_t srvc_inst_id, |
|
uint16_t rpt_uuid, |
|
uint8_t inst_id) { |
|
uint8_t i, hid_inst_id = srvc_inst_id; |
|
tBTA_HH_LE_RPT* p_rpt; |
|
|
|
if (rpt_uuid == GATT_UUID_BATTERY_LEVEL) { |
|
hid_inst_id = |
|
bta_hh_le_find_service_inst_by_battery_inst_id(p_cb, srvc_inst_id); |
|
|
|
if (hid_inst_id == BTA_HH_IDX_INVALID) return NULL; |
|
} |
|
p_rpt = &p_cb->hid_srvc.report[0]; |
|
|
|
for (i = 0; i < BTA_HH_LE_RPT_MAX; i++, p_rpt++) { |
|
if (!p_rpt->in_use || |
|
(p_rpt->uuid == rpt_uuid && p_rpt->srvc_inst_id == srvc_inst_id && |
|
p_rpt->char_inst_id == inst_id)) { |
|
if (!p_rpt->in_use) { |
|
p_rpt->in_use = true; |
|
p_rpt->index = i; |
|
p_rpt->srvc_inst_id = srvc_inst_id; |
|
p_rpt->char_inst_id = inst_id; |
|
p_rpt->uuid = rpt_uuid; |
|
|
|
/* assign report type */ |
|
for (i = 0; i < BTA_LE_HID_RTP_UUID_MAX; i++) { |
|
if (bta_hh_uuid_to_rtp_type[i][0] == rpt_uuid) { |
|
p_rpt->rpt_type = (tBTA_HH_RPT_TYPE)bta_hh_uuid_to_rtp_type[i][1]; |
|
|
|
if (rpt_uuid == GATT_UUID_HID_BT_KB_INPUT || |
|
rpt_uuid == GATT_UUID_HID_BT_KB_OUTPUT) |
|
p_rpt->rpt_id = BTA_HH_KEYBD_RPT_ID; |
|
|
|
if (rpt_uuid == GATT_UUID_HID_BT_MOUSE_INPUT) |
|
p_rpt->rpt_id = BTA_HH_MOUSE_RPT_ID; |
|
|
|
break; |
|
} |
|
} |
|
} |
|
return p_rpt; |
|
} |
|
} |
|
return NULL; |
|
} |
|
|
|
static tBTA_GATTC_DESCRIPTOR* find_descriptor_by_short_uuid( |
|
uint16_t conn_id, uint16_t char_handle, uint16_t short_uuid) { |
|
const tBTA_GATTC_CHARACTERISTIC* p_char = |
|
BTA_GATTC_GetCharacteristic(conn_id, char_handle); |
|
|
|
if (!p_char) { |
|
LOG_WARN(LOG_TAG, "%s No such characteristic: %d", __func__, char_handle); |
|
return NULL; |
|
} |
|
|
|
if (!p_char->descriptors || list_is_empty(p_char->descriptors)) return NULL; |
|
|
|
for (list_node_t* dn = list_begin(p_char->descriptors); |
|
dn != list_end(p_char->descriptors); dn = list_next(dn)) { |
|
tBTA_GATTC_DESCRIPTOR* p_desc = (tBTA_GATTC_DESCRIPTOR*)list_node(dn); |
|
|
|
if (p_char->uuid.len == LEN_UUID_16 && p_desc->uuid.uu.uuid16 == short_uuid) |
|
return p_desc; |
|
} |
|
|
|
return NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_read_char_descriptor |
|
* |
|
* Description read characteristic descriptor |
|
* |
|
******************************************************************************/ |
|
static tBTA_HH_STATUS bta_hh_le_read_char_descriptor(tBTA_HH_DEV_CB* p_cb, |
|
uint16_t char_handle, |
|
uint16_t short_uuid, |
|
GATT_READ_OP_CB cb, |
|
void* cb_data) { |
|
const tBTA_GATTC_DESCRIPTOR* p_desc = |
|
find_descriptor_by_short_uuid(p_cb->conn_id, char_handle, short_uuid); |
|
if (!p_desc) return BTA_HH_ERR; |
|
|
|
gatt_queue_read_op(GATT_READ_DESC, p_cb->conn_id, p_desc->handle, cb, |
|
cb_data); |
|
return BTA_HH_OK; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_save_report_ref |
|
* |
|
* Description save report reference information and move to next one. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_save_report_ref(tBTA_HH_DEV_CB* p_dev_cb, tBTA_HH_LE_RPT* p_rpt, |
|
tGATT_STATUS status, uint8_t* value, |
|
uint16_t len) { |
|
if (status == BTA_GATT_INSUF_AUTHENTICATION) { |
|
/* close connection right away */ |
|
p_dev_cb->status = BTA_HH_ERR_AUTH_FAILED; |
|
/* close the connection and report service discovery complete with error */ |
|
bta_hh_le_api_disc_act(p_dev_cb); |
|
return; |
|
} |
|
|
|
/* if the length of the descriptor value is right, parse it */ |
|
if (status == BTA_GATT_OK && len == 2) { |
|
uint8_t* pp = value; |
|
|
|
STREAM_TO_UINT8(p_rpt->rpt_id, pp); |
|
STREAM_TO_UINT8(p_rpt->rpt_type, pp); |
|
|
|
if (p_rpt->rpt_type > BTA_HH_RPTT_FEATURE) /* invalid report type */ |
|
p_rpt->rpt_type = BTA_HH_RPTT_RESRV; |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("%s: report ID: %d", __func__, p_rpt->rpt_id); |
|
#endif |
|
tBTA_HH_RPT_CACHE_ENTRY rpt_entry; |
|
rpt_entry.rpt_id = p_rpt->rpt_id; |
|
rpt_entry.rpt_type = p_rpt->rpt_type; |
|
rpt_entry.rpt_uuid = p_rpt->uuid; |
|
rpt_entry.srvc_inst_id = p_rpt->srvc_inst_id; |
|
rpt_entry.char_inst_id = p_rpt->char_inst_id; |
|
|
|
bta_hh_le_co_rpt_info(p_dev_cb->addr, &rpt_entry, p_dev_cb->app_id); |
|
} |
|
|
|
if (p_rpt->index < BTA_HH_LE_RPT_MAX - 1) |
|
p_rpt++; |
|
else |
|
p_rpt = NULL; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_register_input_notif |
|
* |
|
* Description Register for all notifications for the report applicable |
|
* for the protocol mode. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_register_input_notif(tBTA_HH_DEV_CB* p_dev_cb, |
|
uint8_t proto_mode, bool register_ba) { |
|
tBTA_HH_LE_RPT* p_rpt = &p_dev_cb->hid_srvc.report[0]; |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("%s: bta_hh_le_register_input_notif mode: %d", __func__, |
|
proto_mode); |
|
#endif |
|
|
|
for (int i = 0; i < BTA_HH_LE_RPT_MAX; i++, p_rpt++) { |
|
if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { |
|
if (register_ba && p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) { |
|
BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, p_dev_cb->addr, |
|
p_rpt->char_inst_id); |
|
} |
|
/* boot mode, deregister report input notification */ |
|
else if (proto_mode == BTA_HH_PROTO_BOOT_MODE) { |
|
if (p_rpt->uuid == GATT_UUID_HID_REPORT && |
|
p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { |
|
APPL_TRACE_DEBUG("%s ---> Deregister Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_DeregisterForNotifications( |
|
bta_hh_cb.gatt_if, p_dev_cb->addr, p_rpt->char_inst_id); |
|
} |
|
/* register boot reports notification */ |
|
else if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || |
|
p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) { |
|
APPL_TRACE_DEBUG("%s <--- Register Boot Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, p_dev_cb->addr, |
|
p_rpt->char_inst_id); |
|
} |
|
} else if (proto_mode == BTA_HH_PROTO_RPT_MODE) { |
|
if ((p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || |
|
p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) && |
|
p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { |
|
APPL_TRACE_DEBUG("%s ---> Deregister Boot Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_DeregisterForNotifications( |
|
bta_hh_cb.gatt_if, p_dev_cb->addr, p_rpt->char_inst_id); |
|
} else if (p_rpt->uuid == GATT_UUID_HID_REPORT && |
|
p_rpt->client_cfg_value == |
|
BTA_GATT_CLT_CONFIG_NOTIFICATION) { |
|
APPL_TRACE_DEBUG("%s <--- Register Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, p_dev_cb->addr, |
|
p_rpt->char_inst_id); |
|
} |
|
} |
|
/* |
|
else unknow protocol mode */ |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_deregister_input_notif |
|
* |
|
* Description Deregister all notifications |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_deregister_input_notif(tBTA_HH_DEV_CB* p_dev_cb) { |
|
tBTA_HH_LE_RPT* p_rpt = &p_dev_cb->hid_srvc.report[0]; |
|
|
|
for (uint8_t i = 0; i < BTA_HH_LE_RPT_MAX; i++, p_rpt++) { |
|
if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { |
|
if (p_rpt->uuid == GATT_UUID_HID_REPORT && |
|
p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { |
|
APPL_TRACE_DEBUG("%s ---> Deregister Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, p_dev_cb->addr, |
|
p_rpt->char_inst_id); |
|
} else if ((p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || |
|
p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT) && |
|
p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) { |
|
APPL_TRACE_DEBUG("%s ---> Deregister Boot Report ID: %d", __func__, |
|
p_rpt->rpt_id); |
|
BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, p_dev_cb->addr, |
|
p_rpt->char_inst_id); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_open_cmpl |
|
* |
|
* Description HID over GATT connection sucessfully opened |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB* p_cb) { |
|
if (p_cb->disc_active == BTA_HH_LE_DISC_NONE) { |
|
#if (BTA_HH_DEBUG == TRUE) |
|
bta_hh_le_hid_report_dbg(p_cb); |
|
#endif |
|
bta_hh_le_register_input_notif(p_cb, p_cb->mode, true); |
|
bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); |
|
|
|
/** M: Bug fix for HOGP connection taking too long time @{ */ |
|
if (bta_hh_le_check_conn_params(p_cb)) { |
|
L2CA_UpdateBleConnParams(p_cb->addr, p_cb->min_conn_int, |
|
p_cb->max_conn_int, p_cb->slave_latency, |
|
p_cb->supervision_tout); |
|
} |
|
/** @} */ |
|
|
|
#if (BTA_HH_LE_RECONN == TRUE) |
|
if (p_cb->status == BTA_HH_OK) { |
|
bta_hh_le_add_dev_bg_conn(p_cb, true); |
|
} |
|
#endif |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_write_ccc |
|
* |
|
* Description Utility function to find and write client configuration of |
|
* a characteristic |
|
* |
|
******************************************************************************/ |
|
bool bta_hh_le_write_ccc(tBTA_HH_DEV_CB* p_cb, uint8_t char_handle, |
|
uint16_t clt_cfg_value, GATT_WRITE_OP_CB cb, |
|
void* cb_data) { |
|
tBTA_GATTC_DESCRIPTOR* p_desc = find_descriptor_by_short_uuid( |
|
p_cb->conn_id, char_handle, GATT_UUID_CHAR_CLIENT_CONFIG); |
|
if (!p_desc) return false; |
|
|
|
vector<uint8_t> value(2); |
|
uint8_t* ptr = value.data(); |
|
UINT16_TO_STREAM(ptr, clt_cfg_value); |
|
|
|
gatt_queue_write_op(GATT_WRITE_DESC, p_cb->conn_id, p_desc->handle, |
|
std::move(value), BTA_GATTC_TYPE_WRITE, cb, cb_data); |
|
return true; |
|
} |
|
|
|
bool bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB* p_cb); |
|
|
|
static void write_rpt_ctl_cfg_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, void* data) { |
|
uint8_t srvc_inst_id, hid_inst_id; |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
const tBTA_GATTC_DESCRIPTOR* p_desc = |
|
BTA_GATTC_GetDescriptor(conn_id, handle); |
|
|
|
uint16_t char_uuid = p_desc->characteristic->uuid.uu.uuid16; |
|
|
|
srvc_inst_id = p_desc->characteristic->service->handle; |
|
hid_inst_id = srvc_inst_id; |
|
switch (char_uuid) { |
|
case GATT_UUID_BATTERY_LEVEL: /* battery level clt cfg registered */ |
|
hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id( |
|
p_dev_cb, srvc_inst_id); |
|
/* FALLTHROUGH */ |
|
case GATT_UUID_HID_BT_KB_INPUT: |
|
case GATT_UUID_HID_BT_MOUSE_INPUT: |
|
case GATT_UUID_HID_REPORT: |
|
if (status == BTA_GATT_OK) |
|
p_dev_cb->hid_srvc.report[p_dev_cb->clt_cfg_idx].client_cfg_value = |
|
BTA_GATT_CLT_CONFIG_NOTIFICATION; |
|
p_dev_cb->clt_cfg_idx++; |
|
bta_hh_le_write_rpt_clt_cfg(p_dev_cb); |
|
break; |
|
|
|
default: |
|
APPL_TRACE_ERROR("Unknown char ID clt cfg: 0x%04x", char_uuid); |
|
} |
|
} |
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_write_rpt_clt_cfg |
|
* |
|
* Description write client configuration. This is only for input report |
|
* enable all input notification upon connection open. |
|
* |
|
******************************************************************************/ |
|
bool bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB* p_cb) { |
|
uint8_t i; |
|
tBTA_HH_LE_RPT* p_rpt = &p_cb->hid_srvc.report[p_cb->clt_cfg_idx]; |
|
|
|
for (i = p_cb->clt_cfg_idx; i < BTA_HH_LE_RPT_MAX && p_rpt->in_use; |
|
i++, p_rpt++) { |
|
/* enable notification for all input report, regardless mode */ |
|
if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) { |
|
if (bta_hh_le_write_ccc(p_cb, p_rpt->char_inst_id, |
|
BTA_GATT_CLT_CONFIG_NOTIFICATION, |
|
write_rpt_ctl_cfg_cb, p_cb)) { |
|
p_cb->clt_cfg_idx = i; |
|
return true; |
|
} |
|
} |
|
} |
|
p_cb->clt_cfg_idx = 0; |
|
|
|
/* client configuration is completed, send open callback */ |
|
if (p_cb->state == BTA_HH_W4_CONN_ST) { |
|
p_cb->disc_active &= ~BTA_HH_LE_DISC_HIDS; |
|
|
|
bta_hh_le_open_cmpl(p_cb); |
|
} |
|
return false; |
|
} |
|
|
|
static void write_proto_mode_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, void* data) { |
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
|
|
if (p_dev_cb->state == BTA_HH_CONN_ST) { |
|
/* Set protocol finished in CONN state*/ |
|
|
|
uint16_t cb_evt = p_dev_cb->w4_evt; |
|
if (cb_evt == 0) return; |
|
|
|
tBTA_HH_CBDATA cback_data; |
|
|
|
cback_data.handle = p_dev_cb->hid_handle; |
|
cback_data.status = (status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR; |
|
|
|
if (status == BTA_GATT_OK) |
|
bta_hh_le_register_input_notif(p_dev_cb, p_dev_cb->mode, false); |
|
|
|
p_dev_cb->w4_evt = 0; |
|
(*bta_hh_cb.p_cback)(cb_evt, (tBTA_HH*)&cback_data); |
|
} else if (p_dev_cb->state == BTA_HH_W4_CONN_ST) { |
|
p_dev_cb->status = (status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR_PROTO; |
|
|
|
if ((p_dev_cb->disc_active & BTA_HH_LE_DISC_HIDS) == 0) |
|
bta_hh_le_open_cmpl(p_dev_cb); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_set_protocol_mode |
|
* |
|
* Description Set remote device protocol mode. |
|
* |
|
******************************************************************************/ |
|
bool bta_hh_le_set_protocol_mode(tBTA_HH_DEV_CB* p_cb, |
|
tBTA_HH_PROTO_MODE mode) { |
|
tBTA_HH_CBDATA cback_data; |
|
|
|
APPL_TRACE_DEBUG("%s attempt mode: %s", __func__, |
|
(mode == BTA_HH_PROTO_RPT_MODE) ? "Report" : "Boot"); |
|
|
|
cback_data.handle = p_cb->hid_handle; |
|
/* boot mode is not supported in the remote device */ |
|
if (p_cb->hid_srvc.proto_mode_handle == 0) { |
|
p_cb->mode = BTA_HH_PROTO_RPT_MODE; |
|
|
|
if (mode == BTA_HH_PROTO_BOOT_MODE) { |
|
APPL_TRACE_ERROR("Set Boot Mode failed!! No PROTO_MODE Char!"); |
|
cback_data.status = BTA_HH_ERR; |
|
} else { |
|
/* if set to report mode, need to de-register all input report |
|
* notification */ |
|
bta_hh_le_register_input_notif(p_cb, p_cb->mode, false); |
|
cback_data.status = BTA_HH_OK; |
|
} |
|
if (p_cb->state == BTA_HH_W4_CONN_ST) { |
|
p_cb->status = |
|
(cback_data.status == BTA_HH_OK) ? BTA_HH_OK : BTA_HH_ERR_PROTO; |
|
} else |
|
(*bta_hh_cb.p_cback)(BTA_HH_SET_PROTO_EVT, (tBTA_HH*)&cback_data); |
|
} else if (p_cb->mode != mode) { |
|
p_cb->mode = mode; |
|
mode = (mode == BTA_HH_PROTO_BOOT_MODE) ? BTA_HH_LE_PROTO_BOOT_MODE |
|
: BTA_HH_LE_PROTO_REPORT_MODE; |
|
|
|
gatt_queue_write_op(GATT_WRITE_CHAR, p_cb->conn_id, |
|
p_cb->hid_srvc.proto_mode_handle, {mode}, |
|
BTA_GATTC_TYPE_WRITE_NO_RSP, write_proto_mode_cb, p_cb); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
/******************************************************************************* |
|
* Function get_protocol_mode_cb |
|
* |
|
* Description Process the Read protocol mode, send GET_PROTO_EVT to |
|
* application with the protocol mode. |
|
* |
|
******************************************************************************/ |
|
static void get_protocol_mode_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, uint8_t* value, |
|
void* data) { |
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
tBTA_HH_HSDATA hs_data; |
|
|
|
hs_data.status = BTA_HH_ERR; |
|
hs_data.handle = p_dev_cb->hid_handle; |
|
hs_data.rsp_data.proto_mode = p_dev_cb->mode; |
|
|
|
if (status == BTA_GATT_OK && len) { |
|
hs_data.status = BTA_HH_OK; |
|
/* match up BTE/BTA report/boot mode def*/ |
|
hs_data.rsp_data.proto_mode = *(value); |
|
/* LE repot mode is the opposite value of BR/EDR report mode, flip it here |
|
*/ |
|
if (hs_data.rsp_data.proto_mode == 0) |
|
hs_data.rsp_data.proto_mode = BTA_HH_PROTO_BOOT_MODE; |
|
else |
|
hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; |
|
|
|
p_dev_cb->mode = hs_data.rsp_data.proto_mode; |
|
} |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("LE GET_PROTOCOL Mode = [%s]", |
|
(hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) |
|
? "Report" |
|
: "Boot"); |
|
#endif |
|
|
|
p_dev_cb->w4_evt = 0; |
|
(*bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH*)&hs_data); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_get_protocol_mode |
|
* |
|
* Description Get remote device protocol mode. |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_get_protocol_mode(tBTA_HH_DEV_CB* p_cb) { |
|
tBTA_HH_HSDATA hs_data; |
|
p_cb->w4_evt = BTA_HH_GET_PROTO_EVT; |
|
|
|
if (p_cb->hid_srvc.in_use && p_cb->hid_srvc.proto_mode_handle != 0) { |
|
gatt_queue_read_op(GATT_READ_CHAR, p_cb->conn_id, |
|
p_cb->hid_srvc.proto_mode_handle, get_protocol_mode_cb, |
|
p_cb); |
|
return; |
|
} |
|
|
|
/* no service support protocol_mode, by default report mode */ |
|
hs_data.status = BTA_HH_OK; |
|
hs_data.handle = p_cb->hid_handle; |
|
hs_data.rsp_data.proto_mode = BTA_HH_PROTO_RPT_MODE; |
|
p_cb->w4_evt = 0; |
|
(*bta_hh_cb.p_cback)(BTA_HH_GET_PROTO_EVT, (tBTA_HH*)&hs_data); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_dis_cback |
|
* |
|
* Description DIS read complete callback |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_dis_cback(const RawAddress& addr, tDIS_VALUE* p_dis_value) { |
|
tBTA_HH_DEV_CB* p_cb = bta_hh_le_find_dev_cb_by_bda(addr); |
|
|
|
if (p_cb == NULL || p_dis_value == NULL) { |
|
APPL_TRACE_ERROR("received unexpected/error DIS callback"); |
|
return; |
|
} |
|
|
|
p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; |
|
/* plug in the PnP info for this device */ |
|
if (p_dis_value->attr_mask & DIS_ATTR_PNP_ID_BIT) { |
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG( |
|
"Plug in PnP info: product_id = %02x, vendor_id = %04x, version = %04x", |
|
p_dis_value->pnp_id.product_id, p_dis_value->pnp_id.vendor_id, |
|
p_dis_value->pnp_id.product_version); |
|
#endif |
|
p_cb->dscp_info.product_id = p_dis_value->pnp_id.product_id; |
|
p_cb->dscp_info.vendor_id = p_dis_value->pnp_id.vendor_id; |
|
p_cb->dscp_info.version = p_dis_value->pnp_id.product_version; |
|
} |
|
bta_hh_le_open_cmpl(p_cb); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_pri_service_discovery |
|
* |
|
* Description Initialize GATT discovery on the remote LE HID device by |
|
* opening a GATT connection first. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB* p_cb) { |
|
tBT_UUID pri_srvc; |
|
|
|
bta_hh_le_co_reset_rpt_cache(p_cb->addr, p_cb->app_id); |
|
|
|
p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS | BTA_HH_LE_DISC_DIS); |
|
|
|
/* read DIS info */ |
|
if (!DIS_ReadDISInfo(p_cb->addr, bta_hh_le_dis_cback, DIS_ATTR_PNP_ID_BIT)) { |
|
APPL_TRACE_ERROR("read DIS failed"); |
|
p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS; |
|
} |
|
|
|
/* in parallel */ |
|
/* start primary service discovery for HID service */ |
|
pri_srvc.len = LEN_UUID_16; |
|
pri_srvc.uu.uuid16 = UUID_SERVCLASS_LE_HID; |
|
BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); |
|
return; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_encrypt_cback |
|
* |
|
* Description link encryption complete callback for bond verification. |
|
* |
|
* Returns None |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_encrypt_cback(const RawAddress* bd_addr, |
|
UNUSED_ATTR tBTA_GATT_TRANSPORT transport, |
|
UNUSED_ATTR void* p_ref_data, tBTM_STATUS result) { |
|
uint8_t idx = bta_hh_find_cb(*bd_addr); |
|
tBTA_HH_DEV_CB* p_dev_cb; |
|
|
|
if (idx != BTA_HH_IDX_INVALID) |
|
p_dev_cb = &bta_hh_cb.kdev[idx]; |
|
else { |
|
APPL_TRACE_ERROR("unexpected encryption callback, ignore"); |
|
return; |
|
} |
|
p_dev_cb->status = (result == BTM_SUCCESS) ? BTA_HH_OK : BTA_HH_ERR_SEC; |
|
p_dev_cb->reason = result; |
|
|
|
bta_hh_sm_execute(p_dev_cb, BTA_HH_ENC_CMPL_EVT, NULL); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_security_cmpl |
|
* |
|
* Description Security check completed, start the service discovery |
|
* if no cache available, otherwise report connection open |
|
* completed |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_security_cmpl(tBTA_HH_DEV_CB* p_cb, |
|
UNUSED_ATTR tBTA_HH_DATA* p_buf) { |
|
APPL_TRACE_DEBUG("%s", __func__); |
|
if (p_cb->status == BTA_HH_OK) { |
|
if (!p_cb->hid_srvc.in_use) { |
|
APPL_TRACE_DEBUG("bta_hh_security_cmpl no reports loaded, try to load"); |
|
|
|
/* start loading the cache if not in stack */ |
|
// TODO(jpawlowski): cache storage is broken, fix it |
|
// tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache; |
|
// uint8_t num_rpt = 0; |
|
// if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt, |
|
// p_cb->app_id)) != NULL) |
|
// { |
|
// bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt); |
|
// } |
|
} |
|
/* discovery has been done for HID service */ |
|
if (p_cb->app_id != 0 && p_cb->hid_srvc.in_use) { |
|
APPL_TRACE_DEBUG("%s: discovery has been done for HID service", __func__); |
|
/* configure protocol mode */ |
|
if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == false) { |
|
bta_hh_le_open_cmpl(p_cb); |
|
} |
|
} |
|
/* start primary service discovery for HID service */ |
|
else { |
|
APPL_TRACE_DEBUG("%s: Starting service discovery", __func__); |
|
bta_hh_le_pri_service_discovery(p_cb); |
|
} |
|
} else { |
|
APPL_TRACE_ERROR("%s() - encryption failed; status=0x%04x, reason=0x%04x", |
|
__func__, p_cb->status, p_cb->reason); |
|
if (!(p_cb->status == BTA_HH_ERR_SEC && p_cb->reason == BTM_ERR_PROCESSING)) |
|
bta_hh_le_api_disc_act(p_cb); |
|
else if (p_cb->conn_id != BTA_GATT_INVALID_CONN_ID) |
|
BTA_GATTC_Close(p_cb->conn_id); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_notify_enc_cmpl |
|
* |
|
* Description process GATT encryption complete event |
|
* |
|
* Returns |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_buf) { |
|
if (p_cb == NULL || p_cb->security_pending == false || p_buf == NULL || |
|
p_buf->le_enc_cmpl.client_if != bta_hh_cb.gatt_if) { |
|
return; |
|
} |
|
|
|
p_cb->security_pending = false; |
|
bta_hh_start_security(p_cb, NULL); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_clear_service_cache |
|
* |
|
* Description clear the service cache |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_clear_service_cache(tBTA_HH_DEV_CB* p_cb) { |
|
tBTA_HH_LE_HID_SRVC* p_hid_srvc = &p_cb->hid_srvc; |
|
|
|
p_cb->app_id = 0; |
|
p_cb->dscp_info.descriptor.dsc_list = NULL; |
|
|
|
osi_free_and_reset((void**)&p_hid_srvc->rpt_map); |
|
memset(p_hid_srvc, 0, sizeof(tBTA_HH_LE_HID_SRVC)); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_start_security |
|
* |
|
* Description start the security check of the established connection |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_start_security(tBTA_HH_DEV_CB* p_cb, |
|
UNUSED_ATTR tBTA_HH_DATA* p_buf) { |
|
uint8_t sec_flag = 0; |
|
tBTM_SEC_DEV_REC* p_dev_rec; |
|
|
|
p_dev_rec = btm_find_dev(p_cb->addr); |
|
if (p_dev_rec) { |
|
if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || |
|
p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { |
|
/* if security collision happened, wait for encryption done */ |
|
p_cb->security_pending = true; |
|
return; |
|
} |
|
} |
|
|
|
/* verify bond */ |
|
BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE); |
|
|
|
/* if link has been encrypted */ |
|
if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { |
|
bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); |
|
} |
|
/* if bonded and link not encrypted */ |
|
else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) { |
|
sec_flag = BTM_BLE_SEC_ENCRYPT; |
|
p_cb->status = BTA_HH_ERR_AUTH_FAILED; |
|
BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_hh_le_encrypt_cback, |
|
NULL, sec_flag); |
|
} |
|
/* unbonded device, report security error here */ |
|
else if (p_cb->sec_mask != BTA_SEC_NONE) { |
|
sec_flag = BTM_BLE_SEC_ENCRYPT_NO_MITM; |
|
p_cb->status = BTA_HH_ERR_AUTH_FAILED; |
|
bta_hh_clear_service_cache(p_cb); |
|
BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_hh_le_encrypt_cback, |
|
NULL, sec_flag); |
|
} |
|
/* otherwise let it go through */ |
|
else { |
|
bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_gatt_open |
|
* |
|
* Description process GATT open event. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_gatt_open(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_buf) { |
|
tBTA_GATTC_OPEN* p_data = &p_buf->le_open; |
|
uint8_t* p2; |
|
tHID_STATUS status = BTA_HH_ERR; |
|
|
|
/* if received invalid callback data , ignore it */ |
|
if (p_cb == NULL || p_data == NULL) return; |
|
|
|
p2 = p_data->remote_bda.address; |
|
|
|
APPL_TRACE_DEBUG( |
|
"bta_hh_gatt_open BTA_GATTC_OPEN_EVT bda= [%08x%04x] status =%d", |
|
((p2[0]) << 24) + ((p2[1]) << 16) + ((p2[2]) << 8) + (p2[3]), |
|
((p2[4]) << 8) + p2[5], p_data->status); |
|
|
|
if (p_data->status == BTA_GATT_OK) { |
|
p_cb->is_le_device = true; |
|
p_cb->in_use = true; |
|
p_cb->conn_id = p_data->conn_id; |
|
p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); |
|
|
|
bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; |
|
|
|
gatt_op_queue_clean(p_cb->conn_id); |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("hid_handle = %2x conn_id = %04x cb_index = %d", |
|
p_cb->hid_handle, p_cb->conn_id, p_cb->index); |
|
#endif |
|
|
|
bta_hh_sm_execute(p_cb, BTA_HH_START_ENC_EVT, NULL); |
|
|
|
} else /* open failure */ |
|
{ |
|
bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA*)&status); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_close |
|
* |
|
* Description This function process the GATT close event and post it as a |
|
* BTA HH internal event |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_close(tBTA_GATTC_CLOSE* p_data) { |
|
tBTA_HH_DEV_CB* p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->remote_bda); |
|
uint16_t sm_event = BTA_HH_GATT_CLOSE_EVT; |
|
|
|
if (p_dev_cb != NULL) { |
|
tBTA_HH_LE_CLOSE* p_buf = |
|
(tBTA_HH_LE_CLOSE*)osi_malloc(sizeof(tBTA_HH_LE_CLOSE)); |
|
p_buf->hdr.event = sm_event; |
|
p_buf->hdr.layer_specific = (uint16_t)p_dev_cb->hid_handle; |
|
p_buf->conn_id = p_data->conn_id; |
|
p_buf->reason = p_data->reason; |
|
|
|
p_dev_cb->conn_id = BTA_GATT_INVALID_CONN_ID; |
|
p_dev_cb->security_pending = false; |
|
bta_sys_sendmsg(p_buf); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_gatt_disc_cmpl |
|
* |
|
* Description Check to see if the remote device is a LE only device |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_gatt_disc_cmpl(tBTA_HH_DEV_CB* p_cb, tBTA_HH_STATUS status) { |
|
APPL_TRACE_DEBUG("bta_hh_le_gatt_disc_cmpl "); |
|
|
|
/* if open sucessful or protocol mode not desired, keep the connection open |
|
* but inform app */ |
|
if (status == BTA_HH_OK || status == BTA_HH_ERR_PROTO) { |
|
/* assign a special APP ID temp, since device type unknown */ |
|
p_cb->app_id = BTA_HH_APP_ID_LE; |
|
|
|
/* set report notification configuration */ |
|
p_cb->clt_cfg_idx = 0; |
|
bta_hh_le_write_rpt_clt_cfg(p_cb); |
|
} else /* error, close the GATT connection */ |
|
{ |
|
/* close GATT connection if it's on */ |
|
bta_hh_le_api_disc_act(p_cb); |
|
} |
|
} |
|
|
|
static void read_hid_info_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, uint8_t* value, |
|
void* data) { |
|
if (status != BTA_GATT_OK) { |
|
APPL_TRACE_ERROR("%s: error: %d", __func__, status); |
|
return; |
|
} |
|
|
|
if (len != 4) { |
|
APPL_TRACE_ERROR("%s: wrong length: %d", __func__, len); |
|
return; |
|
} |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
uint8_t* pp = value; |
|
/* save device information */ |
|
STREAM_TO_UINT16(p_dev_cb->dscp_info.version, pp); |
|
STREAM_TO_UINT8(p_dev_cb->dscp_info.ctry_code, pp); |
|
STREAM_TO_UINT8(p_dev_cb->dscp_info.flag, pp); |
|
} |
|
|
|
static void read_hid_report_map_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, |
|
uint8_t* value, void* data) { |
|
if (status != BTA_GATT_OK) { |
|
APPL_TRACE_ERROR("%s: error reading characteristic: %d", __func__, status); |
|
return; |
|
} |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
tBTA_HH_LE_HID_SRVC* p_srvc = &p_dev_cb->hid_srvc; |
|
|
|
osi_free_and_reset((void**)&p_srvc->rpt_map); |
|
|
|
if (len > 0) { |
|
p_srvc->rpt_map = (uint8_t*)osi_malloc(len); |
|
|
|
uint8_t* pp = value; |
|
STREAM_TO_ARRAY(p_srvc->rpt_map, pp, len); |
|
p_srvc->descriptor.dl_len = len; |
|
p_srvc->descriptor.dsc_list = p_dev_cb->hid_srvc.rpt_map; |
|
} |
|
} |
|
|
|
static void read_ext_rpt_ref_desc_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, |
|
uint8_t* value, void* data) { |
|
if (status != BTA_GATT_OK) { |
|
APPL_TRACE_ERROR("%s: error: %d", __func__, status); |
|
return; |
|
} |
|
|
|
/* if the length of the descriptor value is right, parse it assume it's a 16 |
|
* bits UUID */ |
|
if (len != LEN_UUID_16) { |
|
APPL_TRACE_ERROR("%s: we support only 16bit UUID: %d", __func__, len); |
|
return; |
|
} |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
uint8_t* pp = value; |
|
|
|
STREAM_TO_UINT16(p_dev_cb->hid_srvc.ext_rpt_ref, pp); |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("%s: External Report Reference UUID 0x%04x", __func__, |
|
p_dev_cb->hid_srvc.ext_rpt_ref); |
|
#endif |
|
} |
|
|
|
static void read_report_ref_desc_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, |
|
uint8_t* value, void* data) { |
|
if (status != BTA_GATT_OK) { |
|
APPL_TRACE_ERROR("%s: error: %d", __func__, status); |
|
return; |
|
} |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
const tBTA_GATTC_DESCRIPTOR* p_desc = |
|
BTA_GATTC_GetDescriptor(conn_id, handle); |
|
|
|
if (!p_desc) { |
|
APPL_TRACE_ERROR("%s: error: descriptor is null!", __func__); |
|
return; |
|
} |
|
|
|
tBTA_HH_LE_RPT* p_rpt; |
|
p_rpt = bta_hh_le_find_report_entry( |
|
p_dev_cb, p_desc->characteristic->service->handle, GATT_UUID_HID_REPORT, |
|
p_desc->characteristic->handle); |
|
if (p_rpt) bta_hh_le_save_report_ref(p_dev_cb, p_rpt, status, value, len); |
|
} |
|
|
|
void read_pref_conn_params_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, uint8_t* value, |
|
void* data) { |
|
if (status != BTA_GATT_OK) { |
|
APPL_TRACE_ERROR("%s: error: %d", __func__, status); |
|
return; |
|
} |
|
|
|
if (len != 8) { |
|
APPL_TRACE_ERROR("%s: we support only 16bit UUID: %d", __func__, len); |
|
return; |
|
} |
|
|
|
// TODO(jpawlowski): this should be done by GAP profile, remove when GAP is |
|
// fixed. |
|
uint8_t* pp = value; |
|
uint16_t min, max, latency, tout; |
|
STREAM_TO_UINT16(min, pp); |
|
STREAM_TO_UINT16(max, pp); |
|
STREAM_TO_UINT16(latency, pp); |
|
STREAM_TO_UINT16(tout, pp); |
|
|
|
// Make sure both min, and max are bigger than 11.25ms, lower values can |
|
// introduce |
|
// audio issues if A2DP is also active. |
|
if (min < BTM_BLE_CONN_INT_MIN_LIMIT) min = BTM_BLE_CONN_INT_MIN_LIMIT; |
|
if (max < BTM_BLE_CONN_INT_MIN_LIMIT) max = BTM_BLE_CONN_INT_MIN_LIMIT; |
|
|
|
// If the device has no preferred connection timeout, use the default. |
|
if (tout == BTM_BLE_CONN_PARAM_UNDEF) tout = BTM_BLE_CONN_TIMEOUT_DEF; |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
|
|
if (interop_match_addr(INTEROP_HID_PREF_CONN_SUP_TIMEOUT_3S, |
|
(RawAddress*)&p_dev_cb->addr) == true) { |
|
if (tout < 300) tout = 300; |
|
} |
|
|
|
/** M: Bug fix for HOGP connection taking too long time @{ */ |
|
|
|
// BTM_BleSetPrefConnParams(p_dev_cb->addr, min, max, latency, tout); |
|
// L2CA_UpdateBleConnParams(p_dev_cb->addr, min, max, latency, tout); |
|
bta_hh_le_save_conn_params(p_dev_cb, min, max, latency, tout); |
|
/** @} */ |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_search_hid_chars |
|
* |
|
* Description This function discover all characteristics a service and |
|
* all descriptors available. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB* p_dev_cb, |
|
tBTA_GATTC_SERVICE* service) { |
|
tBTA_HH_LE_RPT* p_rpt; |
|
|
|
for (list_node_t* cn = list_begin(service->characteristics); |
|
cn != list_end(service->characteristics); cn = list_next(cn)) { |
|
tBTA_GATTC_CHARACTERISTIC* p_char = |
|
(tBTA_GATTC_CHARACTERISTIC*)list_node(cn); |
|
|
|
if (p_char->uuid.len != LEN_UUID_16) continue; |
|
|
|
LOG_DEBUG(LOG_TAG, "%s: %s 0x%04d", __func__, |
|
bta_hh_uuid_to_str(p_char->uuid.uu.uuid16), |
|
p_char->uuid.uu.uuid16); |
|
|
|
switch (p_char->uuid.uu.uuid16) { |
|
case GATT_UUID_HID_CONTROL_POINT: |
|
p_dev_cb->hid_srvc.control_point_handle = p_char->handle; |
|
break; |
|
case GATT_UUID_HID_INFORMATION: |
|
/* only one instance per HID service */ |
|
gatt_queue_read_op(GATT_READ_CHAR, p_dev_cb->conn_id, p_char->handle, |
|
read_hid_info_cb, p_dev_cb); |
|
break; |
|
case GATT_UUID_HID_REPORT_MAP: |
|
/* only one instance per HID service */ |
|
gatt_queue_read_op(GATT_READ_CHAR, p_dev_cb->conn_id, p_char->handle, |
|
read_hid_report_map_cb, p_dev_cb); |
|
/* descriptor is optional */ |
|
bta_hh_le_read_char_descriptor(p_dev_cb, p_char->handle, |
|
GATT_UUID_EXT_RPT_REF_DESCR, |
|
read_ext_rpt_ref_desc_cb, p_dev_cb); |
|
break; |
|
|
|
case GATT_UUID_HID_REPORT: |
|
p_rpt = bta_hh_le_find_alloc_report_entry( |
|
p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, GATT_UUID_HID_REPORT, |
|
p_char->handle); |
|
if (p_rpt == NULL) { |
|
APPL_TRACE_ERROR("%s: Add report entry failed !!!", __func__); |
|
break; |
|
} |
|
|
|
if (p_rpt->rpt_type != BTA_HH_RPTT_INPUT) break; |
|
|
|
bta_hh_le_read_char_descriptor(p_dev_cb, p_char->handle, |
|
GATT_UUID_RPT_REF_DESCR, |
|
read_report_ref_desc_cb, p_dev_cb); |
|
break; |
|
|
|
/* found boot mode report types */ |
|
case GATT_UUID_HID_BT_KB_OUTPUT: |
|
case GATT_UUID_HID_BT_MOUSE_INPUT: |
|
case GATT_UUID_HID_BT_KB_INPUT: |
|
if (bta_hh_le_find_alloc_report_entry(p_dev_cb, service->handle, |
|
p_char->uuid.uu.uuid16, |
|
p_char->handle) == NULL) |
|
APPL_TRACE_ERROR("%s: Add report entry failed !!!", __func__); |
|
|
|
break; |
|
|
|
default: |
|
APPL_TRACE_DEBUG("%s: not processing %s 0x%04d", __func__, |
|
bta_hh_uuid_to_str(p_char->uuid.uu.uuid16), |
|
p_char->uuid.uu.uuid16); |
|
} |
|
} |
|
|
|
/* Make sure PROTO_MODE is processed as last */ |
|
for (list_node_t* cn = list_begin(service->characteristics); |
|
cn != list_end(service->characteristics); cn = list_next(cn)) { |
|
tBTA_GATTC_CHARACTERISTIC* p_char = |
|
(tBTA_GATTC_CHARACTERISTIC*)list_node(cn); |
|
|
|
if (p_char->uuid.len == LEN_UUID_16 && |
|
p_char->uuid.uu.uuid16 == GATT_UUID_HID_PROTO_MODE) { |
|
p_dev_cb->hid_srvc.proto_mode_handle = p_char->handle; |
|
bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_srvc_search_cmpl |
|
* |
|
* Description This function process the GATT service search complete. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL* p_data) { |
|
tBTA_HH_DEV_CB* p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); |
|
|
|
/* service search exception or no HID service is supported on remote */ |
|
if (p_dev_cb == NULL) return; |
|
|
|
if (p_data->status != BTA_GATT_OK) { |
|
p_dev_cb->status = BTA_HH_ERR_SDP; |
|
/* close the connection and report service discovery complete with error */ |
|
bta_hh_le_api_disc_act(p_dev_cb); |
|
return; |
|
} |
|
|
|
const list_t* services = BTA_GATTC_GetServices(p_data->conn_id); |
|
|
|
bool have_hid = false; |
|
for (list_node_t* sn = list_begin(services); sn != list_end(services); |
|
sn = list_next(sn)) { |
|
tBTA_GATTC_SERVICE* service = (tBTA_GATTC_SERVICE*)list_node(sn); |
|
|
|
if (service->uuid.uu.uuid16 == UUID_SERVCLASS_LE_HID && |
|
service->is_primary && !have_hid) { |
|
have_hid = true; |
|
|
|
/* found HID primamry service */ |
|
p_dev_cb->hid_srvc.in_use = true; |
|
p_dev_cb->hid_srvc.srvc_inst_id = service->handle; |
|
p_dev_cb->hid_srvc.proto_mode_handle = 0; |
|
p_dev_cb->hid_srvc.control_point_handle = 0; |
|
|
|
bta_hh_le_search_hid_chars(p_dev_cb, service); |
|
|
|
APPL_TRACE_DEBUG("%s: have HID service inst_id= %d", __func__, |
|
p_dev_cb->hid_srvc.srvc_inst_id); |
|
} else if (service->uuid.uu.uuid16 == UUID_SERVCLASS_SCAN_PARAM) { |
|
p_dev_cb->scan_refresh_char_handle = 0; |
|
|
|
for (list_node_t* cn = list_begin(service->characteristics); |
|
cn != list_end(service->characteristics); cn = list_next(cn)) { |
|
tBTA_GATTC_CHARACTERISTIC* p_char = |
|
(tBTA_GATTC_CHARACTERISTIC*)list_node(cn); |
|
if (p_char->uuid.len == LEN_UUID_16 && |
|
p_char->uuid.uu.uuid16 == GATT_UUID_SCAN_REFRESH) { |
|
p_dev_cb->scan_refresh_char_handle = p_char->handle; |
|
|
|
if (p_char->properties & BTA_GATT_CHAR_PROP_BIT_NOTIFY) |
|
p_dev_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT; |
|
else |
|
p_dev_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE; |
|
|
|
break; |
|
} |
|
} |
|
} else if (service->uuid.uu.uuid16 == UUID_SERVCLASS_GAP_SERVER) { |
|
// TODO(jpawlowski): this should be done by GAP profile, remove when GAP |
|
// is fixed. |
|
for (list_node_t* cn = list_begin(service->characteristics); |
|
cn != list_end(service->characteristics); cn = list_next(cn)) { |
|
tBTA_GATTC_CHARACTERISTIC* p_char = |
|
(tBTA_GATTC_CHARACTERISTIC*)list_node(cn); |
|
if (p_char->uuid.len == LEN_UUID_16 && |
|
p_char->uuid.uu.uuid16 == GATT_UUID_GAP_PREF_CONN_PARAM) { |
|
/* read the char value */ |
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
if (!interop_mtk_match_addr_name( |
|
INTEROP_MTK_LE_DISABLE_PREF_CONN_PARAMS, |
|
&p_dev_cb->addr)) |
|
#endif |
|
gatt_queue_read_op(GATT_READ_CHAR, p_dev_cb->conn_id, p_char->handle, |
|
read_pref_conn_params_cb, p_dev_cb); |
|
|
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_input_rpt_notify |
|
* |
|
* Description process the notificaton event, most likely for input report. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_input_rpt_notify(tBTA_GATTC_NOTIFY* p_data) { |
|
tBTA_HH_DEV_CB* p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id); |
|
uint8_t app_id; |
|
uint8_t* p_buf; |
|
tBTA_HH_LE_RPT* p_rpt; |
|
|
|
if (p_dev_cb == NULL) { |
|
APPL_TRACE_ERROR( |
|
"%s: notification received from Unknown device, conn_id: 0x%04x", |
|
__func__, p_data->conn_id); |
|
return; |
|
} |
|
|
|
const tBTA_GATTC_CHARACTERISTIC* p_char = |
|
BTA_GATTC_GetCharacteristic(p_dev_cb->conn_id, p_data->handle); |
|
if (p_char == NULL) { |
|
APPL_TRACE_ERROR( |
|
"%s: notification received for Unknown Characteristic, conn_id: " |
|
"0x%04x, handle: 0x%04x", |
|
__func__, p_dev_cb->conn_id, p_data->handle); |
|
return; |
|
} |
|
|
|
app_id = p_dev_cb->app_id; |
|
|
|
p_rpt = bta_hh_le_find_report_entry(p_dev_cb, p_dev_cb->hid_srvc.srvc_inst_id, |
|
p_char->uuid.uu.uuid16, p_char->handle); |
|
if (p_rpt == NULL) { |
|
APPL_TRACE_ERROR( |
|
"%s: notification received for Unknown Report, uuid: 0x%04x, handle: " |
|
"0x%04x", |
|
__func__, p_char->uuid.uu.uuid16, p_char->handle); |
|
return; |
|
} |
|
|
|
if (p_char->uuid.uu.uuid16 == GATT_UUID_HID_BT_MOUSE_INPUT) |
|
app_id = BTA_HH_APP_ID_MI; |
|
else if (p_char->uuid.uu.uuid16 == GATT_UUID_HID_BT_KB_INPUT) |
|
app_id = BTA_HH_APP_ID_KB; |
|
|
|
APPL_TRACE_DEBUG("Notification received on report ID: %d", p_rpt->rpt_id); |
|
|
|
/* need to append report ID to the head of data */ |
|
if (p_rpt->rpt_id != 0) { |
|
p_buf = (uint8_t*)osi_malloc(p_data->len + 1); |
|
|
|
p_buf[0] = p_rpt->rpt_id; |
|
memcpy(&p_buf[1], p_data->value, p_data->len); |
|
++p_data->len; |
|
} else { |
|
p_buf = p_data->value; |
|
} |
|
|
|
bta_hh_co_data((uint8_t)p_dev_cb->hid_handle, p_buf, p_data->len, |
|
p_dev_cb->mode, 0, /* no sub class*/ |
|
p_dev_cb->dscp_info.ctry_code, p_dev_cb->addr, app_id); |
|
|
|
if (p_buf != p_data->value) osi_free(p_buf); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_gatt_open_fail |
|
* |
|
* Description action function to process the open fail |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_open_fail(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) { |
|
tBTA_HH_CONN conn_dat; |
|
|
|
/* open failure in the middle of service discovery, clear all services */ |
|
if (p_cb->disc_active & BTA_HH_LE_DISC_HIDS) { |
|
bta_hh_clear_service_cache(p_cb); |
|
} |
|
|
|
p_cb->disc_active = BTA_HH_LE_DISC_NONE; |
|
/* Failure in opening connection or GATT discovery failure */ |
|
conn_dat.handle = p_cb->hid_handle; |
|
conn_dat.bda = p_cb->addr; |
|
conn_dat.le_hid = true; |
|
conn_dat.scps_supported = p_cb->scps_supported; |
|
|
|
if (p_cb->status == BTA_HH_OK) |
|
conn_dat.status = (p_data->le_close.reason == BTA_GATT_CONN_UNKNOWN) |
|
? p_cb->status |
|
: BTA_HH_ERR; |
|
else |
|
conn_dat.status = p_cb->status; |
|
|
|
/* Report OPEN fail event */ |
|
(*bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH*)&conn_dat); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_gatt_close |
|
* |
|
* Description action function to process the GATT close int he state |
|
* machine. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_gatt_close(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) { |
|
tBTA_HH_CBDATA disc_dat = {BTA_HH_OK, 0}; |
|
|
|
/* deregister all notification */ |
|
bta_hh_le_deregister_input_notif(p_cb); |
|
/* finaliza device driver */ |
|
bta_hh_co_close(p_cb->hid_handle, p_cb->app_id); |
|
/* update total conn number */ |
|
bta_hh_cb.cnt_num--; |
|
|
|
disc_dat.handle = p_cb->hid_handle; |
|
disc_dat.status = p_cb->status; |
|
|
|
(*bta_hh_cb.p_cback)(BTA_HH_CLOSE_EVT, (tBTA_HH*)&disc_dat); |
|
|
|
/* if no connection is active and HH disable is signaled, disable service */ |
|
if (bta_hh_cb.cnt_num == 0 && bta_hh_cb.w4_disable) { |
|
bta_hh_disc_cmpl(); |
|
} else { |
|
#if (BTA_HH_LE_RECONN == TRUE) |
|
if (p_data->le_close.reason == BTA_GATT_CONN_TIMEOUT) { |
|
bta_hh_le_add_dev_bg_conn(p_cb, false); |
|
} |
|
#endif |
|
} |
|
|
|
return; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_api_disc_act |
|
* |
|
* Description initaite a Close API to a remote HID device |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_api_disc_act(tBTA_HH_DEV_CB* p_cb) { |
|
if (p_cb->conn_id != BTA_GATT_INVALID_CONN_ID) { |
|
gatt_op_queue_clean(p_cb->conn_id); |
|
BTA_GATTC_Close(p_cb->conn_id); |
|
/* remove device from background connection if intended to disconnect, |
|
do not allow reconnection */ |
|
bta_hh_le_remove_dev_bg_conn(p_cb); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function read_report_cb |
|
* |
|
* Description Process the Read report complete, send GET_REPORT_EVT to |
|
* application with the report data. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
static void read_report_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, uint8_t* value, |
|
void* data) { |
|
const tBTA_GATTC_CHARACTERISTIC* p_char = |
|
BTA_GATTC_GetCharacteristic(conn_id, handle); |
|
|
|
if (p_char == NULL) return; |
|
|
|
uint16_t char_uuid = p_char->uuid.uu.uuid16; |
|
|
|
if (char_uuid != GATT_UUID_HID_REPORT && |
|
char_uuid != GATT_UUID_HID_BT_KB_INPUT && |
|
char_uuid != GATT_UUID_HID_BT_KB_OUTPUT && |
|
char_uuid != GATT_UUID_HID_BT_MOUSE_INPUT && |
|
char_uuid != GATT_UUID_BATTERY_LEVEL) { |
|
APPL_TRACE_ERROR("%s: Unexpected Read UUID: 0x%04x", __func__, char_uuid); |
|
return; |
|
} |
|
|
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
if (p_dev_cb->w4_evt != BTA_HH_GET_RPT_EVT) { |
|
APPL_TRACE_ERROR("Unexpected READ cmpl, w4_evt = %d", p_dev_cb->w4_evt); |
|
return; |
|
} |
|
|
|
/* GET_REPORT */ |
|
BT_HDR* p_buf = NULL; |
|
tBTA_HH_LE_RPT* p_rpt; |
|
tBTA_HH_HSDATA hs_data; |
|
uint8_t* pp; |
|
|
|
memset(&hs_data, 0, sizeof(hs_data)); |
|
hs_data.status = BTA_HH_ERR; |
|
hs_data.handle = p_dev_cb->hid_handle; |
|
|
|
if (status == BTA_GATT_OK) { |
|
p_rpt = bta_hh_le_find_report_entry(p_dev_cb, p_char->service->handle, |
|
p_char->uuid.uu.uuid16, p_char->handle); |
|
|
|
if (p_rpt != NULL && len) { |
|
p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR) + len + 1); |
|
/* pack data send to app */ |
|
hs_data.status = BTA_HH_OK; |
|
p_buf->len = len + 1; |
|
p_buf->layer_specific = 0; |
|
p_buf->offset = 0; |
|
|
|
/* attach report ID as the first byte of the report before sending it to |
|
* USB HID driver */ |
|
pp = (uint8_t*)(p_buf + 1); |
|
UINT8_TO_STREAM(pp, p_rpt->rpt_id); |
|
memcpy(pp, value, len); |
|
|
|
hs_data.rsp_data.p_rpt_data = p_buf; |
|
} |
|
} |
|
|
|
p_dev_cb->w4_evt = 0; |
|
(*bta_hh_cb.p_cback)(BTA_HH_GET_RPT_EVT, (tBTA_HH*)&hs_data); |
|
|
|
osi_free_and_reset((void**)&p_buf); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_get_rpt |
|
* |
|
* Description GET_REPORT on a LE HID Report |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_get_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type, |
|
uint8_t rpt_id) { |
|
tBTA_HH_LE_RPT* p_rpt = bta_hh_le_find_rpt_by_idtype( |
|
p_cb->hid_srvc.report, p_cb->mode, r_type, rpt_id); |
|
|
|
if (p_rpt == NULL) { |
|
APPL_TRACE_ERROR("%s: no matching report", __func__); |
|
return; |
|
} |
|
|
|
p_cb->w4_evt = BTA_HH_GET_RPT_EVT; |
|
gatt_queue_read_op(GATT_READ_CHAR, p_cb->conn_id, p_rpt->char_inst_id, |
|
read_report_cb, p_cb); |
|
} |
|
|
|
static void write_report_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, void* data) { |
|
tBTA_HH_CBDATA cback_data; |
|
tBTA_HH_DEV_CB* p_dev_cb = (tBTA_HH_DEV_CB*)data; |
|
uint16_t cb_evt = p_dev_cb->w4_evt; |
|
|
|
if (cb_evt == 0) return; |
|
|
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("bta_hh_le_write_cmpl w4_evt: %d", p_dev_cb->w4_evt); |
|
#endif |
|
|
|
const tBTA_GATTC_CHARACTERISTIC* p_char = |
|
BTA_GATTC_GetCharacteristic(conn_id, handle); |
|
uint16_t uuid = p_char->uuid.uu.uuid16; |
|
if (uuid != GATT_UUID_HID_REPORT && uuid != GATT_UUID_HID_BT_KB_INPUT && |
|
uuid != GATT_UUID_HID_BT_MOUSE_INPUT && |
|
uuid != GATT_UUID_HID_BT_KB_OUTPUT) { |
|
return; |
|
} |
|
|
|
/* Set Report finished */ |
|
cback_data.handle = p_dev_cb->hid_handle; |
|
cback_data.status = (status == BTA_GATT_OK) ? BTA_HH_OK : BTA_HH_ERR; |
|
p_dev_cb->w4_evt = 0; |
|
(*bta_hh_cb.p_cback)(cb_evt, (tBTA_HH*)&cback_data); |
|
} |
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_write_rpt |
|
* |
|
* Description SET_REPORT/or DATA output on a LE HID Report |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_write_rpt(tBTA_HH_DEV_CB* p_cb, tBTA_HH_RPT_TYPE r_type, |
|
BT_HDR* p_buf, uint16_t w4_evt) { |
|
tBTA_HH_LE_RPT* p_rpt; |
|
uint8_t rpt_id; |
|
|
|
if (p_buf == NULL || p_buf->len == 0) { |
|
APPL_TRACE_ERROR("%s: Illegal data", __func__); |
|
return; |
|
} |
|
|
|
/* strip report ID from the data */ |
|
uint8_t* vec_start = (uint8_t*)(p_buf + 1) + p_buf->offset; |
|
STREAM_TO_UINT8(rpt_id, vec_start); |
|
vector<uint8_t> value(vec_start, vec_start + p_buf->len - 1); |
|
|
|
p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc.report, p_cb->mode, |
|
r_type, rpt_id); |
|
if (p_rpt == NULL) { |
|
APPL_TRACE_ERROR("%s: no matching report", __func__); |
|
osi_free(p_buf); |
|
return; |
|
} |
|
|
|
p_cb->w4_evt = w4_evt; |
|
|
|
const tBTA_GATTC_CHARACTERISTIC* p_char = |
|
BTA_GATTC_GetCharacteristic(p_cb->conn_id, p_rpt->char_inst_id); |
|
|
|
tBTA_GATTC_WRITE_TYPE write_type = BTA_GATTC_TYPE_WRITE; |
|
if (p_char && (p_char->properties & BTA_GATT_CHAR_PROP_BIT_WRITE_NR)) |
|
write_type = BTA_GATTC_TYPE_WRITE_NO_RSP; |
|
|
|
gatt_queue_write_op(GATT_WRITE_CHAR, p_cb->conn_id, p_rpt->char_inst_id, |
|
std::move(value), write_type, write_report_cb, p_cb); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_suspend |
|
* |
|
* Description send LE suspend or exit suspend mode to remote device. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_suspend(tBTA_HH_DEV_CB* p_cb, |
|
tBTA_HH_TRANS_CTRL_TYPE ctrl_type) { |
|
ctrl_type -= BTA_HH_CTRL_SUSPEND; |
|
|
|
// We don't care about response |
|
gatt_queue_write_op(GATT_WRITE_CHAR, p_cb->conn_id, |
|
p_cb->hid_srvc.control_point_handle, {(uint8_t)ctrl_type}, |
|
BTA_GATTC_TYPE_WRITE_NO_RSP, NULL, NULL); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_write_dev_act |
|
* |
|
* Description Write LE device action. can be SET/GET/DATA transaction. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_write_dev_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) { |
|
switch (p_data->api_sndcmd.t_type) { |
|
case HID_TRANS_SET_PROTOCOL: |
|
p_cb->w4_evt = BTA_HH_SET_PROTO_EVT; |
|
bta_hh_le_set_protocol_mode(p_cb, p_data->api_sndcmd.param); |
|
break; |
|
|
|
case HID_TRANS_GET_PROTOCOL: |
|
bta_hh_le_get_protocol_mode(p_cb); |
|
break; |
|
|
|
case HID_TRANS_GET_REPORT: |
|
bta_hh_le_get_rpt(p_cb, p_data->api_sndcmd.param, |
|
p_data->api_sndcmd.rpt_id); |
|
break; |
|
|
|
case HID_TRANS_SET_REPORT: |
|
bta_hh_le_write_rpt(p_cb, p_data->api_sndcmd.param, |
|
p_data->api_sndcmd.p_data, BTA_HH_SET_RPT_EVT); |
|
break; |
|
|
|
case HID_TRANS_DATA: /* output report */ |
|
|
|
bta_hh_le_write_rpt(p_cb, p_data->api_sndcmd.param, |
|
p_data->api_sndcmd.p_data, BTA_HH_DATA_EVT); |
|
break; |
|
|
|
case HID_TRANS_CONTROL: |
|
/* no handshake event will be generated */ |
|
/* if VC_UNPLUG is issued, set flag */ |
|
if (p_data->api_sndcmd.param == BTA_HH_CTRL_SUSPEND || |
|
p_data->api_sndcmd.param == BTA_HH_CTRL_EXIT_SUSPEND) { |
|
bta_hh_le_suspend(p_cb, p_data->api_sndcmd.param); |
|
} |
|
break; |
|
|
|
default: |
|
APPL_TRACE_ERROR("%s unsupported transaction for BLE HID device: %d", |
|
__func__, p_data->api_sndcmd.t_type); |
|
break; |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_get_dscp_act |
|
* |
|
* Description Send ReportDescriptor to application for all HID services. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_get_dscp_act(tBTA_HH_DEV_CB* p_cb) { |
|
if (p_cb->hid_srvc.in_use) { |
|
p_cb->dscp_info.descriptor.dl_len = p_cb->hid_srvc.descriptor.dl_len; |
|
p_cb->dscp_info.descriptor.dsc_list = p_cb->hid_srvc.descriptor.dsc_list; |
|
|
|
(*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH*)&p_cb->dscp_info); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_add_dev_bg_conn |
|
* |
|
* Description Remove a LE HID device from back ground connection |
|
* procedure. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB* p_cb, bool check_bond) { |
|
uint8_t sec_flag = 0; |
|
bool to_add = true; |
|
|
|
if (check_bond) { |
|
/* start reconnection if remote is a bonded device */ |
|
/* verify bond */ |
|
BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE); |
|
|
|
if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN) == 0) to_add = false; |
|
} |
|
|
|
if (/*p_cb->dscp_info.flag & BTA_HH_LE_NORMAL_CONN &&*/ |
|
!p_cb->in_bg_conn && to_add) { |
|
/* add device into BG connection to accept remote initiated connection */ |
|
BTA_GATTC_Open(bta_hh_cb.gatt_if, p_cb->addr, false, BTA_GATT_TRANSPORT_LE, |
|
false); |
|
p_cb->in_bg_conn = true; |
|
|
|
BTA_DmBleStartAutoConn(); |
|
} |
|
return; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_add_device |
|
* |
|
* Description Add a LE HID device as a known device, and also add the |
|
* address |
|
* into back ground connection WL for incoming connection. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
uint8_t bta_hh_le_add_device(tBTA_HH_DEV_CB* p_cb, |
|
tBTA_HH_MAINT_DEV* p_dev_info) { |
|
p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); |
|
bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index; |
|
|
|
/* update DI information */ |
|
bta_hh_update_di_info( |
|
p_cb, p_dev_info->dscp_info.vendor_id, p_dev_info->dscp_info.product_id, |
|
p_dev_info->dscp_info.version, p_dev_info->dscp_info.flag); |
|
|
|
/* add to BTA device list */ |
|
bta_hh_add_device_to_list( |
|
p_cb, p_cb->hid_handle, p_dev_info->attr_mask, |
|
&p_dev_info->dscp_info.descriptor, p_dev_info->sub_class, |
|
p_dev_info->dscp_info.ssr_max_latency, p_dev_info->dscp_info.ssr_min_tout, |
|
p_dev_info->app_id); |
|
|
|
bta_hh_le_add_dev_bg_conn(p_cb, false); |
|
|
|
p_cb->sec_mask = BTA_SEC_ENCRYPT; |
|
|
|
return p_cb->hid_handle; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_remove_dev_bg_conn |
|
* |
|
* Description Remove a LE HID device from back ground connection |
|
* procedure. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB* p_dev_cb) { |
|
if (p_dev_cb->in_bg_conn) { |
|
p_dev_cb->in_bg_conn = false; |
|
|
|
BTA_GATTC_CancelOpen(bta_hh_cb.gatt_if, p_dev_cb->addr, false); |
|
} |
|
|
|
/* deregister all notifications */ |
|
bta_hh_le_deregister_input_notif(p_dev_cb); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_gattc_callback |
|
* |
|
* Description This is GATT client callback function used in BTA HH. |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC* p_data) { |
|
tBTA_HH_DEV_CB* p_dev_cb; |
|
#if (BTA_HH_DEBUG == TRUE) |
|
APPL_TRACE_DEBUG("bta_hh_gattc_callback event = %d", event); |
|
#endif |
|
if (p_data == NULL) return; |
|
|
|
switch (event) { |
|
case BTA_GATTC_DEREG_EVT: /* 1 */ |
|
bta_hh_cleanup_disable(p_data->reg_oper.status); |
|
break; |
|
|
|
case BTA_GATTC_OPEN_EVT: /* 2 */ |
|
p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->open.remote_bda); |
|
if (p_dev_cb) { |
|
bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_OPEN_EVT, |
|
(tBTA_HH_DATA*)&p_data->open); |
|
} |
|
break; |
|
|
|
case BTA_GATTC_CLOSE_EVT: /* 5 */ |
|
bta_hh_le_close(&p_data->close); |
|
break; |
|
|
|
case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */ |
|
bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl); |
|
break; |
|
|
|
case BTA_GATTC_NOTIF_EVT: /* 10 */ |
|
bta_hh_le_input_rpt_notify(&p_data->notify); |
|
break; |
|
|
|
case BTA_GATTC_ENC_CMPL_CB_EVT: /* 17 */ |
|
p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->enc_cmpl.remote_bda); |
|
if (p_dev_cb) { |
|
bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_ENC_CMPL_EVT, |
|
(tBTA_HH_DATA*)&p_data->enc_cmpl); |
|
} |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
} |
|
|
|
static void read_report_descriptor_ccc_cb(uint16_t conn_id, tGATT_STATUS status, |
|
uint16_t handle, uint16_t len, |
|
uint8_t* value, void* data) { |
|
tBTA_HH_LE_RPT* p_rpt = (tBTA_HH_LE_RPT*)data; |
|
uint8_t* pp = value; |
|
STREAM_TO_UINT16(p_rpt->client_cfg_value, pp); |
|
|
|
APPL_TRACE_DEBUG("Read Client Configuration: 0x%04x", |
|
p_rpt->client_cfg_value); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_le_hid_read_rpt_clt_cfg |
|
* |
|
* Description a test command to read report descriptor client |
|
* configuration |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_hh_le_hid_read_rpt_clt_cfg(const RawAddress& bd_addr, uint8_t rpt_id) { |
|
tBTA_HH_DEV_CB* p_cb = NULL; |
|
tBTA_HH_LE_RPT* p_rpt; |
|
uint8_t index = BTA_HH_IDX_INVALID; |
|
|
|
index = bta_hh_find_cb(bd_addr); |
|
if (index == BTA_HH_IDX_INVALID) { |
|
APPL_TRACE_ERROR("%s: unknown device", __func__); |
|
return; |
|
} |
|
|
|
p_cb = &bta_hh_cb.kdev[index]; |
|
|
|
p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc.report, p_cb->mode, |
|
BTA_HH_RPTT_INPUT, rpt_id); |
|
|
|
if (p_rpt == NULL) { |
|
APPL_TRACE_ERROR("%s: no matching report", __func__); |
|
return; |
|
} |
|
|
|
bta_hh_le_read_char_descriptor(p_cb, p_rpt->char_inst_id, |
|
GATT_UUID_CHAR_CLIENT_CONFIG, |
|
read_report_descriptor_ccc_cb, p_rpt); |
|
return; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_hh_process_cache_rpt |
|
* |
|
* Description Process the cached reports |
|
* |
|
* Parameters: |
|
* |
|
******************************************************************************/ |
|
// TODO(jpawlowski): uncomment when fixed |
|
// static void bta_hh_process_cache_rpt (tBTA_HH_DEV_CB *p_cb, |
|
// tBTA_HH_RPT_CACHE_ENTRY *p_rpt_cache, |
|
// uint8_t num_rpt) |
|
// { |
|
// uint8_t i = 0; |
|
// tBTA_HH_LE_RPT *p_rpt; |
|
|
|
// if (num_rpt != 0) /* no cache is found */ |
|
// { |
|
// p_cb->hid_srvc.in_use = true; |
|
|
|
// /* set the descriptor info */ |
|
// p_cb->hid_srvc.descriptor.dl_len = |
|
// p_cb->dscp_info.descriptor.dl_len; |
|
// p_cb->hid_srvc.descriptor.dsc_list = |
|
// p_cb->dscp_info.descriptor.dsc_list; |
|
|
|
// for (; i <num_rpt; i ++, p_rpt_cache ++) |
|
// { |
|
// if ((p_rpt = bta_hh_le_find_alloc_report_entry (p_cb, |
|
// p_rpt_cache->srvc_inst_id, |
|
// p_rpt_cache->rpt_uuid, |
|
// p_rpt_cache->char_inst_id, |
|
// p_rpt_cache->prop)) == NULL) |
|
// { |
|
// APPL_TRACE_ERROR("bta_hh_process_cache_rpt: allocation report |
|
// entry failure"); |
|
// break; |
|
// } |
|
// else |
|
// { |
|
// p_rpt->rpt_type = p_rpt_cache->rpt_type; |
|
// p_rpt->rpt_id = p_rpt_cache->rpt_id; |
|
|
|
// if (p_rpt->uuid == GATT_UUID_HID_BT_KB_INPUT || |
|
// p_rpt->uuid == GATT_UUID_HID_BT_MOUSE_INPUT || |
|
// (p_rpt->uuid == GATT_UUID_HID_REPORT && p_rpt->rpt_type |
|
// == BTA_HH_RPTT_INPUT)) |
|
// { |
|
// p_rpt->client_cfg_value = |
|
// BTA_GATT_CLT_CONFIG_NOTIFICATION; |
|
// } |
|
// } |
|
// } |
|
// } |
|
// } |
|
|
|
#endif
|
|
|