diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 3ddc13758a..68e668e9d9 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -55,6 +55,7 @@ if(CONFIG_BT_ENABLED) host/bluedroid/bta/gatt/include host/bluedroid/bta/hf_ag/include host/bluedroid/bta/hf_client/include + host/bluedroid/bta/hd/include host/bluedroid/bta/hh/include host/bluedroid/bta/jv/include host/bluedroid/bta/sdp/include @@ -66,12 +67,12 @@ if(CONFIG_BT_ENABLED) host/bluedroid/external/sbc/plc/include host/bluedroid/btc/profile/esp/include host/bluedroid/btc/profile/std/a2dp/include - host/bluedroid/btc/profile/std/hid/include host/bluedroid/btc/profile/std/include host/bluedroid/btc/include host/bluedroid/stack/btm/include host/bluedroid/stack/gap/include host/bluedroid/stack/gatt/include + host/bluedroid/stack/hid/include host/bluedroid/stack/l2cap/include host/bluedroid/stack/sdp/include host/bluedroid/stack/smp/include @@ -94,6 +95,8 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/api/esp_gatt_common_api.c" "host/bluedroid/api/esp_gattc_api.c" "host/bluedroid/api/esp_gatts_api.c" + "host/bluedroid/api/esp_hidd_api.c" + "host/bluedroid/api/esp_hidh_api.c" "host/bluedroid/api/esp_hf_ag_api.c" "host/bluedroid/api/esp_hf_client_api.c" "host/bluedroid/api/esp_spp_api.c" @@ -128,6 +131,9 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/bta/gatt/bta_gatts_co.c" "host/bluedroid/bta/gatt/bta_gatts_main.c" "host/bluedroid/bta/gatt/bta_gatts_utils.c" + "host/bluedroid/bta/hd/bta_hd_api.c" + "host/bluedroid/bta/hd/bta_hd_act.c" + "host/bluedroid/bta/hd/bta_hd_main.c" "host/bluedroid/bta/hh/bta_hh_act.c" "host/bluedroid/bta/hh/bta_hh_api.c" "host/bluedroid/bta/hh/bta_hh_cfg.c" @@ -184,8 +190,9 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/btc/profile/std/hf_ag/btc_hf_ag.c" "host/bluedroid/btc/profile/std/hf_client/btc_hf_client.c" "host/bluedroid/btc/profile/std/hf_client/bta_hf_client_co.c" - "host/bluedroid/btc/profile/std/hid/hidh_api.c" - "host/bluedroid/btc/profile/std/hid/hidh_conn.c" + "host/bluedroid/btc/profile/std/hid/btc_hd.c" + "host/bluedroid/btc/profile/std/hid/btc_hh.c" + "host/bluedroid/btc/profile/std/hid/bta_hh_co.c" "host/bluedroid/btc/profile/std/gap/btc_gap_ble.c" "host/bluedroid/btc/profile/std/gap/btc_gap_bt.c" "host/bluedroid/btc/profile/std/gap/bta_gap_bt_co.c" @@ -251,6 +258,10 @@ if(CONFIG_BT_ENABLED) "host/bluedroid/stack/avrc/avrc_pars_tg.c" "host/bluedroid/stack/avrc/avrc_sdp.c" "host/bluedroid/stack/avrc/avrc_utils.c" + "host/bluedroid/stack/hid/hidd_api.c" + "host/bluedroid/stack/hid/hidd_conn.c" + "host/bluedroid/stack/hid/hidh_api.c" + "host/bluedroid/stack/hid/hidh_conn.c" "host/bluedroid/stack/btm/btm_acl.c" "host/bluedroid/stack/btm/btm_ble.c" "host/bluedroid/stack/btm/btm_ble_addr.c" diff --git a/components/bt/common/btc/core/btc_task.c b/components/bt/common/btc/core/btc_task.c index 9579446817..9cc311c52c 100644 --- a/components/bt/common/btc/core/btc_task.c +++ b/components/bt/common/btc/core/btc_task.c @@ -53,6 +53,12 @@ #if BTC_HF_CLIENT_INCLUDED #include "btc_hf_client.h" #endif /* #if BTC_HF_CLIENT_INCLUDED */ +#if BTC_HD_INCLUDED == TRUE +#include "btc_hd.h" +#endif /* BTC_HD_INCLUDED */ +#if BTC_HH_INCLUDED == TRUE +#include "btc_hh.h" +#endif /* BTC_HH_INCLUDED */ #endif /* #if CLASSIC_BT_INCLUDED */ #endif @@ -120,6 +126,12 @@ static const btc_func_t profile_tab[BTC_PID_NUM] = { #if BTC_HF_CLIENT_INCLUDED [BTC_PID_HF_CLIENT] = {btc_hf_client_call_handler, btc_hf_client_cb_handler}, #endif /* #if BTC_HF_CLIENT_INCLUDED */ +#if BTC_HD_INCLUDED + [BTC_PID_HD] = {btc_hd_call_handler, btc_hd_cb_handler }, +#endif +#if BTC_HH_INCLUDED + [BTC_PID_HH] = {btc_hh_call_handler, btc_hh_cb_handler }, +#endif #endif /* #if CLASSIC_BT_INCLUDED */ #endif #if CONFIG_BLE_MESH diff --git a/components/bt/common/btc/include/btc/btc_task.h b/components/bt/common/btc/include/btc/btc_task.h index cc9382590a..f8da6ffd42 100644 --- a/components/bt/common/btc/include/btc/btc_task.h +++ b/components/bt/common/btc/include/btc/btc_task.h @@ -65,6 +65,8 @@ typedef enum { BTC_PID_AVRC_CT, BTC_PID_AVRC_TG, BTC_PID_SPP, + BTC_PID_HD, + BTC_PID_HH, #if (BTC_HF_INCLUDED == TRUE) BTC_PID_HF, #endif /* BTC_HF_INCLUDED */ @@ -99,6 +101,10 @@ typedef struct { typedef void (* btc_arg_deep_copy_t)(btc_msg_t *msg, void *dst, void *src); +#ifdef __cplusplus +extern "C" { +#endif + /** * transfer an message to another module in the different task. * @param msg message @@ -124,4 +130,8 @@ void btc_deinit(void); bool btc_check_queue_is_congest(void); int get_btc_work_queue_size(void); +#ifdef __cplusplus +} +#endif + #endif /* __BTC_TASK_H__ */ diff --git a/components/bt/component.mk b/components/bt/component.mk index 8ec9205adf..1ba960e148 100644 --- a/components/bt/component.mk +++ b/components/bt/component.mk @@ -46,6 +46,7 @@ COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \ host/bluedroid/bta/hf_client/include \ host/bluedroid/bta/dm/include \ host/bluedroid/bta/gatt/include \ + host/bluedroid/bta/hd/include \ host/bluedroid/bta/hh/include \ host/bluedroid/bta/jv/include \ host/bluedroid/bta/sdp/include \ @@ -70,6 +71,7 @@ COMPONENT_PRIV_INCLUDEDIRS += host/bluedroid/bta/include \ host/bluedroid/stack/gap/include \ host/bluedroid/stack/gatt/include \ host/bluedroid/stack/hcic/include \ + host/bluedroid/stack/hid/include \ host/bluedroid/stack/l2cap/include \ host/bluedroid/stack/sdp/include \ host/bluedroid/stack/smp/include \ @@ -86,6 +88,7 @@ COMPONENT_ADD_INCLUDEDIRS += host/bluedroid/api/include/api \ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/bta/gatt \ + host/bluedroid/bta/hd \ host/bluedroid/bta/hh \ host/bluedroid/bta/sdp \ host/bluedroid/bta/av \ @@ -118,6 +121,7 @@ COMPONENT_SRCDIRS += host/bluedroid/bta/dm \ host/bluedroid/stack/gap \ host/bluedroid/stack/gatt \ host/bluedroid/stack/hcic \ + host/bluedroid/stack/hid \ host/bluedroid/stack/include \ host/bluedroid/stack/l2cap \ host/bluedroid/stack/sdp \ diff --git a/components/bt/host/bluedroid/Kconfig.in b/components/bt/host/bluedroid/Kconfig.in index 47980eeb28..8e33225264 100644 --- a/components/bt/host/bluedroid/Kconfig.in +++ b/components/bt/host/bluedroid/Kconfig.in @@ -99,13 +99,27 @@ config BT_HFP_WBS_ENABLE This enables Wide Band Speech. Should disable it when SCO data path is PCM. Otherwise there will be no data transmited via GPIOs. -config BT_HID_HOST_ENABLED - bool "Classic BT HID Host" +config BT_HID_ENABLED + bool "Classic BT HID" depends on BT_CLASSIC_ENABLED default n help This enables the BT HID Host +choice BT_HID_ROLE + prompt "Profile Role configuration" + depends on BT_HID_ENABLED + config BT_HID_HOST_ENABLED + bool "Classic BT HID Host" + help + This enables the BT HID Host + + config BT_HID_DEVICE_ENABLED + bool "Classic BT HID Device" + help + This enables the BT HID Device +endchoice + config BT_SSP_ENABLED bool "Secure Simple Pairing" depends on BT_CLASSIC_ENABLED diff --git a/components/bt/host/bluedroid/api/esp_hidd_api.c b/components/bt/host/bluedroid/api/esp_hidd_api.c new file mode 100644 index 0000000000..fafe8f8e6e --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_hidd_api.c @@ -0,0 +1,176 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 +#include "esp_err.h" +#include "esp_bt_main.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" +#include "esp_hidd_api.h" + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t *callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HD, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_device_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_INIT_EVT; + + /* Switch to BTC context */ + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t* app_param, esp_hidd_qos_param_t* in_qos, esp_hidd_qos_param_t* out_qos) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.register_app.app_param = app_param; + args.register_app.in_qos = in_qos; + args.register_app.out_qos = out_qos; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_unregister_app(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNREGISTER_APP_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + memcpy(args.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_CONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_disconnect(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_DISCONNECT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t* data) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_SEND_REPORT_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.send_report.type = type; + args.send_report.id = id; + args.send_report.len = len; + args.send_report.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), btc_hd_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_REPORT_ERROR_EVT; + + btc_hidd_args_t args; + memset(&args, 0, sizeof(btc_hidd_args_t)); + args.error = error; + + bt_status_t stat = btc_transfer_context(&msg, &args, sizeof(btc_hidd_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HD; + msg.act = BTC_HD_UNPLUG_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/api/esp_hidh_api.c b/components/bt/host/bluedroid/api/esp_hidh_api.c new file mode 100644 index 0000000000..b62a1a7006 --- /dev/null +++ b/components/bt/host/bluedroid/api/esp_hidh_api.c @@ -0,0 +1,254 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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 "btc/btc_manage.h" +#include "btc_hh.h" +#include "esp_bt_main.h" +#include "esp_err.h" +#include "esp_hidh_api.h" +#include + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t *callback) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + if (callback == NULL) { + return ESP_FAIL; + } + + btc_profile_cb_set(BTC_PID_HH, callback); + return ESP_OK; +} + +esp_err_t esp_bt_hid_host_init(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_INIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_deinit(void) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + + btc_msg_t msg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DEINIT_EVT; + + bt_status_t stat = btc_transfer_context(&msg, NULL, 0, NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_CONNECT_EVT; + + memcpy(arg.connect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_DISCONNECT_EVT; + + memcpy(arg.disconnect.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_UNPLUG_EVT; + + memcpy(arg.unplug.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_INFO_EVT; + + memcpy(arg.set_info.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_info.hid_info = hid_info; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_PROTO_EVT; + + memcpy(arg.get_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_PROTO_EVT; + + memcpy(arg.set_protocol.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_protocol.protocol_mode = protocol_mode; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_IDLE_EVT; + + memcpy(arg.get_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_IDLE_EVT; + + memcpy(arg.set_idle.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_idle.idle_time = idle_time; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_GET_REPORT_EVT; + + memcpy(arg.get_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.get_report.report_type = report_type; + arg.get_report.report_id = report_id; + arg.get_report.buffer_size = buffer_size; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), NULL); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SET_REPORT_EVT; + + memcpy(arg.set_report.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.set_report.report_type = report_type; + arg.set_report.len = len; + arg.set_report.report = report; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len) +{ + ESP_BLUEDROID_STATUS_CHECK(ESP_BLUEDROID_STATUS_ENABLED); + btc_msg_t msg; + btc_hidh_args_t arg; + + msg.sig = BTC_SIG_API_CALL; + msg.pid = BTC_PID_HH; + msg.act = BTC_HH_SEND_DATA_EVT; + + memcpy(arg.send_data.bd_addr, bd_addr, sizeof(esp_bd_addr_t)); + arg.send_data.len = len; + arg.send_data.data = data; + + bt_status_t stat = btc_transfer_context(&msg, &arg, sizeof(btc_hidh_args_t), btc_hh_arg_deep_copy); + return (stat == BT_STATUS_SUCCESS) ? ESP_OK : ESP_FAIL; +} + +#endif /* defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h new file mode 100644 index 0000000000..de90ff8efc --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h @@ -0,0 +1,379 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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. + +#ifndef __ESP_HIDD_API_H__ +#define __ESP_HIDD_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* sub_class of hid device */ +#define ESP_HID_CLASS_UNKNOWN (0x00<<2) +#define ESP_HID_CLASS_JOS (0x01<<2) /* joy stick */ +#define ESP_HID_CLASS_GPD (0x02<<2) /* game pad */ +#define ESP_HID_CLASS_RMC (0x03<<2) /* remote control */ +#define ESP_HID_CLASS_SED (0x04<<2) /* sensing device */ +#define ESP_HID_CLASS_DGT (0x05<<2) /* Digitizer tablet */ +#define ESP_HID_CLASS_CDR (0x06<<2) /* card reader */ +#define ESP_HID_CLASS_KBD (0x10<<2) /* keyboard */ +#define ESP_HID_CLASS_MIC (0x20<<2) /* pointing device */ +#define ESP_HID_CLASS_COM (0x30<<2) /* Combo keyboard/pointing */ + +/** + * @brief HIDD handshake error + */ +typedef enum { + ESP_HID_PAR_HANDSHAKE_RSP_SUCCESS = 0, + ESP_HID_PAR_HANDSHAKE_RSP_NOT_READY = 1, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID = 2, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ = 3, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM = 4, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN = 14, + ESP_HID_PAR_HANDSHAKE_RSP_ERR_FATAL = 15 +} esp_hidd_handshake_error_t; + +/** + * @brief HIDD report types + */ +typedef enum { + ESP_HIDD_REPORT_TYPE_OTHER = 0, + ESP_HIDD_REPORT_TYPE_INPUT, + ESP_HIDD_REPORT_TYPE_OUTPUT, + ESP_HIDD_REPORT_TYPE_FEATURE, + // special value for reports to be sent on INTR(INPUT is assumed) + ESP_HIDD_REPORT_TYPE_INTRDATA +} esp_hidd_report_type_t; + +/** + * @brief HIDD connection state + */ +typedef enum { + ESP_HIDD_CONN_STATE_CONNECTED, + ESP_HIDD_CONN_STATE_CONNECTING, + ESP_HIDD_CONN_STATE_DISCONNECTED, + ESP_HIDD_CONN_STATE_DISCONNECTING, + ESP_HIDD_CONN_STATE_UNKNOWN +} esp_hidd_connection_state_t; + +/** + * @brief HID device protocol modes + */ +typedef enum { + ESP_HIDD_REPORT_MODE = 0x00, + ESP_HIDD_BOOT_MODE = 0x01, + ESP_HIDD_UNSUPPORTED_MODE = 0xff +} esp_hidd_protocol_mode_t; + + +/** + * @brief HIDD characteristics for SDP report + */ +typedef struct { + const char *name; + const char *description; + const char *provider; + uint8_t subclass; + uint8_t *desc_list; + int desc_list_len; +} esp_hidd_app_param_t; + +/** + * @brief HIDD Quality of Service parameters + */ +typedef struct { + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t access_latency; + uint32_t delay_variation; +} esp_hidd_qos_param_t; + +/** + * @brief HID device callback function events + */ +typedef enum { + ESP_HIDD_INIT_EVT = 0, /*!< When HID device is inited, the event comes */ + ESP_HIDD_DEINIT_EVT, /*!< When HID device is deinited, the event comes */ + ESP_HIDD_REGISTER_APP_EVT, /*!< When HID device application registered, the event comes */ + ESP_HIDD_UNREGISTER_APP_EVT, /*!< When HID device application unregistered, the event comes */ + ESP_HIDD_OPEN_EVT, /*!< When HID device connection to host opened, the event comes */ + ESP_HIDD_CLOSE_EVT, /*!< When HID device connection to host closed, the event comes */ + ESP_HIDD_SEND_REPORT_EVT, /*!< When HID device send report to lower layer, the event comes */ + ESP_HIDD_REPORT_ERR_EVT, /*!< When HID device report handshanke error to lower layer, the event comes */ + ESP_HIDD_GET_REPORT_EVT, /*!< When HID device receives GET_REPORT request from host, the event comes */ + ESP_HIDD_SET_REPORT_EVT, /*!< When HID device receives SET_REPORT request from host, the event comes */ + ESP_HIDD_SET_PROTOCOL_EVT, /*!< When HID device receives SET_PROTOCOL request from host, the event comes */ + ESP_HIDD_INTR_DATA_EVT, /*!< When HID device receives DATA from host on intr, the event comes */ + ESP_HIDD_VC_UNPLUG_EVT, /*!< When HID device initiates Virtual Cable Unplug, the event comes */ + ESP_HIDD_API_ERR_EVT /*!< When HID device has API error, the event comes */ +} esp_hidd_cb_event_t; + +typedef enum { + ESP_HIDD_SUCCESS, + ESP_HIDD_ERROR, /*!< general ESP HD error */ + ESP_HIDD_NO_RES, /*!< out of system resources */ + ESP_HIDD_BUSY, /*!< Temporarily can not handle this request. */ + ESP_HIDD_NO_DATA, /*!< No data. */ + ESP_HIDD_NEED_INIT, /*!< HIDD module shall init first */ + ESP_HIDD_NEED_DEINIT, /*!< HIDD module shall deinit first */ + ESP_HIDD_NEED_REG, /*!< HIDD module shall register first */ + ESP_HIDD_NEED_DEREG, /*!< HIDD module shall deregister first */ + ESP_HIDD_NO_CONNECTION, /*!< connection may have been closed */ +} esp_hidd_status_t; + +/** + * @brief HID device callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDD_INIT_EVT + */ + struct hidd_init_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } init; /*!< HIDD callback param of ESP_HIDD_INIT_EVT */ + + /** + * @brief ESP_HIDD_DEINIT_EVT + */ + struct hidd_deinit_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } deinit; /*!< HIDD callback param of ESP_HIDD_DEINIT_EVT */ + + /** + * @brief ESP_HIDD_REGISTER_APP_EVT + */ + struct hidd_register_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + bool in_use; /*!< indicate whether use virtual cable plug host address */ + esp_bd_addr_t bd_addr; /*!< host address */ + } register_app; /*!< HIDD callback param of ESP_HIDD_REGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_UNREGISTER_APP_EVT + */ + struct hidd_unregister_app_evt_param { + esp_hidd_status_t status; /*!< operation status */ + } unregister_app; /*!< HIDD callback param of ESP_HIDD_UNREGISTER_APP_EVT */ + + /** + * @brief ESP_HIDD_OPEN_EVT + */ + struct hidd_open_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + esp_bd_addr_t bd_addr; /*!< host address */ + } open; /*!< HIDD callback param of ESP_HIDD_OPEN_EVT */ + + /** + * @brief ESP_HIDD_CLOSE_EVT + */ + struct hidd_close_evt_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } close; /*!< HIDD callback param of ESP_HIDD_CLOSE_EVT */ + + /** + * @brief ESP_HIDD_SEND_REPORT_EVT + */ + struct hidd_send_report_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + } send_report; /*!< HIDD callback param of ESP_HIDD_SEND_REPORT_EVT */ + + /** + * @brief ESP_HIDD_REPORT_ERR_EVT + */ + struct hidd_report_err_evt_param { + esp_hidd_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } report_err; /*!< HIDD callback param of ESP_HIDD_REPORT_ERR_EVT */ + + /** + * @brief ESP_HIDD_GET_REPORT_EVT + */ + struct hidd_get_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t buffer_size; /*!< buffer size */ + } get_report; /*!< HIDD callback param of ESP_HIDD_GET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_REPORT_EVT + */ + struct hidd_set_report_evt_param { + esp_hidd_report_type_t report_type; /*!< report type */ + uint8_t report_id; /*!< report id */ + uint16_t len; /*!< set_report data length */ + uint8_t *data; /*!< set_report data pointer */ + } set_report; /*!< HIDD callback param of ESP_HIDD_SET_REPORT_EVT */ + + /** + * @brief ESP_HIDD_SET_PROTOCOL_EVT + */ + struct hidd_set_protocol_evt_param { + esp_hidd_protocol_mode_t protocol_mode; /*!< protocol mode */ + } set_protocol; /*!< HIDD callback param of ESP_HIDD_SET_PROTOCOL_EVT */ + + /** + * @brief ESP_HIDD_INTR_DATA_EVT + */ + struct hidd_intr_data_evt_param { + uint8_t report_id; /*!< interrupt channel report id */ + uint16_t len; /*!< interrupt channel report data length */ + uint8_t *data; /*!< interrupt channel report data pointer */ + } intr_data; /*!< HIDD callback param of ESP_HIDD_INTR_DATA_EVT */ + + /** + * @brief ESP_HIDD_VC_UNPLUG_EVT + */ + struct hidd_vc_unplug_param { + esp_hidd_status_t status; /*!< operation status */ + esp_hidd_connection_state_t conn_status; /*!< connection status */ + } vc_unplug; /*!< HIDD callback param of ESP_HIDD_VC_UNPLUG_EVT */ +} esp_hidd_cb_param_t; + +/** + * @brief HID device callback function type. + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (esp_hd_cb_t)(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID device module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t callback); + +/** + * @brief This function initializes HIDD. This function should be called after esp_bluedroid_enable and + * esp_blueroid_init success, and should be called after esp_bt_hid_device_register_callback. + * When the operation is complete the callback function will be called with ESP_HIDD_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_init(void); + +/** + * @brief This function de-initializes HIDD interface. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_device_init(). When the operation is complete the callback + * function will be called with ESP_HIDD_DEINIT_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_deinit(void); + +/** + * @brief Registers HIDD parameters with SDP and sets l2cap Quality of Service. This function should be called after + * esp_bluedroid_enable and esp_blueroid_init success, and must be done after esp_bt_hid_device_init. When the operation is complete the callback + * function will be called with ESP_HIDD_REGISTER_APP_EVT. + * + * @param[in] app_param: HIDD parameters + * @param[in] in_qos: incoming QoS parameters + * @param[in] out_qos: outgoing QoS parameters + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t *app_param, esp_hidd_qos_param_t *in_qos, + esp_hidd_qos_param_t *out_qos); + +/** + * @brief Removes HIDD parameters from SDP and resets l2cap Quality of Service. This function should be called after esp_bluedroid_enable and + * esp_blueroid_init success, and should be called after esp_bt_hid_device_init. When the operation is complete the callback + * function will be called with ESP_HIDD_UNREGISTER_APP_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_unregister_app(void); + +/** + * @brief This function connects HIDD interface to connected bluetooth device, if not done already. When the operation is complete the callback + * function will be called with ESP_HIDD_OPEN_EVT. + * + * @param[in] bd_addr: Remote host bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr); + +/** + * @brief This function disconnects HIDD interface. When the operation is complete the callback + * function will be called with ESP_HIDD_CLOSE_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_disconnect(void); + +/** + * @brief Send HIDD report. When the operation is complete the callback + * function will be called with ESP_HIDD_SEND_REPORT_EVT. + * + * @param[in] type: type of report + * @param[in] id: report id as defined by descriptor + * @param[in] len: length of report + * @param[in] data: report data + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *data); + +/** + * @brief Sends HIDD handshake with error info for invalid set_report. When the operation is complete the callback + * function will be called with ESP_HIDD_REPORT_ERR_EVT. + * + * @param[in] error: type of error + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error); + +/** + * @brief Unplug virtual cable of HIDD. When the operation is complete the callback + * function will be called with ESP_HIDD_VC_UNPLUG_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_device_virtual_cable_unplug(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h b/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h new file mode 100644 index 0000000000..07abdece2d --- /dev/null +++ b/components/bt/host/bluedroid/api/include/api/esp_hidh_api.h @@ -0,0 +1,465 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// Copyright 2019 Blake Felt +// +// 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. + +#ifndef __ESP_HIDH_API_H__ +#define __ESP_HIDH_API_H__ + +#include "esp_bt_defs.h" +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define BTHH_MAX_DSC_LEN 884 + +/** + * @brief HID host connection state + */ +typedef enum { + ESP_HIDH_CONN_STATE_CONNECTED = 0, /*!< connected state */ + ESP_HIDH_CONN_STATE_CONNECTING, /*!< connecting state */ + ESP_HIDH_CONN_STATE_DISCONNECTED, /*!< disconnected state */ + ESP_HIDH_CONN_STATE_DISCONNECTING, /*!< disconnecting state */ + ESP_HIDH_CONN_STATE_UNKNOWN /*!< unknown state(initial state) */ +} esp_hidh_connection_state_t; + +typedef enum { + ESP_HIDH_OK, + ESP_HIDH_HS_HID_NOT_READY, /*!< handshake error : device not ready */ + ESP_HIDH_HS_INVALID_RPT_ID, /*!< handshake error : invalid report ID */ + ESP_HIDH_HS_TRANS_NOT_SPT, /*!< handshake error : transaction not spt */ + ESP_HIDH_HS_INVALID_PARAM, /*!< handshake error : invalid paremter */ + ESP_HIDH_HS_ERROR, /*!< handshake error : unspecified HS error */ + ESP_HIDH_ERR, /*!< general ESP HH error */ + ESP_HIDH_ERR_SDP, /*!< SDP error */ + ESP_HIDH_ERR_PROTO, /*!< SET_Protocol error, + only used in ESP_HIDH_OPEN_EVT callback */ + + ESP_HIDH_ERR_DB_FULL, /*!< device database full error, used in + ESP_HIDH_OPEN_EVT/ESP_HIDH_ADD_DEV_EVT */ + ESP_HIDH_ERR_TOD_UNSPT, /*!< type of device not supported */ + ESP_HIDH_ERR_NO_RES, /*!< out of system resources */ + ESP_HIDH_ERR_AUTH_FAILED, /*!< authentication fail */ + ESP_HIDH_ERR_HDL, /*!< connection handle error */ + ESP_HIDH_ERR_SEC, /*!< encryption error */ + // self_defined + ESP_HIDH_BUSY, /*!< Temporarily can not handle this request. */ + ESP_HIDH_NO_DATA, /*!< No data. */ + ESP_HIDH_NEED_INIT, /*!< HIDH module shall init first */ + ESP_HIDH_NEED_DEINIT, /*!< HIDH module shall deinit first */ + ESP_HIDH_NO_CONNECTION, /*!< connection may have been closed */ +} esp_hidh_status_t; + +/** + * @brief HID host protocol modes + */ +typedef enum { + ESP_HIDH_BOOT_MODE = 0x00, /*!< boot protocol mode */ + ESP_HIDH_REPORT_MODE = 0x01, /*!< report protocol mode */ + ESP_HIDH_UNSUPPORTED_MODE = 0xff /*!< unsupported protocol mode */ +} esp_hidh_protocol_mode_t; + +/** + * @brief HID host report types + */ +typedef enum { + ESP_HIDH_REPORT_TYPE_OTHER = 0, /*!< unsupported report type */ + ESP_HIDH_REPORT_TYPE_INPUT, /*!< input report type */ + ESP_HIDH_REPORT_TYPE_OUTPUT, /*!< output report type */ + ESP_HIDH_REPORT_TYPE_FEATURE, /*!< feature report type */ +} esp_hidh_report_type_t; + +/** + * @brief HID host callback function events + */ +typedef enum { + ESP_HIDH_INIT_EVT = 0, /*!< When HID host is inited, the event comes */ + ESP_HIDH_DEINIT_EVT, /*!< When HID host is deinited, the event comes */ + ESP_HIDH_OPEN_EVT, /*!< When HID host connection opened, the event comes */ + ESP_HIDH_CLOSE_EVT, /*!< When HID host connection closed, the event comes */ + ESP_HIDH_GET_RPT_EVT, /*!< When Get_Report command is called, the event comes */ + ESP_HIDH_SET_RPT_EVT, /*!< When Set_Report command is called, the event comes */ + ESP_HIDH_GET_PROTO_EVT, /*!< When Get_Protocol command is called, the event comes */ + ESP_HIDH_SET_PROTO_EVT, /*!< When Set_Protocol command is called, the event comes */ + ESP_HIDH_GET_IDLE_EVT, /*!< When Get_Idle command is called, the event comes */ + ESP_HIDH_SET_IDLE_EVT, /*!< When Set_Idle command is called, the event comes */ + ESP_HIDH_GET_DSCP_EVT, /*!< When HIDH is inited, the event comes */ + ESP_HIDH_ADD_DEV_EVT, /*!< When a device is added, the event comes */ + ESP_HIDH_RMV_DEV_EVT, /*!< When a device is removed, the event comes */ + ESP_HIDH_VC_UNPLUG_EVT, /*!< When virtually unplugged, the event comes */ + ESP_HIDH_DATA_EVT, /*!< When send data on interrupt channel, the event comes */ + ESP_HIDH_DATA_IND_EVT, /*!< When receive data on interrupt channel, the event comes */ + ESP_HIDH_SET_INFO_EVT /*!< When set the HID device descriptor, the event comes */ +} esp_hidh_cb_event_t; + +typedef struct { + int attr_mask; + uint8_t sub_class; + uint8_t app_id; + int vendor_id; + int product_id; + int version; + uint8_t ctry_code; + int dl_len; + uint8_t dsc_list[BTHH_MAX_DSC_LEN]; +} esp_hidh_hid_info_t; + +/** + * @brief HID host callback parameters union + */ +typedef union { + /** + * @brief ESP_HIDH_INIT_EVT + */ + struct hidh_init_evt_param { + esp_hidh_status_t status; /*!< status */ + } init; /*!< HIDH callback param of ESP_HIDH_INIT_EVT */ + + /** + * @brief ESP_HIDH_DEINIT_EVT + */ + struct hidh_uninit_evt_param { + esp_hidh_status_t status; /*!< status */ + } deinit; /*!< HIDH callback param of ESP_HIDH_DEINIT_EVT */ + + /** + * @brief ESP_HIDH_OPEN_EVT + */ + struct hidh_open_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + bool is_orig; /*!< indicate if host intiate the connection */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } open; /*!< HIDH callback param of ESP_HIDH_OPEN_EVT */ + + /** + * @brief ESP_HIDH_CLOSE_EVT + */ + struct hidh_close_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } close; /*!< HIDH callback param of ESP_HIDH_CLOSE_EVT */ + + /** + * @brief ESP_HIDH_VC_UNPLUG_EVT + */ + struct hidh_unplug_evt_param { + esp_hidh_status_t status; /*!< operation status */ + esp_hidh_connection_state_t conn_status; /*!< connection status */ + uint8_t handle; /*!< device handle */ + } unplug; /*!< HIDH callback param of ESP_HIDH_VC_UNPLUG_EVT */ + + /** + * @brief ESP_HIDH_GET_PROTO_EVT + */ + struct hidh_get_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + } get_proto; /*!< HIDH callback param of ESP_HIDH_GET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_SET_PROTO_EVT + */ + struct hidh_set_proto_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_proto; /*!< HIDH callback param of ESP_HIDH_SET_PROTO_EVT */ + + /** + * @brief ESP_HIDH_GET_RPT_EVT + */ + struct hidh_get_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } get_rpt; /*!< HIDH callback param of ESP_HIDH_GET_RPT_EVT */ + + /** + * @brief ESP_HIDH_SET_RPT_EVT + */ + struct hidh_set_rpt_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_rpt; /*!< HIDH callback param of ESP_HIDH_SET_RPT_EVT */ + + /** + * @brief ESP_HIDH_DATA_EVT + */ + struct hidh_send_data_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t reason; /*!< lower layer failed reason(ref hiddefs.h) */ + } send_data; /*!< HIDH callback param of ESP_HIDH_DATA_EVT */ + + /** + * @brief ESP_HIDH_GET_IDLE_EVT + */ + struct hidh_get_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + uint8_t idle_rate; /*!< idle rate */ + } get_idle; /*!< HIDH callback param of ESP_HIDH_GET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_SET_IDLE_EVT + */ + struct hidh_set_idle_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + } set_idle; /*!< HIDH callback param of ESP_HIDH_SET_IDLE_EVT */ + + /** + * @brief ESP_HIDH_DATA_IND_EVT + */ + struct hidh_data_ind_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_hidh_protocol_mode_t proto_mode; /*!< protocol mode */ + uint16_t len; /*!< data length */ + uint8_t *data; /*!< data pointer */ + } data_ind; /*!< HIDH callback param of ESP_HIDH_DATA_IND_EVT */ + + /** + * @brief ESP_HIDH_ADD_DEV_EVT + */ + struct hidh_add_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } add_dev; /*!< HIDH callback param of ESP_HIDH_ADD_DEV_EVT */ + + /** + * @brief ESP_HIDH_RMV_DEV_EVT + */ + struct hidh_rmv_dev_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } rmv_dev; /*!< HIDH callback param of ESP_HIDH_RMV_DEV_EVT */ + + /** + * @brief ESP_HIDH_GET_DSCP_EVT + */ + struct hidh_get_dscp_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + bool added; /*!< Indicate if added */ + uint16_t vendor_id; /*!< Vendor ID */ + uint16_t product_id; /*!< Product ID */ + uint16_t version; /*!< Version */ + uint16_t ssr_max_latency; /*!< SSR max latency */ + uint16_t ssr_min_tout; /*!< SSR min timeout */ + uint8_t ctry_code; /*!< Country Code */ + uint16_t dl_len; /*!< Device descriptor length */ + uint8_t *dsc_list; /*!< Device descriptor pointer */ + } dscp; /*!< HIDH callback param of ESP_HIDH_GET_DSCP_EVT */ + + /** + * @brief ESP_HIDH_SET_INFO_EVT + */ + struct hidh_set_info_evt_param { + esp_hidh_status_t status; /*!< operation status */ + uint8_t handle; /*!< device handle */ + esp_bd_addr_t bd_addr; /*!< device address */ + } set_info; /*!< HIDH callback param of ESP_HIDH_SET_INFO_EVT */ +} esp_hidh_cb_param_t; + +/** + * @brief HID host callback function type + * @param event: Event type + * @param param: Point to callback parameter, currently is union type + */ +typedef void (esp_hh_cb_t)(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param); + +/** + * @brief This function is called to init callbacks with HID host module. + * + * @param[in] callback: pointer to the init callback function. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_register_callback(esp_hh_cb_t callback); + +/** + * @brief This function initializes HID host. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_host_register_callback(). + * When the operation is complete the callback function will be called with ESP_HIDH_INIT_EVT. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_init(void); + +/** + * @brief Closes the interface. This function should be called after esp_bluedroid_enable() and + * esp_blueroid_init() success, and should be called after esp_bt_hid_host_init(). + * When the operation is complete the callback function will be called with ESP_HIDH_DEINIT_EVT. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_deinit(void); + +/** + * @brief Connect to hid device. When the operation is complete the callback + * function will be called with ESP_HIDH_OPEN_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_connect(esp_bd_addr_t bd_addr); + +/** + * @brief Disconnect from hid device. When the operation is complete the callback + * function will be called with ESP_HIDH_CLOSE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_disconnect(esp_bd_addr_t bd_addr); + +/** + * @brief Virtual UnPlug (VUP) the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_VC_UNPLUG_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_virtual_cable_unplug(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID device descriptor for the specified HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_INFO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] hid_info: HID device descriptor structure. + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_info(esp_bd_addr_t bd_addr, esp_hidh_hid_info_t *hid_info); + +/** + * @brief Get the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_protocol(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID proto mode. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_PROTO_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] protocol_mode: Protocol mode type. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_protocol(esp_bd_addr_t bd_addr, esp_hidh_protocol_mode_t protocol_mode); + +/** + * @brief Get the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * + * @return + * - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_idle(esp_bd_addr_t bd_addr); + +/** + * @brief Set the HID Idle Time. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_IDLE_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] idle_time: Idle time rate + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_idle(esp_bd_addr_t bd_addr, uint16_t idle_time); + +/** + * @brief Send a GET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_GET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report_id: Report id + * @param[in] buffer_size: Buffer size + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_get_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t report_id, + int buffer_size); + +/** + * @brief Send a SET_REPORT to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_SET_RPT_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] report_type: Report type + * @param[in] report: Report data pointer + * @param[in] len: Report data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_set_report(esp_bd_addr_t bd_addr, esp_hidh_report_type_t report_type, uint8_t *report, + size_t len); + +/** + * @brief Send data to HID device. When the operation is complete the callback + * function will be called with ESP_HIDH_DATA_EVT. + * + * @param[in] bd_addr: Remote device bluetooth device address. + * @param[in] data: Data pointer + * @param[in] len: Data length + * + * @return - ESP_OK: success + * - other: failed + */ +esp_err_t esp_bt_hid_host_send_data(esp_bd_addr_t bd_addr, uint8_t *data, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/bt/host/bluedroid/bta/dm/bta_dm_act.c b/components/bt/host/bluedroid/bta/dm/bta_dm_act.c index 75bc12c60a..4d4e543d8a 100644 --- a/components/bt/host/bluedroid/bta/dm/bta_dm_act.c +++ b/components/bt/host/bluedroid/bta/dm/bta_dm_act.c @@ -4498,6 +4498,22 @@ void bta_dm_set_encryption (tBTA_DM_MSG *p_data) } #endif ///SMP_INCLUDED == TRUE +#if (BTA_HD_INCLUDED == TRUE) +BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr) +{ + APPL_TRACE_DEBUG("%s: count(%d)", __func__, bta_dm_conn_srvcs.count); + for (uint8_t j = 0; j < bta_dm_conn_srvcs.count; j++) { + // Check if profiles other than hid are connected + if ((bta_dm_conn_srvcs.conn_srvc[j].id != BTA_ID_HD) && + !bdcmp(bta_dm_conn_srvcs.conn_srvc[j].peer_bdaddr, peer_addr)) { + APPL_TRACE_DEBUG("%s: Another profile (id=%d) is connected", __func__, bta_dm_conn_srvcs.conn_srvc[j].id); + return FALSE; + } + } + return TRUE; +} +#endif /* BTA_HD_INCLUDED == TRUE */ + #if (BLE_INCLUDED == TRUE) /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c b/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c index 5735941de1..31047b1ae3 100644 --- a/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c +++ b/components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c @@ -117,11 +117,11 @@ tBTA_DM_CFG *const p_bta_dm_cfg = (tBTA_DM_CFG *) &bta_dm_cfg; tBTA_DM_RM *const p_bta_dm_rm_cfg = (tBTA_DM_RM *) &bta_dm_rm_cfg; #if BLE_INCLUDED == TRUE +# define BTA_DM_NUM_PM_ENTRY 10 /* number of entries in bta_dm_pm_cfg except the first */ +# define BTA_DM_NUM_PM_SPEC 10 /* number of entries in bta_dm_pm_spec */ +#else # define BTA_DM_NUM_PM_ENTRY 8 /* number of entries in bta_dm_pm_cfg except the first */ # define BTA_DM_NUM_PM_SPEC 8 /* number of entries in bta_dm_pm_spec */ -#else -# define BTA_DM_NUM_PM_ENTRY 6 /* number of entries in bta_dm_pm_cfg except the first */ -# define BTA_DM_NUM_PM_SPEC 6 /* number of entries in bta_dm_pm_spec */ #endif #if (BTA_DM_PM_INCLUDED == TRUE) @@ -133,10 +133,12 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_CFG bta_dm_pm_cfg[BTA_DM_NUM_PM_ENTRY + 1] {BTA_ID_JV, BTA_APP_ID_1, 2}, /* app BTA_JV_PM_ID_1, reuse ftc spec table */ {BTA_ID_JV, BTA_ALL_APP_ID, 3}, /* reuse fts spec table */ {BTA_ID_HS, BTA_ALL_APP_ID, 4}, /* HS spec table */ - {BTA_ID_AVK, BTA_ALL_APP_ID, 5} /* avk spec table */ + {BTA_ID_AVK, BTA_ALL_APP_ID, 5}, /* avk spec table */ + {BTA_ID_HD, BTA_ALL_APP_ID, 6}, /* hd spec table */ + {BTA_ID_HH, BTA_ALL_APP_ID, 7} /* hh spec table */ #if BLE_INCLUDED == TRUE - , {BTA_ID_GATTC, BTA_ALL_APP_ID, 6} /* gattc spec table */ - , {BTA_ID_GATTS, BTA_ALL_APP_ID, 7} /* gatts spec table */ + , {BTA_ID_GATTC, BTA_ALL_APP_ID, 8} /* gattc spec table */ + , {BTA_ID_GATTS, BTA_ALL_APP_ID, 9} /* gatts spec table */ #endif }; @@ -254,10 +256,48 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = { {{BTA_DM_PM_ACTIVE, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ } + }, + + /* HD : 6 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR3), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close */ + {{BTA_DM_PM_SNIFF_HD_IDLE_IDX, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HD_ACTIVE_IDX, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } + }, + + /* HH : 7 */ + { + (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ +#if (BTM_SSR_INCLUDED == TRUE) + (BTA_DM_PM_SSR1), /* the SSR entry */ +#endif + { + {{BTA_DM_PM_SNIFF_HH_OPEN_IDX, BTA_DM_PM_HH_OPEN_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn open sniff */ + {{BTA_DM_PM_NO_PREF, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* conn close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* app close */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco open */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}}, /* sco close, used for HH suspend */ + {{BTA_DM_PM_SNIFF_HH_IDLE_IDX, BTA_DM_PM_HH_IDLE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* idle */ + {{BTA_DM_PM_SNIFF_HH_ACTIVE_IDX, BTA_DM_PM_HH_ACTIVE_DELAY + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}}, /* busy */ + {{BTA_DM_PM_NO_ACTION, 0}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ + } } #if BLE_INCLUDED == TRUE - /* GATTC : 6 */ + /* GATTC : 8 */ , { (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ #if (BTM_SSR_INCLUDED == TRUE) @@ -278,7 +318,7 @@ tBTA_DM_PM_TYPE_QUALIFIER tBTA_DM_PM_SPEC bta_dm_pm_spec[BTA_DM_NUM_PM_SPEC] = { {{BTA_DM_PM_RETRY, 5000 + BTA_DM_PM_SPEC_TO_OFFSET}, {BTA_DM_PM_NO_ACTION, 0}} /* mode change retry */ } } - /* GATTS : 7 */ + /* GATTS : 9 */ , { (BTA_DM_PM_SNIFF | BTA_DM_PM_PARK), /* allow park & sniff */ #if (BTM_SSR_INCLUDED == TRUE) @@ -372,7 +412,7 @@ tBTA_DM_SSR_SPEC bta_dm_ssr_spec[] = { seting default max latency and min remote timeout as 0, and always read individual device preference from HH module */ {1200, 2, 2}, /* BTA_DM_PM_SSR2 - others (as long as sniff is allowed)*/ - {360, 160, 2} /* BTA_DM_PM_SSR3 - HD */ + {360, 160, 1600} /* BTA_DM_PM_SSR3 - HD */ }; tBTA_DM_SSR_SPEC *const p_bta_dm_ssr_spec = (tBTA_DM_SSR_SPEC *) &bta_dm_ssr_spec; diff --git a/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h b/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h index 8dfd26af5e..8d2f106d0d 100644 --- a/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h +++ b/components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h @@ -26,6 +26,7 @@ #include "common/bt_target.h" #include "freertos/semphr.h" +#include "bta/bta_sys.h" #if (BLE_INCLUDED == TRUE && (defined BTA_GATT_INCLUDED) && (BTA_GATT_INCLUDED == TRUE)) #include "bta/bta_gatt_api.h" #endif @@ -1596,6 +1597,11 @@ extern void bta_dm_ble_get_energy_info(tBTA_DM_MSG *p_data); extern void bta_dm_set_encryption(tBTA_DM_MSG *p_data); extern void bta_dm_confirm(tBTA_DM_MSG *p_data); extern void bta_dm_key_req(tBTA_DM_MSG *p_data); + +#if (BTA_HD_INCLUDED == TRUE) +extern BOOLEAN bta_dm_check_if_only_hd_connected(BD_ADDR peer_addr); +#endif /* BTA_HD_INCLUDED */ + #if (BTM_OOB_INCLUDED == TRUE) extern void bta_dm_loc_oob(tBTA_DM_MSG *p_data); extern void bta_dm_oob_reply(tBTA_DM_MSG *p_data); diff --git a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c index 9f5417bf76..f35851c60b 100644 --- a/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c +++ b/components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c @@ -35,6 +35,7 @@ #include "gatt_int.h" #include "osi/allocator.h" #include "osi/mutex.h" +#include "bta_hh_int.h" #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) #include "bta_hh_int.h" @@ -304,7 +305,10 @@ void bta_gattc_deregister(tBTA_GATTC_CB *p_cb, tBTA_GATTC_RCB *p_clreg) bta_gattc_deregister_cmpl(p_clreg); } } else { - APPL_TRACE_ERROR("bta_gattc_deregister Deregister Failedm unknown client cif"); + APPL_TRACE_ERROR("Deregister Failed unknown client cif"); +#if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) + bta_hh_cleanup_disable(BTA_HH_OK); +#endif } } /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_act.c b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c new file mode 100644 index 0000000000..addb585a1d --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c @@ -0,0 +1,774 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID device action functions. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_sys.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include + +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata); + +static bool check_descriptor(uint8_t *data, uint16_t length, bool *has_report_id) +{ + uint8_t *ptr = data; + *has_report_id = FALSE; + while (ptr < data + length) { + uint8_t item = *ptr++; + switch (item) { + case 0xfe: // long item indicator + if (ptr < data + length) { + ptr += ((*ptr) + 2); + } else { + return false; + } + break; + case 0x85: // Report ID + *has_report_id = TRUE; + default: + ptr += (item & 0x03); + break; + } + } + return (ptr == data + length); +} + +/******************************************************************************* + * + * Function bta_hd_api_enable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_enable(tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + HID_DevInit(); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); + + HID_DevSetSecurityLevel(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT); + /* store parameters */ + bta_hd_cb.p_cback = p_data->api_enable.p_cback; + + ret = HID_DevRegister(bta_hd_cback); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to register HID device (%d)", __func__, ret); + } + + /* signal BTA call back event */ + (*bta_hd_cb.p_cback)(BTA_HD_ENABLE_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_api_disable + * + * Description Disables HID device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_api_disable(void) +{ + tBTA_HD_STATUS status = BTA_HD_ERROR; + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + /* service is not enabled */ + if (bta_hd_cb.p_cback == NULL) + return; + + /* Remove service record */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + } + + /* Deregister with lower layer */ + ret = HID_DevDeregister(); + if (ret == HID_SUCCESS) { + status = BTA_HD_OK; + } else { + APPL_TRACE_ERROR("%s: Failed to deregister HID device (%d)", __func__, ret); + } + + (*bta_hd_cb.p_cback)(BTA_HD_DISABLE_EVT, (tBTA_HD *)&status); + + memset(&bta_hd_cb, 0, sizeof(tBTA_HD_CB)); +} + +/******************************************************************************* + * + * Function bta_hd_register_act + * + * Description Registers SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_register_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD ret; + tBTA_HD_REGISTER_APP *p_app_data = (tBTA_HD_REGISTER_APP *)p_data; + bool use_report_id = FALSE; + + APPL_TRACE_API("%s", __func__); + + ret.reg_status.in_use = FALSE; + + /* Check if len doesn't exceed BTA_HD_APP_DESCRIPTOR_LEN and descriptor + * itself is well-formed. Also check if descriptor has Report Id item so we + * know if report will have prefix or not. */ + if (p_app_data->d_len > BTA_HD_APP_DESCRIPTOR_LEN || + !check_descriptor(p_app_data->d_data, p_app_data->d_len, &use_report_id)) { + APPL_TRACE_ERROR("%s: Descriptor is too long or malformed", __func__); + ret.reg_status.status = BTA_HD_ERROR; + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); + return; + } + + ret.reg_status.status = BTA_HD_OK; + + /* Remove old record if for some reason it's already registered */ + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.use_report_id = use_report_id; + bta_hd_cb.sdp_handle = SDP_CreateRecord(); + HID_DevAddRecord(bta_hd_cb.sdp_handle, p_app_data->name, p_app_data->description, p_app_data->provider, + p_app_data->subclass, p_app_data->d_len, p_app_data->d_data); + bta_sys_add_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + HID_DevSetIncomingQos(p_app_data->in_qos.service_type, p_app_data->in_qos.token_rate, + p_app_data->in_qos.token_bucket_size, p_app_data->in_qos.peak_bandwidth, + p_app_data->in_qos.access_latency, p_app_data->in_qos.delay_variation); + + HID_DevSetOutgoingQos(p_app_data->out_qos.service_type, p_app_data->out_qos.token_rate, + p_app_data->out_qos.token_bucket_size, p_app_data->out_qos.peak_bandwidth, + p_app_data->out_qos.access_latency, p_app_data->out_qos.delay_variation); + + // application is registered so we can accept incoming connections + HID_DevSetIncomingPolicy(TRUE); + + if (HID_DevGetDevice(&ret.reg_status.bda) == HID_SUCCESS) { + ret.reg_status.in_use = TRUE; + } + + (*bta_hd_cb.p_cback)(BTA_HD_REGISTER_APP_EVT, &ret); +} + +/******************************************************************************* + * + * Function bta_hd_unregister_act + * + * Description Unregisters SDP record + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tBTA_HD_STATUS status = BTA_HD_OK; + + APPL_TRACE_API("%s", __func__); + + // application is no longer registered so we do not want incoming connections + HID_DevSetIncomingPolicy(FALSE); + + if (bta_hd_cb.sdp_handle != 0) { + SDP_DeleteRecord(bta_hd_cb.sdp_handle); + } + + bta_hd_cb.sdp_handle = 0; + bta_sys_remove_uuid(UUID_SERVCLASS_HUMAN_INTERFACE); + + (*bta_hd_cb.p_cback)(BTA_HD_UNREGISTER_APP_EVT, (tBTA_HD *)&status); +} + +/******************************************************************************* + * + * Function bta_hd_unregister2_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +void bta_hd_unregister2_act(tBTA_HD_DATA *p_data) +{ + APPL_TRACE_API("%s", __func__); + + // close first + bta_hd_close_act(p_data); + + // then unregister + bta_hd_unregister_act(p_data); + + if (bta_hd_cb.disable_w4_close) { + bta_hd_api_disable(); + } +} + +/******************************************************************************* + * + * Function bta_hd_connect_act + * + * Description Connect to device (must be virtually plugged) + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + do { + ret = HID_DevPlugDevice(p_ctrl->addr); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevPlugDevice returned %d", __func__, ret); + return; + } + + ret = HID_DevConnect(); + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevConnect returned %d", __func__, ret); + return; + } + } while (0); + + bdcpy(cback_data.conn.bda, p_ctrl->addr); + cback_data.conn.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTING; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_disconnect_act + * + * Description Disconnect from device + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_disconnect_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + ret = HID_DevDisconnect(); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevDisconnect returned %d", __func__, ret); + return; + } + + if (HID_DevGetDevice(&cback_data.conn.bda) == HID_SUCCESS) { + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTING; + bta_hd_cb.p_cback(BTA_HD_CLOSE_EVT, &cback_data); + } +} + +/******************************************************************************* + * + * Function bta_hd_add_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_remove_device_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_DEVICE_CTRL *p_ctrl = (tBTA_HD_DEVICE_CTRL *)p_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevUnplugDevice(p_ctrl->addr); +} + +/******************************************************************************* + * + * Function bta_hd_send_report_act + * + * Description Sends report + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_SEND_REPORT *p_report = (tBTA_HD_SEND_REPORT *)p_data; + uint8_t channel; + uint8_t report_id; + tBTA_HD cback_data; + tHID_STATUS ret; + + APPL_TRACE_VERBOSE("%s", __func__); + + channel = p_report->use_intr ? HID_CHANNEL_INTR : HID_CHANNEL_CTRL; + report_id = (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) ? p_report->id : 0x00; + + ret = HID_DevSendReport(channel, p_report->type, report_id, p_report->len, p_report->data); + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + + cback_data.send_report.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.send_report.reason = ret; + cback_data.send_report.report_id = report_id; + cback_data.send_report.report_type = p_report->type; + bta_hd_cb.p_cback(BTA_HD_SEND_REPORT_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_report_error_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_REPORT_ERR *p_report = (tBTA_HD_REPORT_ERR *)p_data; + tHID_STATUS ret; + tBTA_HD cback_data; + + APPL_TRACE_API("%s: error = %d", __func__, p_report->error); + + ret = HID_DevReportError(p_report->error); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevReportError returned %d", __func__, ret); + } + + cback_data.report_err.status = (ret == HID_SUCCESS ? BTA_HD_OK : BTA_HD_ERROR); + cback_data.report_err.reason = ret; + bta_hd_cb.p_cback(BTA_HD_REPORT_ERR_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_act + * + * Description Sends Virtual Cable Unplug + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_act(UNUSED_ATTR tBTA_HD_DATA *p_data) +{ + tHID_STATUS ret; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.vc_unplug = TRUE; + ret = HID_DevVirtualCableUnplug(); + + if (ret != HID_SUCCESS) { + APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); + } + + /* trigger PM */ + bta_sys_busy(BTA_ID_HD, 1, bta_hd_cb.bd_addr); + bta_sys_idle(BTA_ID_HD, 1, bta_hd_cb.bd_addr); +} + +/******************************************************************************* + * + * Function bta_hd_open_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_open_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + HID_DevPlugDevice(p_cback->addr); + bta_sys_conn_open(BTA_ID_HD, 1, p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_CONNECTED; + bta_hd_cb.p_cback(BTA_HD_OPEN_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_close_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_close_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + tBTA_HD_EVT cback_event = BTA_HD_CLOSE_EVT; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + if (bta_hd_cb.vc_unplug) { + bta_hd_cb.vc_unplug = FALSE; + HID_DevUnplugDevice(p_cback->addr); + cback_event = BTA_HD_VC_UNPLUG_EVT; + } + + bdcpy(cback_data.conn.bda, p_cback->addr); + memset(bta_hd_cb.bd_addr, 0, sizeof(BD_ADDR)); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + bta_hd_cb.p_cback(cback_event, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_intr_data_act + * + * Description Handles incoming DATA request on intr + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_INTR_DATA ret; + + APPL_TRACE_API("%s", __func__); + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_INTR_DATA_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_get_report_act + * + * Description Handles incoming GET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + bool rep_size_follows = p_cback->data; + BT_HDR *p_msg = p_cback->p_data; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_GET_REPORT ret = {0, 0, 0}; + uint16_t remaining_len = p_msg->len; + + APPL_TRACE_API("%s", __func__); + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + remaining_len--; + + if (bta_hd_cb.use_report_id) { + if (remaining_len < 1) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.report_id = *p_buf; + p_buf++; + remaining_len--; + } + + if (rep_size_follows) { + if (remaining_len < 2) { + APPL_TRACE_ERROR("%s invalid data, remaining_len:%d", __func__, remaining_len); + return; + } + ret.buffer_size = *p_buf | (*(p_buf + 1) << 8); + } + + (*bta_hd_cb.p_cback)(BTA_HD_GET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_report_act + * + * Description Handles incoming SET_REPORT request + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + BT_HDR *p_msg = p_cback->p_data; + uint16_t len = p_msg->len; + uint8_t *p_buf = (uint8_t *)(p_msg + 1) + p_msg->offset; + tBTA_HD_SET_REPORT ret = {0, 0, 0, NULL}; + + APPL_TRACE_API("%s", __func__); + + ret.report_type = *p_buf & HID_PAR_REP_TYPE_MASK; + p_buf++; + len--; + + if (bta_hd_cb.use_report_id || bta_hd_cb.boot_mode) { + ret.report_id = *p_buf; + len--; + p_buf++; + } else { + ret.report_id = 0; + } + + ret.len = len; + ret.p_data = p_buf; + (*bta_hd_cb.p_cback)(BTA_HD_SET_REPORT_EVT, (tBTA_HD *)&ret); + if (p_msg) { + osi_free(p_msg); + } +} + +/******************************************************************************* + * + * Function bta_hd_set_protocol_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_hd_cb.boot_mode = (p_cback->data == HID_PAR_PROTOCOL_BOOT_MODE); + cback_data.set_protocol = p_cback->data; + + (*bta_hd_cb.p_cback)(BTA_HD_SET_PROTOCOL_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_vc_unplug_done_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + tBTA_HD cback_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_conn_close(BTA_ID_HD, 1, p_cback->addr); + + HID_DevUnplugDevice(p_cback->addr); + + bdcpy(cback_data.conn.bda, p_cback->addr); + bdcpy(bta_hd_cb.bd_addr, p_cback->addr); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + (*bta_hd_cb.p_cback)(BTA_HD_VC_UNPLUG_EVT, &cback_data); +} + +/******************************************************************************* + * + * Function bta_hd_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_exit_suspend_act + * + * Description + * + * Returns void + * + ******************************************************************************/ +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data) +{ + tBTA_HD_CBACK_DATA *p_cback = (tBTA_HD_CBACK_DATA *)p_data; + + APPL_TRACE_API("%s", __func__); + + bta_sys_busy(BTA_ID_HD, 1, p_cback->addr); + bta_sys_idle(BTA_ID_HD, 1, p_cback->addr); +} + +/******************************************************************************* + * + * Function bta_hd_cback + * + * Description BTA HD callback function + * + * Returns void + * + ******************************************************************************/ +static void bta_hd_cback(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR *pdata) +{ + tBTA_HD_CBACK_DATA *p_buf = NULL; + uint16_t sm_event = BTA_HD_INVALID_EVT; + + APPL_TRACE_API("%s: event=%d", __func__, event); + + switch (event) { + case HID_DHOST_EVT_OPEN: + sm_event = BTA_HD_INT_OPEN_EVT; + break; + + case HID_DHOST_EVT_CLOSE: + sm_event = BTA_HD_INT_CLOSE_EVT; + break; + + case HID_DHOST_EVT_GET_REPORT: + sm_event = BTA_HD_INT_GET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_REPORT: + sm_event = BTA_HD_INT_SET_REPORT_EVT; + break; + + case HID_DHOST_EVT_SET_PROTOCOL: + sm_event = BTA_HD_INT_SET_PROTOCOL_EVT; + break; + + case HID_DHOST_EVT_INTR_DATA: + sm_event = BTA_HD_INT_INTR_DATA_EVT; + break; + + case HID_DHOST_EVT_VC_UNPLUG: + sm_event = BTA_HD_INT_VC_UNPLUG_EVT; + break; + + case HID_DHOST_EVT_SUSPEND: + sm_event = BTA_HD_INT_SUSPEND_EVT; + break; + + case HID_DHOST_EVT_EXIT_SUSPEND: + sm_event = BTA_HD_INT_EXIT_SUSPEND_EVT; + break; + } + + if (sm_event != BTA_HD_INVALID_EVT && + (p_buf = (tBTA_HD_CBACK_DATA *)osi_malloc(sizeof(tBTA_HD_CBACK_DATA) + sizeof(BT_HDR))) != NULL) { + p_buf->hdr.event = sm_event; + bdcpy(p_buf->addr, bd_addr); + p_buf->data = data; + p_buf->p_data = pdata; + + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_api.c b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c new file mode 100644 index 0000000000..33875b957a --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_api.c @@ -0,0 +1,287 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID DEVICE API in the subsystem of BTA. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include "osi/allocator.h" +#include +#include +#include + +/***************************************************************************** + * Constants + ****************************************************************************/ +static const tBTA_SYS_REG bta_hd_reg = {bta_hd_hdl_event, BTA_HdDisable}; +/******************************************************************************* + * + * Function BTA_HdEnable + * + * Description Enables HID device + * + * Returns void + * + ******************************************************************************/ +void BTA_HdEnable(tBTA_HD_CBACK *p_cback) +{ + tBTA_HD_API_ENABLE *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_register(BTA_ID_HD, &bta_hd_reg); + p_buf = (tBTA_HD_API_ENABLE *)osi_malloc((uint16_t)sizeof(tBTA_HD_API_ENABLE)); + if (p_buf != NULL) { + memset(p_buf, 0, sizeof(tBTA_HD_API_ENABLE)); + p_buf->hdr.event = BTA_HD_API_ENABLE_EVT; + p_buf->p_cback = p_cback; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdDisable + * + * Description Disables HID device. + * + * Returns void + * + ******************************************************************************/ +void BTA_HdDisable(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + bta_sys_deregister(BTA_ID_HD); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISABLE_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + *registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos) +{ + tBTA_HD_REGISTER_APP *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REGISTER_APP *)osi_malloc(sizeof(tBTA_HD_REGISTER_APP))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REGISTER_APP_EVT; + if (p_app_info->p_name) { + strncpy(p_buf->name, p_app_info->p_name, BTA_HD_APP_NAME_LEN); + p_buf->name[BTA_HD_APP_NAME_LEN] = '\0'; + } else { + p_buf->name[0] = '\0'; + } + if (p_app_info->p_description) { + strncpy(p_buf->description, p_app_info->p_description, BTA_HD_APP_DESCRIPTION_LEN); + p_buf->description[BTA_HD_APP_DESCRIPTION_LEN] = '\0'; + } else { + p_buf->description[0] = '\0'; + } + if (p_app_info->p_provider) { + strncpy(p_buf->provider, p_app_info->p_provider, BTA_HD_APP_PROVIDER_LEN); + p_buf->provider[BTA_HD_APP_PROVIDER_LEN] = '\0'; + } else { + p_buf->provider[0] = '\0'; + } + p_buf->subclass = p_app_info->subclass; + p_buf->d_len = p_app_info->descriptor.dl_len; + memcpy(p_buf->d_data, p_app_info->descriptor.dsc_list, p_app_info->descriptor.dl_len); + // copy qos data as-is + memcpy(&p_buf->in_qos, p_in_qos, sizeof(tBTA_HD_QOS_INFO)); + memcpy(&p_buf->out_qos, p_out_qos, sizeof(tBTA_HD_QOS_INFO)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + *unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_UNREGISTER_APP_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report) +{ + tBTA_HD_SEND_REPORT *p_buf; + APPL_TRACE_VERBOSE("%s", __func__); + if (p_report->len > BTA_HD_REPORT_LEN) { + APPL_TRACE_WARNING("%s, report len (%d) > MTU len (%d), can't send report." + " Increase value of HID_DEV_MTU_SIZE to send larger reports", + __func__, p_report->len, BTA_HD_REPORT_LEN); + return; + } + if ((p_buf = (tBTA_HD_SEND_REPORT *)osi_malloc(sizeof(tBTA_HD_SEND_REPORT))) != NULL) { + p_buf->hdr.event = BTA_HD_API_SEND_REPORT_EVT; + p_buf->use_intr = p_report->use_intr; + p_buf->type = p_report->type; + p_buf->id = p_report->id; + p_buf->len = p_report->len; + memcpy(p_buf->data, p_report->p_data, p_report->len); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_VC_UNPLUG_EVT; + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_CONNECT_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void) +{ + BT_HDR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (BT_HDR *)osi_malloc(sizeof(BT_HDR))) != NULL) { + p_buf->event = BTA_HD_API_DISCONNECT_EVT; + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_ADD_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr) +{ + tBTA_HD_DEVICE_CTRL *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_DEVICE_CTRL *)osi_malloc(sizeof(tBTA_HD_DEVICE_CTRL))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REMOVE_DEVICE_EVT; + memcpy(p_buf->addr, addr, sizeof(BD_ADDR)); + bta_sys_sendmsg(p_buf); + } +} +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error) +{ + tBTA_HD_REPORT_ERR *p_buf; + APPL_TRACE_API("%s", __func__); + if ((p_buf = (tBTA_HD_REPORT_ERR *)osi_malloc(sizeof(tBTA_HD_REPORT_ERR))) != NULL) { + p_buf->hdr.event = BTA_HD_API_REPORT_ERROR_EVT; + p_buf->error = error; + bta_sys_sendmsg(p_buf); + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_main.c b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c new file mode 100644 index 0000000000..4400b3e172 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c @@ -0,0 +1,320 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID host main functions and state machine. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#if defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE) + +#include "bta/bta_hd_api.h" +#include "bta_hd_int.h" +#include + +/***************************************************************************** + * Constants and types + ****************************************************************************/ +/* state machine states */ +enum { + BTA_HD_INIT_ST, + BTA_HD_IDLE_ST, /* not connected, waiting for connection */ + BTA_HD_CONN_ST, /* host connected */ + BTA_HD_TRANSIENT_TO_INIT_ST, /* transient state: going back from CONN to INIT */ +}; +typedef uint8_t tBTA_HD_STATE; + +/* state machine actions */ +enum { + BTA_HD_REGISTER_ACT, + BTA_HD_UNREGISTER_ACT, + BTA_HD_UNREGISTER2_ACT, + BTA_HD_CONNECT_ACT, + BTA_HD_DISCONNECT_ACT, + BTA_HD_ADD_DEVICE_ACT, + BTA_HD_REMOVE_DEVICE_ACT, + BTA_HD_SEND_REPORT_ACT, + BTA_HD_REPORT_ERROR_ACT, + BTA_HD_VC_UNPLUG_ACT, + BTA_HD_OPEN_ACT, + BTA_HD_CLOSE_ACT, + BTA_HD_INTR_DATA_ACT, + BTA_HD_GET_REPORT_ACT, + BTA_HD_SET_REPORT_ACT, + BTA_HD_SET_PROTOCOL_ACT, + BTA_HD_VC_UNPLUG_DONE_ACT, + BTA_HD_SUSPEND_ACT, + BTA_HD_EXIT_SUSPEND_ACT, + BTA_HD_NUM_ACTIONS +}; + +#define BTA_HD_IGNORE BTA_HD_NUM_ACTIONS + +typedef void (*tBTA_HD_ACTION)(tBTA_HD_DATA *p_data); +/* action functions */ +const tBTA_HD_ACTION bta_hd_action[] = { + bta_hd_register_act, bta_hd_unregister_act, bta_hd_unregister2_act, bta_hd_connect_act, + bta_hd_disconnect_act, bta_hd_add_device_act, bta_hd_remove_device_act, bta_hd_send_report_act, + bta_hd_report_error_act, bta_hd_vc_unplug_act, bta_hd_open_act, bta_hd_close_act, + bta_hd_intr_data_act, bta_hd_get_report_act, bta_hd_set_report_act, bta_hd_set_protocol_act, + bta_hd_vc_unplug_done_act, bta_hd_suspend_act, bta_hd_exit_suspend_act, +}; + +/* state table information */ +#define BTA_HD_ACTION 0 /* position of action */ +#define BTA_HD_NEXT_STATE 1 /* position of next state */ +#define BTA_HD_NUM_COLS 2 /* number of columns */ + +const uint8_t bta_hd_st_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_REGISTER_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_INIT_ST}, +}; + +const uint8_t bta_hd_st_idle[][BTA_HD_NUM_COLS] = { + /* Event Action Next state + */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_UNREGISTER_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_CONNECT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_OPEN_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, +}; + +const uint8_t bta_hd_st_conn[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_DISCONNECT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_ADD_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_REPORT_ERROR_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_CONN_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_CLOSE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_INTR_DATA_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_GET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_SET_REPORT_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_SET_PROTOCOL_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_DONE_ACT, BTA_HD_IDLE_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_SUSPEND_ACT, BTA_HD_CONN_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_EXIT_SUSPEND_ACT, BTA_HD_CONN_ST}, +}; + +const uint8_t bta_hd_st_transient_to_init[][BTA_HD_NUM_COLS] = { + /* Event Action Next state */ + /* BTA_HD_API_REGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_UNREGISTER_APP_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_CONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_DISCONNECT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_ADD_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_GET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_REPORT_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_SET_PROTOCOL_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_VC_UNPLUG_EVT */ {BTA_HD_UNREGISTER2_ACT, BTA_HD_INIT_ST}, + /* BTA_HD_INT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, + /* BTA_HD_INT_EXIT_SUSPEND_EVT */ {BTA_HD_IGNORE, BTA_HD_TRANSIENT_TO_INIT_ST}, +}; + +/* type for state table */ +typedef const uint8_t (*tBTA_HD_ST_TBL)[BTA_HD_NUM_COLS]; +/* state table */ +const tBTA_HD_ST_TBL bta_hd_st_tbl[] = {bta_hd_st_init, bta_hd_st_idle, bta_hd_st_conn, bta_hd_st_transient_to_init}; + +/***************************************************************************** + * Global data + ****************************************************************************/ +#if BTA_DYNAMIC_MEMORY == FALSE +tBTA_HD_CB bta_hd_cb; +#else +tBTA_HD_CB *bta_hd_cb_ptr; +#endif + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code); +static const char *bta_hd_state_code(tBTA_HD_STATE state_code); + +/******************************************************************************* + * + * Function bta_hd_sm_execute + * + * Description State machine event handling function for HID Device + * + * Returns void + * + ******************************************************************************/ +void bta_hd_sm_execute(uint16_t event, tBTA_HD_DATA *p_data) +{ + tBTA_HD_ST_TBL state_table; + tBTA_HD_STATE prev_state; + uint8_t action; + tBTA_HD cback_data; + + APPL_TRACE_EVENT("%s: state=%s (%d) event=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state, + bta_hd_evt_code(event), event); + + prev_state = bta_hd_cb.state; + memset(&cback_data, 0, sizeof(tBTA_HD)); + state_table = bta_hd_st_tbl[bta_hd_cb.state]; + event &= 0xff; + + if ((action = state_table[event][BTA_HD_ACTION]) < BTA_HD_IGNORE) { + (*bta_hd_action[action])(p_data); + } + + bta_hd_cb.state = state_table[event][BTA_HD_NEXT_STATE]; + + if (bta_hd_cb.state != prev_state) { + APPL_TRACE_EVENT("%s: [new] state=%s (%d)", __func__, bta_hd_state_code(bta_hd_cb.state), bta_hd_cb.state); + } + return; +} + +/******************************************************************************* + * + * Function bta_hd_hdl_event + * + * Description HID device main event handling function. + * + * Returns void + * + ******************************************************************************/ +bool bta_hd_hdl_event(BT_HDR *p_msg) +{ + APPL_TRACE_API("%s: p_msg->event=%d", __func__, p_msg->event); + + switch (p_msg->event) { + case BTA_HD_API_ENABLE_EVT: + bta_hd_api_enable((tBTA_HD_DATA *)p_msg); + break; + case BTA_HD_API_DISABLE_EVT: + if (bta_hd_cb.state == BTA_HD_CONN_ST) { + APPL_TRACE_WARNING("%s: host connected, disconnect before disabling", __func__); + // unregister (and disconnect) + bta_hd_cb.disable_w4_close = TRUE; + bta_hd_sm_execute(BTA_HD_API_UNREGISTER_APP_EVT, (tBTA_HD_DATA *)p_msg); + } else { + bta_hd_api_disable(); + } + break; + default: + bta_hd_sm_execute(p_msg->event, (tBTA_HD_DATA *)p_msg); + } + return (TRUE); +} + +static const char *bta_hd_evt_code(tBTA_HD_INT_EVT evt_code) +{ + switch (evt_code) { + case BTA_HD_API_REGISTER_APP_EVT: + return "BTA_HD_API_REGISTER_APP_EVT"; + case BTA_HD_API_UNREGISTER_APP_EVT: + return "BTA_HD_API_UNREGISTER_APP_EVT"; + case BTA_HD_API_CONNECT_EVT: + return "BTA_HD_API_CONNECT_EVT"; + case BTA_HD_API_DISCONNECT_EVT: + return "BTA_HD_API_DISCONNECT_EVT"; + case BTA_HD_API_ADD_DEVICE_EVT: + return "BTA_HD_API_ADD_DEVICE_EVT"; + case BTA_HD_API_REMOVE_DEVICE_EVT: + return "BTA_HD_API_REMOVE_DEVICE_EVT"; + case BTA_HD_API_SEND_REPORT_EVT: + return "BTA_HD_API_SEND_REPORT_EVT"; + case BTA_HD_API_REPORT_ERROR_EVT: + return "BTA_HD_API_REPORT_ERROR_EVT"; + case BTA_HD_API_VC_UNPLUG_EVT: + return "BTA_HD_API_VC_UNPLUG_EVT"; + case BTA_HD_INT_OPEN_EVT: + return "BTA_HD_INT_OPEN_EVT"; + case BTA_HD_INT_CLOSE_EVT: + return "BTA_HD_INT_CLOSE_EVT"; + case BTA_HD_INT_INTR_DATA_EVT: + return "BTA_HD_INT_INTR_DATA_EVT"; + case BTA_HD_INT_GET_REPORT_EVT: + return "BTA_HD_INT_GET_REPORT_EVT"; + case BTA_HD_INT_SET_REPORT_EVT: + return "BTA_HD_INT_SET_REPORT_EVT"; + case BTA_HD_INT_SET_PROTOCOL_EVT: + return "BTA_HD_INT_SET_PROTOCOL_EVT"; + case BTA_HD_INT_VC_UNPLUG_EVT: + return "BTA_HD_INT_VC_UNPLUG_EVT"; + case BTA_HD_INT_SUSPEND_EVT: + return "BTA_HD_INT_SUSPEND_EVT"; + case BTA_HD_INT_EXIT_SUSPEND_EVT: + return "BTA_HD_INT_EXIT_SUSPEND_EVT"; + default: + return ""; + } +} + +static const char *bta_hd_state_code(tBTA_HD_STATE state_code) +{ + switch (state_code) { + case BTA_HD_INIT_ST: + return "BTA_HD_INIT_ST"; + case BTA_HD_IDLE_ST: + return "BTA_HD_IDLE_ST"; + case BTA_HD_CONN_ST: + return "BTA_HD_CONN_ST"; + case BTA_HD_TRANSIENT_TO_INIT_ST: + return "BTA_HD_TRANSIENT_TO_INIT_ST"; + default: + return ""; + } +} +#endif /* BTA_HD_INCLUDED */ diff --git a/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h b/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h new file mode 100644 index 0000000000..7a515970e6 --- /dev/null +++ b/components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h @@ -0,0 +1,168 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2005-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains BTA HID Device internal definitions + * + ******************************************************************************/ +#ifndef BTA_HD_INT_H +#define BTA_HD_INT_H + +#include "bta/bta_hd_api.h" +#include "bta/bta_sys.h" +#include "stack/hiddefs.h" + +enum { + BTA_HD_API_REGISTER_APP_EVT = BTA_SYS_EVT_START(BTA_ID_HD), + BTA_HD_API_UNREGISTER_APP_EVT, + BTA_HD_API_CONNECT_EVT, + BTA_HD_API_DISCONNECT_EVT, + BTA_HD_API_ADD_DEVICE_EVT, + BTA_HD_API_REMOVE_DEVICE_EVT, + BTA_HD_API_SEND_REPORT_EVT, + BTA_HD_API_REPORT_ERROR_EVT, + BTA_HD_API_VC_UNPLUG_EVT, + BTA_HD_INT_OPEN_EVT, + BTA_HD_INT_CLOSE_EVT, + BTA_HD_INT_INTR_DATA_EVT, + BTA_HD_INT_GET_REPORT_EVT, + BTA_HD_INT_SET_REPORT_EVT, + BTA_HD_INT_SET_PROTOCOL_EVT, + BTA_HD_INT_VC_UNPLUG_EVT, + BTA_HD_INT_SUSPEND_EVT, + BTA_HD_INT_EXIT_SUSPEND_EVT, + /* handled outside state machine */ + BTA_HD_API_ENABLE_EVT, + BTA_HD_API_DISABLE_EVT +}; +typedef uint16_t tBTA_HD_INT_EVT; +#define BTA_HD_INVALID_EVT (BTA_HD_API_DISABLE_EVT + 1) +typedef struct { + BT_HDR hdr; + tBTA_HD_CBACK *p_cback; +} tBTA_HD_API_ENABLE; +#define BTA_HD_APP_NAME_LEN 50 +#define BTA_HD_APP_DESCRIPTION_LEN 50 +#define BTA_HD_APP_PROVIDER_LEN 50 +#define BTA_HD_APP_DESCRIPTOR_LEN 2048 +#define BTA_HD_STATE_DISABLED 0x00 +#define BTA_HD_STATE_ENABLED 0x01 +#define BTA_HD_STATE_IDLE 0x02 +#define BTA_HD_STATE_CONNECTED 0x03 +#define BTA_HD_STATE_DISABLING 0x04 +#define BTA_HD_STATE_REMOVING 0x05 +typedef struct { + BT_HDR hdr; + char name[BTA_HD_APP_NAME_LEN + 1]; + char description[BTA_HD_APP_DESCRIPTION_LEN + 1]; + char provider[BTA_HD_APP_PROVIDER_LEN + 1]; + uint8_t subclass; + uint16_t d_len; + uint8_t d_data[BTA_HD_APP_DESCRIPTOR_LEN]; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} tBTA_HD_REGISTER_APP; + +#define BTA_HD_REPORT_LEN HID_DEV_MTU_SIZE + +typedef struct { + BT_HDR hdr; + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t data[BTA_HD_REPORT_LEN]; +} tBTA_HD_SEND_REPORT; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; +} tBTA_HD_DEVICE_CTRL; + +typedef struct { + BT_HDR hdr; + uint8_t error; +} tBTA_HD_REPORT_ERR; + +/* union of all event data types */ +typedef union { + BT_HDR hdr; + tBTA_HD_API_ENABLE api_enable; + tBTA_HD_REGISTER_APP register_app; + tBTA_HD_SEND_REPORT send_report; + tBTA_HD_DEVICE_CTRL device_ctrl; + tBTA_HD_REPORT_ERR report_err; +} tBTA_HD_DATA; + +typedef struct { + BT_HDR hdr; + BD_ADDR addr; + uint32_t data; + BT_HDR *p_data; +} tBTA_HD_CBACK_DATA; + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ +typedef struct { + tBTA_HD_CBACK *p_cback; + uint32_t sdp_handle; + uint8_t trace_level; + uint8_t state; + BD_ADDR bd_addr; + bool use_report_id; + bool boot_mode; + bool vc_unplug; + bool disable_w4_close; +} tBTA_HD_CB; + +#if BTA_DYNAMIC_MEMORY == FALSE +extern tBTA_HD_CB bta_hd_cb; +#else +extern tBTA_HD_CB *bta_hd_cb_ptr; +#define bta_hd_cb (*bta_hd_cb_ptr) +#endif + +/***************************************************************************** + * Function prototypes + ****************************************************************************/ +extern bool bta_hd_hdl_event(BT_HDR *p_msg); +extern void bta_hd_api_enable(tBTA_HD_DATA *p_data); +extern void bta_hd_api_disable(void); +extern void bta_hd_register_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister_act(tBTA_HD_DATA *p_data); +extern void bta_hd_unregister2_act(tBTA_HD_DATA *p_data); +extern void bta_hd_connect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_disconnect_act(tBTA_HD_DATA *p_data); +extern void bta_hd_add_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_remove_device_act(tBTA_HD_DATA *p_data); +extern void bta_hd_send_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_act(tBTA_HD_DATA *p_data); +extern void bta_hd_open_act(tBTA_HD_DATA *p_data); +extern void bta_hd_close_act(tBTA_HD_DATA *p_data); +extern void bta_hd_intr_data_act(tBTA_HD_DATA *p_data); +extern void bta_hd_get_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_report_act(tBTA_HD_DATA *p_data); +extern void bta_hd_set_protocol_act(tBTA_HD_DATA *p_data); +extern void bta_hd_vc_unplug_done_act(tBTA_HD_DATA *p_data); +extern void bta_hd_suspend_act(tBTA_HD_DATA *p_data); +extern void bta_hd_exit_suspend_act(tBTA_HD_DATA *p_data); + +#endif diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_act.c b/components/bt/host/bluedroid/bta/hh/bta_hh_act.c index ce34307707..3edce53bc6 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_act.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_act.c @@ -323,6 +323,7 @@ void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) p_cb->sec_mask = p_data->api_conn.sec_mask; p_cb->mode = p_data->api_conn.mode; + p_cb->new_mode = p_data->api_conn.mode; bta_hh_cb.p_cur = p_cb; #if (BTA_HH_LE_INCLUDED == TRUE) @@ -451,6 +452,8 @@ void bta_hh_sdp_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) HID_HostRemoveDev( p_cb->incoming_hid_handle); } conn_dat.status = status; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn_dat); /* move state machine W4_CONN ->IDLE */ @@ -521,6 +524,8 @@ void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) memset((void *)&conn, 0, sizeof (tBTA_HH_CONN)); conn.handle = dev_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; bdcpy(conn.bda, p_cb->addr); /* increase connection number */ @@ -587,6 +592,7 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) APPL_TRACE_EVENT ("bta_hh_open_act: Device[%d] connected", dev_handle); #endif + p_cb->incoming_conn = TRUE; /* SDP has been done */ if (p_cb->app_id != 0) { bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, p_data); @@ -594,7 +600,6 @@ void bta_hh_open_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* app_id == 0 indicates an incoming conenction request arrives without SDP performed, do it first */ { - p_cb->incoming_conn = TRUE; /* store the handle here in case sdp fails - need to disconnect */ p_cb->incoming_hid_handle = dev_handle; @@ -676,6 +681,11 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) case BTA_HH_SET_IDLE_EVT : cback_data.handle = p_cb->hid_handle; cback_data.status = bta_hh_get_trans_status(p_data->hid_cback.data); + if (cback_data.status == BTA_HH_OK) { + p_cb->mode = p_cb->new_mode; + } else { + p_cb->new_mode = p_cb->mode; + } (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&cback_data); p_cb->w4_evt = 0; break; @@ -684,6 +694,8 @@ void bta_hh_handsk_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) case BTA_HH_OPEN_EVT: conn.status = p_data->hid_cback.data ? BTA_HH_ERR_PROTO : BTA_HH_OK; conn.handle = p_cb->hid_handle; + /* check if host initiate the connection*/ + conn.is_orig = !p_cb->incoming_conn; bdcpy(conn.bda, p_cb->addr); (* bta_hh_cb.p_cback)(p_cb->w4_evt, (tBTA_HH *)&conn); #if BTA_HH_DEBUG @@ -787,6 +799,8 @@ void bta_hh_open_failure(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) conn_dat.handle = p_cb->hid_handle; conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; bdcpy(conn_dat.bda, p_cb->addr); HID_HostCloseDev(p_cb->hid_handle); @@ -836,6 +850,8 @@ void bta_hh_close_act (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) /* Failure in opening connection */ conn_dat.handle = p_cb->hid_handle; conn_dat.status = (reason == HID_ERR_AUTH_FAILED) ? BTA_HH_ERR_AUTH_FAILED : BTA_HH_ERR; + /* check if host initiate the connection*/ + conn_dat.is_orig = !p_cb->incoming_conn; bdcpy(conn_dat.bda, p_cb->addr); HID_HostCloseDev(p_cb->hid_handle); @@ -1019,7 +1035,9 @@ void bta_hh_maint_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) *******************************************************************************/ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { + tHID_STATUS status; tBTA_HH_CBDATA cbdata = {BTA_HH_OK, 0}; + tBTA_HH_API_SENDDATA send_data = {BTA_HH_OK, 0, 0}; UINT16 event = (p_data->api_sndcmd.t_type - BTA_HH_FST_BTE_TRANS_EVT) + BTA_HH_FST_TRANS_CB_EVT; @@ -1031,25 +1049,33 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) { cbdata.handle = p_cb->hid_handle; + send_data.handle = p_cb->hid_handle; /* match up BTE/BTA report/boot mode def */ if (p_data->api_sndcmd.t_type == HID_TRANS_SET_PROTOCOL) { + p_cb->new_mode = p_data->api_sndcmd.param; p_data->api_sndcmd.param = ( p_data->api_sndcmd.param == BTA_HH_PROTO_RPT_MODE) ? \ HID_PAR_PROTOCOL_REPORT : HID_PAR_PROTOCOL_BOOT_MODE; } - if (HID_HostWriteDev (p_cb->hid_handle, - p_data->api_sndcmd.t_type, - p_data->api_sndcmd.param, - p_data->api_sndcmd.data, - p_data->api_sndcmd.rpt_id, - p_data->api_sndcmd.p_data) != HID_SUCCESS) { - APPL_TRACE_ERROR("HID_HostWriteDev Error "); + status = HID_HostWriteDev(p_cb->hid_handle, p_data->api_sndcmd.t_type, p_data->api_sndcmd.param, + p_data->api_sndcmd.data, p_data->api_sndcmd.rpt_id, p_data->api_sndcmd.p_data); + if (status != HID_SUCCESS) { + APPL_TRACE_ERROR("HID_HostWriteDev status:%d", status); cbdata.status = BTA_HH_ERR; + send_data.status = BTA_HH_ERR; - if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL && - p_data->api_sndcmd.t_type != HID_TRANS_DATA) { - (* bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + if (p_data->api_sndcmd.t_type != HID_TRANS_CONTROL) { + switch (p_data->api_sndcmd.t_type) { + case HID_TRANS_DATA: + event = BTA_HH_DATA_EVT; + send_data.reason = status; + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&send_data); + break; + default: + (*bta_hh_cb.p_cback)(event, (tBTA_HH *)&cbdata); + break; + } } else if (p_data->api_sndcmd.param == BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG) { (* bta_hh_cb.p_cback)(BTA_HH_VC_UNPLUG_EVT, (tBTA_HH *)&cbdata); } @@ -1070,6 +1096,7 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) p_cb->w4_evt = event; break; case HID_TRANS_DATA: /* output report */ + (*bta_hh_cb.p_cback)(BTA_HH_DATA_EVT, (tBTA_HH *)&send_data); /* fall through */ case HID_TRANS_CONTROL: /* no handshake event will be generated */ @@ -1098,7 +1125,6 @@ void bta_hh_write_dev_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data) bta_sys_busy(BTA_ID_HH, p_cb->app_id, p_cb->addr); } } - } return; } diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c b/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c index 65e06c7345..383f23262c 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c @@ -24,9 +24,9 @@ ******************************************************************************/ #include "common/bt_target.h" -#include "bta/bta_hh_api.h" #if defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" /* max number of device types supported by BTA */ diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c index 967cd61b08..a80314efa6 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_le.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_le.c @@ -32,7 +32,7 @@ #include "bta/utl.h" #define LOG_TAG "bt_bta_hh" -#include "osi/include/log.h" +// #include "osi/include/log.h" #ifndef BTA_HH_LE_RECONN #define BTA_HH_LE_RECONN TRUE diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_main.c b/components/bt/host/bluedroid/bta/hh/bta_hh_main.c index a5ca868723..663d28e96f 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_main.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_main.c @@ -292,6 +292,8 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA *p_data) bdcpy(cback_data.conn.bda, ((tBTA_HH_API_CONN *)p_data)->bd_addr); cback_data.conn.status = BTA_HH_ERR_DB_FULL; cback_data.conn.handle = BTA_HH_INVALID_HANDLE; + /* check if host initiate the connection*/ + cback_data.conn.is_orig = !p_cb->incoming_conn; break; /* DB full, BTA_HhAddDev */ case BTA_HH_API_MAINT_DEV_EVT: @@ -340,7 +342,7 @@ void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA *p_data) default: /* invalid handle, call bad API event */ - APPL_TRACE_ERROR("wrong device handle: [%d]", p_data->hdr.layer_specific); + APPL_TRACE_ERROR("wrong device handle: [%d], event:%d", p_data->hdr.layer_specific, event - BTA_HH_API_OPEN_EVT); /* Free the callback buffer now */ if (p_data != NULL && p_data->hid_cback.p_data != NULL) { osi_free(p_data->hid_cback.p_data); @@ -443,6 +445,10 @@ BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg) } } else if (p_msg->event == BTA_HH_INT_OPEN_EVT) { index = bta_hh_find_cb(((tBTA_HH_CBACK_DATA *)p_msg)->addr); + uint8_t hdl = BTA_HH_IDX_INVALID; + if (HID_HostGetDev(((tBTA_HH_CBACK_DATA *)p_msg)->addr, &hdl) == HID_SUCCESS && hdl != BTA_HH_IDX_INVALID) { + bta_hh_cb.cb_index[hdl] = bta_hh_cb.kdev[index].index; + } } else { index = bta_hh_dev_handle_to_cb_idx((UINT8)p_msg->layer_specific); } diff --git a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c index 121e11834b..8ca8b09c45 100644 --- a/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c +++ b/components/bt/host/bluedroid/bta/hh/bta_hh_utils.c @@ -237,7 +237,7 @@ BOOLEAN bta_hh_tod_spt(tBTA_HH_DEV_CB *p_cb, UINT8 sub_class) } } #if BTA_HH_DEBUG - APPL_TRACE_EVENT("bta_hh_tod_spt sub_class:0x%x NOT supported", sub_class); + APPL_TRACE_ERROR("bta_hh_tod_spt sub_class:0x%x NOT supported", sub_class); #endif return FALSE; } @@ -460,9 +460,11 @@ void bta_hh_cleanup_disable(tBTA_HH_STATUS status) } utl_freebuf((void **)&bta_hh_cb.p_disc_db); - (* bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH *)&status); - /* all connections are down, no waiting for diconnect */ - memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + if (bta_hh_cb.p_cback) { + (*bta_hh_cb.p_cback)(BTA_HH_DISABLE_EVT, (tBTA_HH*)&status); + /* all connections are down, no waiting for diconnect */ + memset(&bta_hh_cb, 0, sizeof(tBTA_HH_CB)); + } } /******************************************************************************* diff --git a/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h b/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h index 58a8b59a3d..0efb2fcab4 100644 --- a/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h +++ b/components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h @@ -243,6 +243,7 @@ typedef struct { UINT8 incoming_hid_handle; /* temporary handle for incoming connection? */ BOOLEAN opened; /* TRUE if device successfully opened HID connection */ tBTA_HH_PROTO_MODE mode; /* protocol mode */ + tBTA_HH_PROTO_MODE new_mode; /* protocol mode */ tBTA_HH_STATE state; /* CB state */ #if (BTA_HH_LE_INCLUDED == TRUE) @@ -364,6 +365,7 @@ extern void bta_hh_disc_cmpl(void); extern tBTA_HH_STATUS bta_hh_read_ssr_param(BD_ADDR bd_addr, UINT16 *p_max_ssr_lat, UINT16 *p_min_ssr_tout); /* functions for LE HID */ +#if (BTA_HH_LE_INCLUDED == TRUE) extern void bta_hh_le_enable(void); extern BOOLEAN bta_hh_le_is_hh_gatt_if(tBTA_GATTC_IF client_if); extern void bta_hh_le_deregister(void); @@ -391,6 +393,7 @@ extern void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); extern void bta_hh_le_update_scpp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); extern void bta_hh_le_notify_enc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data); extern void bta_hh_ci_load_rpt (tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf); +#endif #if BTA_HH_DEBUG extern void bta_hh_trace_dev_db(void); diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_api.h index 32e59d27e2..735c210f2f 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_api.h @@ -78,7 +78,7 @@ typedef UINT8 tBTA_STATUS; #define BTA_SAP_SERVICE_ID 17 /* SIM Access profile */ #define BTA_A2DP_SINK_SERVICE_ID 18 /* A2DP Sink */ #define BTA_AVRCP_SERVICE_ID 19 /* A/V remote control */ -#define BTA_HID_SERVICE_ID 20 /* HID */ +#define BTA_HID_SERVICE_ID 20 /* HID Host*/ #define BTA_VDP_SERVICE_ID 21 /* Video distribution */ #define BTA_PBAP_SERVICE_ID 22 /* PhoneBook Access Server*/ #define BTA_HSP_HS_SERVICE_ID 23 /* HFP HS role */ @@ -1331,8 +1331,8 @@ typedef UINT8 tBTA_DM_PM_ACTION; #endif #ifndef BTA_DM_PM_SNIFF2_MAX -#define BTA_DM_PM_SNIFF2_MAX 180 -#define BTA_DM_PM_SNIFF2_MIN 150 +#define BTA_DM_PM_SNIFF2_MAX 54 //180 +#define BTA_DM_PM_SNIFF2_MIN 30 //150 #define BTA_DM_PM_SNIFF2_ATTEMPT 4 #define BTA_DM_PM_SNIFF2_TIMEOUT 1 #endif @@ -1345,8 +1345,8 @@ typedef UINT8 tBTA_DM_PM_ACTION; #endif #ifndef BTA_DM_PM_SNIFF4_MAX -#define BTA_DM_PM_SNIFF4_MAX 54 -#define BTA_DM_PM_SNIFF4_MIN 30 +#define BTA_DM_PM_SNIFF4_MAX 18 //54 +#define BTA_DM_PM_SNIFF4_MIN 10 //30 #define BTA_DM_PM_SNIFF4_ATTEMPT 4 #define BTA_DM_PM_SNIFF4_TIMEOUT 1 #endif diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h new file mode 100644 index 0000000000..1c7e708e2f --- /dev/null +++ b/components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h @@ -0,0 +1,295 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef BTA_HD_API_H +#define BTA_HD_API_H + +#include "bta_api.h" +#include "stack/hidd_api.h" + +#if BTA_HD_INCLUDED == TRUE + +/***************************************************************************** + * Constants and Type Definitions + ****************************************************************************/ + +#ifndef BTA_HD_DEBUG +#define BTA_HD_DEBUG TRUE +#endif + +/* BTA HID Device callback events */ +#define BTA_HD_ENABLE_EVT 0 /* BT-HD enabled */ +#define BTA_HD_DISABLE_EVT 1 /* BT-HD disabled */ +#define BTA_HD_REGISTER_APP_EVT 2 /* application registered */ +#define BTA_HD_UNREGISTER_APP_EVT 3 /* application unregistered */ +#define BTA_HD_OPEN_EVT 4 /* connection to host opened */ +#define BTA_HD_CLOSE_EVT 5 /* connection to host closed */ +#define BTA_HD_GET_REPORT_EVT 6 /* GET_REPORT request from host */ +#define BTA_HD_SET_REPORT_EVT 7 /* SET_REPORT request from host */ +#define BTA_HD_SET_PROTOCOL_EVT 8 /* SET_PROTOCOL request from host */ +#define BTA_HD_INTR_DATA_EVT 9 /* DATA received from host on intr */ +#define BTA_HD_VC_UNPLUG_EVT 10 /* Virtual Cable Unplug */ +// #define BTA_HD_CONN_STATE_EVT 11 /* Report connection state change */ +#define BTA_HD_SEND_REPORT_EVT 12 /* Send report finish */ +#define BTA_HD_REPORT_ERR_EVT 13 /* Report Handshake finish */ +#define BTA_HD_API_ERR_EVT 99 /* BT-HD API error */ + +typedef uint16_t tBTA_HD_EVT; + +enum { BTA_HD_OK, BTA_HD_ERROR }; + +typedef enum { + BTA_HD_CONN_STATE_CONNECTED, + BTA_HD_CONN_STATE_CONNECTING, + BTA_HD_CONN_STATE_DISCONNECTED, + BTA_HD_CONN_STATE_DISCONNECTING, + BTA_HD_CONN_STATE_UNKNOWN +} tBTA_HD_CONN_STAT; + +typedef uint8_t tBTA_HD_STATUS; +typedef tHID_DEV_DSCP_INFO tBTA_HD_DEV_DESCR; + +typedef struct { + char *p_name; + char *p_description; + char *p_provider; + uint8_t subclass; + tBTA_HD_DEV_DESCR descriptor; +} tBTA_HD_APP_INFO; + +typedef struct { + uint8_t service_type; + uint32_t token_rate; + uint32_t token_bucket_size; + uint32_t peak_bandwidth; + uint32_t access_latency; + uint32_t delay_variation; +} tBTA_HD_QOS_INFO; + +typedef struct { + bool use_intr; + uint8_t type; + uint8_t id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + bool in_use; + BD_ADDR bda; +} tBTA_HD_REG_STATUS; + +typedef struct { + BD_ADDR bda; + tBTA_HD_STATUS status; + tBTA_HD_CONN_STAT conn_status; +} tBTA_HD_CONN; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t buffer_size; +} tBTA_HD_GET_REPORT; + +typedef struct { + uint8_t report_type; + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_SET_REPORT; + +typedef uint8_t tBTA_HD_SET_PROTOCOL; + +typedef struct { + uint8_t report_id; + uint16_t len; + uint8_t *p_data; +} tBTA_HD_INTR_DATA; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; + uint8_t report_type; + uint8_t report_id; +} tBTA_HD_API_SEND_REPORT; + +typedef struct { + tBTA_HD_STATUS status; + uint8_t reason; +} tBTA_HD_API_REPORT_ERR; + +/* union of data associated with HD callback */ +typedef union { + tBTA_HD_STATUS status; /* BTA_HD_ENABLE_EVT + BTA_HD_DISABLE_EVT + BTA_HD_UNREGISTER_APP_EVT */ + tBTA_HD_REG_STATUS reg_status; /* BTA_HD_REGISTER_APP_EVT */ + tBTA_HD_CONN conn; /* BTA_HD_OPEN_EVT + BTA_HD_CLOSE_EVT + BTA_HD_VC_UNPLUG_EVT + BTA_HD_OWN_VC_UNPLUG_EVT */ + tBTA_HD_GET_REPORT get_report; /* BTA_HD_GET_REPORT */ + tBTA_HD_SET_REPORT set_report; /* BTA_HD_SET_REPORT */ + tBTA_HD_SET_PROTOCOL set_protocol; /* BTA_HD_SETPROTOCOL */ + tBTA_HD_INTR_DATA intr_data; /* BTA_HD_INTR_DATA_EVT */ + tBTA_HD_API_SEND_REPORT send_report; /* BTA_HD_API_SEND_REPORT_EVT */ + tBTA_HD_API_REPORT_ERR report_err; /* BTA_HD_API_REPORT_ERR_EVT */ +} tBTA_HD; + +/* BTA HD callback function */ +typedef void (tBTA_HD_CBACK)(tBTA_HD_EVT event, tBTA_HD *p_data); + +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +/******************************************************************************* + * + * Function BTA_HhRegister + * + * Description This function enable HID host and registers HID-Host with + * lower layers. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdEnable(tBTA_HD_CBACK *p_cback); + +/******************************************************************************* + * + * Function BTA_HhDeregister + * + * Description This function is called when the host is about power down. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisable(void); + +/******************************************************************************* + * + * Function BTA_HdRegisterApp + * + * Description This function is called when application should be + * registered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRegisterApp(tBTA_HD_APP_INFO *p_app_info, tBTA_HD_QOS_INFO *p_in_qos, tBTA_HD_QOS_INFO *p_out_qos); + +/******************************************************************************* + * + * Function BTA_HdUnregisterApp + * + * Description This function is called when application should be + * unregistered + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdUnregisterApp(void); + +/******************************************************************************* + * + * Function BTA_HdSendReport + * + * Description This function is called when report is to be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdSendReport(tBTA_HD_REPORT *p_report); + +/******************************************************************************* + * + * Function BTA_HdVirtualCableUnplug + * + * Description This function is called when VCU shall be sent + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function BTA_HdConnect + * + * Description This function is called when connection to host shall be + * made. + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdConnect(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdDisconnect + * + * Description This function is called when host shall be disconnected + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdDisconnect(void); + +/******************************************************************************* + * + * Function BTA_HdAddDevice + * + * Description This function is called when a device is virtually cabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdAddDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdRemoveDevice + * + * Description This function is called when a device is virtually uncabled + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdRemoveDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function BTA_HdReportError + * + * Description This function is called when reporting error for set report + * + * Returns void + * + ******************************************************************************/ +extern void BTA_HdReportError(uint8_t error); + +#ifdef __cplusplus +} +#endif + +#endif /* BTA_HD_INCLUDED */ +#endif /* BTA_HD_API_H */ diff --git a/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h b/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h index e1bf4017fb..88cd192627 100644 --- a/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h +++ b/components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h @@ -58,7 +58,8 @@ #define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */ #define BTA_HH_DATA_EVT 15 #define BTA_HH_API_ERR_EVT 16 /* API error is caught */ -#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ +#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ +#define BTA_HH_DATA_IND_EVT 18 /* Data on interrupt channel */ typedef UINT16 tBTA_HH_EVT; @@ -131,8 +132,8 @@ enum { BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */ BTA_HH_ERR_NO_RES, /* out of system resources */ BTA_HH_ERR_AUTH_FAILED, /* authentication fail */ - BTA_HH_ERR_HDL, - BTA_HH_ERR_SEC + BTA_HH_ERR_HDL, /* connection handle error */ + BTA_HH_ERR_SEC, /* encryption error */ }; typedef UINT8 tBTA_HH_STATUS; @@ -210,6 +211,7 @@ typedef struct { BD_ADDR bda; /* HID device bd address */ tBTA_HH_STATUS status; /* operation status */ UINT8 handle; /* device handle */ + BOOLEAN is_orig; /* indicate if host initiate connection */ #if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) BOOLEAN le_hid; /* is LE devices? */ BOOLEAN scps_supported; /* scan parameter service supported */ @@ -257,9 +259,9 @@ typedef struct { typedef struct { tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */ union { - tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ - tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ - } data_rpt; + tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ + tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ + } data_rpt; } tBTA_HH_BOOT_RPT; /* handshake data */ @@ -267,13 +269,29 @@ typedef struct { tBTA_HH_STATUS status; /* handshake status */ UINT8 handle; /* device handle */ union { - tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ - BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ - UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ - } rsp_data; + tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ + BT_HDR *p_rpt_data; /* GET_RPT_EVT : report data */ + UINT8 idle_rate; /* GET_IDLE_EVT : idle rate */ + } rsp_data; } tBTA_HH_HSDATA; + +/* upper layer send data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + UINT8 reason; /* send data failed reason */ +} tBTA_HH_API_SENDDATA; + +/* interrupt channel data */ +typedef struct { + tBTA_HH_STATUS status; /* handshake status */ + UINT8 handle; /* device handle */ + tBTA_HH_PROTO_MODE proto_mode; /* protocol mode */ + BT_HDR *p_data; /* DATA_EVT : feature report data */ +} tBTA_HH_INTDATA; + /* union of data associated with HD callback */ typedef union { tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */ @@ -290,10 +308,12 @@ typedef union { BTA_HH_GET_RPT_EVT BTA_HH_GET_PROTO_EVT BTA_HH_GET_IDLE_EVT */ + tBTA_HH_API_SENDDATA send_data; /* BTA_HH_DATA_EVT */ + tBTA_HH_INTDATA int_data; /* BTA_HH_DATA_IND_EVT */ } tBTA_HH; /* BTA HH callback function */ -typedef void (tBTA_HH_CBACK) (tBTA_HH_EVT event, tBTA_HH *p_data); +typedef void (tBTA_HH_CBACK)(tBTA_HH_EVT event, tBTA_HH *p_data); /***************************************************************************** diff --git a/components/bt/host/bluedroid/btc/core/btc_storage.c b/components/bt/host/bluedroid/btc/core/btc_storage.c index 97bef05fc7..bbaa7f8389 100644 --- a/components/bt/host/bluedroid/btc/core/btc_storage.c +++ b/components/bt/host/bluedroid/btc/core/btc_storage.c @@ -16,11 +16,13 @@ #include "btc/btc_storage.h" #include "btc/btc_util.h" #include "osi/osi.h" +#include "osi/allocator.h" #include "common/bt_trace.h" #include "esp_system.h" #include "bta/bta_api.h" #include "device/bdaddr.h" #include "btc/btc_config.h" +#include "btc_hh.h" /******************************************************************************* ** @@ -247,3 +249,252 @@ bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *d return BT_STATUS_SUCCESS; } + +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list) +{ + BTC_TRACE_DEBUG("btc_storage_add_hid_device_info:"); + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidAttrMask", attr_mask); + ret &= btc_config_set_int(bdstr, "HidSubClass", sub_class); + ret &= btc_config_set_int(bdstr, "HidAppId", app_id); + ret &= btc_config_set_int(bdstr, "HidVendorId", vendor_id); + ret &= btc_config_set_int(bdstr, "HidProductId", product_id); + ret &= btc_config_set_int(bdstr, "HidVersion", version); + ret &= btc_config_set_int(bdstr, "HidCountryCode", ctry_code); + ret &= btc_config_set_int(bdstr, "HidSSRMaxLatency", ssr_max_latency); + ret &= btc_config_set_int(bdstr, "HidSSRMinTimeout", ssr_min_tout); + if (dl_len > 0) + btc_config_set_bin(bdstr, "HidDescriptor", dsc_list, dl_len); + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("Storage add hid device info %d\n", ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void) +{ + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR) && btc_config_exist(name, "HidAttrMask")) { + btc_config_get_int(name, "HidAttrMask", &value); + uint16_t attr_mask = (uint16_t)value; + + tBTA_HH_DEV_DSCP_INFO dscp_info; + memset(&dscp_info, 0, sizeof(dscp_info)); + + btc_config_get_int(name, "HidSubClass", &value); + uint8_t sub_class = (uint8_t)value; + + btc_config_get_int(name, "HidAppId", &value); + uint8_t app_id = (uint8_t)value; + + btc_config_get_int(name, "HidVendorId", &value); + dscp_info.vendor_id = (uint16_t)value; + + btc_config_get_int(name, "HidProductId", &value); + dscp_info.product_id = (uint16_t)value; + + btc_config_get_int(name, "HidVersion", &value); + dscp_info.version = (uint8_t)value; + + btc_config_get_int(name, "HidCountryCode", &value); + dscp_info.ctry_code = (uint8_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMaxLatency", &value); + dscp_info.ssr_max_latency = (uint16_t)value; + + value = 0; + btc_config_get_int(name, "HidSSRMinTimeout", &value); + dscp_info.ssr_min_tout = (uint16_t)value; + + size_t len = btc_config_get_bin_length(name, "HidDescriptor"); + if (len > 0) { + dscp_info.descriptor.dl_len = (uint16_t)len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(len); + btc_config_get_bin(name, "HidDescriptor", (uint8_t *)dscp_info.descriptor.dsc_list, &len); + } + + // add extracted information to BTA HH + bt_bdaddr_t bd_addr; + if (string_to_bdaddr(name, &bd_addr) && btc_hh_add_added_dev(*(BD_ADDR *)&bd_addr, attr_mask)) { + BTA_HhAddDev(*(BD_ADDR *)&bd_addr, attr_mask, sub_class, app_id, dscp_info); + } + + if (dscp_info.descriptor.dsc_list) { + osi_free(dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dsc_list = NULL; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + int ret = 1; + btc_config_lock(); + if (btc_config_exist(bdstr, "HidAttrMask")) { + ret &= btc_config_remove(bdstr, "HidAttrMask"); + } + if (btc_config_exist(bdstr, "HidSubClass")) { + ret &= btc_config_remove(bdstr, "HidSubClass"); + } + if (btc_config_exist(bdstr, "HidAppId")) { + ret &= btc_config_remove(bdstr, "HidAppId"); + } + if (btc_config_exist(bdstr, "HidVendorId")) { + ret &= btc_config_remove(bdstr, "HidVendorId"); + } + if (btc_config_exist(bdstr, "HidProductId")) { + ret &= btc_config_remove(bdstr, "HidProductId"); + } + if (btc_config_exist(bdstr, "HidVersion")) { + ret &= btc_config_remove(bdstr, "HidVersion"); + } + if (btc_config_exist(bdstr, "HidCountryCode")) { + ret &= btc_config_remove(bdstr, "HidCountryCode"); + } + if (btc_config_exist(bdstr, "HidSSRMaxLatency")) { + ret &= btc_config_remove(bdstr, "HidSSRMaxLatency"); + } + if (btc_config_exist(bdstr, "HidSSRMinTimeout")) { + ret &= btc_config_remove(bdstr, "HidSSRMinTimeout"); + } + if (btc_config_exist(bdstr, "HidDescriptor")) { + ret &= btc_config_remove(bdstr, "HidDescriptor"); + } + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#include "bta/bta_hd_api.h" +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void) +{ + bt_bdaddr_t bd_addr; + int value; + btc_config_lock(); + for (const btc_config_section_iter_t *iter = btc_config_section_begin(); iter != btc_config_section_end(); + iter = btc_config_section_next(iter)) { + const char *name = btc_config_section_name(iter); + if (string_is_bdaddr(name) && btc_config_exist(name, BTC_STORAGE_LINK_KEY_TYPE_STR) && + btc_config_exist(name, BTC_STORAGE_PIN_LENGTH_STR) && btc_config_exist(name, BTC_STORAGE_SC_SUPPORT) && + btc_config_exist(name, BTC_STORAGE_LINK_KEY_STR)) { + BTC_TRACE_DEBUG("Remote device:%s", name); + if (btc_config_get_int(name, "HidDeviceCabled", &value)) { + string_to_bdaddr(name, &bd_addr); + BTA_HdAddDevice(bd_addr.address); + break; + } + } + } + btc_config_unlock(); + return BT_STATUS_SUCCESS; +} + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr = {0}; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + int ret = btc_config_set_int(bdstr, "HidDeviceCabled", 1); + btc_config_flush(); + btc_config_unlock(); + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr) +{ + bdstr_t bdstr; + int ret = 0; + bdaddr_to_string(remote_bd_addr, bdstr, sizeof(bdstr)); + btc_config_lock(); + if (btc_config_exist(bdstr, "HidVersion")) { + ret = btc_config_remove(bdstr, "HidDeviceCabled"); + } + btc_config_flush(); + btc_config_unlock(); + + BTC_TRACE_DEBUG("%s ret:%d", __func__, ret); + return ret ? BT_STATUS_SUCCESS : BT_STATUS_FAIL; +} +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) diff --git a/components/bt/host/bluedroid/btc/core/btc_util.c b/components/bt/host/bluedroid/btc/core/btc_util.c index 09f178565c..0417198a57 100644 --- a/components/bt/host/bluedroid/btc/core/btc_util.c +++ b/components/bt/host/bluedroid/btc/core/btc_util.c @@ -35,6 +35,10 @@ #include "bta/bta_ag_api.h" #endif ///BTA_AG_INCLUDED == TRUE +#if (BTA_HH_INCLUDED == TRUE) +#include "bta/bta_hh_api.h" +#endif ///BTA_HH_INCLUDED == TRUE +#include "bta/bta_hd_api.h" #include "common/bt_defs.h" #include "stack/btm_api.h" #include "bta/bta_api.h" @@ -209,6 +213,56 @@ const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state #endif // #if (BTA_AG_INCLUDED == TRUE) +#if (BTA_HH_INCLUDED == TRUE) +const char *dump_hh_event(uint16_t event) +{ + switch (event) { + CASE_RETURN_STR(BTA_HH_ENABLE_EVT) + CASE_RETURN_STR(BTA_HH_DISABLE_EVT) + CASE_RETURN_STR(BTA_HH_OPEN_EVT) + CASE_RETURN_STR(BTA_HH_CLOSE_EVT) + CASE_RETURN_STR(BTA_HH_GET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_SET_RPT_EVT) + CASE_RETURN_STR(BTA_HH_GET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_SET_PROTO_EVT) + CASE_RETURN_STR(BTA_HH_GET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_SET_IDLE_EVT) + CASE_RETURN_STR(BTA_HH_GET_DSCP_EVT) + CASE_RETURN_STR(BTA_HH_ADD_DEV_EVT) + CASE_RETURN_STR(BTA_HH_RMV_DEV_EVT) + CASE_RETURN_STR(BTA_HH_VC_UNPLUG_EVT) + CASE_RETURN_STR(BTA_HH_DATA_EVT) + CASE_RETURN_STR(BTA_HH_API_ERR_EVT) + CASE_RETURN_STR(BTA_HH_UPDATE_SCPP_EVT) + CASE_RETURN_STR(BTA_HH_DATA_IND_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HH_INCLUDED + +#if BTA_HD_INCLUDED == TRUE +const char* dump_hd_event(uint16_t event) { + switch (event) { + CASE_RETURN_STR(BTA_HD_ENABLE_EVT) + CASE_RETURN_STR(BTA_HD_DISABLE_EVT) + CASE_RETURN_STR(BTA_HD_REGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_UNREGISTER_APP_EVT) + CASE_RETURN_STR(BTA_HD_OPEN_EVT) + CASE_RETURN_STR(BTA_HD_CLOSE_EVT) + CASE_RETURN_STR(BTA_HD_GET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_REPORT_EVT) + CASE_RETURN_STR(BTA_HD_SET_PROTOCOL_EVT) + CASE_RETURN_STR(BTA_HD_INTR_DATA_EVT) + CASE_RETURN_STR(BTA_HD_VC_UNPLUG_EVT) + //CASE_RETURN_STR(BTA_HD_CONN_STATE_EVT) + CASE_RETURN_STR(BTA_HD_API_ERR_EVT) + default: + return "UNKNOWN MSG ID"; + } +} +#endif ///BTA_HD_INCLUDED + UINT32 devclass2uint(DEV_CLASS dev_class) { UINT32 cod = 0; diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_storage.h b/components/bt/host/bluedroid/btc/include/btc/btc_storage.h index f74c3eeb36..57916e522a 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_storage.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_storage.h @@ -27,6 +27,10 @@ #define BTC_STORAGE_PIN_LENGTH_STR "PinLength" #define BTC_STORAGE_SC_SUPPORT "SCSupport" +#ifdef __cplusplus +extern "C" { +#endif + /******************************************************************************* ** ** Function btc_storage_add_bonded_device @@ -91,4 +95,85 @@ int btc_storage_get_num_bt_bond_devices(void); *******************************************************************************/ bt_status_t btc_storage_get_bonded_bt_devices_list(bt_bdaddr_t *bond_dev, int *dev_num); +#if (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) +/******************************************************************************* + * + * Function btc_storage_add_hid_device_info + * + * Description BTC storage API - Adds the hid information of bonded hid + * devices-to NVRAM + * + * Returns BT_STATUS_SUCCESS if the store was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ + +bt_status_t btc_storage_add_hid_device_info(bt_bdaddr_t *remote_bd_addr, uint16_t attr_mask, uint8_t sub_class, + uint8_t app_id, uint16_t vendor_id, uint16_t product_id, uint16_t version, + uint8_t ctry_code, uint16_t ssr_max_latency, uint16_t ssr_min_tout, + uint16_t dl_len, uint8_t *dsc_list); + +/******************************************************************************* + * + * Function btc_storage_load_bonded_hid_info + * + * Description BTIF storage API - Loads hid info for all the bonded devices + * from NVRAM and adds those devices to the BTA_HH. + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_bonded_hid_info(void); + +/******************************************************************************* + * + * Function btc_storage_remove_hid_info + * + * Description BTC storage API - Deletes the bonded hid device info from + * NVRAM + * + * Returns BT_STATUS_SUCCESS if the deletion was successful, + * BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hid_info(bt_bdaddr_t *remote_bd_addr); +#endif // (defined BTC_HH_INCLUDED && BTC_HH_INCLUDED == TRUE) + +#if (defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +/******************************************************************************* + * Function btc_storage_load_hidd + * + * Description Loads hidd bonded device and "plugs" it into hidd + * + * Returns BT_STATUS_SUCCESS if successful, BT_STATUS_FAIL otherwise + * + ******************************************************************************/ +bt_status_t btc_storage_load_hidd(void); + +/******************************************************************************* + * + * Function btc_storage_set_hidd + * + * Description Stores hidd bonded device info in nvram. + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_set_hidd(bt_bdaddr_t *remote_bd_addr); + +/******************************************************************************* + * + * Function btc_storage_remove_hidd + * + * Description Removes hidd bonded device info from nvram + * + * Returns BT_STATUS_SUCCESS + * + ******************************************************************************/ +bt_status_t btc_storage_remove_hidd(bt_bdaddr_t *remote_bd_addr); +#endif //(defined BTC_HD_INCLUDED && BTC_HD_INCLUDED == TRUE) +#ifdef __cplusplus +} +#endif + #endif /* BTC_STORAGE_H */ diff --git a/components/bt/host/bluedroid/btc/include/btc/btc_util.h b/components/bt/host/bluedroid/btc/include/btc/btc_util.h index aa96e46016..1cc3c9bd06 100644 --- a/components/bt/host/bluedroid/btc/include/btc/btc_util.h +++ b/components/bt/host/bluedroid/btc/include/btc/btc_util.h @@ -31,7 +31,9 @@ ********************************************************************************/ typedef char bdstr_t[18]; - +#ifdef __cplusplus +extern "C" { +#endif /******************************************************************************* ** Functions ********************************************************************************/ @@ -48,6 +50,14 @@ const char *dump_hf_call_state(esp_hf_call_status_t call_state); const char* dump_hf_call_setup_state(esp_hf_call_setup_status_t call_setup_state); #endif +#if(BTA_HD_INCLUDED == TRUE) +const char* dump_hd_event(uint16_t event); +#endif + +#if(BTA_HH_INCLUDED == TRUE) +const char* dump_hh_event(uint16_t event); +#endif + UINT32 devclass2uint(DEV_CLASS dev_class); void uint2devclass(UINT32 dev, DEV_CLASS dev_class); void uuid128_be_to_esp_uuid(esp_bt_uuid_t *u, uint8_t* uuid128); @@ -58,4 +68,8 @@ esp_bt_status_t btc_hci_to_esp_status(uint8_t hci_status); esp_bt_status_t btc_btm_status_to_esp_status (uint8_t btm_status); esp_bt_status_t btc_bta_status_to_esp_status (uint8_t bta_status); +#ifdef __cplusplus +} +#endif + #endif /* __BTC_UTIL_H__ */ diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c b/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c new file mode 100644 index 0000000000..5743f89042 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c @@ -0,0 +1,152 @@ +#include "btc_hh.h" +#include "osi/allocator.h" +#include "string.h" +#if HID_HOST_INCLUDED == TRUE + +/******************************************************************************* + * + * Function bta_hh_co_open + * + * Description When connection is opened, this call-out function is executed + * by HH to do platform specific initialization. + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_open(uint8_t dev_handle, uint8_t sub_class, tBTA_HH_ATTR_MASK attr_mask, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + // We found a device with the same handle. Must be a device reconnected. + APPL_TRACE_WARNING("%s: Found an existing device with the same handle dev_status=%d, " + "dev_handle=0x%2x, attr_mask=0x%04x, sub_class=0x%02x, app_id=%d", + __func__, p_dev->dev_status, dev_handle, p_dev->attr_mask, p_dev->sub_class, + p_dev->app_id); + break; + } + p_dev = NULL; + } + + if (p_dev == NULL) { + // Did not find a device reconnection case. Find an empty slot now. + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_UNKNOWN) { + p_dev = &btc_hh_cb.devices[i]; + p_dev->dev_handle = dev_handle; + p_dev->attr_mask = attr_mask; + p_dev->sub_class = sub_class; + p_dev->app_id = app_id; + p_dev->local_vup = false; + + btc_hh_cb.device_num++; + break; + } + } + } + + if (p_dev == NULL) { + APPL_TRACE_ERROR("%s: Error: too many HID devices are connected", __func__); + return; + } + + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + APPL_TRACE_DEBUG("%s: Return device status %d", __func__, p_dev->dev_status); +} + +/******************************************************************************* + * + * Function bta_hh_co_close + * + * Description When connection is closed, this call-out function is executed + * by HH to do platform specific finalization. + * + * Parameters dev_handle - device handle + * app_id - application id + * + * Returns void. + ******************************************************************************/ +void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) +{ + uint32_t i; + btc_hh_device_t *p_dev = NULL; + + APPL_TRACE_WARNING("%s: dev_handle = %d, app_id = %d", __func__, dev_handle, app_id); + if (dev_handle == BTA_HH_INVALID_HANDLE) { + APPL_TRACE_WARNING("%s: Oops, dev_handle (%d) is invalid...", __func__, dev_handle); + return; + } + + for (i = 0; i < BTC_HH_MAX_HID; i++) { + p_dev = &btc_hh_cb.devices[i]; + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && p_dev->dev_handle == dev_handle) { + APPL_TRACE_WARNING("%s: Found an existing device with the same handle " + "dev_status = %d, dev_handle =%d", + __func__, p_dev->dev_status, p_dev->dev_handle); + break; + } + } +} + +/******************************************************************************* + * + * Function bta_hh_co_data + * + * Description This function is executed by BTA when HID host receive a + * data report on interrupt channel. + * + * Parameters dev_handle - device handle + * *p_rpt - pointer to the report data + * len - length of report data + * mode - Hid host Protocol Mode + * sub_clas - Device Subclass + * app_id - application id + * + * Returns void + ******************************************************************************/ +void bta_hh_co_data(UINT8 dev_handle, UINT8 *p_rpt, UINT16 len, tBTA_HH_PROTO_MODE mode, UINT8 sub_class, UINT8 ctry_code, + BD_ADDR peer_addr, UINT8 app_id) +{ + btc_msg_t msg; + tBTA_HH p_data; + BT_HDR *p_buf = NULL; + bt_status_t status; + tBTA_HH_STATUS ret = BTA_HH_OK; + + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_DATA_IND_EVT; + + APPL_TRACE_DEBUG("%s: dev_handle = %d, subclass = 0x%02X, mode = %d, " + "ctry_code = %d, app_id = %d", + __func__, dev_handle, sub_class, mode, ctry_code, app_id); + + do { + if ((p_buf = osi_malloc(sizeof(BT_HDR) + len)) == NULL) { + APPL_TRACE_ERROR("%s malloc failed!", __func__); + ret = BTA_HH_ERR_NO_RES; + break; + } + p_buf->offset = 0; + p_buf->len = len; + p_buf->event = 0; + p_buf->layer_specific = dev_handle; + memcpy(p_buf->data, p_rpt, len); + } while (0); + + p_data.int_data.status = ret; + p_data.int_data.handle = dev_handle; + p_data.int_data.p_data = p_buf; + p_data.int_data.proto_mode = mode; + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL); + assert(status == BT_STATUS_SUCCESS); +} + +#endif /* HID_HOST_INCLUDED == TRUE */ diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c new file mode 100644 index 0000000000..0deccf1251 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c @@ -0,0 +1,831 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include + +#include "bta/bta_api.h" +#include "bta/bta_hd_api.h" +#include "bta/bta_hh_api.h" +#include "bta/utl.h" +#include "btc/btc_storage.h" +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "btc_hd.h" + +#include "osi/allocator.h" + +#include "esp_hidd_api.h" + +#if HID_DEV_INCLUDED == TRUE +#include "bta_dm_int.h" + +/* HD request events */ +typedef enum { BTC_HD_DUMMY_REQ_EVT = 0 } btc_hd_req_evt_t; + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hd_cb_t btc_hd_cb; + +// static tBTA_HD_APP_INFO app_info; +// static tBTA_HD_QOS_INFO in_qos; +// static tBTA_HD_QOS_INFO out_qos; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define BTC_HD_APP_NAME_LEN 50 +#define BTC_HD_APP_DESCRIPTION_LEN 50 +#define BTC_HD_APP_PROVIDER_LEN 50 +#define BTC_HD_APP_DESCRIPTOR_LEN 2048 +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 + +#define is_hidd_init() (btc_hd_cb.status > BTC_HD_DISABLED) +#define is_hidd_app_register() (btc_hd_cb.app_registered) + +typedef void (bt_hid_copy_cb_t)(btc_msg_t *msg, void *p_dest, void *p_src); + +static inline void btc_hd_cb_to_app(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) +{ + esp_hd_cb_t *btc_hd_cb = (esp_hd_cb_t *)btc_profile_cb_get(BTC_PID_HD); + if (btc_hd_cb) { + btc_hd_cb(event, param); + } +} + +static void free_app_info_param(void) +{ + utl_freebuf((void **)&btc_hd_cb.app_info.descriptor.dsc_list); + utl_freebuf((void **)&btc_hd_cb.app_info.p_provider); + utl_freebuf((void **)&btc_hd_cb.app_info.p_description); + utl_freebuf((void **)&btc_hd_cb.app_info.p_name); +} + +static void bte_hd_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HD *p_dst_data = (tBTA_HD *)p_dst; + tBTA_HD *p_src_data = (tBTA_HD *)p_src; + switch (msg->act) + { + case BTA_HD_SET_REPORT_EVT: { + uint8_t *src_data = p_src_data->set_report.p_data; + if (src_data) { + p_dst_data->set_report.p_data = osi_malloc(p_src_data->set_report.len); + if (p_dst_data->set_report.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc set_report data failed!", __func__); + break; + } + memcpy(p_dst_data->set_report.p_data, src_data, p_src_data->set_report.len); + } + break; + } + case BTA_HD_INTR_DATA_EVT: { + uint8_t *src_data = p_src_data->intr_data.p_data; + if (src_data) { + p_dst_data->intr_data.p_data = osi_malloc(p_src_data->intr_data.len); + if (p_dst_data->intr_data.p_data == NULL) { + BTC_TRACE_ERROR("%s malloc intr_data data failed!", __func__); + break; + } + memcpy(p_dst_data->intr_data.p_data, src_data, p_src_data->intr_data.len); + } + break; + } + default: + break; + } +} + +/******************************************************************************* + * + * Function btc_hd_remove_device + * + * Description Removes plugged device + * + * Returns void + * + ******************************************************************************/ +void btc_hd_remove_device(bt_bdaddr_t bd_addr) +{ + BTA_HdRemoveDevice((uint8_t *)&bd_addr); + // btc_storage_remove_hidd(&bd_addr); +} + +/******************************************************************************* + * + * Function bte_hd_evt + * + * Description Switches context from BTE to BTC for all BT-HD events + * + * Returns void + * + ******************************************************************************/ +static void bte_hd_evt(tBTA_HD_EVT event, tBTA_HD *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HD_ENABLE_EVT: + case BTA_HD_DISABLE_EVT: + case BTA_HD_UNREGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_STATUS); + break; + case BTA_HD_REGISTER_APP_EVT: + param_len = sizeof(tBTA_HD_REG_STATUS); + break; + case BTA_HD_OPEN_EVT: + case BTA_HD_CLOSE_EVT: + case BTA_HD_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HD_CONN); + break; + case BTA_HD_GET_REPORT_EVT: + param_len += sizeof(tBTA_HD_GET_REPORT); + break; + case BTA_HD_SET_REPORT_EVT: + param_len = sizeof(tBTA_HD_SET_REPORT); + break; + case BTA_HD_SET_PROTOCOL_EVT: + param_len += sizeof(p_data->set_protocol); + break; + case BTA_HD_INTR_DATA_EVT: + param_len = sizeof(tBTA_HD_INTR_DATA); + break; + case BTA_HD_SEND_REPORT_EVT: + param_len = sizeof(tBTA_HD_API_SEND_REPORT); + break; + case BTA_HD_REPORT_ERR_EVT: + param_len = sizeof(tBTA_HD_API_REPORT_ERR); + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HD; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hd_arg_deep_copy); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("context transfer failed"); + } +} + +/******************************************************************************* + * + * Function btc_hd_init + * + * Description Initializes BT-HD interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has been initiated, shall uninit first!", __func__); + ret = ESP_HIDD_NEED_DEINIT; + break; + } + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + /* enable HD */ + BTA_HdEnable(bte_hd_evt); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.init.status = ret; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_deinit + * + * Description de-initializes the hd interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(void); +static void btc_hd_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + btc_hd_cb.service_dereg_active = FALSE; + btc_hd_cb.status = BTC_HD_DISABLING; + // unresgister app will also relase the connection + // and disable after receiving unregister event from lower layer + if (is_hidd_app_register()) { + btc_hd_unregister_app(); + } else { + BTC_TRACE_WARNING("%s disabling hid device service now", __func__); + BTA_HdDisable(); + } + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.deinit.status = ret; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_register_app + * + * Description Registers HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_register_app(esp_hidd_app_param_t *p_app_param, esp_hidd_qos_param_t *p_in_qos, + esp_hidd_qos_param_t *p_out_qos) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application already registered, shall deregister first!", __func__); + ret = ESP_HIDD_NEED_DEREG; + break; + } + + if ((btc_hd_cb.app_info.p_name = (char *)osi_malloc(BTC_HD_APP_NAME_LEN)) == NULL || + (btc_hd_cb.app_info.p_description = (char *)osi_malloc(BTC_HD_APP_DESCRIPTION_LEN)) == NULL || + (btc_hd_cb.app_info.p_provider = (char *)osi_malloc(BTC_HD_APP_PROVIDER_LEN)) == NULL || + (btc_hd_cb.app_info.descriptor.dsc_list = (uint8_t *)osi_malloc(p_app_param->desc_list_len)) == NULL) { + BTC_TRACE_ERROR( + "%s malloc app_info failed! p_name:%p, p_description:%p, p_provider:%p, descriptor.dsc_list:%p", + __func__, btc_hd_cb.app_info.p_name, btc_hd_cb.app_info.p_description, btc_hd_cb.app_info.p_provider, + btc_hd_cb.app_info.descriptor.dsc_list); + ret = ESP_HIDD_NO_RES; + break; + } + memcpy(btc_hd_cb.app_info.p_name, p_app_param->name, BTC_HD_APP_NAME_LEN); + memcpy(btc_hd_cb.app_info.p_description, p_app_param->description, BTC_HD_APP_DESCRIPTION_LEN); + memcpy(btc_hd_cb.app_info.p_provider, p_app_param->provider, BTC_HD_APP_PROVIDER_LEN); + memcpy(btc_hd_cb.app_info.descriptor.dsc_list, p_app_param->desc_list, p_app_param->desc_list_len); + btc_hd_cb.app_info.subclass = p_app_param->subclass; + btc_hd_cb.app_info.descriptor.dl_len = p_app_param->desc_list_len; + + btc_hd_cb.in_qos.service_type = p_in_qos->service_type; + btc_hd_cb.in_qos.token_rate = p_in_qos->token_rate; + btc_hd_cb.in_qos.token_bucket_size = p_in_qos->token_bucket_size; + btc_hd_cb.in_qos.peak_bandwidth = p_in_qos->peak_bandwidth; + btc_hd_cb.in_qos.access_latency = p_in_qos->access_latency; + btc_hd_cb.in_qos.delay_variation = p_in_qos->delay_variation; + btc_hd_cb.out_qos.service_type = p_out_qos->service_type; + btc_hd_cb.out_qos.token_rate = p_out_qos->token_rate; + btc_hd_cb.out_qos.token_bucket_size = p_out_qos->token_bucket_size; + btc_hd_cb.out_qos.peak_bandwidth = p_out_qos->peak_bandwidth; + btc_hd_cb.out_qos.access_latency = p_out_qos->access_latency; + btc_hd_cb.out_qos.delay_variation = p_out_qos->delay_variation; + + BTA_HdRegisterApp(&btc_hd_cb.app_info, &btc_hd_cb.in_qos, &btc_hd_cb.out_qos); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.register_app.status = ret; + param.register_app.in_use = false; + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + } + free_app_info_param(); +} + +/******************************************************************************* + * + * Function btc_hd_unregister_app + * + * Description Unregisters HID Device application + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_unregister_app(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + + if (btc_hd_cb.service_dereg_active) { + BTC_TRACE_ERROR("%s: BT-HD deregistering in progress", __func__); + ret = ESP_HIDD_BUSY; + break; + } + btc_hd_cb.service_dereg_active = TRUE; + BTA_HdUnregisterApp(); + } while(0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.unregister_app.status = ret; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_connect + * + * Description Connects to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_connect(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdConnect(bd_addr); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.open.status = ret; + param.open.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + memcpy(param.open.bd_addr, bd_addr, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_disconnect + * + * Description Disconnects from host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_disconnect(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdDisconnect(); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.close.status = ret; + param.close.conn_status = ESP_HIDD_CONN_STATE_DISCONNECTED; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_send_report + * + * Description Sends Reports to hid host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + tBTA_HD_REPORT report; + + BTC_TRACE_API("%s: type=%d id=%d len=%d", __func__, type, id, len); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + if (type == ESP_HIDD_REPORT_TYPE_INTRDATA) { + report.type = ESP_HIDD_REPORT_TYPE_INPUT; + report.use_intr = TRUE; + } else { + report.type = (type & 0x03); + report.use_intr = FALSE; + } + + report.id = id; + report.len = len; + report.p_data = p_data; + + BTA_HdSendReport(&report); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.send_report.status = ret; + param.send_report.reason = 0; + param.send_report.report_type = report.type; + param.send_report.report_id = report.id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_report_error + * + * Description Sends HANDSHAKE with error info for invalid SET_REPORT + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_report_error(uint8_t error) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdReportError(error); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.report_err.status = ret; + param.report_err.reason = 0; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hd_virtual_cable_unplug + * + * Description Sends Virtual Cable Unplug to host + * + * Returns void + * + ******************************************************************************/ +static void btc_hd_virtual_cable_unplug(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidd_status_t ret = ESP_HIDD_SUCCESS; + do { + if (!is_hidd_init()) { + BTC_TRACE_ERROR("%s HD has not been initiated, shall init first!", __func__); + ret = ESP_HIDD_NEED_INIT; + break; + } + + if (!is_hidd_app_register()) { + BTC_TRACE_ERROR("%s: application has not been registered, shall register first!", __func__); + ret = ESP_HIDD_NEED_REG; + break; + } + BTA_HdVirtualCableUnplug(); + } while (0); + + if (ret != ESP_HIDD_SUCCESS) { + esp_hidd_cb_param_t param; + param.report_err.status = ret; + param.report_err.reason = 0; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + } +} + +static void btc_hd_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + utl_freebuf((void **)&arg->send_report.data); + break; + default: + break; + } +} + +void btc_hd_call_handler(btc_msg_t *msg) +{ + btc_hidd_args_t *arg = (btc_hidd_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HD_INIT_EVT: + btc_hd_init(); + break; + case BTC_HD_DEINIT_EVT: + btc_hd_deinit(); + break; + case BTC_HD_REGISTER_APP_EVT: + btc_hd_register_app(arg->register_app.app_param, arg->register_app.in_qos, arg->register_app.out_qos); + break; + case BTC_HD_UNREGISTER_APP_EVT: + btc_hd_unregister_app(); + break; + case BTC_HD_CONNECT_EVT: + btc_hd_connect(arg->connect.bd_addr); + break; + case BTC_HD_DISCONNECT_EVT: + btc_hd_disconnect(); + break; + case BTC_HD_SEND_REPORT_EVT: + btc_hd_send_report(arg->send_report.type, arg->send_report.id, arg->send_report.len, arg->send_report.data); + break; + case BTC_HD_REPORT_ERROR_EVT: + btc_hd_report_error(arg->error); + break; + case BTC_HD_UNPLUG_EVT: + btc_hd_virtual_cable_unplug(); + break; + default: + BTC_TRACE_WARNING("unknown hidd action %i", msg->act); + break; + } + btc_hd_call_arg_deep_free(msg); +} + +static void btc_hd_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HD *arg = (tBTA_HD *)msg->arg; + + switch (msg->act) { + case BTA_HD_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.p_data); + break; + case BTA_HD_INTR_DATA_EVT: + utl_freebuf((void **)&arg->intr_data.p_data); + break; + default: + break; + } +} + +void btc_hd_cb_handler(btc_msg_t *msg) +{ + uint16_t event = msg->act; + tBTA_HD *p_data = (tBTA_HD *)msg->arg; + esp_hidd_cb_param_t param = {0}; + BTC_TRACE_API("%s: event=%s", __func__, dump_hd_event(event)); + + switch (event) { + case BTA_HD_ENABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK) { + btc_storage_load_hidd(); + btc_hd_cb.status = BTC_HD_ENABLED; + } else { + btc_hd_cb.status = BTC_HD_DISABLED; + BTC_TRACE_WARNING("Failed to enable BT-HD, status=%d", p_data->status); + } + param.init.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_INIT_EVT, ¶m); + break; + case BTA_HD_DISABLE_EVT: + BTC_TRACE_DEBUG("%s: status=%d", __func__, p_data->status); + if (p_data->status == BTA_HD_OK){ + btc_hd_cb.status = BTC_HD_DISABLED; + if (btc_hd_cb.service_dereg_active) { + btc_hd_cb.service_dereg_active = FALSE; + } + free_app_info_param(); + memset(&btc_hd_cb, 0, sizeof(btc_hd_cb)); + } else { + BTC_TRACE_WARNING("Failed to disable BT-HD, status=%d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_DEINIT_EVT, ¶m); + break; + case BTA_HD_REGISTER_APP_EVT: + if (p_data->reg_status.status == BTA_HD_OK) { + btc_hd_cb.app_registered = TRUE; + } + param.register_app.status = p_data->reg_status.status; + param.register_app.in_use = p_data->reg_status.in_use; + if (!p_data->reg_status.in_use) { + memset(param.register_app.bd_addr, 0, BD_ADDR_LEN); + } else { + memcpy(param.register_app.bd_addr, p_data->reg_status.bda, BD_ADDR_LEN); + } + btc_hd_cb_to_app(ESP_HIDD_REGISTER_APP_EVT, ¶m); + break; + case BTA_HD_UNREGISTER_APP_EVT: + btc_hd_cb.app_registered = FALSE; + param.unregister_app.status = p_data->status; + btc_hd_cb_to_app(ESP_HIDD_UNREGISTER_APP_EVT, ¶m); + if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_WARNING("disabling hid device service now"); + BTA_HdDisable(); + } + break; + case BTA_HD_OPEN_EVT: { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_EVENT("BTA_HD_OPEN_EVT, address (%02x:%02x:%02x:%02x:%02x:%02x)", addr->address[0], addr->address[1], + addr->address[2], addr->address[3], addr->address[4], addr->address[5]); + if (p_data->conn.status == BTA_HD_OK && p_data->conn.conn_status == BTA_HD_CONN_STATE_CONNECTED) { + // /* Check if the connection is from hid host and not hid device */ + // if (check_cod_hid(addr)) { + // /* Incoming connection from hid device, reject it */ + // BTC_TRACE_WARNING("remote device is not hid host, disconnecting"); + // btc_hd_cb.forced_disc = TRUE; + // BTA_HdDisconnect(); + // break; + // } + // btc_storage_set_hidd((bt_bdaddr_t *)&p_data->conn.bda); + } + param.open.status = p_data->conn.status; + param.open.conn_status = p_data->conn.conn_status; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hd_cb_to_app(ESP_HIDD_OPEN_EVT, ¶m); + break; + } + case BTA_HD_CLOSE_EVT: + if (btc_hd_cb.forced_disc && p_data->conn.conn_status == BTA_HD_CONN_STATE_DISCONNECTED) { + bt_bdaddr_t *addr = (bt_bdaddr_t *)&p_data->conn.bda; + BTC_TRACE_WARNING("remote device was forcefully disconnected"); + btc_hd_remove_device(*addr); + btc_hd_cb.forced_disc = FALSE; + break; + } + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + break; + case BTA_HD_GET_REPORT_EVT: + param.get_report.report_type = p_data->get_report.report_type; + param.get_report.report_id = p_data->get_report.report_id; + param.get_report.buffer_size = p_data->get_report.buffer_size; + btc_hd_cb_to_app(ESP_HIDD_GET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_REPORT_EVT: + param.set_report.report_type = p_data->set_report.report_type; + param.set_report.report_id = p_data->set_report.report_id; + param.set_report.len = p_data->set_report.len; + param.set_report.data = p_data->set_report.p_data; + btc_hd_cb_to_app(ESP_HIDD_SET_REPORT_EVT, ¶m); + break; + case BTA_HD_SET_PROTOCOL_EVT: + switch (p_data->set_protocol) { + case HID_PAR_PROTOCOL_BOOT_MODE: + param.set_protocol.protocol_mode = ESP_HIDD_BOOT_MODE; + break; + case HID_PAR_PROTOCOL_REPORT: + param.set_protocol.protocol_mode = ESP_HIDD_REPORT_MODE; + break; + default: + param.set_protocol.protocol_mode = ESP_HIDD_UNSUPPORTED_MODE; + break; + } + btc_hd_cb_to_app(ESP_HIDD_SET_PROTOCOL_EVT, ¶m); + break; + case BTA_HD_INTR_DATA_EVT: + param.intr_data.report_id = p_data->intr_data.report_id; + param.intr_data.len = p_data->intr_data.len; + param.intr_data.data = p_data->intr_data.p_data; + btc_hd_cb_to_app(ESP_HIDD_INTR_DATA_EVT, ¶m); + break; + case BTA_HD_VC_UNPLUG_EVT: { + bt_bdaddr_t *bd_addr = (bt_bdaddr_t *)&p_data->conn.bda; + if (bta_dm_check_if_only_hd_connected(p_data->conn.bda)) { + BTC_TRACE_DEBUG("%s: Removing bonding as only HID profile connected", __func__); + BTA_DmRemoveDevice((uint8_t *)&p_data->conn.bda, BT_TRANSPORT_BR_EDR); + } else { + BTC_TRACE_DEBUG("%s: Only removing HID data as some other profiles connected", __func__); + btc_hd_remove_device(*bd_addr); + } + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + param.vc_unplug.status = p_data->conn.status; + param.vc_unplug.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); + break; + } + case BTA_HD_SEND_REPORT_EVT: + param.send_report.status = p_data->send_report.status; + param.send_report.reason = p_data->send_report.reason; + param.send_report.report_type = p_data->send_report.report_type; + param.send_report.report_id = p_data->send_report.report_id; + btc_hd_cb_to_app(ESP_HIDD_SEND_REPORT_EVT, ¶m); + break; + case BTA_HD_REPORT_ERR_EVT: + param.report_err.status = p_data->report_err.status; + param.report_err.reason = p_data->report_err.reason; + btc_hd_cb_to_app(ESP_HIDD_REPORT_ERR_EVT, ¶m); + break; + default: + BTC_TRACE_WARNING("%s: unknown event (%d)", __func__, event); + break; + } + btc_hd_cb_arg_deep_free(msg); +} + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidd_args_t *dst = (btc_hidd_args_t *)p_dest; + btc_hidd_args_t *src = (btc_hidd_args_t *)p_src; + + switch (msg->act) { + case BTC_HD_SEND_REPORT_EVT: + dst->send_report.data = (uint8_t *)osi_malloc(src->send_report.len); + if (dst->send_report.data) { + memcpy(dst->send_report.data, src->send_report.data, src->send_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_DEV_INCLUDED==TRUE diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c new file mode 100644 index 0000000000..d0744c3236 --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c @@ -0,0 +1,1569 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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. + * + ******************************************************************************/ +/************************************************************************************ + * + * Filename: btc_hd.c + * + * Description: HID Device Profile Bluetooth Interface + * + * + ***********************************************************************************/ + +#include +#include +#include +#include "btc/btc_util.h" +#include "btc/btc_manage.h" +#include "device/bdaddr.h" +#include "btc/btc_storage.h" +#include "osi/allocator.h" +#include "bta/utl.h" +#include "bta/bta_hh_api.h" +#include "stack/l2c_api.h" +// #include "bta_dm_int.h" + +#if HID_HOST_INCLUDED == TRUE +#include "btc_hh.h" + + +/******************************************************************************* + * Static variables + ******************************************************************************/ +btc_hh_cb_t btc_hh_cb; +static bdstr_t bdstr; + +/****************************************************************************** + * Constants & Macros + *****************************************************************************/ +#define COD_MASK 0x07FF + +#define COD_UNCLASSIFIED ((0x1F) << 8) +#define COD_HID_KEYBOARD 0x0540 +#define COD_HID_POINTING 0x0580 +#define COD_HID_COMBO 0x05C0 +#define COD_HID_MAJOR 0x0500 +#define COD_HID_MASK 0x0700 + +#define is_hidh_init() (btc_hh_cb.status > BTC_HH_DISABLED) +#define BTC_TIMEOUT_VUP_MS (3 * 1000) + +static inline void btc_hh_cb_to_app(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param) +{ + esp_hh_cb_t *btc_hh_cb = (esp_hh_cb_t *)btc_profile_cb_get(BTC_PID_HH); + if (btc_hh_cb) { + btc_hh_cb(event, param); + } +} + +/******************************************************************************* + * + * Function proto_mode_change_to_lower_layer + * + * Description Change the upper layer protocol mode definition to the lower layer protocol mode definition + * + * Returns Lower layer protocol mode definition + ******************************************************************************/ +static inline tBTA_HH_PROTO_MODE proto_mode_change_to_lower_layer(esp_hidh_protocol_mode_t protocol_mode) +{ + tBTA_HH_PROTO_MODE proto_mode = BTA_HH_PROTO_UNKNOWN; + + switch (protocol_mode) { + case ESP_HIDH_REPORT_MODE: + proto_mode = BTA_HH_PROTO_RPT_MODE; + break; + case ESP_HIDH_BOOT_MODE: + proto_mode = BTA_HH_PROTO_BOOT_MODE; + break; + default: + break; + } + + return proto_mode; +} + +/******************************************************************************* + * + * Function proto_mode_change_to_upper_layer + * + * Description Change the lower layer protocol mode definition to the upper layer protocol mode definition + * + * Returns Upper layer protocol mode definition + ******************************************************************************/ +static inline esp_hidh_protocol_mode_t proto_mode_change_to_upper_layer(tBTA_HH_PROTO_MODE proto_mode) +{ + esp_hidh_protocol_mode_t protocol_mode = ESP_HIDH_UNSUPPORTED_MODE; + + switch (proto_mode) { + case BTA_HH_PROTO_RPT_MODE: + protocol_mode = ESP_HIDH_REPORT_MODE; + break; + case BTA_HH_PROTO_BOOT_MODE: + protocol_mode = ESP_HIDH_BOOT_MODE; + break; + default: + break; + } + + return protocol_mode; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_handle + * + * Description Return the connected device pointer of the specified device + * handle + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +btc_hh_device_t *btc_hh_find_connected_dev_by_handle(uint8_t handle) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && btc_hh_cb.devices[i].dev_handle == handle) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_dev_by_bda + * + * Description Return the device pointer of the specified bd_addr. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_dev_by_bda(BD_ADDR bd_addr) +{ + uint8_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status != ESP_HIDH_CONN_STATE_UNKNOWN && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_find_connected_dev_by_bda + * + * Description Return the connected device pointer of the specified + * RawAddress. + * + * Returns Device entry pointer in the device table + ******************************************************************************/ +static btc_hh_device_t *btc_hh_find_connected_dev_by_bda(BD_ADDR bd_addr) +{ + uint32_t i; + for (i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED && + memcmp(btc_hh_cb.devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return &btc_hh_cb.devices[i]; + } + } + return NULL; +} + +/******************************************************************************* + * + * Function btc_hh_stop_vup_timer + * + * Description stop vitual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_stop_vup_timer(BD_ADDR bd_addr) +{ + BTIF_TRACE_API("%s", __func__); + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + + if (p_dev != NULL) { + BTC_TRACE_DEBUG("stop VUP timer"); + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + } +} + +/******************************************************************************* + * + * Function btc_hh_timer_timeout + * + * Description Process timer timeout + * + * Returns void + ******************************************************************************/ +void btc_hh_timer_timeout(void *data) +{ + btc_hh_device_t *p_dev = (btc_hh_device_t *)data; + bt_status_t status; + tBTA_HH p_data; + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = BTA_HH_VC_UNPLUG_EVT; + + BTC_TRACE_API("%s", __func__); + if (p_dev->dev_status != ESP_HIDH_CONN_STATE_CONNECTED){ + BTC_TRACE_ERROR("%s Device[%s] is not connected!", __func__, + bdaddr_to_string((const bt_bdaddr_t *)p_dev->bd_addr, bdstr, sizeof(bdstr))); + return; + } + + memset(&p_data, 0, sizeof(tBTA_HH)); + p_data.dev_status.status = BTA_HH_ERR; + p_data.dev_status.handle = p_dev->dev_handle; + + /* switch context to btif task context */ + status = btc_transfer_context(&msg, &p_data, sizeof(tBTA_HH), NULL); + if (status != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("%s context transfer failed", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_start_vup_timer + * + * Description start virtual unplug timer + * + * Returns void + ******************************************************************************/ +void btc_hh_start_vup_timer(BD_ADDR bd_addr) +{ + BTC_TRACE_API("%s", __func__); + + btc_hh_device_t *p_dev = btc_hh_find_connected_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + return; + } + + if (p_dev->vup_timer) { + osi_alarm_free(p_dev->vup_timer); + p_dev->vup_timer = NULL; + } + if ((p_dev->vup_timer = osi_alarm_new("btc_hh.vup_timer", btc_hh_timer_timeout, p_dev, BTC_TIMEOUT_VUP_MS)) == + NULL) { + BTC_TRACE_ERROR("%s unable to malloc vup_timer!", __func__); + } +} + +/******************************************************************************* + * + * Function btc_hh_add_added_dev + * + * Description Add a new device to the added device list. + * + * Returns true if add successfully, otherwise false. + ******************************************************************************/ +bool btc_hh_add_added_dev(BD_ADDR bd_addr, tBTA_HH_ATTR_MASK attr_mask) +{ + int i; + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN) == 0) { + return false; + } + } + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, bd_addr_null, BD_ADDR_LEN) == 0) { + memcpy(btc_hh_cb.added_devices[i].bd_addr, bd_addr, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + btc_hh_cb.added_devices[i].attr_mask = attr_mask; + return true; + } + } + + BTC_TRACE_ERROR("%s: Error, out of space to add device", __func__); + return false; +} + +/******************************************************************************* + ** + ** Function btc_hh_remove_device + ** + ** Description Remove an added device from the stack. + ** + ** Returns void + ******************************************************************************/ +void btc_hh_remove_device(BD_ADDR bd_addr) +{ + int i; + btc_hh_device_t *p_dev; + btc_hh_added_device_t *p_added_dev; + + for (i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + p_added_dev = &btc_hh_cb.added_devices[i]; + if (p_added_dev->bd_addr == bd_addr) { + BTA_HhRemoveDev(p_added_dev->dev_handle); + btc_storage_remove_hid_info((bt_bdaddr_t *)p_added_dev->bd_addr); + memset(p_added_dev->bd_addr, 0, 6); + p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE; + break; + } + } + + p_dev = btc_hh_find_dev_by_bda(bd_addr); + if (p_dev == NULL) { + BTC_TRACE_ERROR("%s Oops, can't find device", __func__); + return; + } + + /* need to notify up-layer device is disconnected to avoid state out of sync + * with up-layer */ //[boblane] + // HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr), BTHH_CONN_STATE_DISCONNECTED); + + p_dev->dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + p_dev->dev_handle = BTA_HH_INVALID_HANDLE; + p_dev->ready_for_data = false; + + if (btc_hh_cb.device_num > 0) { + btc_hh_cb.device_num--; + } else { + BTC_TRACE_WARNING("%s: device_num = 0", __func__); + } +} + +static void bte_hh_arg_deep_copy(btc_msg_t *msg, void *p_dst, void *p_src) +{ + tBTA_HH *p_dst_data = (tBTA_HH *)p_dst; + tBTA_HH *p_src_data = (tBTA_HH *)p_src; + switch (msg->act) + { + case BTA_HH_GET_RPT_EVT: { + BT_HDR *src_hdr = p_src_data->hs_data.rsp_data.p_rpt_data; + if (src_hdr) { + p_dst_data->hs_data.rsp_data.p_rpt_data = osi_malloc(sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + if (p_dst_data->hs_data.rsp_data.p_rpt_data == NULL) { + BTC_TRACE_ERROR("%s malloc p_rpt_data failed!", __func__); + p_dst_data->hs_data.status = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(p_dst_data->hs_data.rsp_data.p_rpt_data, src_hdr, sizeof(BT_HDR) + src_hdr->offset + src_hdr->len); + } + break; + } + default: + break; + } +} + +static void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH *p_data) +{ + bt_status_t status; + int param_len = 0; + + BTC_TRACE_API("%s event=%d", __func__, event); + + switch (event) { + case BTA_HH_ENABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_DISABLE_EVT: + param_len = sizeof(tBTA_HH_STATUS); + break; + case BTA_HH_OPEN_EVT: + param_len = sizeof(tBTA_HH_CONN); + break; + case BTA_HH_CLOSE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_RPT_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_RPT_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_PROTO_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_PROTO_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_IDLE_EVT: + param_len = sizeof(tBTA_HH_HSDATA); + break; + case BTA_HH_SET_IDLE_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_GET_DSCP_EVT: + param_len = sizeof(tBTA_HH_DEV_DSCP_INFO); + break; + case BTA_HH_ADD_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_RMV_DEV_EVT: + param_len = sizeof(tBTA_HH_DEV_INFO); + break; + case BTA_HH_VC_UNPLUG_EVT: + param_len = sizeof(tBTA_HH_CBDATA); + break; + case BTA_HH_DATA_EVT: + param_len = sizeof(tBTA_HH_API_SENDDATA); + break; + case BTA_HH_API_ERR_EVT: + param_len = 0; + break; + } + + btc_msg_t msg; + msg.sig = BTC_SIG_API_CB; + msg.pid = BTC_PID_HH; + msg.act = event; + + status = btc_transfer_context(&msg, p_data, param_len, bte_hh_arg_deep_copy); + assert(status == BT_STATUS_SUCCESS); +} + +/******************************************************************************* + * + * Function btc_hh_init + * + * Description initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_init(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has been initiated, shall uninit first!", __func__); + ret = ESP_HIDH_NEED_DEINIT; + break; + } + + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + BTA_HhEnable(BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT, bte_hh_evt); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.init.status = ret; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_deinit + * + * Description de-initializes the hh interface + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_deinit(void) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + // close all connections + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if(btc_hh_cb.devices[i].dev_status == ESP_HIDH_CONN_STATE_CONNECTED){ + BTA_HhClose(btc_hh_cb.devices[i].dev_handle); + } + } + btc_hh_cb.service_dereg_active = TRUE; + btc_hh_cb.status = BTC_HH_DISABLING; + BTA_HhDisable(); + } while (0); + + if (ret != ESP_HIDH_OK) { + esp_hidh_cb_param_t param; + param.deinit.status = ret; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_connect + * + * Description connection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_connect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_added_device_t* added_dev = NULL; + btc_hh_device_t* dev = NULL; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + if (btc_hh_cb.status == BTC_HH_DEV_CONNECTING) { + BTC_TRACE_ERROR("%s HH is connecting, ignore!", __func__); + ret = ESP_HIDH_BUSY; + break; + } + + dev = btc_hh_find_dev_by_bda(arg->connect.bd_addr); + if (!dev && btc_hh_cb.device_num >= BTC_HH_MAX_HID) { + BTC_TRACE_ERROR("%s exceeded the maximum supported HID device number %d!", __func__, BTC_HH_MAX_HID); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + + for (int i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, arg->connect.bd_addr, BD_ADDR_LEN) == 0) { + added_dev = &btc_hh_cb.added_devices[i]; + BTC_TRACE_WARNING("%s Device[%s] already added, attr_mask = 0x%x", __func__, + bdaddr_to_string((const bt_bdaddr_t *)arg->connect.bd_addr, bdstr, sizeof(bdstr)), + added_dev->attr_mask); + } + } + + if (added_dev != NULL) { + if (added_dev->dev_handle == BTA_HH_INVALID_HANDLE) { + // No space for more HID device now. + BTC_TRACE_ERROR("device added but addition failed"); + memset(added_dev->bd_addr, 0, sizeof(added_dev->bd_addr)); + ret = ESP_HIDH_ERR; + break; + } + } + + /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways + sending this request from host, for subsequent user initiated connection. If the remote is + not in pagescan mode, we will do 2 retries to connect before giving up */ + btc_hh_cb.status = BTC_HH_DEV_CONNECTING; + memcpy(btc_hh_cb.pending_conn_address, arg->connect.bd_addr, BD_ADDR_LEN); + BTA_HhOpen(arg->connect.bd_addr, BTA_HH_PROTO_RPT_MODE, (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)); + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTING; + ret = ESP_HIDH_OK; + } while (0); + + if (ret != ESP_HIDH_OK) { + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.status = ret; + param.open.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.open.bd_addr, arg->connect.bd_addr, BD_ADDR_LEN); + param.open.is_orig = true; + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_disconnect + * + * Description disconnection initiated from the BTC thread context + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_disconnect(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->disconnect.bd_addr); + if (p_dev != NULL) { + BTA_HhClose(p_dev->dev_handle); + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.close.handle = p_dev->dev_handle; + } else { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + } + + } while (0); + + if (ret != ESP_HIDH_OK) { + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.handle = BTA_HH_INVALID_HANDLE; + } + param.close.status = ret; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_virtual_unplug + * + * Description Virtual unplug initiated from the BTC thread context + * Special handling for HID mouse- + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_virtual_unplug(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + btc_hh_device_t *p_dev; + esp_hidh_cb_param_t param; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_dev_by_bda(arg->unplug.bd_addr); + if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED) && + (p_dev->attr_mask & HID_VIRTUAL_CABLE)) { + BTC_TRACE_DEBUG("%s: Sending BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhSendCtrl(p_dev->dev_handle, BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else if ((p_dev != NULL) && (p_dev->dev_status == ESP_HIDH_CONN_STATE_CONNECTED)) { + BTC_TRACE_WARNING("%s: Virtual unplug not suported, disconnecting device", __func__); + /* start the timer */ + btc_hh_start_vup_timer(arg->unplug.bd_addr); + p_dev->local_vup = true; + BTA_HhClose(p_dev->dev_handle); + + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTING; + param.unplug.handle = p_dev->dev_handle; + } else { + BTC_TRACE_ERROR("%s: Error, device not opened, status = %d", __func__, btc_hh_cb.status); + ret = ESP_HIDH_NO_CONNECTION; + if (memcmp(btc_hh_cb.pending_conn_address, arg->unplug.bd_addr, BD_ADDR_LEN) == 0 && + (btc_hh_cb.status == BTC_HH_DEV_CONNECTING)) { + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + } + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.unplug.handle = BTA_HH_INVALID_HANDLE; + } + param.unplug.status = ret; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); +} + +/******************************************************************************* + * + * Function btc_hh_set_info + * + * Description Set the HID device descriptor for the specified HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_info(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + tBTA_HH_DEV_DSCP_INFO dscp_info; + + BTC_TRACE_DEBUG("%s: sub_class = 0x%02x, app_id = %d, vendor_id = 0x%04x, " + "product_id = 0x%04x, version= 0x%04x", + __func__, arg->set_info.hid_info->sub_class, arg->set_info.hid_info->app_id, + arg->set_info.hid_info->vendor_id, arg->set_info.hid_info->product_id, + arg->set_info.hid_info->version); + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + memset(&dscp_info, 0, sizeof(dscp_info)); + dscp_info.vendor_id = arg->set_info.hid_info->vendor_id; + dscp_info.product_id = arg->set_info.hid_info->product_id; + dscp_info.version = arg->set_info.hid_info->version; + dscp_info.ctry_code = arg->set_info.hid_info->ctry_code; + + dscp_info.descriptor.dl_len = arg->set_info.hid_info->dl_len; + dscp_info.descriptor.dsc_list = (uint8_t *)osi_malloc(dscp_info.descriptor.dl_len); + if (dscp_info.descriptor.dsc_list == NULL) { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR_NO_RES; + break; + } + memcpy(dscp_info.descriptor.dsc_list, arg->set_info.hid_info->dsc_list, dscp_info.descriptor.dl_len); + + if (btc_hh_add_added_dev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask)) { + btc_hh_cb.add_event = BTC_HH_SET_INFO_EVT; + BTA_HhAddDev(arg->set_info.bd_addr, arg->set_info.hid_info->attr_mask, arg->set_info.hid_info->sub_class, + arg->set_info.hid_info->app_id, dscp_info); + } else { + BTC_TRACE_ERROR("%s malloc dsc_list failed!", __func__); + ret = ESP_HIDH_ERR; + break; + } + } while(0); + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + + if (ret != ESP_HIDH_OK) { + param.set_info.status = ret; + param.set_info.handle = BTA_HH_INVALID_HANDLE; + memcpy(param.set_info.bd_addr, arg->set_info.bd_addr, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_protocol + * + * Description Get the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetProtoMode(p_dev->dev_handle); + } while(0); + + if (ret != ESP_HIDH_OK) { + param.get_proto.proto_mode = ESP_HIDH_UNSUPPORTED_MODE; + param.get_proto.handle = BTA_HH_INVALID_HANDLE; + param.get_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_set_protocol + * + * Description Set the HID proto mode. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_protocol(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_protocol.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->set_protocol.protocol_mode != ESP_HIDH_REPORT_MODE && arg->set_protocol.protocol_mode != ESP_HIDH_BOOT_MODE) { + BTC_TRACE_ERROR("%s: Error, device proto_mode = %d.", __func__, arg->set_protocol.protocol_mode); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhSetProtoMode(p_dev->dev_handle, proto_mode_change_to_lower_layer(arg->set_protocol.protocol_mode)); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_proto.handle = BTA_HH_INVALID_HANDLE; + param.set_proto.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_get_report + * + * Description Send a GET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_get_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->get_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->get_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->get_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BTA_HhGetReport(p_dev->dev_handle, arg->get_report.report_type, arg->get_report.report_id, + arg->get_report.buffer_size); + } + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_rpt.handle = BTA_HH_INVALID_HANDLE; + param.get_rpt.status = ret; + param.get_rpt.len = 0; + param.get_rpt.data = NULL; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function create_pbuf + * + * Description Helper function to create p_buf for send_data or set_report + * + * Returns BT_HDR * + * + ******************************************************************************/ +static BT_HDR *create_pbuf(uint16_t len, uint8_t *data) +{ + uint8_t *pbuf_data; + BT_HDR *p_buf = (BT_HDR *)osi_malloc(len + BTA_HH_MIN_OFFSET + sizeof(BT_HDR)); + if (p_buf == NULL) { + BTC_TRACE_ERROR("%s failed!", __func__); + return NULL; + } + p_buf->len = len; + p_buf->offset = BTA_HH_MIN_OFFSET; + + pbuf_data = (uint8_t *)(p_buf + 1) + p_buf->offset; + memcpy(pbuf_data, data, len); + + return p_buf; +} + +/******************************************************************************* + * + * Function btc_hh_set_report + * + * Description Send a SET_REPORT to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_set_report(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_report.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (((int)arg->set_report.report_type) <= BTA_HH_RPTT_RESRV || + ((int)arg->set_report.report_type) > BTA_HH_RPTT_FEATURE) { + BTC_TRACE_ERROR("%s Error: report type=%d not supported!", __func__, arg->set_report.report_type); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else if (arg->set_report.report == NULL || arg->set_report.len == 0) { + BTC_TRACE_ERROR("%s Error: report is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR* p_buf = create_pbuf(arg->set_report.len, arg->set_report.report); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + BTA_HhSetReport(p_dev->dev_handle, arg->set_report.report_type, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.set_rpt.handle = BTA_HH_INVALID_HANDLE; + param.set_rpt.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + } +} + +/******************************************************************************* + * + * Function btc_hh_send_data + * + * Description Send a SEND_DATA to HID device. + * + * Returns void + * + ******************************************************************************/ +static void btc_hh_send_data(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->send_data.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } else if (arg->send_data.data == NULL || arg->send_data.len == 0) { + BTC_TRACE_ERROR("%s Error: send data is empty!", __func__); + ret = ESP_HIDH_HS_INVALID_PARAM; + break; + } else { + BT_HDR *p_buf = create_pbuf(arg->send_data.len, arg->send_data.data); + if (p_buf == NULL) { + ret = ESP_HIDH_ERR_NO_RES; + break; + } + p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; + BTA_HhSendData(p_dev->dev_handle, arg->send_data.bd_addr, p_buf); + } + } while(0); + + if (ret != ESP_HIDH_OK) { + param.send_data.handle = BTA_HH_INVALID_HANDLE; + param.send_data.status = ret; + param.send_data.reason = 0; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_get_idle_time +** +** Description Get the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_get_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->get_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhGetIdle(p_dev->dev_handle); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.get_idle.handle = BTA_HH_INVALID_HANDLE; + param.get_idle.status = ret; + param.get_idle.idle_rate = 0; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + } +} + +/******************************************************************************* +** +** Function btc_hh_set_idle_time +** +** Description Set the HID idle time +** +** Returns void +** +*******************************************************************************/ +static void btc_hh_set_idle_time(btc_hidh_args_t *arg) +{ + BTC_TRACE_API("%s", __func__); + esp_hidh_status_t ret = ESP_HIDH_OK; + esp_hidh_cb_param_t param; + btc_hh_device_t *p_dev; + + do { + if (!is_hidh_init()) { + BTC_TRACE_ERROR("%s HH has not been initiated, shall init first!", __func__); + ret = ESP_HIDH_NEED_INIT; + break; + } + + p_dev = btc_hh_find_connected_dev_by_bda(arg->set_idle.bd_addr); + if (p_dev == NULL) { + ret = ESP_HIDH_NO_CONNECTION; + BTC_TRACE_ERROR("%s Error: device not connected!", __func__); + break; + } + BTA_HhSetIdle(p_dev->dev_handle, arg->set_idle.idle_time); + } while (0); + + if (ret != ESP_HIDH_OK) { + param.set_idle.handle = BTA_HH_INVALID_HANDLE; + param.set_idle.status = ret; + btc_hh_cb_to_app(ESP_HIDH_SET_IDLE_EVT, ¶m); + } +} + +static void btc_hh_call_arg_deep_free(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)msg->arg; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + utl_freebuf((void **)&arg->set_info.hid_info); + break; + case BTC_HH_SET_REPORT_EVT: + utl_freebuf((void **)&arg->set_report.report); + break; + case BTC_HH_SEND_DATA_EVT: + utl_freebuf((void **)&arg->send_data.data); + break; + default: + break; + } +} + +void btc_hh_call_handler(btc_msg_t *msg) +{ + btc_hidh_args_t *arg = (btc_hidh_args_t *)(msg->arg); + switch (msg->act) { + case BTC_HH_INIT_EVT: + btc_hh_init(); + break; + case BTC_HH_CONNECT_EVT: + btc_hh_connect(arg); + break; + case BTC_HH_DISCONNECT_EVT: + btc_hh_disconnect(arg); + break; + case BTC_HH_UNPLUG_EVT: + btc_hh_virtual_unplug(arg); + break; + case BTC_HH_SET_INFO_EVT: + btc_hh_set_info(arg); + break; + case BTC_HH_GET_PROTO_EVT: + btc_hh_get_protocol(arg); + break; + case BTC_HH_SET_PROTO_EVT: + btc_hh_set_protocol(arg); + break; + case BTC_HH_GET_IDLE_EVT: + btc_hh_get_idle_time(arg); + break; + case BTC_HH_SET_IDLE_EVT: + btc_hh_set_idle_time(arg); + break; + case BTC_HH_GET_REPORT_EVT: + btc_hh_get_report(arg); + break; + case BTC_HH_SET_REPORT_EVT: + btc_hh_set_report(arg); + break; + case BTC_HH_SEND_DATA_EVT: + btc_hh_send_data(arg); + break; + case BTC_HH_DEINIT_EVT: + btc_hh_deinit(); + break; + default: + BTC_TRACE_WARNING("unknown hidh action %d", msg->act); + break; + } + btc_hh_call_arg_deep_free(msg); +} + +static void btc_hh_cb_arg_deep_free(btc_msg_t *msg) +{ + tBTA_HH *arg = (tBTA_HH *)msg->arg; + + switch (msg->act) { + case BTA_HH_GET_RPT_EVT: + utl_freebuf((void **)&arg->hs_data.rsp_data.p_rpt_data); + break; + case BTA_HH_DATA_IND_EVT: + utl_freebuf((void **)&arg->int_data.p_data); + break; + default: + break; + } +} + +bool btc_hh_copy_hid_info(tBTA_HH_DEV_DSCP_INFO *dest, tBTA_HH_DEV_DSCP_INFO *src) +{ + dest->descriptor.dl_len = 0; + if (src->descriptor.dl_len > 0) { + dest->descriptor.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (dest->descriptor.dsc_list) { + memcpy(dest->descriptor.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + dest->descriptor.dl_len = src->descriptor.dl_len; + } + dest->vendor_id = src->vendor_id; + dest->product_id = src->product_id; + dest->version = src->version; + dest->ctry_code = src->ctry_code; + dest->ssr_max_latency = src->ssr_max_latency; + dest->ssr_min_tout = src->ssr_min_tout; + return true; +} + +bool btc_hh_cb_copy_hid_info(esp_hidh_cb_param_t *param, tBTA_HH_DEV_DSCP_INFO *src) +{ + param->dscp.dl_len = 0; + if (src->descriptor.dl_len > 0) { + param->dscp.dsc_list = (uint8_t *)osi_malloc(src->descriptor.dl_len); + } + if (param->dscp.dsc_list) { + memcpy(param->dscp.dsc_list, src->descriptor.dsc_list, src->descriptor.dl_len); + param->dscp.dl_len = src->descriptor.dl_len; + } + param->dscp.vendor_id = src->vendor_id; + param->dscp.product_id = src->product_id; + param->dscp.version = src->version; + param->dscp.ctry_code = src->ctry_code; + param->dscp.ssr_max_latency = src->ssr_max_latency; + param->dscp.ssr_min_tout = src->ssr_min_tout; + return true; +} + +void btc_hh_cb_handler(btc_msg_t *msg) +{ + esp_hidh_cb_param_t param = {0}; + tBTA_HH *p_data = (tBTA_HH *)msg->arg; + btc_hh_device_t *p_dev = NULL; + int len, i; + BTC_TRACE_DEBUG("%s: event=%s dereg = %d", __func__, dump_hh_event(msg->act), btc_hh_cb.service_dereg_active); + switch (msg->act) { + case BTA_HH_ENABLE_EVT: + if (p_data->status == BTA_HH_OK) { + btc_hh_cb.status = BTC_HH_ENABLED; + BTC_TRACE_DEBUG("Loading added devices"); + /* Add hid descriptors for already bonded hid devices*/ + // btc_storage_load_bonded_hid_info(); + } else { + btc_hh_cb.status = BTC_HH_DISABLED; + BTC_TRACE_ERROR("Error, HH enabling failed, status = %d", p_data->status); + } + param.init.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_INIT_EVT, ¶m); + break; + case BTA_HH_DISABLE_EVT: + btc_hh_cb.status = BTC_HH_DISABLED; + if (btc_hh_cb.service_dereg_active) { + BTIF_TRACE_DEBUG("BTA_HH_DISABLE_EVT: enabling HID Device service"); + // btif_hd_service_registration(); + btc_hh_cb.service_dereg_active = FALSE; + } + if (p_data->status == BTA_HH_OK) { + // Clear the control block + for (uint8_t i = 0; i < BTC_HH_MAX_HID; i++) { + if (btc_hh_cb.devices[i].vup_timer) { + osi_alarm_free(btc_hh_cb.devices[i].vup_timer); + } + } + memset(&btc_hh_cb, 0, sizeof(btc_hh_cb)); + for (i = 0; i < BTC_HH_MAX_HID; i++) { + btc_hh_cb.devices[i].dev_status = ESP_HIDH_CONN_STATE_UNKNOWN; + } + } else { + BTC_TRACE_ERROR("Error, HH disabling failed, status = %d", p_data->status); + } + param.deinit.status = p_data->status; + btc_hh_cb_to_app(ESP_HIDH_DEINIT_EVT, ¶m); + break; + case BTA_HH_OPEN_EVT: + BTC_TRACE_DEBUG("handle=%d, status =%d", p_data->conn.handle, p_data->conn.status); + memset(btc_hh_cb.pending_conn_address, 0, BD_ADDR_LEN); + if (p_data->conn.status == BTA_HH_OK) { + p_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + if (p_dev == NULL) { + BTC_TRACE_ERROR("Error, cannot find device with handle %d", p_data->conn.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + // The connect request must come from device side and exceeded the + // connected HID device number. + BTA_HhClose(p_data->conn.handle); + + param.open.status = ESP_HIDH_ERR; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } else { + BTC_TRACE_DEBUG("Found device...Getting dscp info for handle " + "... %d", + p_data->conn.handle); + memcpy(p_dev->bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_CONNECTED; + // Send set_idle if the peer_device is a keyboard [boblane] + // if (check_cod(&p_data->conn.bda, COD_HID_KEYBOARD) || check_cod(&p_data->conn.bda, COD_HID_COMBO)) + // BTA_HhSetIdle(p_data->conn.handle, 0); + btc_hh_cb.p_curr_dev = btc_hh_find_connected_dev_by_handle(p_data->conn.handle); + BTA_HhGetDscpInfo(p_data->conn.handle); + p_dev->dev_status = ESP_HIDH_CONN_STATE_CONNECTED; + + param.open.status = ESP_HIDH_OK; + param.open.conn_status = ESP_HIDH_CONN_STATE_CONNECTED; + } + } else { + p_dev = btc_hh_find_dev_by_bda(p_data->conn.bda); + if (p_dev != NULL) { + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + + param.open.status = p_data->conn.status; + param.open.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + } + param.open.handle = p_data->conn.handle; + param.open.is_orig = p_data->conn.is_orig; + memcpy(param.open.bd_addr, p_data->conn.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_OPEN_EVT, ¶m); + break; + case BTA_HH_GET_DSCP_EVT: + len = p_data->dscp_info.descriptor.dl_len; + BTC_TRACE_DEBUG("len = %d", len); + do { + param.dscp.status = ESP_HIDH_OK; + param.dscp.handle = BTA_HH_INVALID_HANDLE; + param.dscp.added = false; + p_dev = btc_hh_cb.p_curr_dev; + if (p_dev == NULL) { + BTC_TRACE_ERROR("No HID device is currently connected"); + param.dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + + if (btc_hh_add_added_dev(p_dev->bd_addr, p_dev->attr_mask)) { + tBTA_HH_DEV_DSCP_INFO dscp_info; + bt_status_t ret; + btc_hh_copy_hid_info(&dscp_info, &p_data->dscp_info); + btc_hh_cb.add_event = BTC_HH_CONNECT_EVT; + BTA_HhAddDev(p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, dscp_info); + // write hid info to nvs + ret = btc_storage_add_hid_device_info((bt_bdaddr_t *)p_dev->bd_addr, p_dev->attr_mask, p_dev->sub_class, p_dev->app_id, + p_data->dscp_info.vendor_id, p_data->dscp_info.product_id, + p_data->dscp_info.version, p_data->dscp_info.ctry_code, + p_data->dscp_info.ssr_max_latency, p_data->dscp_info.ssr_min_tout, + len, p_data->dscp_info.descriptor.dsc_list); + + if (ret != BT_STATUS_SUCCESS) { + BTC_TRACE_ERROR("write hid info to nvs failed!"); + } + // Free buffer created for dscp_info; + if (dscp_info.descriptor.dl_len > 0 && dscp_info.descriptor.dsc_list != NULL) { + utl_freebuf((void **)&dscp_info.descriptor.dsc_list); + dscp_info.descriptor.dl_len = 0; + } + } else { + // Device already added. + BTC_TRACE_WARNING("%s: Device already added ", __func__); + param.dscp.added = true; + } + btc_hh_cb_copy_hid_info(¶m, &p_data->dscp_info); + param.dscp.handle = p_dev->dev_handle; + } while(0); + btc_hh_cb_to_app(ESP_HIDH_GET_DSCP_EVT, ¶m); + if (param.dscp.dl_len > 0 && param.dscp.dsc_list != NULL) { + utl_freebuf((void **)¶m.dscp.dsc_list); + param.dscp.dl_len = 0; + } + break; + case BTA_HH_CLOSE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + if (p_dev != NULL) { + BTC_TRACE_DEBUG("uhid local_vup=%d", p_dev->local_vup); + btc_hh_stop_vup_timer(p_dev->bd_addr); + /* If this is a locally initiated VUP, remove the bond as ACL got + * disconnected while VUP being processed. + */ + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } + + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + param.close.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.close.status = ESP_HIDH_NO_CONNECTION; + } + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + break; + case BTA_HH_VC_UNPLUG_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + p_dev = btc_hh_find_connected_dev_by_handle(p_data->dev_status.handle); + btc_hh_cb.status = (BTC_HH_STATUS)BTC_HH_DEV_DISCONNECTED; + if (p_dev != NULL) { + /* Stop the VUP timer */ + btc_hh_stop_vup_timer(p_dev->bd_addr); + p_dev->dev_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + BTC_TRACE_DEBUG("%s---Sending connection state change", __func__); + param.close.status = ESP_HIDH_OK; + param.close.handle = p_data->dev_status.handle; + param.close.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_CLOSE_EVT, ¶m); + BTC_TRACE_DEBUG("%s---Removing HID bond", __func__); + /* If it is locally initiated VUP or remote device has its major COD as + Peripheral removed the bond.*/ + // [boblane] + if (p_dev->local_vup) { + p_dev->local_vup = false; + BTA_DmRemoveDevice(p_dev->bd_addr, BT_TRANSPORT_BR_EDR); + } else { + btc_hh_remove_device(p_dev->bd_addr); + } + param.unplug.status = p_data->dev_status.status; + } else { + BTC_TRACE_ERROR("Error: cannot find device with handle %d", p_data->dev_status.handle); + param.unplug.status = ESP_HIDH_NO_CONNECTION; + } + param.unplug.handle = p_data->dev_status.handle; + param.unplug.conn_status = ESP_HIDH_CONN_STATE_DISCONNECTED; + btc_hh_cb_to_app(ESP_HIDH_VC_UNPLUG_EVT, ¶m); + break; + case BTA_HH_DATA_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->send_data.status, + p_data->send_data.handle); + param.send_data.handle = p_data->send_data.handle; + param.send_data.status = p_data->send_data.status; + param.send_data.reason = p_data->send_data.reason; + btc_hh_cb_to_app(ESP_HIDH_DATA_EVT, ¶m); + break; + case BTA_HH_GET_PROTO_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d, proto = [%d], %s", p_data->hs_data.status, + p_data->hs_data.handle, p_data->hs_data.rsp_data.proto_mode, + (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_RPT_MODE) + ? "Report Mode" + : (p_data->hs_data.rsp_data.proto_mode == BTA_HH_PROTO_BOOT_MODE) ? "Boot Mode" + : "Unsupported"); + param.get_proto.proto_mode = proto_mode_change_to_upper_layer(p_data->hs_data.rsp_data.proto_mode); + param.get_proto.handle = p_data->hs_data.handle; + param.get_proto.status = p_data->hs_data.status; + btc_hh_cb_to_app(ESP_HIDH_GET_PROTO_EVT, ¶m); + break; + case BTA_HH_SET_PROTO_EVT: + BTIF_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_proto.handle = p_data->dev_status.handle; + param.set_proto.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_PROTO_EVT, ¶m); + break; + case BTA_HH_GET_RPT_EVT: { + BT_HDR *hdr = p_data->hs_data.rsp_data.p_rpt_data; + uint8_t *data = NULL; + uint16_t len = 0; + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->hs_data.status, p_data->hs_data.handle); + /* p_rpt_data is NULL in HANDSHAKE response case */ + if (hdr) { + data = (uint8_t *)(hdr + 1) + hdr->offset; + len = hdr->len; + } + param.get_rpt.handle = p_data->hs_data.handle; + param.get_rpt.status = p_data->hs_data.status; + param.get_rpt.len = len; + param.get_rpt.data = data; + btc_hh_cb_to_app(ESP_HIDH_GET_RPT_EVT, ¶m); + break; + } + case BTA_HH_SET_RPT_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, + p_data->dev_status.handle); + param.set_rpt.handle = p_data->dev_status.handle; + param.set_rpt.status = p_data->dev_status.status; + btc_hh_cb_to_app(ESP_HIDH_SET_RPT_EVT, ¶m); + break; + case BTA_HH_GET_IDLE_EVT: + BTC_TRACE_DEBUG("handle = %d, status = %d, rate = %d", p_data->hs_data.handle, p_data->hs_data.status, + p_data->hs_data.rsp_data.idle_rate); + param.get_idle.handle = p_data->hs_data.handle; + param.get_idle.status = p_data->hs_data.status; + param.get_idle.idle_rate = p_data->hs_data.rsp_data.idle_rate; + btc_hh_cb_to_app(ESP_HIDH_GET_IDLE_EVT, ¶m); + break; + case BTA_HH_SET_IDLE_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_status.status, p_data->dev_status.handle); + param.set_idle.handle = p_data->dev_status.handle; + param.set_idle.status = p_data->dev_status.status; + btc_hh_cb_to_app(BTA_HH_SET_IDLE_EVT, ¶m); + break; + case BTA_HH_ADD_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + for (uint8_t i = 0; i < BTC_HH_MAX_ADDED_DEV; i++) { + if (memcmp(btc_hh_cb.added_devices[i].bd_addr, p_data->dev_info.bda, BD_ADDR_LEN) == 0) { + if (p_data->dev_info.status == BTA_HH_OK) { + btc_hh_cb.added_devices[i].dev_handle = p_data->dev_info.handle; + } else { + memset(btc_hh_cb.added_devices[i].bd_addr, 0, BD_ADDR_LEN); + btc_hh_cb.added_devices[i].dev_handle = BTA_HH_INVALID_HANDLE; + } + break; + } + } + if (btc_hh_cb.add_event == BTC_HH_SET_INFO_EVT) { + param.set_info.handle = p_data->dev_info.handle; + param.set_info.status = p_data->dev_info.status; + memcpy(param.set_info.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_SET_INFO_EVT, ¶m); + } else { + param.add_dev.handle = p_data->dev_info.handle; + param.add_dev.status = p_data->dev_info.status; + memcpy(param.add_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_ADD_DEV_EVT, ¶m); + } + break; + case BTA_HH_RMV_DEV_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->dev_info.status, p_data->dev_info.handle); + param.rmv_dev.handle = p_data->dev_info.status; + param.rmv_dev.status = p_data->dev_info.handle; + memcpy(param.rmv_dev.bd_addr, p_data->dev_info.bda, BD_ADDR_LEN); + btc_hh_cb_to_app(ESP_HIDH_RMV_DEV_EVT, ¶m); + break; + case BTA_HH_DATA_IND_EVT: + BTC_TRACE_DEBUG("status = %d, handle = %d", p_data->int_data.status, p_data->int_data.handle); + if (p_data->int_data.status == BTA_HH_OK && p_data->int_data.p_data) { + param.data_ind.len = p_data->int_data.p_data->len; + param.data_ind.data = p_data->int_data.p_data->data + p_data->int_data.p_data->offset; + } + param.data_ind.handle = p_data->int_data.handle; + param.data_ind.status = p_data->int_data.status; + param.data_ind.proto_mode = proto_mode_change_to_upper_layer(p_data->int_data.proto_mode); + btc_hh_cb_to_app(ESP_HIDH_DATA_IND_EVT, ¶m); + break; + case BTA_HH_API_ERR_EVT: + break; + default: + BTC_TRACE_WARNING("%s: Unhandled event: %d", __func__, msg->act); + break; + } + btc_hh_cb_arg_deep_free(msg); +} + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src) +{ + btc_hidh_args_t *dst = (btc_hidh_args_t *)p_dest; + btc_hidh_args_t *src = (btc_hidh_args_t *)p_src; + + switch (msg->act) { + case BTC_HH_SET_INFO_EVT: + dst->set_info.hid_info = (esp_hidh_hid_info_t *)osi_malloc(sizeof(esp_hidh_hid_info_t)); + if (dst->set_info.hid_info) { + memcpy(dst->set_info.hid_info, src->set_info.hid_info, sizeof(esp_hidh_hid_info_t)); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SET_REPORT_EVT: + dst->set_report.report = (uint8_t *)osi_malloc(src->set_report.len); + if (dst->set_report.report) { + memcpy(dst->set_report.report, src->set_report.report, src->set_report.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + case BTC_HH_SEND_DATA_EVT: + dst->send_data.data = (uint8_t *)osi_malloc(src->send_data.len); + if (dst->send_data.data) { + memcpy(dst->send_data.data, src->send_data.data, src->send_data.len); + } else { + BTC_TRACE_ERROR("%s %d osi_malloc failed\n", __func__, msg->act); + } + break; + default: + break; + } +} + +#endif // HID_HOST_INCLUDED == TRUE diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h new file mode 100644 index 0000000000..a2b92cf7ff --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h @@ -0,0 +1,103 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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. + * + ******************************************************************************/ +#ifndef BTC_HD_H +#define BTC_HD_H + +#if BTC_HD_INCLUDED == TRUE + +#include +#include "bta/bta_hd_api.h" +#include "btc/btc_task.h" +#include "esp_hidd_api.h" + +typedef enum { + BTC_HD_INIT_EVT = 0, + BTC_HD_DEINIT_EVT, + BTC_HD_REGISTER_APP_EVT, + BTC_HD_UNREGISTER_APP_EVT, + BTC_HD_CONNECT_EVT, + BTC_HD_DISCONNECT_EVT, + BTC_HD_SEND_REPORT_EVT, + BTC_HD_REPORT_ERROR_EVT, + BTC_HD_UNPLUG_EVT, +} BTC_HD_EVT; + +typedef enum { BTC_HD_DISABLED = 0, BTC_HD_ENABLED, BTC_HD_DISABLING } BTC_HD_STATUS; + +/* BTIF-HD control block */ +typedef struct { + BTC_HD_STATUS status; + bool app_registered; + bool service_dereg_active; + bool forced_disc; + tBTA_HD_APP_INFO app_info; + tBTA_HD_QOS_INFO in_qos; + tBTA_HD_QOS_INFO out_qos; +} btc_hd_cb_t; + +/* btc_hidd_args_t */ +typedef union { + // BTC_HD_CONNECT_EVT + struct connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HD_REGISTER_APP_EVT + struct register_app_arg { + esp_hidd_app_param_t *app_param; + esp_hidd_qos_param_t *in_qos; + esp_hidd_qos_param_t *out_qos; + } register_app; + + // BTC_HD_SEND_REPORT_EVT + struct send_report_arg { + esp_hidd_report_type_t type; + uint8_t id; + uint16_t len; + uint8_t *data; + } send_report; + + // BTC_HD_REPORT_ERROR_EVT + uint8_t error; +} btc_hidd_args_t; + +#ifdef __cplusplus +extern "C" { +#endif + +/****************************************************************************** + * Functions + ******************************************************************************/ +void btc_hd_call_handler(btc_msg_t *msg); + +void btc_hd_cb_handler(btc_msg_t *msg); + +// extern btc_hd_cb_t btc_hd_cb; +// extern void btc_hd_remove_device(bt_bdaddr_t bd_addr); +// extern void btc_hd_service_registration(); + +void btc_hd_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +#ifdef __cplusplus +} +#endif + +#endif /* BTC_HD_INCLUDED == TRUE */ +#endif /* BTC_HD_H */ diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h new file mode 100644 index 0000000000..c07636ed1c --- /dev/null +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h @@ -0,0 +1,187 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2009-2012 Broadcom Corporation + * Copyright (C) 2019 Blake Felt + * + * 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. + * + ******************************************************************************/ +#ifndef BTC_HH_H +#define BTC_HH_H + +#include +#include "bta/bta_hh_api.h" +#include "btc/btc_task.h" +#include "osi/alarm.h" +#include "esp_hidh_api.h" + +#define BTC_HH_MAX_HID 8 +#define BTC_HH_MAX_ADDED_DEV 32 + +#define BTC_HH_MAX_KEYSTATES 3 +#define BTC_HH_KEYSTATE_MASK_NUMLOCK 0x01 +#define BTC_HH_KEYSTATE_MASK_CAPSLOCK 0x02 +#define BTC_HH_KEYSTATE_MASK_SCROLLLOCK 0x04 + +#define BTC_HH_MAX_POLLING_ATTEMPTS 10 +#define BTC_HH_POLLING_SLEEP_DURATION_US 5000 + +/******************************************************************************* + * Type definitions and return values + ******************************************************************************/ +typedef enum { + BTC_HH_INIT_EVT = 0, + BTC_HH_CONNECT_EVT, + BTC_HH_DISCONNECT_EVT, + BTC_HH_UNPLUG_EVT, + BTC_HH_SET_INFO_EVT, + BTC_HH_GET_PROTO_EVT, + BTC_HH_SET_PROTO_EVT, + BTC_HH_GET_IDLE_EVT, + BTC_HH_SET_IDLE_EVT, + BTC_HH_GET_REPORT_EVT, + BTC_HH_SET_REPORT_EVT, + BTC_HH_SEND_DATA_EVT, + BTC_HH_DEINIT_EVT, +} BTC_HH_EVT; + +typedef enum { + BTC_HH_DISABLED = 0, + BTC_HH_ENABLED, + BTC_HH_DISABLING, + BTC_HH_DEV_UNKNOWN, + BTC_HH_DEV_CONNECTING, + BTC_HH_DEV_CONNECTED, + BTC_HH_DEV_DISCONNECTED +} BTC_HH_STATUS; + +typedef struct { + esp_hidh_connection_state_t dev_status; + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; + uint8_t sub_class; + uint8_t app_id; + bool ready_for_data; + osi_alarm_t *vup_timer; + bool local_vup; // Indicated locally initiated VUP +} btc_hh_device_t; + +/* Control block to maintain properties of devices */ +typedef struct { + uint8_t dev_handle; + BD_ADDR bd_addr; + uint16_t attr_mask; +} btc_hh_added_device_t; + +/** + * BTC-HH control block to maintain added devices and currently + * connected hid devices + */ +typedef struct { + BTC_HH_STATUS status; + btc_hh_device_t devices[BTC_HH_MAX_HID]; + uint32_t device_num; + BTC_HH_EVT add_event; + btc_hh_added_device_t added_devices[BTC_HH_MAX_ADDED_DEV]; + btc_hh_device_t *p_curr_dev; + bool service_dereg_active; + BD_ADDR pending_conn_address; +} btc_hh_cb_t; + +/* btc_spp_args_t */ +typedef union { + // BTC_HH_CONNECT_EVT + struct connect_arg { + BD_ADDR bd_addr; + } connect; + + // BTC_HH_DISCONNECT_EVT + struct disconnect_arg { + BD_ADDR bd_addr; + } disconnect; + + // BTC_HH_UNPLUG_EVT + struct unplug_arg { + BD_ADDR bd_addr; + } unplug; + + // BTC_HH_SET_INFO_EVT + struct set_info_arg { + BD_ADDR bd_addr; + esp_hidh_hid_info_t *hid_info; + } set_info; + + // BTC_HH_GET_PROTO_EVT + struct get_protocol_arg { + BD_ADDR bd_addr; + } get_protocol; + + // BTC_HH_SET_PROTO_EVT + struct set_protocol_arg { + BD_ADDR bd_addr; + esp_hidh_protocol_mode_t protocol_mode; + } set_protocol; + + // BTC_HH_GET_IDLE_EVT + struct get_idle_arg { + BD_ADDR bd_addr; + } get_idle; + + // BTC_HH_SET_IDLE_EVT + struct set_idle_arg { + BD_ADDR bd_addr; + uint16_t idle_time; + } set_idle; + + // BTC_HH_GET_REPORT_EVT + struct get_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + uint8_t report_id; + int buffer_size; + } get_report; + + // BTC_HH_SET_REPORT_EVT + struct set_report_arg { + BD_ADDR bd_addr; + esp_hidh_report_type_t report_type; + size_t len; + uint8_t *report; + } set_report; + + // BTC_HH_SEND_DATA_EVT + struct send_data_arg { + BD_ADDR bd_addr; + size_t len; + uint8_t *data; + } send_data; +} btc_hidh_args_t; +/******************************************************************************* + * Variables + ******************************************************************************/ +extern btc_hh_cb_t btc_hh_cb; +/******************************************************************************* + * Functions + ******************************************************************************/ + +void btc_hh_call_handler(btc_msg_t *msg); + +void btc_hh_cb_handler(btc_msg_t *msg); + +void btc_hh_arg_deep_copy(btc_msg_t *msg, void *p_dest, void *p_src); + +bool btc_hh_add_added_dev(BD_ADDR bd_addr, uint16_t attr_mask); + +#endif /* BTC_HH_H */ diff --git a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h index 07663e6da2..b139b760a9 100644 --- a/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h +++ b/components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h @@ -67,6 +67,13 @@ #define UC_BT_HFP_CLIENT_ENABLED FALSE #endif +//HID +#ifdef CONFIG_BT_HID_ENABLED +#define UC_BT_HID_ENABLED CONFIG_BT_HID_ENABLED +#else +#define UC_BT_HID_ENABLED FALSE +#endif + //HID HOST(BT) #ifdef CONFIG_BT_HID_HOST_ENABLED #define UC_BT_HID_HOST_ENABLED CONFIG_BT_HID_HOST_ENABLED @@ -74,6 +81,13 @@ #define UC_BT_HID_HOST_ENABLED FALSE #endif +//HID Device(BT) +#ifdef CONFIG_BT_HID_DEVICE_ENABLED +#define UC_BT_HID_DEVICE_ENABLED CONFIG_BT_HID_DEVICE_ENABLED +#else +#define UC_BT_HID_DEVICE_ENABLED FALSE +#endif + //SSP #ifdef CONFIG_BT_SSP_ENABLED #define UC_BT_SSP_ENABLED CONFIG_BT_SSP_ENABLED @@ -365,10 +379,18 @@ #define UC_BT_LOG_MCA_TRACE_LEVEL UC_TRACE_LEVEL_WARNING #endif -#ifdef CONFIG_BT_LOG_HIDH_TRACE_LEVEL -#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HIDH_TRACE_LEVEL +#ifdef CONFIG_BT_LOG_HID_TRACE_LEVEL +#if UC_BT_HID_HOST_ENABLED +#define UC_BT_LOG_HIDH_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#elif UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL CONFIG_BT_LOG_HID_TRACE_LEVEL +#endif #else +#if UC_BT_HID_HOST_ENABLED #define UC_BT_LOG_HIDH_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#elif UC_BT_HID_DEVICE_ENABLED +#define UC_BT_LOG_HIDD_TRACE_LEVEL UC_TRACE_LEVEL_WARNING +#endif #endif #ifdef CONFIG_BT_LOG_APPL_TRACE_LEVEL diff --git a/components/bt/host/bluedroid/common/include/common/bt_target.h b/components/bt/host/bluedroid/common/include/common/bt_target.h index 598fa92774..4afb853e6e 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_target.h +++ b/components/bt/host/bluedroid/common/include/common/bt_target.h @@ -130,11 +130,22 @@ #define BT_SSP_INCLUDED TRUE #endif /* UC_BT_SSP_ENABLED */ +#if UC_BT_HID_ENABLED +#define BT_HID_INCLUDED TRUE +#endif /* UC_BT_HID_ENABLED */ + #if UC_BT_HID_HOST_ENABLED #define HID_HOST_INCLUDED TRUE #define BTA_HH_INCLUDED TRUE +#define BTC_HH_INCLUDED TRUE #endif /* UC_BT_HID_HOST_ENABLED */ +#if UC_BT_HID_DEVICE_ENABLED +#define HID_DEV_INCLUDED TRUE +#define BTA_HD_INCLUDED TRUE +#define BTC_HD_INCLUDED TRUE +#endif /* UC_BT_HID_DEVICE_ENABLED */ + #endif /* UC_BT_CLASSIC_ENABLED */ /* This is set to enable use of GAP L2CAP connections. */ @@ -321,6 +332,14 @@ #define BTC_SPP_INCLUDED FALSE #endif +#ifndef BTC_HH_INCLUDED +#define BTC_HH_INCLUDED FALSE +#endif + +#ifndef BTC_HD_INCLUDED +#define BTC_HD_INCLUDED FALSE +#endif + #ifndef SBC_DEC_INCLUDED #define SBC_DEC_INCLUDED FALSE #endif @@ -350,6 +369,10 @@ #define BTA_PAN_INCLUDED FALSE #endif +#ifndef BTA_HD_INCLUDED +#define BTA_HD_INCLUDED FALSE +#endif + #ifndef BTA_HH_INCLUDED #define BTA_HH_INCLUDED FALSE #endif @@ -1378,7 +1401,11 @@ /* The maximum number of attributes in each record. */ #ifndef SDP_MAX_REC_ATTR +#if defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) +#define SDP_MAX_REC_ATTR 25 +#else #define SDP_MAX_REC_ATTR 8 +#endif /* defined(HID_DEV_INCLUDED) && (HID_DEV_INCLUDED==TRUE) */ #endif #ifndef SDP_MAX_PAD_LEN @@ -1845,6 +1872,18 @@ Range: 2 octets ** HID ** ******************************************************************************/ +#ifndef BT_HID_INCLUDED +#define BT_HID_INCLUDED FALSE +#endif + +/* HID Device Role Included */ +#ifndef HID_DEV_INCLUDED +#define HID_DEV_INCLUDED FALSE +#endif + +#ifndef HID_DEV_SUBCLASS +#define HID_DEV_SUBCLASS COD_MINOR_POINTING +#endif #ifndef HID_CONTROL_BUF_SIZE #define HID_CONTROL_BUF_SIZE BT_DEFAULT_BUFFER_SIZE @@ -1854,6 +1893,14 @@ Range: 2 octets #define HID_INTERRUPT_BUF_SIZE BT_DEFAULT_BUFFER_SIZE #endif +#ifndef HID_DEV_MTU_SIZE +#define HID_DEV_MTU_SIZE 64 +#endif + +#ifndef HID_DEV_FLUSH_TO +#define HID_DEV_FLUSH_TO 0xffff +#endif + /************************************************************************* ** Definitions for Both HID-Host & Device */ diff --git a/components/bt/host/bluedroid/common/include/common/bt_trace.h b/components/bt/host/bluedroid/common/include/common/bt_trace.h index 55babed961..a4f1ca749a 100644 --- a/components/bt/host/bluedroid/common/include/common/bt_trace.h +++ b/components/bt/host/bluedroid/common/include/common/bt_trace.h @@ -106,6 +106,7 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define BTTRC_ID_STK_CE 51 #define BTTRC_ID_STK_SNEP 52 #define BTTRC_ID_STK_NDEF 53 +#define BTTRC_ID_STK_HIDD 54 /* LayerIDs for BTA */ #define BTTRC_ID_BTA_ACC 55 /* Advanced Camera Client */ @@ -199,6 +200,7 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define AVRC_INITIAL_TRACE_LEVEL UC_BT_LOG_AVRC_TRACE_LEVEL #define MCA_INITIAL_TRACE_LEVEL UC_BT_LOG_MCA_TRACE_LEVEL #define HIDH_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDH_TRACE_LEVEL +#define HIDD_INITIAL_TRACE_LEVEL UC_BT_LOG_HIDD_TRACE_LEVEL #define APPL_INITIAL_TRACE_LEVEL UC_BT_LOG_APPL_TRACE_LEVEL #define GATT_INITIAL_TRACE_LEVEL UC_BT_LOG_GATT_TRACE_LEVEL #define SMP_INITIAL_TRACE_LEVEL UC_BT_LOG_SMP_TRACE_LEVEL @@ -258,6 +260,14 @@ static inline void trc_dump_buffer(const char *prefix, uint8_t *data, uint16_t l #define HIDH_TRACE_EVENT(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDH,EVENT)) BT_PRINT_D("BT_HIDH", fmt, ## args);} #define HIDH_TRACE_DEBUG(fmt, args...) {if (hh_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDH,DEBUG)) BT_PRINT_D("BT_HIDH", fmt, ## args);} +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(HIDD, ERROR)) BT_PRINT_E("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_WARNING(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_WARNING && BT_LOG_LEVEL_CHECK(HIDD, WARNING)) BT_PRINT_W("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_API(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_API && BT_LOG_LEVEL_CHECK(HIDD,API)) BT_PRINT_I("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_EVENT(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_EVENT && BT_LOG_LEVEL_CHECK(HIDD,EVENT)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_DEBUG(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_DEBUG && BT_LOG_LEVEL_CHECK(HIDD,DEBUG)) BT_PRINT_D("BT_HIDD", fmt, ## args);} +#define HIDD_TRACE_VERBOSE(fmt, args...) {if (hd_cb.trace_level >= BT_TRACE_LEVEL_VERBOSE && BT_LOG_LEVEL_CHECK(HIDD,VERBOSE)) BT_PRINT_D("BT_HIDD", fmt, ## args);} + /* define traces for BNEP */ #define BNEP_TRACE_ERROR(fmt, args...) {if (bnep_cb.trace_level >= BT_TRACE_LEVEL_ERROR && BT_LOG_LEVEL_CHECK(BNEP, ERROR)) BT_PRINT_E("BT_BNEP", fmt, ## args);} @@ -418,6 +428,14 @@ extern UINT8 btif_trace_level; #define HIDH_TRACE_EVENT(fmt, args...) #define HIDH_TRACE_DEBUG(fmt, args...) +/* define traces for HID Device */ +#define HIDD_TRACE_ERROR(fmt, args...) +#define HIDD_TRACE_WARNING(fmt, args...) +#define HIDD_TRACE_API(fmt, args...) +#define HIDD_TRACE_EVENT(fmt, args...) +#define HIDD_TRACE_DEBUG(fmt, args...) +#define HIDD_TRACE_VERBOSE(fmt, args...) + /* define traces for BNEP */ #define BNEP_TRACE_ERROR(fmt, args...) diff --git a/components/bt/host/bluedroid/main/bte_init.c b/components/bt/host/bluedroid/main/bte_init.c index d2373d10eb..80c4357b6a 100644 --- a/components/bt/host/bluedroid/main/bte_init.c +++ b/components/bt/host/bluedroid/main/bte_init.c @@ -52,6 +52,14 @@ #include "pan_api.h" #endif +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) +#include "stack/hidh_api.h" +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +#endif + #if (defined(AVRC_INCLUDED) && AVRC_INCLUDED == TRUE) #include "stack/avrc_api.h" #endif @@ -118,6 +126,10 @@ #include "bta_hh_int.h" #endif +#if BTA_HD_INCLUDED==TRUE +#include "bta_hd_int.h" +#endif + #if BTA_JV_INCLUDED==TRUE #include "bta_jv_int.h" #endif @@ -175,6 +187,12 @@ void BTE_DeinitStack(void) bta_gattc_cb_ptr = NULL; } #endif +#if BTA_HD_INCLUDED==TRUE + if (bta_hd_cb_ptr){ + osi_free(bta_hd_cb_ptr); + bta_hd_cb_ptr = NULL; + } +#endif #if BTA_HH_INCLUDED==TRUE if (bta_hh_cb_ptr){ osi_free(bta_hh_cb_ptr); @@ -249,6 +267,14 @@ void BTE_DeinitStack(void) } #endif // BTA_INCLUDED == TRUE +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + HID_DevDeinit(); +#endif + +#if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) + HID_HostDeinit(); +#endif + #if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE) GAP_Deinit(); #endif @@ -347,7 +373,15 @@ bt_status_t BTE_InitStack(void) #endif #if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE) - HID_HostInit(); + if (HID_HostInit() != HID_SUCCESS) { + goto error_exit; + } +#endif + +#if (defined(HID_DEV_INCLUDED) && HID_DEV_INCLUDED == TRUE) + if (HID_DevInit() != HID_SUCCESS) { + goto error_exit; + } #endif #if (defined(MCA_INCLUDED) && MCA_INCLUDED == TRUE) @@ -434,6 +468,12 @@ bt_status_t BTE_InitStack(void) } memset((void *)bta_hh_cb_ptr, 0, sizeof(tBTA_HH_CB)); #endif +#if BTA_HD_INCLUDED==TRUE + if ((bta_hd_cb_ptr = (tBTA_HD_CB *)osi_malloc(sizeof(tBTA_HD_CB))) == NULL) { + goto error_exit; + } + memset((void *)bta_hd_cb_ptr, 0, sizeof(tBTA_HD_CB)); +#endif #if BTA_HL_INCLUDED==TRUE memset((void *)bta_hl_cb_ptr, 0, sizeof(tBTA_HL_CB)); #endif diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h index 017eb89223..790806283b 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h @@ -394,9 +394,7 @@ tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); void btm_ble_stop_scan(void); void btm_clear_all_pending_le_entry(void); -BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, - UINT32 scan_win, UINT8 addr_type_own, - UINT8 scan_filter_policy); +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, UINT32 scan_win, UINT8 addr_type_own, UINT8 scan_filter_policy); void btm_ble_stop_inquiry(void); void btm_ble_init (void); void btm_ble_free (void); @@ -407,8 +405,7 @@ void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced); void btm_read_ble_local_supported_states_complete(UINT8 *p, UINT16 evt_len); tBTM_BLE_CONN_ST btm_ble_get_conn_st(void); void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st); -UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, - tBTM_BLE_ADV_DATA *p_data); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); tBTM_STATUS btm_ble_start_adv(void); tBTM_STATUS btm_ble_stop_adv(void); tBTM_STATUS btm_ble_start_scan(void); diff --git a/components/bt/host/bluedroid/stack/btm/include/btm_int.h b/components/bt/host/bluedroid/stack/btm/include/btm_int.h index ade758a81b..d1498a38a2 100644 --- a/components/bt/host/bluedroid/stack/btm/include/btm_int.h +++ b/components/bt/host/bluedroid/stack/btm/include/btm_int.h @@ -961,12 +961,10 @@ extern tBTM_CallbackFunc conn_param_update_cb; typedef UINT8 tBTM_SEC_ACTION; -/* #ifdef __cplusplus extern "C" { #endif -*/ #if BTM_DYNAMIC_MEMORY == FALSE extern tBTM_CB btm_cb; diff --git a/components/bt/host/bluedroid/stack/btu/btu_task.c b/components/bt/host/bluedroid/stack/btu/btu_task.c index e60fcfa43b..83d4219542 100644 --- a/components/bt/host/bluedroid/stack/btu/btu_task.c +++ b/components/bt/host/bluedroid/stack/btu/btu_task.c @@ -54,7 +54,7 @@ #endif #if (defined(HID_HOST_INCLUDED) && HID_HOST_INCLUDED == TRUE ) -#include "hidh_int.h" +#include "hid_int.h" #endif #if (defined(AVDT_INCLUDED) && AVDT_INCLUDED == TRUE) diff --git a/components/bt/host/bluedroid/stack/hid/hidd_api.c b/components/bt/host/bluedroid/stack/hid/hidd_api.c new file mode 100644 index 0000000000..347ef7ebc2 --- /dev/null +++ b/components/bt/host/bluedroid/stack/hid/hidd_api.c @@ -0,0 +1,586 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * This file contains the HID Device API entry points + * + ******************************************************************************/ +//#include +//#include +//#include +#include "stack/hidd_api.h" +#include "esp_hidd_api.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hiddefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +#if HID_DYNAMIC_MEMORY == FALSE +tHID_DEV_CTB hd_cb; +#else +tHID_DEV_CTB *hidd_cb_ptr = NULL; +#endif + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevInit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (!hidd_cb_ptr) { + hidd_cb_ptr = (tHID_DEV_CTB *)osi_malloc(sizeof(tHID_DEV_CTB)); + if (!hidd_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ + memset(&hd_cb, 0, sizeof(tHID_DEV_CTB)); +#if defined(HIDD_INITIAL_TRACE_LEVEL) + hd_cb.trace_level = HIDD_INITIAL_TRACE_LEVEL; +#else + hd_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevDeinit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +void HID_DevDeinit(void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidd_cb_ptr) { + osi_free(hidd_cb_ptr); + hidd_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ +} + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with + * a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +uint8_t HID_DevSetTraceLevel(uint8_t new_level) +{ + if (new_level != 0xFF) { + hd_cb.trace_level = new_level; + } + + return (hd_cb.trace_level); +} + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK *host_cback) +{ + tHID_STATUS st; + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.reg_flag) { + return HID_ERR_ALREADY_REGISTERED; + } + + if (host_cback == NULL) { + return HID_ERR_INVALID_PARAM; + } + /* Register with L2CAP */ + if ((st = hidd_conn_reg()) != HID_SUCCESS) { + return st; + } + + hd_cb.callback = host_cback; + hd_cb.reg_flag = TRUE; + + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDeregister(void) +{ + HIDD_TRACE_API("%s", __func__); + + if (!hd_cb.reg_flag) + return (HID_ERR_NOT_REGISTERED); + hidd_conn_dereg(); + hd_cb.reg_flag = FALSE; + + return (HID_SUCCESS); +} + +tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl) +{ + HIDD_TRACE_API("%s", __func__); + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 1 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_SEC_CTRL, sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, + HIDD_SEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 2 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 3 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_NOSEC_CTRL, BTM_SEC_NONE, HID_PSM_CONTROL, + BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN)) { + HIDD_TRACE_ERROR("Security Registration 4 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 5 failed"); + return (HID_ERR_NO_RESOURCES); + } + if (!BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HIDD_INTR, BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, + 0)) { + HIDD_TRACE_ERROR("Security Registration 6 failed"); + return (HID_ERR_NO_RESOURCES); + } + return (HID_SUCCESS); +} + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevAddRecord(uint32_t handle, char *p_name, char *p_description, char *p_provider, uint16_t subclass, + uint16_t desc_len, uint8_t *p_desc_data) +{ + bool result = TRUE; + + HIDD_TRACE_API("%s", __func__); + + // Service Class ID List + if (result) { + uint16_t uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + result &= SDP_AddServiceClassIdList(handle, 1, &uuid); + } + // Protocol Descriptor List + if (result) { + tSDP_PROTOCOL_ELEM proto_list[2]; + proto_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + proto_list[0].num_params = 1; + proto_list[0].params[0] = BT_PSM_HIDC; + proto_list[1].protocol_uuid = UUID_PROTOCOL_HIDP; + proto_list[1].num_params = 0; + result &= SDP_AddProtocolList(handle, 2, proto_list); + } + // Language Base Attribute ID List + if (result) { + result &= + SDP_AddLanguageBaseAttrIDList(handle, LANG_ID_CODE_ENGLISH, LANG_ID_CHAR_ENCODE_UTF8, LANGUAGE_BASE_ID); + } + // Additional Protocol Descriptor List + if (result) { + tSDP_PROTO_LIST_ELEM add_proto_list; + add_proto_list.num_elems = 2; + add_proto_list.list_elem[0].protocol_uuid = UUID_PROTOCOL_L2CAP; + add_proto_list.list_elem[0].num_params = 1; + add_proto_list.list_elem[0].params[0] = BT_PSM_HIDI; + add_proto_list.list_elem[1].protocol_uuid = UUID_PROTOCOL_HIDP; + add_proto_list.list_elem[1].num_params = 0; + result &= SDP_AddAdditionProtoLists(handle, 1, &add_proto_list); + } + // Service Name (O) + // Service Description (O) + // Provider Name (O) + if (result) { + const char *srv_name = p_name; + const char *srv_desc = p_description; + const char *provider_name = p_provider; + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE, strlen(srv_name) + 1, + (uint8_t *)srv_name); + result &= SDP_AddAttribute(handle, ATTR_ID_SERVICE_DESCRIPTION, TEXT_STR_DESC_TYPE, strlen(srv_desc) + 1, + (uint8_t *)srv_desc); + result &= SDP_AddAttribute(handle, ATTR_ID_PROVIDER_NAME, TEXT_STR_DESC_TYPE, strlen(provider_name) + 1, + (uint8_t *)provider_name); + } + // Bluetooth Profile Descriptor List + if (result) { + const uint16_t profile_uuid = UUID_SERVCLASS_HUMAN_INTERFACE; + const uint16_t version = 0x0100; + result &= SDP_AddProfileDescriptorList(handle, profile_uuid, version); + } + // HID Parser Version + if (result) { + uint8_t *p; + const uint16_t rel_num = 0x0100; + const uint16_t parser_version = 0x0111; + const uint16_t prof_ver = 0x0100; + const uint8_t dev_subclass = subclass; + const uint8_t country_code = 0x21; + const uint8_t bool_false = 0x00; + const uint8_t bool_true = 0x01; + uint16_t temp; + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, rel_num); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_RELNUM, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, parser_version); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PARSER_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DEVICE_SUBCLASS, UINT_DESC_TYPE, 1, (uint8_t *)&dev_subclass); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_COUNTRY_CODE, UINT_DESC_TYPE, 1, (uint8_t *)&country_code); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_VIRTUAL_CABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_RECONNECT_INITIATE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + { + static uint8_t cdt = 0x22; + uint8_t *p_buf; + uint8_t seq_len = 4 + desc_len; + p_buf = (uint8_t *)osi_malloc(2048); + if (p_buf == NULL) { + HIDD_TRACE_ERROR("%s: Buffer allocation failure for size = 2048 ", __func__); + return HID_ERR_NOT_REGISTERED; + } + p = p_buf; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_ONE_BYTE); + UINT8_TO_BE_STREAM(p, cdt); + UINT8_TO_BE_STREAM(p, (TEXT_STR_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, desc_len); + ARRAY_TO_BE_STREAM(p, p_desc_data, (int)desc_len); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_DESCRIPTOR_LIST, DATA_ELE_SEQ_DESC_TYPE, p - p_buf, p_buf); + osi_free(p_buf); + } + { + uint8_t lang_buf[8]; + p = lang_buf; + uint8_t seq_len = 6; + uint16_t lang_english = 0x0409; + UINT8_TO_BE_STREAM(p, (DATA_ELE_SEQ_DESC_TYPE << 3) | SIZE_IN_NEXT_BYTE); + UINT8_TO_BE_STREAM(p, seq_len); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, lang_english); + UINT8_TO_BE_STREAM(p, (UINT_DESC_TYPE << 3) | SIZE_TWO_BYTES); + UINT16_TO_BE_STREAM(p, LANGUAGE_BASE_ID); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_LANGUAGE_ID_BASE, DATA_ELE_SEQ_DESC_TYPE, p - lang_buf, lang_buf); + } + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BATTERY_POWER, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_REMOTE_WAKE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_false); + result &= + SDP_AddAttribute(handle, ATTR_ID_HID_NORMALLY_CONNECTABLE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_BOOT_DEVICE, BOOLEAN_DESC_TYPE, 1, (uint8_t *)&bool_true); + p = (uint8_t *)&temp; + UINT16_TO_BE_STREAM(p, prof_ver); + result &= SDP_AddAttribute(handle, ATTR_ID_HID_PROFILE_VERSION, UINT_DESC_TYPE, 2, (uint8_t *)&temp); + } + if (result) { + uint16_t browse_group = UUID_SERVCLASS_PUBLIC_BROWSE_GROUP; + result &= SDP_AddUuidSequence(handle, ATTR_ID_BROWSE_GROUP_LIST, 1, &browse_group); + } + if (!result) { + HIDD_TRACE_ERROR("%s: failed to complete SDP record", __func__); + return HID_ERR_NOT_REGISTERED; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, uint16_t len, uint8_t *p_data) +{ + HIDD_TRACE_VERBOSE("%s: channel=%d type=%d id=%d len=%d", __func__, channel, type, id, len); + + if (channel == HID_CHANNEL_CTRL) { + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, type, id, len, p_data); + } + + if (channel == HID_CHANNEL_INTR && type == HID_PAR_REP_TYPE_INPUT) { + // on INTR we can only send INPUT + return hidd_conn_send_data(HID_CHANNEL_INTR, HID_TRANS_DATA, HID_PAR_REP_TYPE_INPUT, id, len, p_data); + } + + return HID_ERR_INVALID_PARAM; +} + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevVirtualCableUnplug(void) +{ + HIDD_TRACE_API("%s", __func__); + + return hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_CONTROL, HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevPlugDevice(BD_ADDR addr) +{ + hd_cb.device.in_use = TRUE; + memcpy(hd_cb.device.addr, addr, sizeof(BD_ADDR)); + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr) +{ + if (!memcmp(hd_cb.device.addr, addr, sizeof(BD_ADDR))) { + hd_cb.device.in_use = FALSE; + hd_cb.device.conn.conn_state = HID_CONN_STATE_UNUSED; + hd_cb.device.conn.ctrl_cid = 0; + hd_cb.device.conn.intr_cid = 0; + } + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevConnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state != HIDD_DEV_NO_CONN) { + return HID_ERR_ALREADY_CONN; + } + return hidd_conn_initiate(); +} + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevDisconnect(void) +{ + if (!hd_cb.reg_flag) { + return HID_ERR_NOT_REGISTERED; + } + if (!hd_cb.device.in_use) { + return HID_ERR_INVALID_PARAM; + } + if (hd_cb.device.state == HIDD_DEV_NO_CONN) { + return HID_ERR_NO_CONNECTION; + } + return hidd_conn_disconnect(); +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingPolicy(bool allow) +{ + hd_cb.allow_incoming = allow; + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevReportError(uint8_t error) +{ + uint8_t handshake_param; + + HIDD_TRACE_API("%s: error = %d", __func__, error); + + switch (error) { + case HID_PAR_HANDSHAKE_RSP_SUCCESS: + case HID_PAR_HANDSHAKE_RSP_NOT_READY: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID: + case HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ: + case HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM: + case HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN: + case HID_PAR_HANDSHAKE_RSP_ERR_FATAL: + handshake_param = error; + break; + default: + handshake_param = HID_PAR_HANDSHAKE_RSP_ERR_UNKNOWN; + break; + } + + return hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, handshake_param, 0, 0, NULL); +} + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevGetDevice(BD_ADDR *addr) +{ + HIDD_TRACE_API("%s", __func__); + + if (hd_cb.device.in_use) { + memcpy(addr, hd_cb.device.addr, sizeof(BD_ADDR)); + } else { + return HID_ERR_NOT_REGISTERED; + } + + return HID_SUCCESS; +} + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetIncomingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.use_in_qos = TRUE; + hd_cb.in_qos.service_type = service_type; + hd_cb.in_qos.token_rate = token_rate; + hd_cb.in_qos.token_bucket_size = token_bucket_size; + hd_cb.in_qos.peak_bandwidth = peak_bandwidth; + hd_cb.in_qos.latency = latency; + hd_cb.in_qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS HID_DevSetOutgoingQos(uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation) +{ + HIDD_TRACE_API("%s", __func__); + hd_cb.l2cap_intr_cfg.qos_present = TRUE; + hd_cb.l2cap_intr_cfg.qos.service_type = service_type; + hd_cb.l2cap_intr_cfg.qos.token_rate = token_rate; + hd_cb.l2cap_intr_cfg.qos.token_bucket_size = token_bucket_size; + hd_cb.l2cap_intr_cfg.qos.peak_bandwidth = peak_bandwidth; + hd_cb.l2cap_intr_cfg.qos.latency = latency; + hd_cb.l2cap_intr_cfg.qos.delay_variation = delay_variation; + return HID_SUCCESS; +} +#endif diff --git a/components/bt/host/bluedroid/stack/hid/hidd_conn.c b/components/bt/host/bluedroid/stack/hid/hidd_conn.c new file mode 100644 index 0000000000..05a14799e0 --- /dev/null +++ b/components/bt/host/bluedroid/stack/hid/hidd_conn.c @@ -0,0 +1,780 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +/****************************************************************************** + * + * this file contains the connection interface functions + * + ******************************************************************************/ +#include "btm_int.h" +#include "hid_conn.h" +#include "hid_int.h" +#include "osi/allocator.h" +#include "osi/osi.h" +#include "stack/btm_api.h" +#include "stack/btu.h" +#include "stack/hidd_api.h" +#include "stack/hiddefs.h" +#include "stack/l2c_api.h" +#include "stack/l2cdefs.h" +#include +#include +#include + +#if (HID_DEV_INCLUDED == TRUE) + +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id); +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg); +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed); +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result); +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg); +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested); + +static const tL2CAP_APPL_INFO dev_reg_info = {hidd_l2cif_connect_ind, + hidd_l2cif_connect_cfm, + NULL, + hidd_l2cif_config_ind, + hidd_l2cif_config_cfm, + hidd_l2cif_disconnect_ind, + hidd_l2cif_disconnect_cfm, + NULL, + hidd_l2cif_data_ind, + hidd_l2cif_cong_ind, + NULL}; +/******************************************************************************* + * + * Function hidd_check_config_done + * + * Description Checks if connection is configured and callback can be fired + * + * Returns void + * + ******************************************************************************/ +static void hidd_check_config_done(void) +{ + tHID_CONN *p_hcon; + p_hcon = &hd_cb.device.conn; + if (((p_hcon->conn_flags & HID_CONN_FLAGS_ALL_CONFIGURED) == HID_CONN_FLAGS_ALL_CONFIGURED) && + (p_hcon->conn_state == HID_CONN_STATE_CONFIG)) { + p_hcon->conn_state = HID_CONN_STATE_CONNECTED; + hd_cb.device.state = HIDD_DEV_CONNECTED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_OPEN, 0, NULL); + // send outstanding data on intr + if (hd_cb.pending_data) { + L2CA_DataWrite(p_hcon->intr_cid, hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + } +} +/******************************************************************************* + * + * Function hidh_sec_check_complete_term + * + * Description HID security check complete callback function. + * + * Returns Send L2CA_ConnectRsp OK if secutiry check succeed; otherwise + * send security block L2C connection response. + * + ******************************************************************************/ +static void hidd_sec_check_complete(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY) { + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else if (res != BTM_SUCCESS) { + HIDD_TRACE_WARNING("%s: connection rejected by security", __func__); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + p_dev->conn.conn_state = HID_CONN_STATE_UNUSED; + L2CA_ConnectRsp(p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_SECURITY_BLOCK, + L2CAP_CONN_OK); + return; + } +} +/******************************************************************************* + * + * Function hidd_sec_check_complete_orig + * + * Description HID security check complete callback function (device + *originated) + * + * Returns void + * + ******************************************************************************/ +void hidd_sec_check_complete_orig(UNUSED_ATTR BD_ADDR bd_addr, UNUSED_ATTR tBT_TRANSPORT transport, void *p_ref_data, + uint8_t res) +{ + tHID_DEV_DEV_CTB *p_dev = (tHID_DEV_DEV_CTB *)p_ref_data; + if (p_dev->conn.conn_state != HID_CONN_STATE_SECURITY) { + HIDD_TRACE_WARNING("%s: invalid state (%02x)", __func__, p_dev->conn.conn_state); + return; + } + if (res == BTM_SUCCESS) { + HIDD_TRACE_EVENT("%s: security ok", __func__); + p_dev->conn.disc_reason = HID_SUCCESS; + p_dev->conn.conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(p_dev->conn.ctrl_cid, &hd_cb.l2cap_cfg); + } else { + HIDD_TRACE_WARNING("%s: security check failed (%02x)", __func__, res); + p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; + hidd_conn_disconnect(); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_ind + * + * Description Handles incoming L2CAP connection (we act as server) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_ind(BD_ADDR bd_addr, uint16_t cid, uint16_t psm, uint8_t id) +{ + tHID_CONN *p_hcon; + tHID_DEV_DEV_CTB *p_dev; + bool accept = TRUE; // accept by default + HIDD_TRACE_EVENT("%s: psm=%04x cid=%04x id=%02x", __func__, psm, cid, id); + p_dev = &hd_cb.device; + if (!hd_cb.allow_incoming) { + HIDD_TRACE_WARNING("%s: incoming connections not allowed, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + if (p_dev->in_use && memcmp(bd_addr, p_dev->addr, sizeof(BD_ADDR))) { + HIDD_TRACE_WARNING("%s: incoming connections from different device, rejecting", __func__); + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } else if (!p_dev->in_use) { + p_dev->in_use = TRUE; + memcpy(p_dev->addr, bd_addr, sizeof(BD_ADDR)); + p_dev->state = HIDD_DEV_NO_CONN; + } + p_hcon = &hd_cb.device.conn; + switch (psm) { + case HID_PSM_INTERRUPT: + if (p_hcon->ctrl_cid == 0) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR without CTRL, rejecting", __func__); + } + if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming INTR in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + case HID_PSM_CONTROL: + if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) { + accept = FALSE; + HIDD_TRACE_WARNING("%s: incoming CTRL in invalid state (%d), rejecting", __func__, p_hcon->conn_state); + } + break; + default: + accept = FALSE; + HIDD_TRACE_ERROR("%s: received invalid PSM, rejecting", __func__); + break; + } + if (!accept) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_NO_RESOURCES, 0); + return; + } + // for CTRL we need to go through security and we reply in callback from there + if (psm == HID_PSM_CONTROL) { + p_hcon->conn_flags = 0; + p_hcon->ctrl_cid = cid; + p_hcon->ctrl_id = id; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + if (btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, FALSE, BTM_SEC_PROTO_HID, HIDD_NOSEC_CHN, + &hidd_sec_check_complete, p_dev) == BTM_CMD_STARTED) { + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK); + } + return; + } + // for INTR we go directly to config state + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + p_hcon->intr_cid = cid; + L2CA_ConnectRsp(bd_addr, id, cid, L2CAP_CONN_OK, L2CAP_CONN_OK); + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); +} +/******************************************************************************* + * + * Function hidd_l2cif_connect_cfm + * + * Description Handles L2CAP connection response (we act as client) + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_connect_cfm(uint16_t cid, uint16_t result) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + tHID_CONN *p_hcon = &hd_cb.device.conn; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d, conn_state=%d", __func__, cid, result, p_hcon->conn_state); + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) || + ((cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR))) || + ((cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDD_TRACE_WARNING("%s: unexpected, cid:0x%04x, ctrl_cid:0x%04x, intr_cid:0x%04x, conn_state:%d", __func__, cid, + p_hcon->ctrl_cid, p_hcon->intr_cid, p_hcon->conn_state); + return; + } + if (result != L2CAP_CONN_OK) { + HIDD_TRACE_WARNING("%s: connection failed, now disconnect", __func__); + if (cid == p_hcon->ctrl_cid) + p_hcon->ctrl_cid = 0; + else + p_hcon->intr_cid = 0; + hidd_conn_disconnect(); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_L2CAP_CONN_FAIL | (uint32_t)result, NULL); + return; + } + /* CTRL connect conf */ + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_SECURITY; + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* in case disconnected before sec completed */ + btm_sec_mx_access_request(p_dev->addr, HID_PSM_CONTROL, TRUE, BTM_SEC_PROTO_HID, HIDD_SEC_CHN, + &hidd_sec_check_complete_orig, p_dev); + } else { + p_hcon->conn_state = HID_CONN_STATE_CONFIG; + L2CA_ConfigReq(cid, &hd_cb.l2cap_intr_cfg); + } + return; +} +/******************************************************************************* + * + * Function hidd_l2cif_config_ind + * + * Description Handles incoming L2CAP configuration request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_ind(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_DEV_MTU_SIZE)) + p_hcon->rem_mtu_size = HID_DEV_MTU_SIZE; + else + p_hcon->rem_mtu_size = p_cfg->mtu; + // accept without changes + p_cfg->flush_to_present = FALSE; + p_cfg->mtu_present = FALSE; + p_cfg->result = L2CAP_CFG_OK; + if (cid == p_hcon->intr_cid && hd_cb.use_in_qos && !p_cfg->qos_present) { + p_cfg->qos_present = TRUE; + memcpy(&p_cfg->qos, &hd_cb.in_qos, sizeof(FLOW_SPEC)); + } + L2CA_ConfigRsp(cid, p_cfg); + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_MY_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_config_cfm + * + * Description Handles incoming L2CAP configuration response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_config_cfm(uint16_t cid, tL2CAP_CFG_INFO *p_cfg) +{ + tHID_CONN *p_hcon; + uint32_t reason; + HIDD_TRACE_EVENT("%s: cid=%04x pcfg->result=%d", __func__, cid, p_cfg->result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNACCEPTABLE_PARAMS && p_cfg->qos_present) { + tL2CAP_CFG_INFO new_qos; + // QoS parameters not accepted for intr, try again with host proposal + memcpy(&new_qos, &hd_cb.l2cap_intr_cfg, sizeof(new_qos)); + memcpy(&new_qos.qos, &p_cfg->qos, sizeof(FLOW_SPEC)); + new_qos.qos_present = TRUE; + HIDD_TRACE_WARNING("%s: config failed, retry", __func__); + L2CA_ConfigReq(cid, &new_qos); + return; + } else if (p_hcon->intr_cid == cid && p_cfg->result == L2CAP_CFG_UNKNOWN_OPTIONS) { + // QoS not understood by remote device, try configuring without QoS + HIDD_TRACE_WARNING("%s: config failed, retry without QoS", __func__); + L2CA_ConfigReq(cid, &hd_cb.l2cap_cfg); + return; + } else if (p_cfg->result != L2CAP_CFG_OK) { + HIDD_TRACE_WARNING("%s: config failed, disconnecting", __func__); + hidd_conn_disconnect(); + reason = HID_L2CAP_CFG_FAIL | (uint32_t)p_cfg->result; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, reason, NULL); + return; + } + // update flags + if (cid == p_hcon->ctrl_cid) { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_CTRL_CFG_DONE; + if ((p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG) && (p_hcon->conn_flags & HID_CONN_FLAGS_HIS_CTRL_CFG_DONE)) { + p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; + if ((p_hcon->intr_cid = L2CA_ConnectReq(HID_PSM_INTERRUPT, hd_cb.device.addr)) == 0) { + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hidd_conn_disconnect(); + HIDD_TRACE_WARNING("%s: could not start L2CAP connection for INTR", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + return; + } else { + p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR; + } + } + } else { + p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE; + } + hidd_check_config_done(); +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_ind + * + * Description Handler incoming L2CAP disconnection request + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_ind(uint16_t cid, bool ack_needed) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x ack_needed=%d", __func__, cid, ack_needed); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (ack_needed) + L2CA_DisconnectRsp(cid); + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + } else { + p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } +} +/******************************************************************************* + * + * Function hidd_l2cif_disconnect_cfm + * + * Description Handles L2CAP disconection response + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_disconnect_cfm(uint16_t cid, uint16_t result) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x result=%d", __func__, cid, result); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (cid == p_hcon->ctrl_cid) { + p_hcon->ctrl_cid = 0; + } else { + p_hcon->intr_cid = 0; + // now disconnect CTRL + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { + HIDD_TRACE_EVENT("%s: INTR and CTRL disconnected", __func__); + hd_cb.device.state = HIDD_DEV_NO_CONN; + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + if (hd_cb.pending_vc_unplug) { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_VC_UNPLUG, p_hcon->disc_reason, NULL); + hd_cb.pending_vc_unplug = FALSE; + } else { + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, p_hcon->disc_reason, NULL); + } + } +} +/******************************************************************************* + * + * Function hidd_l2cif_cong_ind + * + * Description Handles L2CAP congestion status event + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_cong_ind(uint16_t cid, bool congested) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_EVENT("%s: cid=%04x congested=%d", __func__, cid, congested); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + return; + } + if (congested) { + p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED; + } else { + p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED; + } +} +/******************************************************************************* + * + * Function hidd_l2cif_data_ind + * + * Description Handler incoming data on L2CAP channel + * + * Returns void + * + ******************************************************************************/ +static void hidd_l2cif_data_ind(uint16_t cid, BT_HDR *p_msg) +{ + tHID_CONN *p_hcon; + uint8_t *p_data = (uint8_t *)(p_msg + 1) + p_msg->offset; + uint8_t msg_type, param; + bool err = FALSE; + HIDD_TRACE_EVENT("%s: cid=%04x", __func__, cid); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_state == HID_CONN_STATE_UNUSED || (p_hcon->ctrl_cid != cid && p_hcon->intr_cid != cid)) { + HIDD_TRACE_WARNING("%s: unknown cid", __func__); + osi_free(p_msg); + return; + } + msg_type = HID_GET_TRANS_FROM_HDR(*p_data); + param = HID_GET_PARAM_FROM_HDR(*p_data); + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // skip HID header + p_msg->offset++; + p_msg->len--; + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_INTR_DATA, 0, p_msg); + return; + } + switch (msg_type) { + case HID_TRANS_GET_REPORT: + // at this stage we don't know if Report Id shall be included in request + // so we pass complete packet in callback and let other code analyze this + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_GET_REPORT, !!(param & HID_PAR_GET_REP_BUFSIZE_FOLLOWS), p_msg); + break; + case HID_TRANS_SET_REPORT: + // as above + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_REPORT, 0, p_msg); + break; + case HID_TRANS_GET_IDLE: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, hd_cb.device.idle_time, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_IDLE: + if (p_msg->len != 2) { + HIDD_TRACE_ERROR("%s: invalid len (%d) set idle request received", __func__, p_msg->len); + err = TRUE; + } else { + hd_cb.device.idle_time = p_data[1]; + HIDD_TRACE_DEBUG("%s: idle_time = %d", __func__, hd_cb.device.idle_time); + if (hd_cb.device.idle_time) { + HIDD_TRACE_WARNING("%s: idle_time of %d ms not supported by HID Device", __func__, + (hd_cb.device.idle_time * 4)); + err = TRUE; + } + } + if (!err) { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + } else { + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM, 0, 0, NULL); + } + osi_free(p_msg); + break; + case HID_TRANS_GET_PROTOCOL: + hidd_conn_send_data(HID_CHANNEL_CTRL, HID_TRANS_DATA, HID_PAR_REP_TYPE_OTHER, !hd_cb.device.boot_mode, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_SET_PROTOCOL: + hd_cb.device.boot_mode = !(param & HID_PAR_PROTOCOL_MASK); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SET_PROTOCOL, param & HID_PAR_PROTOCOL_MASK, NULL); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_SUCCESS, 0, 0, NULL); + osi_free(p_msg); + break; + case HID_TRANS_CONTROL: + switch (param) { + case HID_PAR_CONTROL_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_EXIT_SUSPEND: + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_EXIT_SUSPEND, 0, NULL); + break; + case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG: + hidd_conn_disconnect(); + // set flag so we can notify properly when disconnected + hd_cb.pending_vc_unplug = TRUE; + break; + } + osi_free(p_msg); + break; + case HID_TRANS_DATA: + default: + HIDD_TRACE_WARNING("%s: got unsupported msg (%d)", __func__, msg_type); + hidd_conn_send_data(0, HID_TRANS_HANDSHAKE, HID_PAR_HANDSHAKE_RSP_ERR_UNSUPPORTED_REQ, 0, 0, NULL); + osi_free(p_msg); + break; + } +} +/******************************************************************************* + * + * Function hidd_conn_reg + * + * Description Registers L2CAP channels + * + * Returns void + * + ******************************************************************************/ +tHID_STATUS hidd_conn_reg(void) +{ + HIDD_TRACE_API("%s", __func__); + memset(&hd_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_cfg.mtu_present = TRUE; + hd_cb.l2cap_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_cfg.flush_to_present = TRUE; + hd_cb.l2cap_cfg.flush_to = HID_DEV_FLUSH_TO; + memset(&hd_cb.l2cap_intr_cfg, 0, sizeof(tL2CAP_CFG_INFO)); + hd_cb.l2cap_intr_cfg.mtu_present = TRUE; + hd_cb.l2cap_intr_cfg.mtu = HID_DEV_MTU_SIZE; + hd_cb.l2cap_intr_cfg.flush_to_present = TRUE; + hd_cb.l2cap_intr_cfg.flush_to = HID_DEV_FLUSH_TO; + if (!L2CA_Register(HID_PSM_CONTROL, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + HIDD_TRACE_ERROR("HID Control (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + if (!L2CA_Register(HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *)&dev_reg_info)) { + L2CA_Deregister(HID_PSM_CONTROL); + HIDD_TRACE_ERROR("HID Interrupt (device) registration failed"); + return (HID_ERR_L2CAP_FAILED); + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_dereg + * + * Description Deregisters L2CAP channels + * + * Returns void + * + ******************************************************************************/ +void hidd_conn_dereg(void) +{ + HIDD_TRACE_API("%s", __func__); + L2CA_Deregister(HID_PSM_CONTROL); + L2CA_Deregister(HID_PSM_INTERRUPT); +} +/******************************************************************************* + * + * Function hidd_conn_initiate + * + * Description Initiates HID connection to plugged device + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_initiate(void) +{ + tHID_DEV_DEV_CTB *p_dev = &hd_cb.device; + HIDD_TRACE_API("%s", __func__); + if (!p_dev->in_use) { + HIDD_TRACE_WARNING("%s: no virtual cable established", __func__); + return (HID_ERR_NOT_REGISTERED); + } + if (p_dev->conn.conn_state != HID_CONN_STATE_UNUSED) { + HIDD_TRACE_WARNING("%s: connection already in progress", __func__); + return (HID_ERR_CONN_IN_PROCESS); + } + p_dev->conn.ctrl_cid = 0; + p_dev->conn.intr_cid = 0; + p_dev->conn.disc_reason = HID_L2CAP_CONN_FAIL; + p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG; + BTM_SetOutService(p_dev->addr, BTM_SEC_SERVICE_HIDD_SEC_CTRL, HIDD_SEC_CHN); + /* Check if L2CAP started the connection process */ + if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq(HID_PSM_CONTROL, p_dev->addr)) == 0) { + HIDD_TRACE_WARNING("%s: could not start L2CAP connection", __func__); + hd_cb.callback(hd_cb.device.addr, HID_DHOST_EVT_CLOSE, HID_ERR_L2CAP_FAILED, NULL); + } else { + p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_disconnect + * + * Description Disconnects existing HID connection + * + * Returns HID_SUCCESS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_disconnect(void) +{ + tHID_CONN *p_hcon; + HIDD_TRACE_API("%s", __func__); + // clean any outstanding data on intr + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + hd_cb.pending_data = NULL; + } + p_hcon = &hd_cb.device.conn; + if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { + /* Set l2cap idle timeout to 0 (so ACL link is disconnected + * immediately after last channel is closed) */ + L2CA_SetIdleTimeoutByBdAddr(hd_cb.device.addr, 0, BT_TRANSPORT_BR_EDR); + if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; + L2CA_DisconnectReq(p_hcon->intr_cid); + } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; + L2CA_DisconnectReq(p_hcon->ctrl_cid); + } + } else { + HIDD_TRACE_WARNING("%s: already disconnected", __func__); + p_hcon->conn_state = HID_CONN_STATE_UNUSED; + } + return (HID_SUCCESS); +} +/******************************************************************************* + * + * Function hidd_conn_send_data + * + * Description Sends data to host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data) +{ + tHID_CONN *p_hcon; + BT_HDR *p_buf; + uint8_t *p_out; + uint16_t cid; + uint16_t buf_size; + HIDD_TRACE_VERBOSE("%s: channel(%d), msg_type(%d), len(%d)", __func__, channel, msg_type, len); + p_hcon = &hd_cb.device.conn; + if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) { + return HID_ERR_CONGESTED; + } + switch (msg_type) { + case HID_TRANS_HANDSHAKE: + case HID_TRANS_CONTROL: + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + break; + case HID_TRANS_DATA: + if (channel == HID_CHANNEL_CTRL) { + cid = p_hcon->ctrl_cid; + buf_size = HID_CONTROL_BUF_SIZE; + } else { + cid = p_hcon->intr_cid; + buf_size = HID_INTERRUPT_BUF_SIZE; + } + break; + default: + return (HID_ERR_INVALID_PARAM); + } + p_buf = (BT_HDR *)osi_malloc(buf_size); + if (p_buf == NULL) + return (HID_ERR_NO_RESOURCES); + p_buf->offset = L2CAP_MIN_OFFSET; + p_out = (uint8_t *)(p_buf + 1) + p_buf->offset; + *p_out = HID_BUILD_HDR(msg_type, param); + p_out++; + p_buf->len = 1; // start with header only + // add report id prefix only if non-zero (which is reserved) + if (msg_type == HID_TRANS_DATA && (data || param == HID_PAR_REP_TYPE_OTHER)) { + *p_out = data; // report_id + p_out++; + p_buf->len++; + } + if (len > 0 && p_data != NULL) { + memcpy(p_out, p_data, len); + p_buf->len += len; + } + // check if connected + if (hd_cb.device.state != HIDD_DEV_CONNECTED) { + // for DATA on intr we hold transfer and try to reconnect + if (msg_type == HID_TRANS_DATA && cid == p_hcon->intr_cid) { + // drop previous data, we do not queue it for now + if (hd_cb.pending_data) { + osi_free(hd_cb.pending_data); + } + hd_cb.pending_data = p_buf; + if (hd_cb.device.conn.conn_state == HID_CONN_STATE_UNUSED) { + hidd_conn_initiate(); + } + return HID_SUCCESS; + } + return HID_ERR_NO_CONNECTION; + } +#ifdef REPORT_TRANSFER_TIMESTAMP + if (report_transfer) { + HIDD_TRACE_ERROR("%s: report sent", __func__); + } +#endif + HIDD_TRACE_VERBOSE("%s: report sent", __func__); + if (!L2CA_DataWrite(cid, p_buf)) + return (HID_ERR_CONGESTED); + return (HID_SUCCESS); +} + +#endif diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c b/components/bt/host/bluedroid/stack/hid/hidh_api.c similarity index 91% rename from components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c rename to components/bt/host/bluedroid/stack/hid/hidh_api.c index e5a6ab6fcd..188aa0a4ee 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c +++ b/components/bt/host/bluedroid/stack/hid/hidh_api.c @@ -31,7 +31,7 @@ #include "stack/bt_types.h" #include "stack/hiddefs.h" #include "stack/hidh_api.h" -#include "hidh_int.h" +#include "hid_int.h" #include "stack/btm_api.h" #include "stack/btu.h" #include "btm_int.h" @@ -39,7 +39,9 @@ #if (HID_HOST_INCLUDED == TRUE) #if HID_DYNAMIC_MEMORY == FALSE -tHID_HOST_CTB hh_cb; +tHID_HOST_CTB hh_cb; +#else +tHID_HOST_CTB *hidh_cb_ptr = NULL; #endif static void hidh_search_callback (UINT16 sdp_result); @@ -218,18 +220,46 @@ static void hidh_search_callback (UINT16 sdp_result) ** ** Description This function initializes the control block and trace variable ** -** Returns void +** Returns tHID_STATUS ** *******************************************************************************/ -void HID_HostInit (void) +tHID_STATUS HID_HostInit (void) { +#if (HID_DYNAMIC_MEMORY) + if (!hidh_cb_ptr) { + hidh_cb_ptr = (tHID_HOST_CTB *)osi_malloc(sizeof(tHID_HOST_CTB)); + if (!hidh_cb_ptr) { + return HID_ERR_NO_RESOURCES; + } + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ memset(&hh_cb, 0, sizeof(tHID_HOST_CTB)); -#if defined(HID_INITIAL_TRACE_LEVEL) - hh_cb.trace_level = HID_INITIAL_TRACE_LEVEL; +#if defined(HIDH_INITIAL_TRACE_LEVEL) + hh_cb.trace_level = HIDH_INITIAL_TRACE_LEVEL; #else hh_cb.trace_level = BT_TRACE_LEVEL_NONE; #endif + return HID_SUCCESS; +} + +/******************************************************************************* +** +** Function HID_HostInit +** +** Description This function deinitializes the control block +** +** Returns void +** +*******************************************************************************/ +void HID_HostDeinit (void) +{ +#if (HID_DYNAMIC_MEMORY) + if (hidh_cb_ptr) { + osi_free(hidh_cb_ptr); + hidh_cb_ptr = NULL; + } +#endif /* #if (HID_DYNAMIC_MEMORY) */ } /******************************************************************************* @@ -362,6 +392,36 @@ tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ) return (HID_SUCCESS); } +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle) +{ + int i; + /* Find an entry for this device in hh_cb.devices array */ + if (!hh_cb.reg_flag) { + return (HID_ERR_NOT_REGISTERED); + } + + for (i = 0; i < HID_HOST_MAX_DEVICES; i++) { + if ((hh_cb.devices[i].in_use) && (!memcmp(addr, hh_cb.devices[i].addr, BD_ADDR_LEN))) { + break; + } + } + + if (i == HID_HOST_MAX_DEVICES) { + *handle = 0xff; + } else { + *handle = i; + } + return (HID_SUCCESS); +} /******************************************************************************* ** diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c b/components/bt/host/bluedroid/stack/hid/hidh_conn.c similarity index 97% rename from components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c rename to components/bt/host/bluedroid/stack/hid/hidh_conn.c index 83a4c484af..d94d9d1fa0 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c +++ b/components/bt/host/bluedroid/stack/hid/hidh_conn.c @@ -41,7 +41,7 @@ #include "stack/hiddefs.h" #include "stack/hidh_api.h" -#include "hidh_int.h" +#include "hid_int.h" #include "osi/osi.h" #if (HID_HOST_INCLUDED == TRUE) @@ -132,15 +132,16 @@ tHID_STATUS hidh_conn_disconnect (UINT8 dhandle) HIDH_TRACE_EVENT ("HID-Host disconnect"); if ((p_hcon->ctrl_cid != 0) || (p_hcon->intr_cid != 0)) { - p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; /* Set l2cap idle timeout to 0 (so ACL link is disconnected * immediately after last channel is closed) */ L2CA_SetIdleTimeoutByBdAddr(hh_cb.devices[dhandle].addr, 0, BT_TRANSPORT_BR_EDR); /* Disconnect both interrupt and control channels */ if (p_hcon->intr_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; L2CA_DisconnectReq (p_hcon->intr_cid); } else if (p_hcon->ctrl_cid) { + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; L2CA_DisconnectReq (p_hcon->ctrl_cid); } } else { @@ -360,12 +361,12 @@ static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result) p_hcon = &hh_cb.devices[dhandle].conn; } - if ((p_hcon == NULL) - || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) - || ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL)) - || ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) - && (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING))) { - HIDH_TRACE_WARNING ("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); + if ((p_hcon == NULL) || (!(p_hcon->conn_flags & HID_CONN_FLAGS_IS_ORIG)) || + ((l2cap_cid == p_hcon->ctrl_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_CTRL) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_INTR)) || + ((l2cap_cid == p_hcon->intr_cid) && (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) && + (p_hcon->conn_state != HID_CONN_STATE_DISCONNECTING_CTRL))) { + HIDH_TRACE_WARNING("HID-Host Rcvd unexpected conn cnf, CID 0x%x ", l2cap_cid); return; } @@ -592,12 +593,12 @@ static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed) HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid); - p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING; - if (l2cap_cid == p_hcon->ctrl_cid) { p_hcon->ctrl_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_CTRL; } else { p_hcon->intr_cid = 0; + p_hcon->conn_state = HID_CONN_STATE_DISCONNECTING_INTR; } if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) { diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h b/components/bt/host/bluedroid/stack/hid/include/hid_conn.h similarity index 78% rename from components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h rename to components/bt/host/bluedroid/stack/hid/include/hid_conn.h index 320694c537..38bb5978f8 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h +++ b/components/bt/host/bluedroid/stack/hid/include/hid_conn.h @@ -26,18 +26,21 @@ #define HID_CONN_H #include "common/bt_defs.h" -#if (HID_HOST_INCLUDED == TRUE) + +#if (BT_HID_INCLUDED == TRUE) /* Define the HID Connection Block */ typedef struct hid_conn { -#define HID_CONN_STATE_UNUSED (0) -#define HID_CONN_STATE_CONNECTING_CTRL (1) -#define HID_CONN_STATE_CONNECTING_INTR (2) -#define HID_CONN_STATE_CONFIG (3) -#define HID_CONN_STATE_CONNECTED (4) -#define HID_CONN_STATE_DISCONNECTING (5) -#define HID_CONN_STATE_SECURITY (6) +#define HID_CONN_STATE_UNUSED (0) +#define HID_CONN_STATE_CONNECTING_CTRL (1) +#define HID_CONN_STATE_CONNECTING_INTR (2) +#define HID_CONN_STATE_CONFIG (3) +#define HID_CONN_STATE_CONNECTED (4) +#define HID_CONN_STATE_DISCONNECTING (5) +#define HID_CONN_STATE_SECURITY (6) +#define HID_CONN_STATE_DISCONNECTING_CTRL (7) +#define HID_CONN_STATE_DISCONNECTING_INTR (8) UINT8 conn_state; @@ -62,9 +65,8 @@ typedef struct hid_conn { #define HID_SEC_CHN 1 #define HID_NOSEC_CHN 2 +#define HIDD_SEC_CHN 3 +#define HIDD_NOSEC_CHN 4 -#define HIDD_SEC_CHN 3 -#define HIDD_NOSEC_CHN 4 - -#endif ///HID_HOST_INCLUDED == TRUE +#endif ///BT_HID_INCLUDED == TRUE #endif diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h b/components/bt/host/bluedroid/stack/hid/include/hid_int.h similarity index 60% rename from components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h rename to components/bt/host/bluedroid/stack/hid/include/hid_int.h index 20eda6e3a0..d926e3a891 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h +++ b/components/bt/host/bluedroid/stack/hid/include/hid_int.h @@ -1,5 +1,6 @@ /****************************************************************************** * + * Copyright (C) 2016 The Android Open Source Project * Copyright (C) 2002-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -15,26 +16,21 @@ * limitations under the License. * ******************************************************************************/ - /****************************************************************************** * - * This file contains HID HOST internal definitions + * This file contains HID DEVICE internal definitions * ******************************************************************************/ +#ifndef HID_INT_H +#define HID_INT_H -#ifndef HIDH_INT_H -#define HIDH_INT_H - -#include "stack/hidh_api.h" #include "hid_conn.h" #include "stack/l2c_api.h" +#if (BT_HID_INCLUDED == TRUE) #if (HID_HOST_INCLUDED == TRUE) - -enum { - HID_DEV_NO_CONN, - HID_DEV_CONNECTED -}; +#include "stack/hidh_api.h" +enum { HID_DEV_NO_CONN, HID_DEV_CONNECTED }; typedef struct per_device_ctb { BOOLEAN in_use; @@ -70,17 +66,16 @@ extern void hidh_conn_dereg( void ); extern tHID_STATUS hidh_conn_disconnect (UINT8 dhandle); extern tHID_STATUS hidh_conn_initiate (UINT8 dhandle); extern void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle); - #ifdef __cplusplus -extern "C" -{ +extern "C" { #endif /****************************************************************************** -** Main Control Block -*******************************************************************************/ + * Main Control Block + ******************************************************************************/ + #if HID_DYNAMIC_MEMORY == FALSE -extern tHID_HOST_CTB hh_cb; +extern tHID_HOST_CTB hh_cb; #else extern tHID_HOST_CTB *hidh_cb_ptr; #define hh_cb (*hidh_cb_ptr) @@ -89,7 +84,60 @@ extern tHID_HOST_CTB *hidh_cb_ptr; #ifdef __cplusplus } #endif +#endif /* HID_HOST_INCLUDED == TRUE */ -#endif ///HID_HOST_INCLUDED == TRUE +#if (HID_DEV_INCLUDED == TRUE) +#include "stack/hidd_api.h" +enum { HIDD_DEV_NO_CONN, HIDD_DEV_CONNECTED }; +typedef struct device_ctb { + bool in_use; + BD_ADDR addr; + uint8_t state; + tHID_CONN conn; + bool boot_mode; + uint8_t idle_time; +} tHID_DEV_DEV_CTB; + +typedef struct dev_ctb { + tHID_DEV_DEV_CTB device; + tHID_DEV_HOST_CALLBACK *callback; + tL2CAP_CFG_INFO l2cap_cfg; + tL2CAP_CFG_INFO l2cap_intr_cfg; + bool use_in_qos; + FLOW_SPEC in_qos; + bool reg_flag; + uint8_t trace_level; + bool allow_incoming; + BT_HDR *pending_data; + bool pending_vc_unplug; +} tHID_DEV_CTB; + +extern tHID_STATUS hidd_conn_reg(void); +extern void hidd_conn_dereg(void); +extern tHID_STATUS hidd_conn_initiate(void); +extern tHID_STATUS hidd_conn_disconnect(void); +extern tHID_STATUS hidd_conn_send_data(uint8_t channel, uint8_t msg_type, uint8_t param, uint8_t data, uint16_t len, + uint8_t *p_data); +#ifdef __cplusplus +extern "C" { #endif + +/****************************************************************************** + * Main Control Block + ******************************************************************************/ + +#if HID_DYNAMIC_MEMORY == FALSE +extern tHID_DEV_CTB hd_cb; +#else +extern tHID_DEV_CTB *hidd_cb_ptr; +#define hd_cb (*hidd_cb_ptr) +#endif + +#ifdef __cplusplus +} +#endif +#endif /* HID_DEV_INCLUDED == TRUE */ + +#endif /* BT_HID_INCLUDED == TRUE */ +#endif /* HID_INT_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/btm_api.h b/components/bt/host/bluedroid/stack/include/stack/btm_api.h index 6af1b3ba05..6c4b590fda 100644 --- a/components/bt/host/bluedroid/stack/include/stack/btm_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/btm_api.h @@ -1252,9 +1252,12 @@ typedef UINT8 tBTM_LINK_KEY_TYPE; #define BTM_SEC_SERVICE_HDP_SNK 48 #define BTM_SEC_SERVICE_HDP_SRC 49 #define BTM_SEC_SERVICE_ATT 50 +#define BTM_SEC_SERVICE_HIDD_SEC_CTRL 51 +#define BTM_SEC_SERVICE_HIDD_NOSEC_CTRL 52 +#define BTM_SEC_SERVICE_HIDD_INTR 53 /* Update these as services are added */ -#define BTM_SEC_SERVICE_FIRST_EMPTY 51 +#define BTM_SEC_SERVICE_FIRST_EMPTY 54 #ifndef BTM_SEC_MAX_SERVICES #define BTM_SEC_MAX_SERVICES 65 @@ -1915,11 +1918,11 @@ typedef UINT8 tBTM_CONTRL_STATE; /***************************************************************************** ** EXTERNAL FUNCTION DECLARATIONS *****************************************************************************/ -/* + #ifdef __cplusplus extern "C" { #endif -*/ + /***************************************************************************** ** DEVICE CONTROL and COMMON FUNCTIONS *****************************************************************************/ @@ -4141,10 +4144,8 @@ tBTM_STATUS BTM_SetAfhChannels (AFH_CHANNELS channels, tBTM_CMPL_CB *p_afh_chann *******************************************************************************/ tBTM_STATUS BTM_BleSetChannels (BLE_CHANNELS channels, tBTM_CMPL_CB *p_ble_channels_cmpl_cback); -/* #ifdef __cplusplus } #endif -*/ #endif /* BTM_API_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/hidd_api.h b/components/bt/host/bluedroid/stack/include/stack/hidd_api.h new file mode 100644 index 0000000000..3e99db9da1 --- /dev/null +++ b/components/bt/host/bluedroid/stack/include/stack/hidd_api.h @@ -0,0 +1,273 @@ +/****************************************************************************** + * + * Copyright (C) 2016 The Android Open Source Project + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#ifndef HIDD_API_H +#define HIDD_API_H +#include "hiddefs.h" +#include "sdp_api.h" + +#if (HID_DEV_INCLUDED == TRUE) +/***************************************************************************** + * Type Definitions + ****************************************************************************/ +enum { + HID_CHANNEL_INTR, + HID_CHANNEL_CTRL +}; +/* + HID_DHOST_EVT_OPEN - connected to host device (CTRL and INTR), data = n/a + HID_DHOST_EVT_CLOSE - disconnected from host device, data=reason + HID_DHOST_EVT_GET_REPORT - got GET_REPORT from host + HID_DHOST_EVT_SET_REPORT - got SET_REPORT from host + HID_DHOST_EVT_SET_PROTOCOL - got SET_PROTOCOL from host +*/ + +enum { + HID_DHOST_EVT_OPEN, + HID_DHOST_EVT_CLOSE, + HID_DHOST_EVT_GET_REPORT, + HID_DHOST_EVT_SET_REPORT, + HID_DHOST_EVT_SET_PROTOCOL, + HID_DHOST_EVT_INTR_DATA, + HID_DHOST_EVT_VC_UNPLUG, + HID_DHOST_EVT_SUSPEND, + HID_DHOST_EVT_EXIT_SUSPEND, +}; + +typedef void (tHID_DEV_HOST_CALLBACK)(BD_ADDR bd_addr, uint8_t event, uint32_t data, BT_HDR* p_buf); + +#ifdef __cplusplus +extern "C" { +#endif +/***************************************************************************** + * External Function Declarations + ****************************************************************************/ + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Initializes control block + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevInit(void); + +/******************************************************************************* + * + * Function HID_DevInit + * + * Description Deinitializes control block + * + * Returns void + * + ******************************************************************************/ +extern void HID_DevDeinit(void); + +/******************************************************************************* + * + * Function HID_DevRegister + * + * Description Registers HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevRegister(tHID_DEV_HOST_CALLBACK* host_cback); + +/******************************************************************************* + * + * Function HID_DevDeregister + * + * Description Deregisters HID device with lower layers + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDeregister(void); + +/******************************************************************************* + * + * Function HID_DevSetSecurityLevel + * + * Description Sets security level for HID device connections + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetSecurityLevel(uint8_t sec_lvl); + +/******************************************************************************* + * + * Function HID_DevAddRecord + * + * Description Creates SDP record for HID device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevAddRecord(uint32_t handle, char* p_name, + char* p_description, char* p_provider, + uint16_t subclass, uint16_t desc_len, + uint8_t* p_desc_data); + +/******************************************************************************* + * + * Function HID_DevSendReport + * + * Description Sends report + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSendReport(uint8_t channel, uint8_t type, uint8_t id, + uint16_t len, uint8_t* p_data); + +/******************************************************************************* + * + * Function HID_DevVirtualCableUnplug + * + * Description Sends Virtual Cable Unplug + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevVirtualCableUnplug(void); + +/******************************************************************************* + * + * Function HID_DevPlugDevice + * + * Description Establishes virtual cable to given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevPlugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevUnplugDevice + * + * Description Unplugs virtual cable from given host + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevUnplugDevice(BD_ADDR addr); + +/******************************************************************************* + * + * Function HID_DevConnect + * + * Description Connects to device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevConnect(void); + +/******************************************************************************* + * + * Function HID_DevDisconnect + * + * Description Disconnects from device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevDisconnect(void); + +/******************************************************************************* + * + * Function HID_DevSetIncomingPolicy + * + * Description Sets policy for incoming connections (allowed/disallowed) + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingPolicy(bool allow); + +/******************************************************************************* + * + * Function HID_DevReportError + * + * Description Reports error for Set Report via HANDSHAKE + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevReportError(uint8_t error); + +/******************************************************************************* + * + * Function HID_DevGetDevice + * + * Description Returns the BD Address of virtually cabled device + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevGetDevice(BD_ADDR* addr); + +/******************************************************************************* + * + * Function HID_DevSetIncomingQos + * + * Description Sets Incoming QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetIncomingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetOutgoingQos + * + * Description Sets Outgoing QoS values for Interrupt L2CAP Channel + * + * Returns tHID_STATUS + * + ******************************************************************************/ +extern tHID_STATUS HID_DevSetOutgoingQos( + uint8_t service_type, uint32_t token_rate, uint32_t token_bucket_size, + uint32_t peak_bandwidth, uint32_t latency, uint32_t delay_variation); + +/******************************************************************************* + * + * Function HID_DevSetTraceLevel + * + * Description This function sets the trace level for HID Dev. If called + * with a value of 0xFF, it simply reads the current trace level. + * + * Returns the new (current) trace level + * + ******************************************************************************/ +extern uint8_t HID_DevSetTraceLevel(uint8_t new_level); + +#ifdef __cplusplus +} +#endif + +#endif +#endif /* HIDD_API_H */ diff --git a/components/bt/host/bluedroid/stack/include/stack/hiddefs.h b/components/bt/host/bluedroid/stack/include/stack/hiddefs.h index fab7ce2b53..71dd73e052 100644 --- a/components/bt/host/bluedroid/stack/include/stack/hiddefs.h +++ b/components/bt/host/bluedroid/stack/include/stack/hiddefs.h @@ -25,7 +25,8 @@ #ifndef HIDDEFS_H #define HIDDEFS_H #include "common/bt_target.h" -#if (HID_HOST_INCLUDED == TRUE) + +#if (HID_HOST_INCLUDED == TRUE || HID_DEV_INCLUDED == TRUE) #if (SDP_INCLUDED == TRUE) #include "stack/sdp_api.h" diff --git a/components/bt/host/bluedroid/stack/include/stack/hidh_api.h b/components/bt/host/bluedroid/stack/include/stack/hidh_api.h index 3211138cd4..2ddaf1c51a 100644 --- a/components/bt/host/bluedroid/stack/include/stack/hidh_api.h +++ b/components/bt/host/bluedroid/stack/include/stack/hidh_api.h @@ -139,6 +139,17 @@ extern tHID_STATUS HID_HostDeregister(void); extern tHID_STATUS HID_HostAddDev (BD_ADDR addr, UINT16 attr_mask, UINT8 *handle ); +/******************************************************************************* +** +** Function HID_HostGetDev +** +** Description This is called so HID-host can find this device. +** +** Returns tHID_STATUS +** +*******************************************************************************/ +extern tHID_STATUS HID_HostGetDev(BD_ADDR addr, UINT8 *handle); + /******************************************************************************* ** ** Function HID_HostRemoveDev @@ -191,9 +202,18 @@ extern tHID_STATUS HID_HostCloseDev(UINT8 dev_handle ); ** ** Description This function initializes the control block and trace variable ** +** Returns tHID_STATUS +*******************************************************************************/ +extern tHID_STATUS HID_HostInit(void); + +/******************************************************************************* +** Function HID_HostDeinit +** +** Description This function deinitializes the control block +** ** Returns void *******************************************************************************/ -extern void HID_HostInit(void); +extern void HID_HostDeinit(void); /******************************************************************************* ** Function HID_HostSetSecurityLevel diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/CMakeLists.txt new file mode 100644 index 0000000000..03756ccaba --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/CMakeLists.txt @@ -0,0 +1,7 @@ + +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(bt_hid_mouse_device) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/Makefile b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/Makefile new file mode 100644 index 0000000000..9f94f135ec --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := bt_hid_mouse_device + +include $(IDF_PATH)/make/project.mk diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/README.md b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/README.md new file mode 100644 index 0000000000..16d12a71ac --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/README.md @@ -0,0 +1,11 @@ +| Supported Targets | ESP32 | +| ----------------- | ----- | + +# ESP-IDF HID Device over Bluetooth Classic Demo + +Demo of HID Device over Bluetooth Classic. + +This turns the device into a mouse, but can be altered to be any kind of HID device. + +After loading the code, connect with a computer to a device broadcasting +as "HID Mouse Example". The mouse should move left and right while they are connected. diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/CMakeLists.txt b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/CMakeLists.txt new file mode 100644 index 0000000000..5d47675980 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/CMakeLists.txt @@ -0,0 +1,7 @@ +#set(COMPONENT_SRCS "main.c") +#set(COMPONENT_ADD_INCLUDEDIRS "") + +#register_component() + +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/component.mk b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/component.mk new file mode 100644 index 0000000000..a98f634eae --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c new file mode 100644 index 0000000000..c7d753fc82 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c @@ -0,0 +1,436 @@ + +#include "esp_log.h" +#include "esp_hidd_api.h" +#include "esp_bt_main.h" +#include "esp_bt_device.h" +#include "esp_bt.h" +#include "esp_err.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "esp_gap_bt_api.h" +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#define BOOT_PROTO_MOUSE_RPT_ID 0x02 +typedef struct +{ + esp_hidd_app_param_t app_param; + esp_hidd_qos_param_t both_qos; + uint8_t protocol_mode; + SemaphoreHandle_t mouse_mutex; + xTaskHandle mouse_task_hdl; + uint8_t buffer[4]; + int8_t x_dir; +} local_param_t; + +static local_param_t s_local_param = {0}; + +bool check_report_id_type(uint8_t report_id, uint8_t report_type) +{ + bool ret = false; + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + do { + if (report_type != ESP_HIDD_REPORT_TYPE_INPUT) { + break; + } + if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) { + if (report_id == BOOT_PROTO_MOUSE_RPT_ID) { + ret = true; + break; + } + } else { + if (report_id == 0) { + ret = true; + break; + } + } + } while (0); + + if (!ret) { + if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) { + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID); + } else { + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID); + } + } + xSemaphoreGive(s_local_param.mouse_mutex); + return ret; +} + +// send the buttons, change in x, and change in y +void send_mouse(uint8_t buttons, char dx, char dy, char wheel) +{ + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) { + s_local_param.buffer[0] = buttons; + s_local_param.buffer[1] = dx; + s_local_param.buffer[2] = dy; + s_local_param.buffer[3] = wheel; + esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, 0x00, 4, s_local_param.buffer); + } else if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) { + s_local_param.buffer[0] = buttons; + s_local_param.buffer[1] = dx; + s_local_param.buffer[2] = dy; + esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, BOOT_PROTO_MOUSE_RPT_ID, 3, s_local_param.buffer); + } + xSemaphoreGive(s_local_param.mouse_mutex); +} + +// move the mouse left and right +void mouse_move_task(void* pvParameters) { + const char* TAG = "mouse_move_task"; + + ESP_LOGI(TAG, "starting"); + for(;;) { + s_local_param.x_dir = 1; + int8_t step = 10; + for (int i = 0; i < 2; i++) { + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + s_local_param.x_dir *= -1; + xSemaphoreGive(s_local_param.mouse_mutex); + for (int j = 0; j < 100; j++) { + send_mouse(0, s_local_param.x_dir * step, 0, 0); + vTaskDelay(50 / portTICK_PERIOD_MS); + } + } + vTaskDelay(1000 / portTICK_PERIOD_MS); + } +} + +static void print_bt_address(void) { + const char* TAG = "bt_address"; + const uint8_t* bd_addr; + + bd_addr = esp_bt_dev_get_address(); + ESP_LOGI(TAG, "my bluetooth address is %02X:%02X:%02X:%02X:%02X:%02X", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); +} + +// a generic mouse descriptor +uint8_t hid_descriptor_mouse_boot_mode[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x03, // USAGE_MAXIMUM (Button 3) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x05, // REPORT_SIZE (5) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x06, // INPUT (Data,Var,Rel) + + 0xc0, // END_COLLECTION + 0xc0 // END_COLLECTION +}; +int hid_descriptor_mouse_boot_mode_len = sizeof(hid_descriptor_mouse_boot_mode); + +void esp_bt_gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param) +{ + const char* TAG = "esp_bt_gap_cb"; + switch (event) { + case ESP_BT_GAP_AUTH_CMPL_EVT:{ + if (param->auth_cmpl.stat == ESP_BT_STATUS_SUCCESS) { + ESP_LOGI(TAG, "authentication success: %s", param->auth_cmpl.device_name); + esp_log_buffer_hex(TAG, param->auth_cmpl.bda, ESP_BD_ADDR_LEN); + } else { + ESP_LOGE(TAG, "authentication failed, status:%d", param->auth_cmpl.stat); + } + break; + } + case ESP_BT_GAP_PIN_REQ_EVT:{ + ESP_LOGI(TAG, "ESP_BT_GAP_PIN_REQ_EVT min_16_digit:%d", param->pin_req.min_16_digit); + if (param->pin_req.min_16_digit) { + ESP_LOGI(TAG, "Input pin code: 0000 0000 0000 0000"); + esp_bt_pin_code_t pin_code = {0}; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 16, pin_code); + } else { + ESP_LOGI(TAG, "Input pin code: 1234"); + esp_bt_pin_code_t pin_code; + pin_code[0] = '1'; + pin_code[1] = '2'; + pin_code[2] = '3'; + pin_code[3] = '4'; + esp_bt_gap_pin_reply(param->pin_req.bda, true, 4, pin_code); + } + break; + } + +#if (CONFIG_BT_SSP_ENABLED == true) + case ESP_BT_GAP_CFM_REQ_EVT: + ESP_LOGI(TAG, "ESP_BT_GAP_CFM_REQ_EVT Please compare the numeric value: %d", param->cfm_req.num_val); + esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true); + break; + case ESP_BT_GAP_KEY_NOTIF_EVT: + ESP_LOGI(TAG, "ESP_BT_GAP_KEY_NOTIF_EVT passkey:%d", param->key_notif.passkey); + break; + case ESP_BT_GAP_KEY_REQ_EVT: + ESP_LOGI(TAG, "ESP_BT_GAP_KEY_REQ_EVT Please enter passkey!"); + break; +#endif + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(TAG, "ESP_BT_GAP_MODE_CHG_EVT mode:%d", param->mode_chg.mode); + break; + default: + ESP_LOGI(TAG, "event: %d", event); + break; + } + return; +} + +void bt_app_task_start_up(void) +{ + s_local_param.mouse_mutex = xSemaphoreCreateMutex(); + memset(s_local_param.buffer, 0, 4); + xTaskCreate(mouse_move_task, "mouse_move_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_local_param.mouse_task_hdl); + return; +} + +void bt_app_task_shut_down(void) +{ + if (s_local_param.mouse_task_hdl) { + vTaskDelete(s_local_param.mouse_task_hdl); + s_local_param.mouse_task_hdl = NULL; + } + + if (s_local_param.mouse_mutex) { + vSemaphoreDelete(s_local_param.mouse_mutex); + s_local_param.mouse_mutex = NULL; + } + return; +} + +void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) +{ + static const char* TAG = "esp_bt_hidd_cb"; + switch (event) { + case ESP_HIDD_INIT_EVT: + if (param->init.status == ESP_HIDD_SUCCESS) { + ESP_LOGI(TAG, "setting hid parameters"); + esp_bt_hid_device_register_app(&s_local_param.app_param, &s_local_param.both_qos, &s_local_param.both_qos); + } else { + ESP_LOGE(TAG, "init hidd failed!"); + } + break; + case ESP_HIDD_DEINIT_EVT: + break; + case ESP_HIDD_REGISTER_APP_EVT: + if (param->register_app.status == ESP_HIDD_SUCCESS) { + ESP_LOGI(TAG, "setting hid parameters success!"); + ESP_LOGI(TAG, "setting to connectable, discoverable"); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + if (param->register_app.in_use && param->register_app.bd_addr != NULL) { + ESP_LOGI(TAG, "start virtual cable plug!"); + esp_bt_hid_device_connect(param->register_app.bd_addr); + } + } else { + ESP_LOGE(TAG, "setting hid parameters failed!"); + } + break; + case ESP_HIDD_UNREGISTER_APP_EVT: + if (param->unregister_app.status == ESP_HIDD_SUCCESS) { + ESP_LOGI(TAG, "unregister app success!"); + } else { + ESP_LOGE(TAG, "unregister app failed!"); + } + break; + case ESP_HIDD_OPEN_EVT: + if (param->open.status == ESP_HIDD_SUCCESS) { + if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTING) { + ESP_LOGI(TAG, "connecting..."); + } else if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) { + ESP_LOGI(TAG, "connected to %02x:%02x:%02x:%02x:%02x:%02x", param->open.bd_addr[0], + param->open.bd_addr[1], param->open.bd_addr[2], param->open.bd_addr[3], param->open.bd_addr[4], + param->open.bd_addr[5]); + bt_app_task_start_up(); + ESP_LOGI(TAG, "making self non-discoverable and non-connectable."); + esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); + } else { + ESP_LOGE(TAG, "unknown connection status"); + } + } else { + ESP_LOGE(TAG, "open failed!"); + } + break; + case ESP_HIDD_CLOSE_EVT: + ESP_LOGI(TAG, "ESP_HIDD_CLOSE_EVT"); + if (param->close.status == ESP_HIDD_SUCCESS) { + if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTING) { + ESP_LOGI(TAG, "disconnecting..."); + } else if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) { + ESP_LOGI(TAG, "disconnected!"); + bt_app_task_shut_down(); + ESP_LOGI(TAG, "making self discoverable and connectable again."); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(TAG, "unknown connection status"); + } + } else { + ESP_LOGE(TAG, "close failed!"); + } + break; + case ESP_HIDD_SEND_REPORT_EVT: + ESP_LOGI(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d", param->send_report.report_id, + param->send_report.report_type); + break; + case ESP_HIDD_REPORT_ERR_EVT: + ESP_LOGI(TAG, "ESP_HIDD_REPORT_ERR_EVT"); + break; + case ESP_HIDD_GET_REPORT_EVT: + ESP_LOGI(TAG, "ESP_HIDD_GET_REPORT_EVT id:0x%02x, type:%d, size:%d", param->get_report.report_id, + param->get_report.report_type, param->get_report.buffer_size); + if (check_report_id_type(param->get_report.report_id, param->get_report.report_type)) { + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + if (s_local_param.protocol_mode == ESP_HIDD_REPORT_MODE) { + esp_bt_hid_device_send_report(param->get_report.report_type, 0x00, 4, s_local_param.buffer); + } else if (s_local_param.protocol_mode == ESP_HIDD_BOOT_MODE) { + esp_bt_hid_device_send_report(param->get_report.report_type, 0x02, 3, s_local_param.buffer); + } + xSemaphoreGive(s_local_param.mouse_mutex); + } else { + ESP_LOGE(TAG, "check_report_id failed!"); + } + break; + case ESP_HIDD_SET_REPORT_EVT: + ESP_LOGI(TAG, "ESP_HIDD_SET_REPORT_EVT"); + break; + case ESP_HIDD_SET_PROTOCOL_EVT: + ESP_LOGI(TAG, "ESP_HIDD_SET_PROTOCOL_EVT"); + if (param->set_protocol.protocol_mode == ESP_HIDD_BOOT_MODE) { + ESP_LOGI(TAG, " - boot protocol"); + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + s_local_param.x_dir = -1; + xSemaphoreGive(s_local_param.mouse_mutex); + } else if (param->set_protocol.protocol_mode == ESP_HIDD_REPORT_MODE) { + ESP_LOGI(TAG, " - report protocol"); + } + xSemaphoreTake(s_local_param.mouse_mutex, portMAX_DELAY); + s_local_param.protocol_mode = param->set_protocol.protocol_mode; + xSemaphoreGive(s_local_param.mouse_mutex); + break; + case ESP_HIDD_INTR_DATA_EVT: + ESP_LOGI(TAG, "ESP_HIDD_INTR_DATA_EVT"); + break; + case ESP_HIDD_VC_UNPLUG_EVT: + ESP_LOGI(TAG, "ESP_HIDD_VC_UNPLUG_EVT"); + if (param->vc_unplug.status == ESP_HIDD_SUCCESS) { + if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) { + ESP_LOGI(TAG, "disconnected!"); + bt_app_task_shut_down(); + ESP_LOGI(TAG, "making self discoverable and connectable again."); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(TAG, "unknown connection status"); + } + } else { + ESP_LOGE(TAG, "close failed!"); + } + break; + default: + break; + } +} + +void app_main(void) { + const char* TAG = "app_main"; + esp_err_t ret; + + s_local_param.app_param.name = "Mouse"; + s_local_param.app_param.description = "Mouse Example"; + s_local_param.app_param.provider = "ESP32"; + s_local_param.app_param.subclass = ESP_HID_CLASS_MIC; + s_local_param.app_param.desc_list = hid_descriptor_mouse_boot_mode; + s_local_param.app_param.desc_list_len = hid_descriptor_mouse_boot_mode_len; + memset(&s_local_param.both_qos, 0, sizeof(esp_hidd_qos_param_t)); // don't set the qos parameters + s_local_param.protocol_mode = ESP_HIDD_REPORT_MODE; + + ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK( ret ); + + ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_BLE)); + + esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); + if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) { + ESP_LOGE(TAG, "initialize controller failed: %s\n", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_controller_enable(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) { + ESP_LOGE(TAG, "enable controller failed: %s\n", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_init()) != ESP_OK) { + ESP_LOGE(TAG, "initialize bluedroid failed: %s\n", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bluedroid_enable()) != ESP_OK) { + ESP_LOGE(TAG, "enable bluedroid failed: %s\n", esp_err_to_name(ret)); + return; + } + + if ((ret = esp_bt_gap_register_callback(esp_bt_gap_cb)) != ESP_OK) { + ESP_LOGE(TAG, "gap register failed: %s\n", esp_err_to_name(ret)); + return; + } + + + ESP_LOGI(TAG, "setting device name"); + esp_bt_dev_set_device_name("HID Mouse Example"); + + ESP_LOGI(TAG, "setting cod major, peripheral"); + esp_bt_cod_t cod; + cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL; + esp_bt_gap_set_cod(cod ,ESP_BT_SET_COD_MAJOR_MINOR); + + vTaskDelay(2000 / portTICK_PERIOD_MS); + + ESP_LOGI(TAG, "register hid device callback"); + esp_bt_hid_device_register_callback(esp_bt_hidd_cb); + + ESP_LOGI(TAG, "starting hid device"); + esp_bt_hid_device_init(); + +#if (CONFIG_BT_SSP_ENABLED == true) + /* Set default parameters for Secure Simple Pairing */ + esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE; + esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); +#endif + + /* + * Set default parameters for Legacy Pairing + * Use variable pin, input pin code when pairing + */ + esp_bt_pin_type_t pin_type = ESP_BT_PIN_TYPE_VARIABLE; + esp_bt_pin_code_t pin_code; + esp_bt_gap_set_pin(pin_type, 0, pin_code); + + print_bt_address(); + ESP_LOGI(TAG, "exiting"); +} diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/sdkconfig.defaults b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/sdkconfig.defaults new file mode 100644 index 0000000000..c3ff70a003 --- /dev/null +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/sdkconfig.defaults @@ -0,0 +1,7 @@ +CONFIG_BT_ENABLED=y +CONFIG_BTDM_CTRL_MODE_BLE_ONLY=n +CONFIG_BTDM_CTRL_MODE_BR_EDR_ONLY=y +CONFIG_BTDM_CTRL_MODE_BTDM=n +CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_HID_ENABLED=y +CONFIG_BT_HID_DEVICE_ENABLED=y