Quellcode durchsuchen

1. transparent HID device
2. add esp API for HID
3. add PM config for HID
4. add HID device demo

Closes https://github.com/espressif/esp-idf/issues/5311
Closes https://github.com/espressif/esp-idf/issues/5635
Merges https://github.com/espressif/esp-idf/pull/3425

liqigan vor 4 Jahren
Ursprung
Commit
1c15c9207c
59 geänderte Dateien mit 9027 neuen und 115 gelöschten Zeilen
  1. 14 3
      components/bt/CMakeLists.txt
  2. 12 0
      components/bt/common/btc/core/btc_task.c
  3. 10 0
      components/bt/common/btc/include/btc/btc_task.h
  4. 4 0
      components/bt/component.mk
  5. 16 2
      components/bt/host/bluedroid/Kconfig.in
  6. 176 0
      components/bt/host/bluedroid/api/esp_hidd_api.c
  7. 254 0
      components/bt/host/bluedroid/api/esp_hidh_api.c
  8. 379 0
      components/bt/host/bluedroid/api/include/api/esp_hidd_api.h
  9. 465 0
      components/bt/host/bluedroid/api/include/api/esp_hidh_api.h
  10. 16 0
      components/bt/host/bluedroid/bta/dm/bta_dm_act.c
  11. 49 9
      components/bt/host/bluedroid/bta/dm/bta_dm_cfg.c
  12. 6 0
      components/bt/host/bluedroid/bta/dm/include/bta_dm_int.h
  13. 5 1
      components/bt/host/bluedroid/bta/gatt/bta_gattc_act.c
  14. 774 0
      components/bt/host/bluedroid/bta/hd/bta_hd_act.c
  15. 287 0
      components/bt/host/bluedroid/bta/hd/bta_hd_api.c
  16. 320 0
      components/bt/host/bluedroid/bta/hd/bta_hd_main.c
  17. 168 0
      components/bt/host/bluedroid/bta/hd/include/bta_hd_int.h
  18. 38 12
      components/bt/host/bluedroid/bta/hh/bta_hh_act.c
  19. 1 1
      components/bt/host/bluedroid/bta/hh/bta_hh_cfg.c
  20. 1 1
      components/bt/host/bluedroid/bta/hh/bta_hh_le.c
  21. 7 1
      components/bt/host/bluedroid/bta/hh/bta_hh_main.c
  22. 6 4
      components/bt/host/bluedroid/bta/hh/bta_hh_utils.c
  23. 3 0
      components/bt/host/bluedroid/bta/hh/include/bta_hh_int.h
  24. 5 5
      components/bt/host/bluedroid/bta/include/bta/bta_api.h
  25. 295 0
      components/bt/host/bluedroid/bta/include/bta/bta_hd_api.h
  26. 31 11
      components/bt/host/bluedroid/bta/include/bta/bta_hh_api.h
  27. 251 0
      components/bt/host/bluedroid/btc/core/btc_storage.c
  28. 54 0
      components/bt/host/bluedroid/btc/core/btc_util.c
  29. 85 0
      components/bt/host/bluedroid/btc/include/btc/btc_storage.h
  30. 15 1
      components/bt/host/bluedroid/btc/include/btc/btc_util.h
  31. 152 0
      components/bt/host/bluedroid/btc/profile/std/hid/bta_hh_co.c
  32. 831 0
      components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c
  33. 1569 0
      components/bt/host/bluedroid/btc/profile/std/hid/btc_hh.c
  34. 103 0
      components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h
  35. 187 0
      components/bt/host/bluedroid/btc/profile/std/include/btc_hh.h
  36. 24 2
      components/bt/host/bluedroid/common/include/common/bluedroid_user_config.h
  37. 47 0
      components/bt/host/bluedroid/common/include/common/bt_target.h
  38. 18 0
      components/bt/host/bluedroid/common/include/common/bt_trace.h
  39. 41 1
      components/bt/host/bluedroid/main/bte_init.c
  40. 2 5
      components/bt/host/bluedroid/stack/btm/include/btm_ble_int.h
  41. 0 2
      components/bt/host/bluedroid/stack/btm/include/btm_int.h
  42. 1 1
      components/bt/host/bluedroid/stack/btu/btu_task.c
  43. 586 0
      components/bt/host/bluedroid/stack/hid/hidd_api.c
  44. 780 0
      components/bt/host/bluedroid/stack/hid/hidd_conn.c
  45. 66 6
      components/bt/host/bluedroid/stack/hid/hidh_api.c
  46. 11 10
      components/bt/host/bluedroid/stack/hid/hidh_conn.c
  47. 14 12
      components/bt/host/bluedroid/stack/hid/include/hid_conn.h
  48. 66 18
      components/bt/host/bluedroid/stack/hid/include/hid_int.h
  49. 6 5
      components/bt/host/bluedroid/stack/include/stack/btm_api.h
  50. 273 0
      components/bt/host/bluedroid/stack/include/stack/hidd_api.h
  51. 2 1
      components/bt/host/bluedroid/stack/include/stack/hiddefs.h
  52. 21 1
      components/bt/host/bluedroid/stack/include/stack/hidh_api.h
  53. 7 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/CMakeLists.txt
  54. 8 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/Makefile
  55. 11 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/README.md
  56. 7 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/CMakeLists.txt
  57. 4 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/component.mk
  58. 436 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c
  59. 7 0
      examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/sdkconfig.defaults

+ 14 - 3
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"

+ 12 - 0
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

+ 10 - 0
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__ */

+ 4 - 0
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                   \

+ 16 - 2
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

+ 176 - 0
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 <string.h>
+#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 */

+ 254 - 0
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 <string.h>
+
+#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 */

+ 379 - 0
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

+ 465 - 0
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

+ 16 - 0
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)
 /*******************************************************************************
 **

+ 49 - 9
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;

+ 6 - 0
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);

+ 5 - 1
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
     }
 }
 /*******************************************************************************

+ 774 - 0
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 <string.h>
+
+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 */

+ 287 - 0
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*****************************************************************************
+ *  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 */

+ 320 - 0
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 <string.h>
+
+/*****************************************************************************
+ * 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 "<unknown>";
+    }
+}
+
+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 "<unknown>";
+    }
+}
+#endif /* BTA_HD_INCLUDED */

+ 168 - 0
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

+ 38 - 12
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;
 }

+ 1 - 1
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 */

+ 1 - 1
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

+ 7 - 1
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);
         }

+ 6 - 4
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));
+    }
 }
 
 /*******************************************************************************

+ 3 - 0
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);

+ 5 - 5
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

+ 295 - 0
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 */

+ 31 - 11
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);
 
 
 /*****************************************************************************

+ 251 - 0
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)

+ 54 - 0
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;

+ 85 - 0
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 */

+ 15 - 1
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__ */

+ 152 - 0
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 */

+ 831 - 0
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+    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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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

+ 1569 - 0
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+ *
+ * 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, &param);
+    }
+}
+
+/*******************************************************************************
+**
+** 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, &param);
+    }
+}
+
+/*******************************************************************************
+**
+** 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, &param);
+    }
+}
+
+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, &param);
+        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, &param);
+        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, &param);
+        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(&param, &p_data->dscp_info);
+            param.dscp.handle = p_dev->dev_handle;
+        } while(0);
+        btc_hh_cb_to_app(ESP_HIDH_GET_DSCP_EVT, &param);
+        if (param.dscp.dl_len > 0 && param.dscp.dsc_list != NULL) {
+            utl_freebuf((void **)&param.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, &param);
+        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, &param);
+            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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        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, &param);
+        } 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, &param);
+        }
+        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, &param);
+        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, &param);
+        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

+ 103 - 0
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 <stdint.h>
+#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 */

+ 187 - 0
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 <stdint.h>
+#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 */

+ 24 - 2
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

+ 47 - 0
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
 */

+ 18 - 0
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...)

+ 41 - 1
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

+ 2 - 5
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);

+ 0 - 2
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;

+ 1 - 1
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)

+ 586 - 0
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 <errno.h>
+//#include <hardware/bluetooth.h>
+//#include <hardware/bt_hd.h>
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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

+ 780 - 0
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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#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

+ 66 - 6
components/bt/host/bluedroid/btc/profile/std/hid/hidh_api.c → 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);
+}
 
 /*******************************************************************************
 **

+ 11 - 10
components/bt/host/bluedroid/btc/profile/std/hid/hidh_conn.c → 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)) {

+ 14 - 12
components/bt/host/bluedroid/btc/profile/std/hid/include/hid_conn.h → 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

+ 66 - 18
components/bt/host/bluedroid/btc/profile/std/hid/include/hidh_int.h → 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 */
+
+#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
 
-#endif  ///HID_HOST_INCLUDED == TRUE
+/******************************************************************************
+ * 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 */

+ 6 - 5
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 */

+ 273 - 0
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 */

+ 2 - 1
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"

+ 21 - 1
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

+ 7 - 0
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)

+ 8 - 0
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

+ 11 - 0
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.

+ 7 - 0
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 ".")

+ 4 - 0
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.)

+ 436 - 0
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 <string.h>
+
+#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");
+}

+ 7 - 0
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