| /****************************************************************************** |
| * |
| * 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. |
| * |
| ******************************************************************************/ |
| |
| #include "bta_api.h" |
| #include "bta_hh_int.h" |
| |
| #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) |
| |
| #include "bta_api.h" |
| #include <string.h> |
| #include "btm_api.h" |
| #include "btm_ble_api.h" |
| #include "bta_hh_co.h" |
| #include "bta_gatt_api.h" |
| #include "srvc_api.h" |
| #include "btm_int.h" |
| #include "utl.h" |
| |
| #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_RPT_INST_ID_MAP(s,c) (UINT8)(((s)<<4)|(c)) |
| #define BTA_HH_LE_RPT_GET_SRVC_INST_ID(x) (UINT8)(x >> 4) |
| #define BTA_HH_LE_RPT_GET_RPT_INST_ID(x) (UINT8)(x & 0x0f) |
| |
| |
| #define BTA_HH_LE_PROTO_BOOT_MODE 0x00 |
| #define BTA_HH_LE_PROTO_REPORT_MODE 0x01 |
| |
| #define BTA_HH_SCPP_INST_DEF 0 |
| |
| #define BTA_HH_LE_DISC_CHAR_NUM 8 |
| static const UINT16 bta_hh_le_disc_char_uuid[BTA_HH_LE_DISC_CHAR_NUM] = |
| { |
| GATT_UUID_HID_INFORMATION, |
| GATT_UUID_HID_REPORT_MAP, |
| GATT_UUID_HID_CONTROL_POINT, |
| GATT_UUID_HID_REPORT, |
| GATT_UUID_HID_BT_KB_INPUT, |
| GATT_UUID_HID_BT_KB_OUTPUT, |
| GATT_UUID_HID_BT_MOUSE_INPUT, |
| GATT_UUID_HID_PROTO_MODE /* always make sure this is the last attribute to discover */ |
| }; |
| |
| #define BTA_LE_HID_RTP_UUID_MAX 5 |
| static const UINT16 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_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb); |
| static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb); |
| static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb); |
| static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb); |
| static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); |
| static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status); |
| static void bta_hh_le_add_dev_bg_conn(tBTA_HH_DEV_CB *p_cb, BOOLEAN check_bond); |
| |
| #define BTA_HH_LE_SRVC_DEF 0 |
| |
| #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) |
| { |
| UINT8 i , j; |
| tBTA_HH_LE_RPT *p_rpt; |
| char * rpt_name; |
| |
| APPL_TRACE_DEBUG("HID Report DB"); |
| for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) |
| { |
| if (p_cb->hid_srvc[i].in_use) |
| { |
| p_rpt = &p_cb->hid_srvc[i].report[0]; |
| |
| APPL_TRACE_DEBUG("\t HID serivce inst: %d", i); |
| |
| for (j = 0; j < BTA_HH_LE_RPT_MAX; j ++, p_rpt++) |
| { |
| rpt_name = "Unknown"; |
| if (p_rpt->in_use) |
| { |
| 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] [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->inst_id, |
| p_rpt->client_cfg_value); |
| } |
| else |
| break; |
| } |
| } |
| else |
| break; |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_uuid_to_str |
| ** |
| ** Description |
| ** |
| ** Returns void |
| ** |
| *******************************************************************************/ |
| static char *bta_hh_uuid_to_str(UINT16 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) |
| { |
| char app_name[LEN_UUID_128 + 1]; |
| tBT_UUID app_uuid = {LEN_UUID_128,{0}}; |
| UINT8 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; |
| |
| memset (app_name, 0, LEN_UUID_128 + 1); |
| strncpy(app_name, "BTA HH OVER LE", LEN_UUID_128); |
| |
| memcpy((void *)app_uuid.uu.uuid128, (void *)app_name, LEN_UUID_128); |
| |
| BTA_GATTC_AppRegister(&app_uuid, bta_hh_gattc_callback); |
| |
| return; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_register_cmpl |
| ** |
| ** Description BTA HH register with BTA GATTC completed |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_register_cmpl(tBTA_GATTC_REG *p_reg) |
| { |
| tBTA_HH_STATUS status = BTA_HH_ERR; |
| |
| if (p_reg->status == BTA_GATT_OK) |
| { |
| bta_hh_cb.gatt_if = p_reg->client_if; |
| 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 |
| ** |
| *******************************************************************************/ |
| BOOLEAN 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: |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_is_le_device(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda) |
| { |
| p_cb->is_le_device = BTM_UseLeLink (remote_bda); |
| |
| return p_cb->is_le_device; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_hid_srvc_cached |
| ** |
| ** Description Check to see if LE HID service has been discovered and cached |
| ** |
| ** Parameters: TRUE : has cache; FALSE: none. |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_le_hid_srvc_cached(tBTA_HH_DEV_CB *p_dev_cb) |
| { |
| if (p_dev_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use) |
| return TRUE; |
| else |
| return FALSE; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_add_hid_srvc_entry |
| ** |
| ** Description Add a HID service entry in the HID device control block |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_le_add_hid_srvc_entry(tBTA_HH_DEV_CB *p_dev_cb, UINT8 idx) |
| { |
| BOOLEAN added = FALSE; |
| |
| if (idx < BTA_HH_LE_HID_SRVC_MAX) |
| { |
| p_dev_cb->hid_srvc[idx].in_use = TRUE; |
| added = TRUE; |
| } |
| else |
| { |
| APPL_TRACE_ERROR("DB full,max HID service entry!"); |
| } |
| return added; |
| |
| } |
| /******************************************************************************* |
| ** |
| ** 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, BD_ADDR remote_bda) |
| { |
| /* update cb_index[] map */ |
| p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index); |
| memcpy(p_cb->addr, remote_bda, BD_ADDR_LEN); |
| 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); |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_fill_16bits_gatt_id |
| ** |
| ** Description Utility function to fill a GATT ID strucure |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_fill_16bits_gatt_id(UINT8 inst_id, UINT16 uuid, tBTA_GATT_ID *p_output) |
| { |
| p_output->inst_id = inst_id; |
| p_output->uuid.len = LEN_UUID_16; |
| p_output->uuid.uu.uuid16 = uuid; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_fill_16bits_srvc_id |
| ** |
| ** Description Utility function to fill a service ID strucure with a 16 bits |
| ** service UUID. |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_fill_16bits_srvc_id(BOOLEAN is_pri, UINT8 inst_id, UINT16 srvc_uuid, |
| tBTA_GATT_SRVC_ID *p_output) |
| { |
| memset((void *)p_output, 0, sizeof(tBTA_GATT_SRVC_ID)); |
| p_output->is_primary = is_pri; |
| bta_hh_le_fill_16bits_gatt_id(inst_id, srvc_uuid, &p_output->id); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_fill_16bits_char_id |
| ** |
| ** Description Utility function to fill a char ID strucure with a 16 bits |
| ** char UUID. |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_fill_16bits_char_id(UINT8 inst_id, UINT16 char_uuid, |
| tBTA_GATT_ID *p_output) |
| { |
| memset((void *)p_output, 0, sizeof(tBTA_GATT_ID)); |
| bta_hh_le_fill_16bits_gatt_id(inst_id, char_uuid, p_output); |
| } |
| /******************************************************************************* |
| ** |
| ** 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 conn_id) |
| { |
| UINT8 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(BD_ADDR bda) |
| { |
| UINT8 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 && |
| memcmp(p_dev_cb->addr, bda, BD_ADDR_LEN) == 0) |
| return p_dev_cb; |
| } |
| return NULL; |
| } |
| UINT8 bta_hh_le_find_service_inst_by_battery_inst_id(tBTA_HH_DEV_CB *p_cb, UINT8 ba_inst_id) |
| { |
| UINT8 i; |
| |
| for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) |
| { |
| if (p_cb->hid_srvc[i].in_use && |
| p_cb->hid_srvc[i].incl_srvc_inst == ba_inst_id) |
| { |
| return i; |
| } |
| } |
| 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 srvc_inst_id, /* service instance ID */ |
| UINT16 rpt_uuid, |
| UINT8 char_inst_id) |
| { |
| UINT8 i; |
| UINT8 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[hid_inst_id].report[0]; |
| |
| for (i = 0; i < BTA_HH_LE_RPT_MAX; i ++, p_rpt ++) |
| { |
| if (p_rpt->uuid == rpt_uuid && |
| p_rpt->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_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 mode, |
| tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) |
| { |
| tBTA_HH_LE_RPT *p_rpt = p_head; |
| UINT8 i; |
| |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("bta_hh_le_find_rpt_by_idtype: r_tpye: %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 srvc_inst_id, |
| UINT16 rpt_uuid, |
| UINT8 inst_id, |
| UINT8 prop) |
| { |
| UINT8 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[hid_inst_id].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->inst_id == BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id))) |
| { |
| if (!p_rpt->in_use) |
| { |
| p_rpt->in_use = TRUE; |
| p_rpt->index = i; |
| p_rpt->inst_id = BTA_HH_LE_RPT_INST_ID_MAP(srvc_inst_id, inst_id); |
| p_rpt->prop = prop; |
| 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; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_read_char_dscrpt |
| ** |
| ** Description read cahracteristic descriptor |
| ** |
| *******************************************************************************/ |
| tBTA_HH_STATUS bta_hh_le_read_char_dscrpt(tBTA_HH_DEV_CB *p_cb, UINT16 srvc_uuid, UINT8 srvc_inst_id, |
| UINT16 char_uuid, UINT8 char_inst_id, UINT16 char_descp_uuid) |
| { |
| tBTA_GATTC_CHAR_ID char_id; |
| tBT_UUID descr_uuid; |
| tBTA_GATTC_CHAR_DESCR_ID descr_id; |
| tBTA_HH_STATUS status = BTA_HH_ERR; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid, &char_id.char_id); |
| |
| descr_uuid.len = LEN_UUID_16; |
| descr_uuid.uu.uuid16 = char_descp_uuid; |
| |
| /* find the report reference descriptor */ |
| if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, |
| &char_id, |
| &descr_uuid, |
| &descr_id) == BTA_GATT_OK) |
| { |
| BTA_GATTC_ReadCharDescr(p_cb->conn_id, |
| &descr_id, |
| BTA_GATT_AUTH_REQ_NONE); |
| |
| status = BTA_HH_OK; |
| } |
| else |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_ERROR("bta_hh_le_read_char_dscrpt: No such descrpt exists: %s(0x%04x)", |
| bta_hh_uuid_to_str(char_descp_uuid), char_descp_uuid); |
| #endif |
| } |
| return status; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_read_rpt_ref_descr |
| ** |
| ** Description read report refernece descriptors in service discovery process |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_read_rpt_ref_descr(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt) |
| { |
| BOOLEAN started = FALSE; |
| UINT16 srvc_uuid, char_uuid; |
| |
| while (p_rpt != NULL) |
| { |
| if(!p_rpt->in_use) |
| break; |
| |
| if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT) |
| { |
| /* is battery report */ |
| if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("read battery level report reference descriptor"); |
| #endif |
| srvc_uuid = UUID_SERVCLASS_BATTERY; |
| char_uuid = GATT_UUID_BATTERY_LEVEL; |
| } |
| else |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("read HID report reference descriptor"); |
| #endif |
| srvc_uuid = UUID_SERVCLASS_LE_HID; |
| char_uuid = GATT_UUID_HID_REPORT; |
| } |
| |
| if (bta_hh_le_read_char_dscrpt(p_dev_cb, |
| srvc_uuid, |
| BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), |
| char_uuid, |
| BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), |
| GATT_UUID_RPT_REF_DESCR) |
| == BTA_HH_OK) |
| { |
| started = TRUE; |
| break; |
| } |
| } |
| |
| if (p_rpt->index == BTA_HH_LE_RPT_MAX - 1) |
| break; |
| |
| p_rpt ++; |
| } |
| |
| |
| /* if no report reference descriptor */ |
| if (!started) |
| { |
| /* explore next char */ |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_save_rpt_ref |
| ** |
| ** Description save report reference information and move to next one. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_save_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_LE_RPT *p_rpt, |
| tBTA_GATTC_READ *p_data) |
| { |
| UINT8 *pp; |
| |
| /* if the length of the descriptor value is right, parse it */ |
| if (p_data->status == BTA_GATT_OK && |
| p_data->p_value && p_data->p_value->unformat.len == 2) |
| { |
| pp = p_data->p_value->unformat.p_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("report ID: %d", p_rpt->rpt_id); |
| #endif |
| } |
| |
| if (p_rpt->index < BTA_HH_LE_RPT_MAX - 1) |
| p_rpt ++; |
| else |
| p_rpt = NULL; |
| /* read next report reference descriptor */ |
| bta_hh_le_read_rpt_ref_descr(p_dev_cb, p_rpt); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_save_rpt_ref |
| ** |
| ** Description save report reference information and move to next one. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_save_ext_rpt_ref(tBTA_HH_DEV_CB *p_dev_cb, |
| tBTA_GATTC_READ *p_data) |
| { |
| UINT8 *pp; |
| |
| /* if the length of the descriptor value is right, parse it |
| assume it's a 16 bits UUID */ |
| if (p_data->status == BTA_GATT_OK && |
| p_data->p_value && p_data->p_value->unformat.len == 2) |
| { |
| pp = p_data->p_value->unformat.p_value; |
| STREAM_TO_UINT16(p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref, pp); |
| |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("External Report Reference UUID 0x%04x", |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].ext_rpt_ref); |
| #endif |
| } |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** 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 srvc_inst, |
| UINT8 proto_mode, BOOLEAN register_ba) |
| { |
| tBTA_HH_LE_RPT *p_rpt = &p_dev_cb->hid_srvc[srvc_inst].report[0]; |
| tBTA_GATTC_CHAR_ID char_id; |
| UINT8 i; |
| UINT16 srvc_uuid; |
| |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("bta_hh_le_register_input_notif mode: %d", proto_mode); |
| #endif |
| |
| for (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_BATTERY_LEVEL) |
| srvc_uuid = UUID_SERVCLASS_BATTERY; |
| else |
| srvc_uuid = UUID_SERVCLASS_LE_HID; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), srvc_uuid, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), p_rpt->uuid, &char_id.char_id); |
| |
| if (register_ba && p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) |
| { |
| BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_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("---> Deregister Report ID: %d", p_rpt->rpt_id); |
| BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_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("<--- Register Boot Report ID: %d", p_rpt->rpt_id); |
| BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_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("---> Deregister Boot Report ID: %d", p_rpt->rpt_id); |
| BTA_GATTC_DeregisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_id); |
| } |
| else if (p_rpt->uuid == GATT_UUID_HID_REPORT && |
| p_rpt->client_cfg_value == BTA_GATT_CLT_CONFIG_NOTIFICATION) |
| { |
| APPL_TRACE_DEBUG("<--- Register Report ID: %d", p_rpt->rpt_id); |
| BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_id); |
| } |
| } |
| /* |
| else unknow protocol mode */ |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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 |
| bta_hh_le_hid_report_dbg(p_cb); |
| #endif |
| bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE); |
| bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL); |
| #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_char_clt_cfg |
| ** |
| ** Description Utility function to find and write client configuration of |
| ** a characteristic |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_le_write_char_clt_cfg(tBTA_HH_DEV_CB *p_cb, |
| UINT8 srvc_inst_id, UINT16 srvc_uuid16, |
| UINT8 char_inst_id, UINT16 char_uuid16, |
| UINT16 clt_cfg_value) |
| { |
| tBTA_GATTC_CHAR_ID char_id; |
| tBT_UUID descr_cond; |
| tBTA_GATTC_CHAR_DESCR_ID descr_id; |
| tBTA_GATT_UNFMT value; |
| UINT8 buf[2], *pp = buf; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst_id, srvc_uuid16, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(char_inst_id, char_uuid16, &char_id.char_id); |
| |
| descr_cond.len = LEN_UUID_16; |
| descr_cond.uu.uuid16 = GATT_UUID_CHAR_CLIENT_CONFIG; |
| |
| value.len = 2; |
| value.p_value = buf; |
| |
| UINT16_TO_STREAM(pp, clt_cfg_value); |
| |
| if (BTA_GATTC_GetFirstCharDescr(p_cb->conn_id, |
| &char_id, |
| &descr_cond, |
| &descr_id) == BTA_GATT_OK) |
| { |
| BTA_GATTC_WriteCharDescr(p_cb->conn_id, |
| &descr_id, |
| BTA_GATTC_TYPE_WRITE_NO_RSP, |
| &value, |
| BTA_GATT_AUTH_REQ_NONE); |
| |
| return TRUE; |
| } |
| return FALSE; |
| } |
| /******************************************************************************* |
| ** |
| ** 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. |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst_id) |
| { |
| UINT8 i; |
| tBTA_HH_LE_RPT *p_rpt = &p_cb->hid_srvc[srvc_inst_id].report[p_cb->clt_cfg_idx]; |
| UINT16 srvc_uuid; |
| |
| 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 (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) |
| srvc_uuid = UUID_SERVCLASS_BATTERY; |
| else |
| srvc_uuid = UUID_SERVCLASS_LE_HID; |
| |
| if (bta_hh_le_write_char_clt_cfg(p_cb, |
| BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id), |
| srvc_uuid, |
| BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id), |
| p_rpt->uuid, |
| BTA_GATT_CLT_CONFIG_NOTIFICATION)) |
| { |
| 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; |
| |
| /* discover scan parameter profile is act as report host */ |
| bta_hh_le_search_scps(p_cb); |
| } |
| return FALSE; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_set_protocol_mode |
| ** |
| ** Description Set remote device protocol mode. |
| ** |
| *******************************************************************************/ |
| BOOLEAN bta_hh_le_set_protocol_mode(tBTA_HH_DEV_CB *p_cb, tBTA_HH_PROTO_MODE mode) |
| { |
| tBTA_GATTC_CHAR_ID char_id; |
| tBTA_HH_CBDATA cback_data ; |
| BOOLEAN exec = TRUE; |
| |
| APPL_TRACE_DEBUG("bta_hh_le_set_protocol_mode attempt mode: %s", |
| (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[BTA_HH_LE_SRVC_DEF].option_char & BTA_HH_LE_PROTO_MODE_BIT) == 0) |
| { |
| exec = FALSE; |
| 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, 0, 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 |
| { |
| bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); |
| |
| p_cb->mode = mode; |
| mode = (mode == BTA_HH_PROTO_BOOT_MODE)? BTA_HH_LE_PROTO_BOOT_MODE : BTA_HH_LE_PROTO_REPORT_MODE; |
| |
| BTA_GATTC_WriteCharValue(p_cb->conn_id, |
| &char_id, |
| BTA_GATTC_TYPE_WRITE_NO_RSP, |
| 1, |
| &mode, |
| BTA_GATT_AUTH_REQ_NONE); |
| } |
| return exec; |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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_GATTC_CHAR_ID char_id; |
| tBTA_HH_HSDATA hs_data; |
| UINT8 i; |
| |
| p_cb->w4_evt = BTA_HH_GET_PROTO_EVT; |
| |
| for (i = 0; i< BTA_HH_LE_HID_SRVC_MAX; i ++) |
| { |
| if (p_cb->hid_srvc[i].in_use && |
| p_cb->hid_srvc[i].option_char & BTA_HH_LE_PROTO_MODE_BIT) |
| { |
| bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_PROTO_MODE, &char_id.char_id); |
| |
| BTA_GATTC_ReadCharacteristic(p_cb->conn_id, |
| &char_id, |
| BTA_GATT_AUTH_REQ_NONE); |
| break; |
| } |
| } |
| /* no service support protocol_mode, by default report mode */ |
| if (i == BTA_HH_LE_HID_SRVC_MAX) |
| { |
| 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_expl_rpt |
| ** |
| ** Description explore all report characteristic |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_expl_rpt(tBTA_HH_DEV_CB *p_dev_cb, |
| tBTA_GATTC_CHAR_ID *p_char_id, |
| tBT_UUID *p_char_cond, |
| tBTA_GATT_CHAR_PROP prop) |
| { |
| tBTA_GATTC_CHAR_ID char_result; |
| |
| do |
| { |
| if (bta_hh_le_find_alloc_report_entry(p_dev_cb, |
| p_dev_cb->cur_srvc_index, |
| GATT_UUID_HID_REPORT, |
| p_char_id->char_id.inst_id, |
| prop) == NULL) |
| { |
| APPL_TRACE_ERROR("Add report entry failed !!!"); |
| break; |
| } |
| |
| APPL_TRACE_DEBUG("Find more REPORT"); |
| |
| if (BTA_GATTC_GetNextChar(p_dev_cb->conn_id, |
| p_char_id, |
| p_char_cond, |
| &char_result, |
| &prop) != BTA_GATT_OK) |
| break; |
| |
| p_char_id = &char_result; |
| } |
| while (1); |
| |
| APPL_TRACE_ERROR("all report searched"); |
| bta_hh_le_read_rpt_ref_descr(p_dev_cb, |
| &p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].report[0]); |
| |
| |
| return ; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_expl_boot_rpt |
| ** |
| ** Description explore boot report |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_expl_boot_rpt(tBTA_HH_DEV_CB *p_dev_cb, UINT16 char_uuid, |
| tBTA_GATT_CHAR_PROP prop) |
| { |
| if (bta_hh_le_find_alloc_report_entry(p_dev_cb, |
| p_dev_cb->cur_srvc_index, |
| char_uuid, |
| 0, |
| prop) == NULL) |
| |
| { |
| APPL_TRACE_ERROR("Add report entry failed !!!"); |
| } |
| |
| return; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_dis_cback |
| ** |
| ** Description DIS read complete callback |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_dis_cback(BD_ADDR 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; |
| |
| 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)) |
| { |
| 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(BD_ADDR bd_addr, tBTA_GATT_TRANSPORT transport, |
| void *p_ref_data, tBTM_STATUS result) |
| { |
| UINT8 idx = bta_hh_find_cb(bd_addr); |
| tBTA_HH_DEV_CB *p_dev_cb; |
| UNUSED(p_ref_data); |
| UNUSED (transport); |
| |
| APPL_TRACE_ERROR("bta_hh_le_encrypt_cback"); |
| |
| 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; |
| |
| 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, tBTA_HH_DATA *p_buf) |
| { |
| UNUSED(p_buf); |
| |
| if (p_cb->status == BTA_HH_OK) |
| { |
| /* discovery has been done for HID service */ |
| if (p_cb->app_id != 0 && bta_hh_le_hid_srvc_cached(p_cb)) |
| { |
| /* configure protocol mode */ |
| if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == FALSE) |
| { |
| APPL_TRACE_ERROR("bta_hh_security_cmpl"); |
| bta_hh_le_open_cmpl(p_cb); |
| } |
| } |
| /* start primary service discovery for HID service */ |
| else |
| { |
| bta_hh_le_pri_service_discovery(p_cb); |
| } |
| } |
| else |
| bta_hh_le_api_disc_act(p_cb); |
| |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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_start_security |
| ** |
| ** Description start the security check of the established connection |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf) |
| { |
| UINT8 sec_flag=0; |
| tBTM_SEC_DEV_REC *p_dev_rec; |
| UNUSED(p_buf); |
| |
| 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, &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; |
| BTM_SetEncryption(p_cb->addr, BTA_TRANSPORT_LE, bta_hh_le_encrypt_cback, &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 *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; |
| |
| 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->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; |
| |
| #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); |
| tBTA_HH_LE_CLOSE *p_buf = NULL; |
| UINT16 sm_event = BTA_HH_GATT_CLOSE_EVT; |
| |
| if (p_dev_cb != NULL && |
| (p_buf = (tBTA_HH_LE_CLOSE *)GKI_getbuf(sizeof(tBTA_HH_LE_CLOSE))) != NULL) |
| { |
| p_buf->hdr.event = sm_event; |
| p_buf->hdr.layer_specific = (UINT16)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_search_result |
| ** |
| ** Description This function process the GATT service search result. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_search_result(tBTA_GATTC_SRVC_RES *p_srvc_result) |
| { |
| tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_srvc_result->conn_id); |
| |
| if (p_dev_cb != NULL) |
| { |
| switch (p_srvc_result->service_uuid.id.uuid.uu.uuid16) |
| { |
| case UUID_SERVCLASS_LE_HID: |
| if (p_srvc_result->service_uuid.is_primary) |
| { |
| /* found HID primamry service */ |
| /* TODO: proceed to find battery and device info */ |
| if (bta_hh_le_add_hid_srvc_entry(p_dev_cb, p_dev_cb->total_srvc)) |
| p_dev_cb->total_srvc ++; |
| APPL_TRACE_DEBUG("num of hid service: %d", p_dev_cb->total_srvc); |
| } |
| break; |
| |
| case UUID_SERVCLASS_SCAN_PARAM : /* scan parameter service */ |
| bta_hh_le_search_scps_chars(p_dev_cb); |
| break; |
| } |
| |
| } |
| |
| } |
| |
| |
| /******************************************************************************* |
| ** |
| ** 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, BTA_HH_LE_SRVC_DEF); |
| } |
| else /* error, close the GATT connection */ |
| { |
| /* close GATT connection if it's on */ |
| bta_hh_le_api_disc_act(p_cb); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_srvc_expl_srvc |
| ** |
| ** Description This function discover the next avaible HID service. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb) |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("bta_hh_le_srvc_expl_srvc cur_srvc_index = %d in_use = %d", |
| p_dev_cb->cur_srvc_index, |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use); |
| #endif |
| |
| if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX && |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use) |
| { |
| if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc) |
| /* explore included service first */ |
| { |
| bta_hh_le_search_hid_included(p_dev_cb); |
| } |
| else |
| { |
| /* explore characterisc */ |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| } |
| else /* all service discvery finished */ |
| { |
| bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status); |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** 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->total_srvc == 0) |
| { |
| 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); |
| } |
| /* GATT service discovery sucessfully finished */ |
| else |
| { |
| if (p_dev_cb->disc_active & BTA_HH_LE_DISC_SCPS) |
| { |
| p_dev_cb->disc_active &= ~BTA_HH_LE_DISC_SCPS; |
| bta_hh_le_open_cmpl(p_dev_cb); |
| } |
| else /* discover HID service */ |
| { |
| p_dev_cb->cur_srvc_index = 0; |
| bta_hh_le_srvc_expl_srvc(p_dev_cb); |
| } |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_search_hid_included |
| ** |
| ** Description This function search the included service within the HID service. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| static void bta_hh_le_search_hid_included(tBTA_HH_DEV_CB *p_dev_cb) |
| { |
| tBT_UUID srvc_cond, char_cond; |
| tBTA_GATTC_INCL_SVC_ID inc_srvc_result; |
| tBTA_GATT_SRVC_ID srvc_id; |
| tBTA_GATTC_CHAR_ID char_result; |
| tBTA_GATT_CHAR_PROP prop = 0; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); |
| |
| srvc_cond.len = LEN_UUID_16; |
| srvc_cond.uu.uuid16 = UUID_SERVCLASS_BATTERY; |
| |
| if (BTA_GATTC_GetFirstIncludedService(p_dev_cb->conn_id, |
| &srvc_id, |
| &srvc_cond, |
| &inc_srvc_result) == BTA_GATT_OK) |
| { |
| /* read include service UUID */ |
| |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].incl_srvc_inst = inc_srvc_result.incl_svc_id.id.inst_id; |
| |
| char_cond.len = LEN_UUID_16; |
| char_cond.uu.uuid16 = GATT_UUID_BATTERY_LEVEL; |
| |
| /* find the battery characteristic */ |
| if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, |
| &inc_srvc_result.incl_svc_id, |
| &char_cond, |
| &char_result, |
| &prop) == BTA_GATT_OK) |
| { |
| if (bta_hh_le_find_alloc_report_entry(p_dev_cb, |
| char_result.srvc_id.id.inst_id, |
| GATT_UUID_BATTERY_LEVEL, |
| char_result.char_id.inst_id, |
| prop) == NULL) |
| { |
| APPL_TRACE_ERROR("Add battery report entry failed !!!") |
| } |
| |
| /* read the battery characteristic */ |
| BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, |
| &char_result, |
| BTA_GATT_AUTH_REQ_NONE); |
| |
| return; |
| |
| } |
| else |
| { |
| APPL_TRACE_ERROR("Remote device does not have battery level"); |
| } |
| } |
| |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; |
| |
| bta_hh_le_srvc_expl_srvc(p_dev_cb); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_read_battery_level_cmpl |
| ** |
| ** Description This function process the battery level read |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_read_battery_level_cmpl(UINT8 status, tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) |
| { |
| UNUSED(status); |
| UNUSED(p_data); |
| |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc = TRUE; |
| bta_hh_le_srvc_expl_srvc(p_dev_cb); |
| } |
| /******************************************************************************* |
| ** |
| ** 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) |
| { |
| tBT_UUID char_cond; |
| tBTA_GATTC_CHAR_ID char_result; |
| tBTA_GATT_CHAR_PROP prop; |
| BOOLEAN next = TRUE; |
| UINT16 char_uuid = 0; |
| tBTA_GATT_SRVC_ID srvc_id; |
| |
| if (p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx == BTA_HH_LE_DISC_CHAR_NUM || |
| (p_dev_cb->status != BTA_HH_OK && p_dev_cb->status != BTA_HH_ERR_PROTO)) |
| { |
| p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0; |
| /* explore next service */ |
| p_dev_cb->cur_srvc_index ++; |
| bta_hh_le_srvc_expl_srvc(p_dev_cb); |
| return; |
| } |
| |
| p_dev_cb->hid_srvc[ p_dev_cb->cur_srvc_index].cur_expl_char_idx ++; |
| char_uuid = bta_hh_le_disc_char_uuid[p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx - 1]; |
| |
| char_cond.len = LEN_UUID_16; |
| char_cond.uu.uuid16 = char_uuid; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id); |
| |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("bta_hh_le_search_hid_chars: looking for %s(0x%04x)", |
| bta_hh_uuid_to_str(char_uuid), char_uuid); |
| #endif |
| |
| if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id, |
| &srvc_id, |
| &char_cond, |
| &char_result, |
| &prop) == BTA_GATT_OK) |
| { |
| switch (char_uuid) |
| { |
| case GATT_UUID_HID_CONTROL_POINT: |
| p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_CP_BIT; |
| next = TRUE; |
| break; |
| case GATT_UUID_HID_INFORMATION: |
| case GATT_UUID_HID_REPORT_MAP: |
| /* read the char value */ |
| BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id, |
| &char_result, |
| BTA_GATT_AUTH_REQ_NONE); |
| next = FALSE; |
| break; |
| |
| case GATT_UUID_HID_PROTO_MODE: |
| p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_PROTO_MODE_BIT; |
| next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); |
| break; |
| |
| case GATT_UUID_HID_REPORT: |
| bta_hh_le_expl_rpt(p_dev_cb, &char_result, &char_cond, prop); |
| next = FALSE; |
| 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: |
| bta_hh_le_expl_boot_rpt(p_dev_cb, char_uuid, prop); |
| break; |
| } |
| } |
| else |
| { |
| if (char_uuid == GATT_UUID_HID_PROTO_MODE) |
| next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode); |
| |
| } |
| |
| if (next == TRUE) |
| { |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_save_rpt_map |
| ** |
| ** Description save the report map into the control block. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_save_rpt_map(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) |
| { |
| UINT8 *pp ; |
| tBTA_HH_LE_HID_SRVC *p_srvc = &p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id]; |
| |
| pp = p_data->p_value->unformat.p_value; |
| |
| /* save report descriptor */ |
| if (p_srvc->rpt_map != NULL) |
| GKI_freebuf((void*)p_srvc->rpt_map); |
| |
| if (p_data->p_value->unformat.len > 0) |
| p_srvc->rpt_map = (UINT8 *)GKI_getbuf(p_data->p_value->unformat.len); |
| |
| if (p_srvc->rpt_map != NULL) |
| { |
| STREAM_TO_ARRAY(p_srvc->rpt_map, pp, p_data->p_value->unformat.len); |
| p_srvc->descriptor.dl_len = p_data->p_value->unformat.len; |
| p_srvc->descriptor.dsc_list = p_dev_cb->hid_srvc[p_data->srvc_id.id.inst_id].rpt_map; |
| } |
| |
| if (bta_hh_le_read_char_dscrpt(p_dev_cb, |
| UUID_SERVCLASS_LE_HID, |
| p_data->srvc_id.id.inst_id, |
| GATT_UUID_HID_REPORT_MAP, |
| p_data->char_id.inst_id, |
| GATT_UUID_EXT_RPT_REF_DESCR) != BTA_HH_OK) |
| { |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_proc_get_rpt_cmpl |
| ** |
| ** Description Process the Read report complete, send GET_REPORT_EVT to application |
| ** with the report data. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_proc_get_rpt_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_data) |
| { |
| BT_HDR *p_buf = NULL; |
| tBTA_HH_LE_RPT *p_rpt; |
| tBTA_HH_HSDATA hs_data; |
| UINT8 *pp ; |
| |
| 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; |
| } |
| |
| hs_data.status = BTA_HH_ERR; |
| hs_data.handle = p_dev_cb->hid_handle; |
| |
| if (p_data->status == BTA_GATT_OK) |
| { |
| p_rpt = bta_hh_le_find_report_entry(p_dev_cb, |
| p_data->srvc_id.id.inst_id,//BTA_HH_LE_SRVC_DEF, |
| p_data->char_id.uuid.uu.uuid16, |
| p_data->char_id.inst_id); |
| |
| if (p_rpt != NULL && |
| p_data->p_value != NULL && |
| (p_buf = (BT_HDR *)GKI_getbuf((UINT16)(sizeof(BT_HDR) +p_data->p_value->unformat.len + 1))) != NULL) |
| { |
| /* pack data send to app */ |
| hs_data.status = BTA_HH_OK; |
| p_buf->len = p_data->p_value->unformat.len + 1; |
| p_buf->layer_specific = 0; |
| |
| /* attach report ID as the first byte of the report before sending it to USB HID driver */ |
| pp = (UINT8*)(p_buf + 1); |
| UINT8_TO_STREAM(pp, p_rpt->rpt_id); |
| memcpy(pp, p_data->p_value->unformat.p_value, p_data->p_value->unformat.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); |
| |
| utl_freebuf((void **)&p_buf); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_proc_read_proto_mode |
| ** |
| ** Description Process the Read protocol mode, send GET_PROTO_EVT to application |
| ** with the protocol mode. |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_proc_read_proto_mode(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ *p_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 (p_data->status == BTA_GATT_OK && p_data->p_value) |
| { |
| hs_data.status = BTA_HH_OK; |
| /* match up BTE/BTA report/boot mode def*/ |
| hs_data.rsp_data.proto_mode = *(p_data->p_value->unformat.p_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 |
| 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_w4_le_read_char_cmpl |
| ** |
| ** Description process the GATT read complete in W4_CONN state. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_w4_le_read_char_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; |
| UINT8 *pp ; |
| |
| if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_BATTERY_LEVEL) |
| { |
| bta_hh_read_battery_level_cmpl(p_data->status, p_dev_cb, p_data); |
| } |
| else |
| { |
| if (p_data->status == BTA_GATT_OK && p_data->p_value) |
| { |
| pp = p_data->p_value->unformat.p_value; |
| |
| switch (p_data->char_id.uuid.uu.uuid16) |
| { |
| /* save device information */ |
| case GATT_UUID_HID_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); |
| break; |
| |
| case GATT_UUID_HID_REPORT_MAP: |
| bta_hh_le_save_rpt_map(p_dev_cb, p_data); |
| return; |
| |
| default: |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_ERROR("Unexpected read %s(0x%04x)", |
| bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), |
| p_data->char_id.uuid.uu.uuid16); |
| #endif |
| break; |
| } |
| } |
| else |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_ERROR("read uuid %s[0x%04x] error: %d", |
| bta_hh_uuid_to_str(p_data->char_id.uuid.uu.uuid16), |
| p_data->char_id.uuid.uu.uuid16, |
| p_data->status); |
| #else |
| APPL_TRACE_ERROR("read uuid [0x%04x] error: %d", p_data->char_id.uuid.uu.uuid16, p_data->status); |
| #endif |
| } |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_read_char_cmpl |
| ** |
| ** Description a characteristic value is received. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_read_char_cmpl (tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; |
| |
| switch (p_data->char_id.uuid.uu.uuid16) |
| { |
| /* GET_REPORT */ |
| case GATT_UUID_HID_REPORT: |
| case GATT_UUID_HID_BT_KB_INPUT: |
| case GATT_UUID_HID_BT_KB_OUTPUT: |
| case GATT_UUID_HID_BT_MOUSE_INPUT: |
| case GATT_UUID_BATTERY_LEVEL: /* read battery level */ |
| bta_hh_le_proc_get_rpt_cmpl(p_dev_cb, p_data); |
| break; |
| |
| case GATT_UUID_HID_PROTO_MODE: |
| bta_hh_le_proc_read_proto_mode(p_dev_cb, p_data); |
| break; |
| |
| default: |
| APPL_TRACE_ERROR("Unexpected Read UUID: 0x%04x", p_data->char_id.uuid.uu.uuid16); |
| break; |
| } |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_read_descr_cmpl |
| ** |
| ** Description read characteristic descriptor is completed in CONN st. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_HH_LE_RPT *p_rpt; |
| tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; |
| UINT8 *pp; |
| |
| /* if a report client configuration */ |
| if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) |
| { |
| if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, |
| BTA_HH_LE_SRVC_DEF, |
| p_data->char_id.uuid.uu.uuid16, |
| p_data->char_id.inst_id)) != NULL) |
| { |
| pp = p_data->p_value->unformat.p_value; |
| STREAM_TO_UINT16(p_rpt->client_cfg_value, pp); |
| |
| APPL_TRACE_DEBUG("Read Client Configuration: 0x%04x", p_rpt->client_cfg_value); |
| } |
| } |
| } |
| |
| void bta_hh_le_read_battery_level_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATTC_READ * p_data) |
| { |
| tBTA_HH_LE_RPT *p_rpt; |
| UINT16 descr_uuid = p_data->descr_type.uuid.uu.uuid16; |
| |
| /* read report reference descriptor for battery level is completed */ |
| if (descr_uuid == GATT_UUID_RPT_REF_DESCR) |
| { |
| if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, |
| p_data->srvc_id.id.inst_id, |
| GATT_UUID_BATTERY_LEVEL, |
| p_data->char_id.inst_id)) == NULL) |
| { |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| else |
| bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); |
| |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_w4_le_read_descr_cmpl |
| ** |
| ** Description read characteristic descriptor is completed in W4_CONN st. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_w4_le_read_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_HH_LE_RPT *p_rpt; |
| tBTA_GATTC_READ * p_data = (tBTA_GATTC_READ *)p_buf; |
| UINT16 char_uuid16; |
| |
| if (p_data == NULL) |
| return; |
| |
| char_uuid16 = p_data->char_id.uuid.uu.uuid16; |
| |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_DEBUG("bta_hh_w4_le_read_descr_cmpl uuid: %s(0x%04x)", |
| bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), |
| p_data->descr_type.uuid.uu.uuid16); |
| #endif |
| switch (char_uuid16) |
| { |
| case GATT_UUID_HID_REPORT: |
| if ((p_rpt = bta_hh_le_find_report_entry(p_dev_cb, |
| p_data->srvc_id.id.inst_id, |
| GATT_UUID_HID_REPORT, |
| p_data->char_id.inst_id)) == NULL) |
| { |
| bta_hh_le_search_hid_chars(p_dev_cb); |
| } |
| else |
| bta_hh_le_save_rpt_ref(p_dev_cb, p_rpt, p_data); |
| break; |
| |
| case GATT_UUID_HID_REPORT_MAP: |
| bta_hh_le_save_ext_rpt_ref(p_dev_cb, p_data); |
| break; |
| |
| case GATT_UUID_BATTERY_LEVEL: |
| bta_hh_le_read_battery_level_descr_cmpl(p_dev_cb, p_data); |
| break; |
| |
| default: |
| APPL_TRACE_ERROR("unknown descriptor read complete for uuid: 0x%04x", char_uuid16); |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_w4_le_write_cmpl |
| ** |
| ** Description Write charactersitic complete event at W4_CONN st. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_w4_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; |
| |
| if (p_data == NULL) |
| return; |
| |
| if (p_data->char_id.uuid.uu.uuid16 == GATT_UUID_HID_PROTO_MODE) |
| { |
| p_dev_cb->status = (p_data->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_search_hid_chars(p_dev_cb); |
| } |
| else |
| { |
| bta_hh_le_open_cmpl(p_dev_cb); |
| } |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_write_cmpl |
| ** |
| ** Description Write charactersitic complete event at CONN st. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_write_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; |
| tBTA_HH_CBDATA cback_data ; |
| UINT16 cb_evt = p_dev_cb->w4_evt; |
| |
| if (p_data == NULL || cb_evt == 0) |
| return; |
| |
| #if BTA_HH_DEBUG |
| APPL_TRACE_DEBUG("bta_hh_le_write_cmpl w4_evt: %d", p_dev_cb->w4_evt); |
| #endif |
| switch (p_data->char_id.uuid.uu.uuid16) |
| { |
| /* Set protocol finished */ |
| case GATT_UUID_HID_PROTO_MODE: |
| cback_data.handle = p_dev_cb->hid_handle; |
| if (p_data->status == BTA_GATT_OK) |
| { |
| bta_hh_le_register_input_notif(p_dev_cb, p_data->srvc_id.id.inst_id, p_dev_cb->mode, FALSE); |
| cback_data.status = BTA_HH_OK; |
| } |
| else |
| cback_data.status = BTA_HH_ERR; |
| p_dev_cb->w4_evt = 0; |
| (* bta_hh_cb.p_cback)(cb_evt, (tBTA_HH *)&cback_data); |
| break; |
| |
| /* Set Report finished */ |
| case GATT_UUID_HID_REPORT: |
| case GATT_UUID_HID_BT_KB_INPUT: |
| case GATT_UUID_HID_BT_MOUSE_INPUT: |
| case GATT_UUID_HID_BT_KB_OUTPUT: |
| cback_data.handle = p_dev_cb->hid_handle; |
| cback_data.status = (p_data->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); |
| break; |
| |
| case GATT_UUID_SCAN_INT_WINDOW: |
| bta_hh_le_register_scpp_notif(p_dev_cb, p_data->status); |
| break; |
| |
| |
| default: |
| break; |
| } |
| |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_write_char_descr_cmpl |
| ** |
| ** Description Write charactersitic descriptor complete event |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_WRITE *p_data = (tBTA_GATTC_WRITE *)p_buf; |
| UINT8 srvc_inst_id, hid_inst_id; |
| |
| /* only write client configuration possible */ |
| if (p_data && p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG) |
| { |
| srvc_inst_id = p_data->srvc_id.id.inst_id; |
| hid_inst_id = srvc_inst_id; |
| switch (p_data->char_id.uuid.uu.uuid16) |
| { |
| 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); |
| /* fall through */ |
| case GATT_UUID_HID_BT_KB_INPUT: |
| case GATT_UUID_HID_BT_MOUSE_INPUT: |
| case GATT_UUID_HID_REPORT: |
| if (p_data->status == BTA_GATT_OK) |
| p_dev_cb->hid_srvc[hid_inst_id].report[p_dev_cb->clt_cfg_idx ++].client_cfg_value = |
| BTA_GATT_CLT_CONFIG_NOTIFICATION; |
| |
| bta_hh_le_write_rpt_clt_cfg(p_dev_cb, hid_inst_id); |
| |
| break; |
| |
| case GATT_UUID_SCAN_REFRESH: |
| bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, p_data->status); |
| break; |
| default: |
| APPL_TRACE_ERROR("Unknown char ID clt cfg: 0x%04x", p_data->char_id.uuid.uu.uuid16); |
| } |
| } |
| else |
| { |
| #if BTA_HH_DEBUG == TRUE |
| APPL_TRACE_ERROR("Unexpected write to %s(0x%04x)", |
| bta_hh_uuid_to_str(p_data->descr_type.uuid.uu.uuid16), |
| p_data->descr_type.uuid.uu.uuid16); |
| #else |
| APPL_TRACE_ERROR("Unexpected write to (0x%04x)", p_data->descr_type.uuid.uu.uuid16); |
| #endif |
| } |
| |
| } |
| /******************************************************************************* |
| ** |
| ** 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 app_id; |
| UINT8 *p_buf; |
| tBTA_HH_LE_RPT *p_rpt; |
| |
| if (p_dev_cb == NULL) |
| { |
| APPL_TRACE_ERROR("notification received from Unknown device"); |
| return; |
| } |
| |
| app_id= p_dev_cb->app_id; |
| |
| p_rpt = bta_hh_le_find_report_entry(p_dev_cb, |
| BTA_HH_LE_SRVC_DEF, |
| p_data->char_id.char_id.uuid.uu.uuid16, |
| p_data->char_id.char_id.inst_id); |
| if (p_rpt == NULL) |
| { |
| APPL_TRACE_ERROR("notification received for Unknown Report"); |
| return; |
| } |
| |
| if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_MOUSE_INPUT) |
| app_id = BTA_HH_APP_ID_MI; |
| else if (p_data->char_id.char_id.uuid.uu.uuid16 == GATT_UUID_HID_BT_KB_INPUT) |
| app_id = BTA_HH_APP_ID_KB; |
| |
| APPL_TRACE_ERROR("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) |
| { |
| if ((p_buf = (UINT8 *)GKI_getbuf((UINT16)(p_data->len + 1))) == NULL) |
| { |
| APPL_TRACE_ERROR("No resources to send report data"); |
| return; |
| } |
| |
| 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)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) |
| GKI_freebuf(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 ; |
| tBTA_HH_LE_HID_SRVC *p_hid_srvc = &p_cb->hid_srvc[0]; |
| UINT8 i; |
| |
| p_cb->disc_active = BTA_HH_LE_DISC_NONE; |
| /* Failure in opening connection or GATT discovery failure */ |
| conn_dat.handle = p_cb->hid_handle; |
| memcpy(conn_dat.bda, p_cb->addr, BD_ADDR_LEN); |
| 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; |
| |
| for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++, p_hid_srvc ++) |
| { |
| utl_freebuf((void **)&p_hid_srvc->rpt_map); |
| memset(p_hid_srvc, 0, sizeof(tBTA_HH_LE_HID_SRVC)); |
| } |
| |
| /* 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}; |
| |
| /* 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) |
| BTA_GATTC_Close(p_cb->conn_id); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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, UINT8 srvc_inst, tBTA_HH_RPT_TYPE r_type, UINT8 rpt_id) |
| { |
| tBTA_HH_LE_RPT *p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); |
| tBTA_GATTC_CHAR_ID char_id; |
| UINT16 srvc_uuid = UUID_SERVCLASS_LE_HID; |
| |
| if (p_rpt == NULL) |
| { |
| APPL_TRACE_ERROR("bta_hh_le_get_rpt: no matching report"); |
| return; |
| } |
| if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL) |
| srvc_uuid = UUID_SERVCLASS_BATTERY; |
| |
| p_cb->w4_evt = BTA_HH_GET_RPT_EVT; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, srvc_uuid, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); |
| |
| BTA_GATTC_ReadCharacteristic(p_cb->conn_id, |
| &char_id, |
| BTA_GATT_AUTH_REQ_NONE); |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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, UINT8 srvc_inst, |
| tBTA_GATTC_WRITE_TYPE write_type, |
| tBTA_HH_RPT_TYPE r_type, |
| BT_HDR *p_buf, UINT16 w4_evt ) |
| { |
| tBTA_HH_LE_RPT *p_rpt; |
| tBTA_GATTC_CHAR_ID char_id; |
| UINT8 *p_value, rpt_id; |
| |
| if (p_buf == NULL || p_buf->len == 0) |
| { |
| APPL_TRACE_ERROR("bta_hh_le_write_rpt: Illegal data"); |
| return; |
| } |
| |
| /* strip report ID from the data */ |
| p_value = (UINT8 *)(p_buf + 1) + p_buf->offset; |
| STREAM_TO_UINT8(rpt_id, p_value); |
| p_buf->len -= 1; |
| |
| p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[srvc_inst].report, p_cb->mode, r_type, rpt_id); |
| |
| if (p_rpt == NULL) |
| { |
| APPL_TRACE_ERROR("bta_hh_le_write_rpt: no matching report"); |
| return; |
| } |
| |
| APPL_TRACE_ERROR("bta_hh_le_write_rpt: ReportID: 0x%02x Data Len: %d", rpt_id, p_buf->len); |
| |
| p_cb->w4_evt = w4_evt; |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, srvc_inst, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(p_rpt->inst_id, p_rpt->uuid, &char_id.char_id); |
| |
| BTA_GATTC_WriteCharValue(p_cb->conn_id, |
| &char_id, |
| write_type, /* default to use write request */ |
| p_buf->len, |
| p_value, |
| BTA_GATT_AUTH_REQ_NONE); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** 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) |
| { |
| UINT8 i; |
| tBTA_GATTC_CHAR_ID char_id; |
| |
| ctrl_type -= BTA_HH_CTRL_SUSPEND; |
| |
| for (i = 0; i < BTA_HH_LE_HID_SRVC_MAX; i ++) |
| { |
| bta_hh_le_fill_16bits_srvc_id(TRUE, i, UUID_SERVCLASS_LE_HID, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(0, GATT_UUID_HID_CONTROL_POINT, &char_id.char_id); |
| |
| BTA_GATTC_WriteCharValue(p_cb->conn_id, |
| &char_id, |
| BTA_GATTC_TYPE_WRITE_NO_RSP, /* default to use write request */ |
| 1, |
| &ctrl_type, |
| BTA_GATT_AUTH_REQ_NONE); |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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, |
| BTA_HH_LE_SRVC_DEF, |
| p_data->api_sndcmd.param, |
| p_data->api_sndcmd.rpt_id); |
| break; |
| |
| case HID_TRANS_SET_REPORT: |
| bta_hh_le_write_rpt(p_cb, |
| BTA_HH_LE_SRVC_DEF, |
| BTA_GATTC_TYPE_WRITE, |
| 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, |
| BTA_HH_LE_SRVC_DEF, |
| BTA_GATTC_TYPE_WRITE_NO_RSP, |
| 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("unsupported trsanction for LE HID device: %d", 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) |
| { |
| UINT8 i; |
| |
| for (i = 0 ;i < BTA_HH_LE_HID_SRVC_MAX; i ++) |
| { |
| if (p_cb->hid_srvc[i].in_use) |
| { |
| p_cb->dscp_info.descriptor.dl_len = p_cb->hid_srvc[i].descriptor.dl_len; |
| p_cb->dscp_info.descriptor.dsc_list = p_cb->hid_srvc[i].descriptor.dsc_list; |
| |
| (*bta_hh_cb.p_cback)(BTA_HH_GET_DSCP_EVT, (tBTA_HH *)&p_cb->dscp_info); |
| } |
| else |
| break; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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, BOOLEAN check_bond) |
| { |
| UINT8 sec_flag=0; |
| BOOLEAN to_add = TRUE; |
| |
| if (check_bond) |
| { |
| /* start reconnection if remote is a bonded device */ |
| /* verify bond */ |
| BTM_GetSecurityFlags(p_cb->addr, &sec_flag); |
| |
| 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); |
| p_cb->in_bg_conn = TRUE; |
| |
| BTA_DmBleSetBgConnType(BTA_DM_BLE_CONN_AUTO, NULL); |
| } |
| 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 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); |
| |
| 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); |
| } |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_update_scpp |
| ** |
| ** Description action function to update the scan parameters on remote HID |
| ** device |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf) |
| { |
| tBTA_GATTC_CHAR_ID char_id; |
| UINT8 value[4], *p = value; |
| tBTA_HH_CBDATA cback_data ; |
| |
| if (!p_dev_cb->is_le_device || |
| p_dev_cb->mode != BTA_HH_PROTO_RPT_MODE || |
| p_dev_cb->scps_supported == FALSE) |
| { |
| APPL_TRACE_ERROR("Can not set ScPP scan paramter as boot host, or remote does not support ScPP "); |
| |
| cback_data.handle = p_dev_cb->hid_handle; |
| cback_data.status = BTA_HH_ERR; |
| (* bta_hh_cb.p_cback)(BTA_HH_UPDATE_SCPP_EVT, (tBTA_HH *)&cback_data); |
| |
| return; |
| } |
| |
| p_dev_cb->w4_evt = BTA_HH_UPDATE_SCPP_EVT; |
| |
| UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_int); |
| UINT16_TO_STREAM(p, p_buf->le_scpp_update.scan_win); |
| |
| bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_INT_WINDOW, &char_id.char_id); |
| |
| BTA_GATTC_WriteCharValue(p_dev_cb->conn_id, |
| &char_id, |
| BTA_GATTC_TYPE_WRITE_NO_RSP, |
| 2, |
| value, |
| BTA_GATT_AUTH_REQ_NONE); |
| |
| } |
| /******************************************************************************* |
| ** |
| ** 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; |
| UINT16 evt; |
| #if BTA_HH_DEBUG |
| APPL_TRACE_DEBUG("bta_hh_gattc_callback event = %d", event); |
| #endif |
| if (p_data == NULL) |
| return; |
| |
| switch (event) |
| { |
| case BTA_GATTC_REG_EVT: /* 0 */ |
| bta_hh_le_register_cmpl(&p_data->reg_oper); |
| break; |
| |
| 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_READ_CHAR_EVT: /* 3 */ |
| case BTA_GATTC_READ_DESCR_EVT: /* 8 */ |
| p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->read.conn_id); |
| if (event == BTA_GATTC_READ_CHAR_EVT) |
| evt = BTA_HH_GATT_READ_CHAR_CMPL_EVT; |
| else |
| evt = BTA_HH_GATT_READ_DESCR_CMPL_EVT; |
| |
| bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->read); |
| break; |
| |
| case BTA_GATTC_WRITE_DESCR_EVT: /* 9 */ |
| case BTA_GATTC_WRITE_CHAR_EVT: /* 4 */ |
| p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->write.conn_id); |
| if (event == BTA_GATTC_WRITE_CHAR_EVT) |
| evt = BTA_HH_GATT_WRITE_CHAR_CMPL_EVT; |
| else |
| evt = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT; |
| |
| bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->write); |
| 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_SEARCH_RES_EVT: /* 7 */ |
| bta_hh_le_search_result(&p_data->srvc_res); |
| 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; |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** 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(BD_ADDR bd_addr, UINT8 rpt_id) |
| { |
| tBTA_HH_DEV_CB *p_cb = NULL; |
| tBTA_HH_LE_RPT *p_rpt ; |
| UINT8 index = BTA_HH_IDX_INVALID; |
| |
| index = bta_hh_find_cb(bd_addr); |
| if ((index = bta_hh_find_cb(bd_addr))== BTA_HH_IDX_INVALID) |
| { |
| APPL_TRACE_ERROR("unknown device"); |
| return; |
| } |
| |
| p_cb = &bta_hh_cb.kdev[index]; |
| |
| p_rpt = bta_hh_le_find_rpt_by_idtype(p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].report, p_cb->mode, BTA_HH_RPTT_INPUT, rpt_id); |
| |
| if (p_rpt == NULL) |
| { |
| APPL_TRACE_ERROR("bta_hh_le_write_rpt: no matching report"); |
| return; |
| } |
| |
| bta_hh_le_read_char_dscrpt(p_cb, |
| UUID_SERVCLASS_LE_HID, |
| BTA_HH_LE_SRVC_DEF, |
| p_rpt->uuid, |
| p_rpt->inst_id, |
| GATT_UUID_CHAR_CLIENT_CONFIG); |
| |
| |
| |
| return; |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_search_scps |
| ** |
| ** Description discovery scan parameter service if act as report host, otherwise |
| ** finish LE connection. |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb) |
| { |
| tBT_UUID pri_srvc; |
| |
| if ( p_cb->mode == BTA_HH_PROTO_RPT_MODE) |
| { |
| p_cb->disc_active |= BTA_HH_LE_DISC_SCPS; |
| /* start service discovery for Scan Parameter service */ |
| pri_srvc.len = LEN_UUID_16; |
| pri_srvc.uu.uuid16 = UUID_SERVCLASS_SCAN_PARAM; |
| |
| BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc); |
| } |
| else |
| bta_hh_le_open_cmpl(p_cb); |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_search_scps_chars |
| ** |
| ** Description find ScPS optional characteristics scan refresh |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| static void bta_hh_le_search_scps_chars(tBTA_HH_DEV_CB *p_cb) |
| { |
| tBTA_GATT_SRVC_ID srvc_id; |
| tBT_UUID char_cond; |
| tBTA_GATTC_CHAR_ID char_result; |
| tBTA_GATT_CHAR_PROP prop; |
| |
| p_cb->scps_supported = TRUE; |
| bta_hh_le_fill_16bits_srvc_id(TRUE, 0, UUID_SERVCLASS_SCAN_PARAM, &srvc_id); |
| |
| char_cond.len = LEN_UUID_16; |
| char_cond.uu.uuid16 = GATT_UUID_SCAN_REFRESH; |
| |
| /* look for scan refresh */ |
| if (BTA_GATTC_GetFirstChar( p_cb->conn_id, |
| &srvc_id, |
| &char_cond, |
| &char_result, |
| &prop) == BTA_GATT_OK) |
| { |
| if (prop & BTA_GATT_CHAR_PROP_BIT_NOTIFY) |
| p_cb->scps_notify |= BTA_HH_LE_SCPS_NOTIFY_SPT; |
| else |
| p_cb->scps_notify = BTA_HH_LE_SCPS_NOTIFY_NONE; |
| |
| } |
| } |
| |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_register_scpp_notif |
| ** |
| ** Description register scan parameter refresh notitication complete |
| ** |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| static void bta_hh_le_register_scpp_notif(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) |
| { |
| UINT8 sec_flag=0; |
| tBTA_GATTC_CHAR_ID char_id; |
| |
| /* if write scan parameter sucessful */ |
| /* if bonded and notification is not enabled, configure the client configuration */ |
| if (status == BTA_GATT_OK && |
| (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_SPT) != 0 && |
| (p_dev_cb->scps_notify & BTA_HH_LE_SCPS_NOTIFY_ENB) == 0) |
| { |
| BTM_GetSecurityFlags(p_dev_cb->addr, &sec_flag); |
| if ((sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)) |
| { |
| if (bta_hh_le_write_char_clt_cfg (p_dev_cb, |
| BTA_HH_SCPP_INST_DEF, |
| UUID_SERVCLASS_SCAN_PARAM, |
| BTA_HH_SCPP_INST_DEF, |
| GATT_UUID_SCAN_REFRESH, |
| BTA_GATT_CLT_CONFIG_NOTIFICATION)) |
| { |
| bta_hh_le_fill_16bits_srvc_id(TRUE, BTA_HH_SCPP_INST_DEF, UUID_SERVCLASS_SCAN_PARAM, &char_id.srvc_id); |
| bta_hh_le_fill_16bits_char_id(BTA_HH_SCPP_INST_DEF, GATT_UUID_SCAN_REFRESH, &char_id.char_id); |
| |
| BTA_GATTC_RegisterForNotifications(bta_hh_cb.gatt_if, |
| p_dev_cb->addr, |
| &char_id); |
| return; |
| } |
| } |
| } |
| bta_hh_le_register_scpp_notif_cmpl(p_dev_cb, status); |
| } |
| /******************************************************************************* |
| ** |
| ** Function bta_hh_le_register_scpp_notif_cmpl |
| ** |
| ** Description action function to register scan parameter refresh notitication |
| ** |
| ** Parameters: |
| ** |
| *******************************************************************************/ |
| static void bta_hh_le_register_scpp_notif_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_GATT_STATUS status) |
| { |
| tBTA_HH_CBDATA cback_data ; |
| UINT16 cb_evt = p_dev_cb->w4_evt; |
| |
| if (status == BTA_GATT_OK) |
| p_dev_cb->scps_notify = (BTA_HH_LE_SCPS_NOTIFY_ENB | BTA_HH_LE_SCPS_NOTIFY_SPT); |
| |
| 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); |
| |
| |
| } |
| #endif |
| |
| |
| |
| |