summaryrefslogtreecommitdiff
path: root/lib/bt/host/bluedroid/stack/hid
diff options
context:
space:
mode:
authorjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
committerjacqueline <me@jacqueline.id.au>2024-03-28 14:32:49 +1100
commitee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch)
tree8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/hid
parent239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff)
downloadtangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/host/bluedroid/stack/hid')
-rw-r--r--lib/bt/host/bluedroid/stack/hid/hidd_api.c586
-rw-r--r--lib/bt/host/bluedroid/stack/hid/hidd_conn.c784
-rw-r--r--lib/bt/host/bluedroid/stack/hid/hidh_api.c654
-rw-r--r--lib/bt/host/bluedroid/stack/hid/hidh_conn.c1044
-rw-r--r--lib/bt/host/bluedroid/stack/hid/include/hid_conn.h72
-rw-r--r--lib/bt/host/bluedroid/stack/hid/include/hid_int.h144
6 files changed, 3284 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/hid/hidd_api.c b/lib/bt/host/bluedroid/stack/hid/hidd_api.c
new file mode 100644
index 00000000..347ef7eb
--- /dev/null
+++ b/lib/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
diff --git a/lib/bt/host/bluedroid/stack/hid/hidd_conn.c b/lib/bt/host/bluedroid/stack/hid/hidd_conn.c
new file mode 100644
index 00000000..3b44402e
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/hid/hidd_conn.c
@@ -0,0 +1,784 @@
+/******************************************************************************
+ *
+ * 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_TRACE_WARNING("%s: try to reconnect!", __func__);
+ return 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) == L2CAP_DW_FAILED)
+ return (HID_ERR_CONGESTED);
+ return (HID_SUCCESS);
+}
+
+#endif
diff --git a/lib/bt/host/bluedroid/stack/hid/hidh_api.c b/lib/bt/host/bluedroid/stack/hid/hidh_api.c
new file mode 100644
index 00000000..ea8e73a7
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/hid/hidh_api.c
@@ -0,0 +1,654 @@
+/******************************************************************************
+ *
+ * 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 HOST API entry points
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "common/bt_target.h"
+#include "osi/allocator.h"
+#include "stack/bt_types.h"
+#include "stack/hiddefs.h"
+#include "stack/hidh_api.h"
+#include "hid_int.h"
+#include "stack/btm_api.h"
+#include "stack/btu.h"
+#include "btm_int.h"
+
+#if (HID_HOST_INCLUDED == TRUE)
+
+#if HID_DYNAMIC_MEMORY == FALSE
+tHID_HOST_CTB hh_cb;
+#else
+tHID_HOST_CTB *hidh_cb_ptr = NULL;
+#endif
+
+static void hidh_search_callback (UINT16 sdp_result);
+
+/*******************************************************************************
+**
+** Function HID_HostGetSDPRecord
+**
+** Description This function reads the device SDP record
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS HID_HostGetSDPRecord ( BD_ADDR addr, tSDP_DISCOVERY_DB *p_db, UINT32 db_len,
+ tHID_HOST_SDP_CALLBACK *sdp_cback )
+{
+ tSDP_UUID uuid_list;
+
+ if ( hh_cb.sdp_busy ) {
+ return HID_ERR_SDP_BUSY;
+ }
+
+ uuid_list.len = 2;
+ uuid_list.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
+
+ hh_cb.p_sdp_db = p_db;
+ SDP_InitDiscoveryDb (p_db, db_len, 1, &uuid_list, 0, NULL);
+
+ if (SDP_ServiceSearchRequest (addr, p_db, hidh_search_callback)) {
+ hh_cb.sdp_cback = sdp_cback ;
+ hh_cb.sdp_busy = TRUE;
+ return HID_SUCCESS;
+ } else {
+ return HID_ERR_NO_RESOURCES;
+ }
+}
+
+void hidh_get_str_attr( tSDP_DISC_REC *p_rec, UINT16 attr_id, UINT16 max_len, char *str )
+{
+ tSDP_DISC_ATTR *p_attr;
+ UINT16 name_len;
+
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, attr_id)) != NULL) {
+ if ((name_len = SDP_DISC_ATTR_LEN(p_attr->attr_len_type)) < max_len ) {
+ memcpy( str, (char *) p_attr->attr_value.v.array, name_len );
+ str[name_len] = '\0';
+ } else {
+ memcpy( str, (char *) p_attr->attr_value.v.array, max_len - 1 );
+ str[max_len - 1] = '\0';
+ }
+ } else {
+ str[0] = '\0';
+ }
+}
+
+
+static void hidh_search_callback (UINT16 sdp_result)
+{
+ tSDP_DISCOVERY_DB *p_db = hh_cb.p_sdp_db;
+ tSDP_DISC_REC *p_rec;
+ tSDP_DISC_ATTR *p_attr, *p_subattr1, *p_subattr2, *p_repdesc;
+ tBT_UUID hid_uuid;
+ tHID_DEV_SDP_INFO *p_nvi = &hh_cb.sdp_rec;
+ UINT16 attr_mask = 0;
+
+ hid_uuid.len = LEN_UUID_16;
+ hid_uuid.uu.uuid16 = UUID_SERVCLASS_HUMAN_INTERFACE;
+
+ hh_cb.sdp_busy = FALSE;
+
+ if (sdp_result != SDP_SUCCESS) {
+ hh_cb.sdp_cback(sdp_result, 0, NULL);
+ return;
+ }
+
+ if ((p_rec = SDP_FindServiceUUIDInDb (p_db, &hid_uuid, NULL)) == NULL) {
+ hh_cb.sdp_cback(HID_SDP_NO_SERV_UUID, 0, NULL);
+ return;
+ }
+
+ memset (&hh_cb.sdp_rec, 0, sizeof( tHID_DEV_SDP_INFO ));
+
+ /* First, verify the mandatory fields we care about */
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DESCRIPTOR_LIST)) == NULL)
+ || (SDP_DISC_ATTR_TYPE(p_attr->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
+ || ((p_subattr1 = p_attr->attr_value.v.p_sub_attr) == NULL)
+ || (SDP_DISC_ATTR_TYPE(p_subattr1->attr_len_type) != DATA_ELE_SEQ_DESC_TYPE)
+ || ((p_subattr2 = p_subattr1->attr_value.v.p_sub_attr) == NULL)
+ || ((p_repdesc = p_subattr2->p_next_attr) == NULL)
+ || (SDP_DISC_ATTR_TYPE(p_repdesc->attr_len_type) != TEXT_STR_DESC_TYPE)) {
+ hh_cb.sdp_cback(HID_SDP_MANDATORY_MISSING, 0, NULL);
+ return;
+ }
+
+ if ((p_nvi->dscp_info.dl_len = SDP_DISC_ATTR_LEN(p_repdesc->attr_len_type)) != 0) {
+ p_nvi->dscp_info.dsc_list = (UINT8 *) &p_repdesc->attr_value;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_VIRTUAL_CABLE)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_VIRTUAL_CABLE;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_RECONNECT_INITIATE)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_RECONN_INIT;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_NORMALLY_CONNECTABLE)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_NORMALLY_CONNECTABLE;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SDP_DISABLE)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_SDP_DISABLE;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_BATTERY_POWER)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_BATTERY_POWER;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_REMOTE_WAKE)) != NULL) &&
+ (p_attr->attr_value.v.u8) ) {
+ attr_mask |= HID_REMOTE_WAKE;
+ }
+
+ hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_NAME, HID_MAX_SVC_NAME_LEN, p_nvi->svc_name );
+ hidh_get_str_attr( p_rec, ATTR_ID_SERVICE_DESCRIPTION, HID_MAX_SVC_DESCR_LEN, p_nvi->svc_descr );
+ hidh_get_str_attr( p_rec, ATTR_ID_PROVIDER_NAME, HID_MAX_PROV_NAME_LEN, p_nvi->prov_name );
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_RELNUM)) != NULL)) {
+ p_nvi->rel_num = p_attr->attr_value.v.u16;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_COUNTRY_CODE)) != NULL)) {
+ p_nvi->ctry_code = p_attr->attr_value.v.u8;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_DEVICE_SUBCLASS)) != NULL)) {
+ p_nvi->sub_class = p_attr->attr_value.v.u8;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_PARSER_VERSION)) != NULL)) {
+ p_nvi->hpars_ver = p_attr->attr_value.v.u16;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_LINK_SUPERVISION_TO)) != NULL)) {
+ attr_mask |= HID_SUP_TOUT_AVLBL;
+ p_nvi->sup_timeout = p_attr->attr_value.v.u16;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MAX_LAT)) != NULL)) {
+ attr_mask |= HID_SSR_MAX_LATENCY;
+ p_nvi->ssr_max_latency = p_attr->attr_value.v.u16;
+ } else {
+ p_nvi->ssr_max_latency = HID_SSR_PARAM_INVALID;
+ }
+
+ if (((p_attr = SDP_FindAttributeInRec (p_rec, ATTR_ID_HID_SSR_HOST_MIN_TOUT)) != NULL)) {
+ attr_mask |= HID_SSR_MIN_TOUT;
+ p_nvi->ssr_min_tout = p_attr->attr_value.v.u16;
+ } else {
+ p_nvi->ssr_min_tout = HID_SSR_PARAM_INVALID;
+ }
+
+ hh_cb.sdp_rec.p_sdp_layer_rec = p_rec;
+ hh_cb.sdp_cback(SDP_SUCCESS, attr_mask, &hh_cb.sdp_rec);
+}
+
+
+/*******************************************************************************
+**
+** Function HID_HostInit
+**
+** Description This function initializes the control block and trace variable
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+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(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) */
+}
+
+/*******************************************************************************
+**
+** Function HID_HostSetTraceLevel
+**
+** Description This function sets the trace level for HID Host. If called with
+** a value of 0xFF, it simply reads the current trace level.
+**
+** Returns the new (current) trace level
+**
+*******************************************************************************/
+UINT8 HID_HostSetTraceLevel (UINT8 new_level)
+{
+ if (new_level != 0xFF) {
+ hh_cb.trace_level = new_level;
+ }
+
+ return (hh_cb.trace_level);
+}
+
+/*******************************************************************************
+**
+** Function HID_HostRegister
+**
+** Description This function registers HID-Host with lower layers
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS HID_HostRegister (tHID_HOST_DEV_CALLBACK *dev_cback)
+{
+ tHID_STATUS st;
+
+ if ( hh_cb.reg_flag ) {
+ return HID_ERR_ALREADY_REGISTERED;
+ }
+
+ if ( dev_cback == NULL ) {
+ return HID_ERR_INVALID_PARAM;
+ }
+
+ /* Register with L2CAP */
+ if ( (st = hidh_conn_reg()) != HID_SUCCESS ) {
+ return st;
+ }
+
+ hh_cb.callback = dev_cback ;
+ hh_cb.reg_flag = TRUE;
+
+ return (HID_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function HID_HostDeregister
+**
+** Description This function is called when the host is about power down.
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS HID_HostDeregister(void)
+{
+ UINT8 i;
+
+ if ( !hh_cb.reg_flag ) {
+ return (HID_ERR_NOT_REGISTERED);
+ }
+
+ for ( i = 0; i < HID_HOST_MAX_DEVICES; i++ ) {
+ HID_HostRemoveDev( i ) ;
+ }
+
+ hidh_conn_dereg();
+ hh_cb.reg_flag = FALSE;
+
+ return (HID_SUCCESS) ;
+}
+
+/*******************************************************************************
+**
+** Function HID_HostAddDev
+**
+** Description This is called so HID-host may manage this device.
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS HID_HostAddDev ( BD_ADDR addr, UINT16 attr_mask, 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 ) {
+ for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) {
+ if ( !hh_cb.devices[i].in_use) {
+ break;
+ }
+ }
+ }
+
+ if ( i == HID_HOST_MAX_DEVICES ) {
+ return HID_ERR_NO_RESOURCES;
+ }
+
+ if (!hh_cb.devices[i].in_use) {
+ hh_cb.devices[i].in_use = TRUE;
+ hh_cb.devices[i].delay_remove = FALSE;
+ memcpy( hh_cb.devices[i].addr, addr, sizeof( BD_ADDR ) ) ;
+ hh_cb.devices[i].state = HID_DEV_NO_CONN;
+ hh_cb.devices[i].conn_tries = 0 ;
+ }
+
+ if (attr_mask != HID_ATTR_MASK_IGNORE) {
+ hh_cb.devices[i].attr_mask = attr_mask;
+ }
+
+ *handle = i;
+
+ 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);
+}
+
+/*******************************************************************************
+**
+** Function HID_HostRemoveDev
+**
+** Description This removes the device from list devices that host has to manage.
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS HID_HostRemoveDev ( UINT8 dev_handle )
+{
+ if ( !hh_cb.reg_flag ) {
+ return (HID_ERR_NOT_REGISTERED);
+ }
+
+ if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
+ return HID_ERR_INVALID_PARAM;
+ }
+
+ HID_HostCloseDev( dev_handle ) ;
+
+ if (hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_INTR ||
+ hh_cb.devices[dev_handle].conn.conn_state == HID_CONN_STATE_DISCONNECTING_CTRL) {
+ // delay the remove action, to close the control and the interrupt channel
+ hh_cb.devices[dev_handle].delay_remove = TRUE;
+ } else {
+ HIDH_TRACE_WARNING("%s dev_handle:%d conn_state:%d", __func__, dev_handle,
+ hh_cb.devices[dev_handle].conn.conn_state);
+ hh_cb.devices[dev_handle].in_use = FALSE;
+ hh_cb.devices[dev_handle].conn.conn_state = HID_CONN_STATE_UNUSED;
+ hh_cb.devices[dev_handle].conn.ctrl_cid = hh_cb.devices[dev_handle].conn.intr_cid = 0;
+ hh_cb.devices[dev_handle].attr_mask = 0;
+ }
+
+ return HID_SUCCESS;
+}
+
+/*******************************************************************************
+**
+** Function HID_HostOpenDev
+**
+** Description This function is called when the user wants to initiate a
+** connection attempt to a device.
+**
+** Returns void
+**
+*******************************************************************************/
+tHID_STATUS HID_HostOpenDev ( UINT8 dev_handle )
+{
+ if ( !hh_cb.reg_flag ) {
+ return (HID_ERR_NOT_REGISTERED);
+ }
+
+ if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
+ return HID_ERR_INVALID_PARAM;
+ }
+
+ if ( hh_cb.devices[dev_handle].state != HID_DEV_NO_CONN ) {
+ return HID_ERR_ALREADY_CONN;
+ }
+
+ hh_cb.devices[dev_handle].conn_tries = 1;
+ return hidh_conn_initiate( dev_handle );
+}
+
+/*******************************************************************************
+**
+** Function HID_HostWriteDev
+**
+** Description This function is called when the host has a report to send.
+**
+** report_id: is only used on GET_REPORT transaction if is specified.
+** only valid when it's a non-zero value.
+**
+** Returns void
+**
+*******************************************************************************/
+tHID_STATUS HID_HostWriteDev( UINT8 dev_handle, UINT8 t_type,
+ UINT8 param, UINT16 data, UINT8 report_id, BT_HDR *pbuf )
+{
+ tHID_STATUS status = HID_SUCCESS;
+
+ if ( !hh_cb.reg_flag ) {
+ HIDH_TRACE_ERROR("HID_ERR_NOT_REGISTERED");
+ status = HID_ERR_NOT_REGISTERED;
+ }
+
+ if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
+ HIDH_TRACE_ERROR("HID_ERR_INVALID_PARAM");
+ status = HID_ERR_INVALID_PARAM;
+ }
+
+ else if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) {
+ HIDH_TRACE_ERROR("HID_ERR_NO_CONNECTION dev_handle %d", dev_handle);
+ status = HID_ERR_NO_CONNECTION;
+ }
+
+ if (status != HID_SUCCESS) {
+ if (pbuf) {
+ osi_free ((void *)pbuf);
+ }
+ } else {
+ status = hidh_conn_snd_data( dev_handle, t_type, param, data, report_id, pbuf ) ;
+ }
+
+ return status;
+}
+
+/*******************************************************************************
+**
+** Function HID_HostCloseDev
+**
+** Description This function disconnects the device.
+**
+** Returns void
+**
+*******************************************************************************/
+tHID_STATUS HID_HostCloseDev( UINT8 dev_handle )
+{
+ if ( !hh_cb.reg_flag ) {
+ return (HID_ERR_NOT_REGISTERED);
+ }
+
+ if ( (dev_handle >= HID_HOST_MAX_DEVICES) || (!hh_cb.devices[dev_handle].in_use) ) {
+ return HID_ERR_INVALID_PARAM;
+ }
+
+ hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1;
+ btu_stop_timer( &(hh_cb.devices[dev_handle].conn.timer_entry) ) ;
+
+ if ( hh_cb.devices[dev_handle].state != HID_DEV_CONNECTED ) {
+ return HID_ERR_NO_CONNECTION;
+ }
+
+ hh_cb.devices[dev_handle].conn_tries = HID_HOST_MAX_CONN_RETRY + 1;
+ return hidh_conn_disconnect( dev_handle );
+}
+
+tHID_STATUS HID_HostSetSecurityLevel( char serv_name[], UINT8 sec_lvl )
+{
+ if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL,
+ sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) {
+ HIDH_TRACE_ERROR ("Security Registration 1 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_SEC_CTRL,
+ sec_lvl, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_SEC_CHN)) {
+ HIDH_TRACE_ERROR ("Security Registration 2 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL,
+ BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) {
+ HIDH_TRACE_ERROR ("Security Registration 3 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_NOSEC_CTRL,
+ BTM_SEC_NONE, HID_PSM_CONTROL, BTM_SEC_PROTO_HID, HID_NOSEC_CHN)) {
+ HIDH_TRACE_ERROR ("Security Registration 4 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ if (!BTM_SetSecurityLevel (TRUE, serv_name, BTM_SEC_SERVICE_HIDH_INTR,
+ BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) {
+ HIDH_TRACE_ERROR ("Security Registration 5 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ if (!BTM_SetSecurityLevel (FALSE, serv_name, BTM_SEC_SERVICE_HIDH_INTR,
+ BTM_SEC_NONE, HID_PSM_INTERRUPT, BTM_SEC_PROTO_HID, 0)) {
+ HIDH_TRACE_ERROR ("Security Registration 6 failed");
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ return ( HID_SUCCESS );
+}
+
+/******************************************************************************
+**
+** Function hid_known_hid_device
+**
+** Description check if this device is of type HID Device
+**
+** Returns TRUE if device is HID Device else FALSE
+**
+*******************************************************************************/
+BOOLEAN hid_known_hid_device (BD_ADDR bd_addr)
+{
+ UINT8 i;
+ tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(bd_addr);
+
+ if ( !hh_cb.reg_flag ) {
+ return FALSE;
+ }
+
+ /* First check for class of device , if Inq DB has information about this device*/
+ if (p_inq_info != NULL) {
+ /* Check if remote major device class is of type BTM_COD_MAJOR_PERIPHERAL */
+ if ((p_inq_info->results.dev_class[1] & BTM_COD_MAJOR_CLASS_MASK)
+ == BTM_COD_MAJOR_PERIPHERAL ) {
+ HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in InqDB & COD matches HID dev");
+ return TRUE;
+ }
+ } else {
+ /* Look for this device in security device DB */
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr);
+ if ((p_dev_rec != NULL) &&
+ ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL )) {
+ HIDH_TRACE_DEBUG("hid_known_hid_device:dev found in SecDevDB & COD matches HID dev");
+ return TRUE;
+ }
+ }
+
+ /* Find an entry for this device in hh_cb.devices array */
+ for ( i = 0; i < HID_HOST_MAX_DEVICES; i++) {
+ if ((hh_cb.devices[i].in_use) &&
+ (memcmp(bd_addr, hh_cb.devices[i].addr, BD_ADDR_LEN) == 0)) {
+ return TRUE;
+ }
+ }
+ /* Check if this device is marked as HID Device in IOP Dev */
+ HIDH_TRACE_DEBUG("hid_known_hid_device:remote is not HID device");
+ return FALSE;
+}
+
+#endif //HID_HOST_INCLUDED
diff --git a/lib/bt/host/bluedroid/stack/hid/hidh_conn.c b/lib/bt/host/bluedroid/stack/hid/hidh_conn.c
new file mode 100644
index 00000000..801f087c
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/hid/hidh_conn.c
@@ -0,0 +1,1044 @@
+/******************************************************************************
+ *
+ * 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 HID HOST internal definitions
+ *
+ ******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+
+#include "common/bt_target.h"
+#include "osi/allocator.h"
+#include "stack/bt_types.h"
+
+#include "stack/l2cdefs.h"
+#include "stack/l2c_api.h"
+
+#include "stack/btu.h"
+#include "stack/btm_api.h"
+#include "btm_int.h"
+
+#include "stack/hiddefs.h"
+
+#include "stack/hidh_api.h"
+#include "hid_int.h"
+#include "osi/osi.h"
+
+#if (HID_HOST_INCLUDED == TRUE)
+
+static UINT8 find_conn_by_cid (UINT16 cid);
+static void hidh_conn_retry (UINT8 dhandle);
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid,
+ UINT16 psm, UINT8 l2cap_id);
+static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result);
+static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg);
+static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed);
+static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg);
+static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result);
+static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested);
+
+static const tL2CAP_APPL_INFO hst_reg_info = {
+ hidh_l2cif_connect_ind,
+ hidh_l2cif_connect_cfm,
+ NULL,
+ hidh_l2cif_config_ind,
+ hidh_l2cif_config_cfm,
+ hidh_l2cif_disconnect_ind,
+ hidh_l2cif_disconnect_cfm,
+ NULL,
+ hidh_l2cif_data_ind,
+ hidh_l2cif_cong_ind,
+ NULL /* tL2CA_TX_COMPLETE_CB */
+};
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_reg
+**
+** Description This function initializes the SDP unit.
+**
+** Returns void
+**
+*******************************************************************************/
+tHID_STATUS hidh_conn_reg (void)
+{
+ int xx;
+
+ /* Initialize the L2CAP configuration. We only care about MTU and flush */
+ memset(&hh_cb.l2cap_cfg, 0, sizeof(tL2CAP_CFG_INFO));
+
+ hh_cb.l2cap_cfg.mtu_present = TRUE;
+ hh_cb.l2cap_cfg.mtu = HID_HOST_MTU;
+ hh_cb.l2cap_cfg.flush_to_present = TRUE;
+ hh_cb.l2cap_cfg.flush_to = HID_HOST_FLUSH_TO;
+
+ /* Now, register with L2CAP */
+ if (!L2CA_Register (HID_PSM_CONTROL, (tL2CAP_APPL_INFO *) &hst_reg_info)) {
+ HIDH_TRACE_ERROR ("HID-Host Control Registration failed");
+ return (HID_ERR_L2CAP_FAILED) ;
+ }
+ if (!L2CA_Register (HID_PSM_INTERRUPT, (tL2CAP_APPL_INFO *) &hst_reg_info)) {
+ L2CA_Deregister( HID_PSM_CONTROL ) ;
+ HIDH_TRACE_ERROR ("HID-Host Interrupt Registration failed");
+ return (HID_ERR_L2CAP_FAILED) ;
+ }
+
+ for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) {
+ hh_cb.devices[xx].in_use = FALSE ;
+ hh_cb.devices[xx].delay_remove = FALSE;
+ hh_cb.devices[xx].conn.conn_state = HID_CONN_STATE_UNUSED;
+ }
+
+ return (HID_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function hidh_conn_disconnect
+**
+** Description This function disconnects a connection.
+**
+** Returns TRUE if disconnect started, FALSE if already disconnected
+**
+*******************************************************************************/
+tHID_STATUS hidh_conn_disconnect (UINT8 dhandle)
+{
+ tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;
+
+ HIDH_TRACE_EVENT ("HID-Host disconnect");
+
+ 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(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 {
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+ }
+
+ return (HID_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** 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.
+**
+*******************************************************************************/
+void hidh_sec_check_complete_term (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
+{
+ tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data;
+ UNUSED(bd_addr);
+ UNUSED (transport);
+
+ if ( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) {
+ p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
+
+ p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_INTR;
+
+ /* Send response to the L2CAP layer. */
+ L2CA_ConnectRsp (p_dev->addr, p_dev->conn.ctrl_id, p_dev->conn.ctrl_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
+
+ /* Send a Configuration Request. */
+ L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg);
+
+ }
+ /* security check fail */
+ else if (res != BTM_SUCCESS) {
+ p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */
+ 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);
+ }
+}
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_connect_ind
+**
+** Description This function handles an inbound connection indication
+** from L2CAP. This is the case where we are acting as a
+** server.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_connect_ind (BD_ADDR bd_addr, UINT16 l2cap_cid, UINT16 psm, UINT8 l2cap_id)
+{
+ tHID_CONN *p_hcon;
+ BOOLEAN bAccept = TRUE;
+ UINT8 i = HID_HOST_MAX_DEVICES;
+ tHID_HOST_DEV_CTB *p_dev;
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, PSM: 0x%04x CID 0x%x", psm, l2cap_cid);
+
+ /* always add incoming connection device into HID database by default */
+ if (HID_HostAddDev(bd_addr, HID_SEC_REQUIRED, &i) != HID_SUCCESS) {
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_SECURITY_BLOCK, 0);
+ return;
+ }
+
+ p_hcon = &hh_cb.devices[i].conn;
+ p_dev = &hh_cb.devices[i];
+
+ /* Check we are in the correct state for this */
+ if (psm == HID_PSM_INTERRUPT) {
+ if (p_hcon->ctrl_cid == 0) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, but no CTL channel");
+ bAccept = FALSE;
+ }
+ if (p_hcon->conn_state != HID_CONN_STATE_CONNECTING_INTR) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd INTR L2CAP conn ind, wrong state: %d",
+ p_hcon->conn_state);
+ bAccept = FALSE;
+ }
+ } else { /* CTRL channel */
+#if defined(HID_HOST_ACPT_NEW_CONN) && (HID_HOST_ACPT_NEW_CONN == TRUE)
+ p_hcon->ctrl_cid = p_hcon->intr_cid = 0;
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+#else
+ if (p_hcon->conn_state != HID_CONN_STATE_UNUSED) {
+ HIDH_TRACE_WARNING ("HID-Host - Rcvd CTL L2CAP conn ind, wrong state: %d",
+ p_hcon->conn_state);
+ bAccept = FALSE;
+ }
+#endif
+ }
+
+ if (!bAccept) {
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_NO_RESOURCES, 0);
+ return;
+ }
+
+ if (psm == HID_PSM_CONTROL) {
+ p_hcon->conn_flags = 0;
+ p_hcon->ctrl_cid = l2cap_cid;
+ p_hcon->ctrl_id = l2cap_id;
+ p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to 'connection failure' */
+
+ 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,
+ (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
+ &hidh_sec_check_complete_term, p_dev) == BTM_CMD_STARTED) {
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_PENDING, L2CAP_CONN_OK);
+ }
+
+ return;
+ }
+
+ /* Transition to the next appropriate state, configuration */
+ p_hcon->conn_state = HID_CONN_STATE_CONFIG;
+ p_hcon->intr_cid = l2cap_cid;
+
+ /* Send response to the L2CAP layer. */
+ L2CA_ConnectRsp (bd_addr, l2cap_id, l2cap_cid, L2CAP_CONN_OK, L2CAP_CONN_OK);
+
+ /* Send a Configuration Request. */
+ L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP conn ind, sent config req, PSM: 0x%04x CID 0x%x",
+ psm, l2cap_cid);
+}
+
+/*******************************************************************************
+**
+** Function hidh_proc_repage_timeout
+**
+** Description This function handles timeout (to page device).
+**
+** Returns void
+**
+*******************************************************************************/
+void hidh_proc_repage_timeout (TIMER_LIST_ENT *p_tle)
+{
+ hidh_conn_initiate( (UINT8) p_tle->param ) ;
+ hh_cb.devices[p_tle->param].conn_tries++;
+ hh_cb.callback( (UINT8) p_tle->param, hh_cb.devices[p_tle->param].addr,
+ HID_HDEV_EVT_RETRYING, hh_cb.devices[p_tle->param].conn_tries, NULL ) ;
+}
+
+/*******************************************************************************
+**
+** Function hidh_sec_check_complete_orig
+**
+** Description This function checks to see if security procedures are being
+** carried out or not..
+**
+** Returns void
+**
+*******************************************************************************/
+void hidh_sec_check_complete_orig (BD_ADDR bd_addr, tBT_TRANSPORT transport, void *p_ref_data, UINT8 res)
+{
+ tHID_HOST_DEV_CTB *p_dev = (tHID_HOST_DEV_CTB *) p_ref_data;
+ UINT8 dhandle;
+ UNUSED(bd_addr);
+ UNUSED (transport);
+
+ dhandle = ((UINT32)p_dev - (UINT32) & (hh_cb.devices[0])) / sizeof(tHID_HOST_DEV_CTB);
+ if ( res == BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) {
+ HIDH_TRACE_EVENT ("HID-Host Originator security pass.");
+ p_dev->conn.disc_reason = HID_SUCCESS; /* Authentication passed. Reset disc_reason (from HID_ERR_AUTH_FAILED) */
+
+ /* Transition to the next appropriate state, configuration */
+ p_dev->conn.conn_state = HID_CONN_STATE_CONFIG;
+ L2CA_ConfigReq (p_dev->conn.ctrl_cid, &hh_cb.l2cap_cfg);
+ HIDH_TRACE_EVENT ("HID-Host Got Control conn cnf, sent cfg req, CID: 0x%x", p_dev->conn.ctrl_cid);
+
+ }
+
+ if ( res != BTM_SUCCESS && p_dev->conn.conn_state == HID_CONN_STATE_SECURITY ) {
+#if (HID_HOST_MAX_CONN_RETRY > 0)
+ if ( res == BTM_DEVICE_TIMEOUT ) {
+ if ( p_dev->conn_tries <= HID_HOST_MAX_CONN_RETRY ) {
+ hidh_conn_retry (dhandle);
+ return;
+ }
+ }
+#endif
+ p_dev->conn.disc_reason = HID_ERR_AUTH_FAILED; /* Save reason for disconnecting */
+ hidh_conn_disconnect(dhandle);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_connect_cfm
+**
+** Description This function handles the connect confirm events
+** from L2CAP. This is the case when we are acting as a
+** client and have sent a connect request.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_connect_cfm (UINT16 l2cap_cid, UINT16 result)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+ UINT32 reason;
+ tHID_HOST_DEV_CTB *p_dev = NULL;
+
+ /* Find CCB based on CID, and verify we are in a state to accept this message */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_dev = &hh_cb.devices[dhandle];
+ 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) &&
+ (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;
+ }
+
+ if (result != L2CAP_CONN_OK) {
+ if (l2cap_cid == p_hcon->ctrl_cid) {
+ p_hcon->ctrl_cid = 0;
+ } else {
+ p_hcon->intr_cid = 0;
+ }
+
+ hidh_conn_disconnect(dhandle);
+
+#if (HID_HOST_MAX_CONN_RETRY > 0)
+ if ( (hh_cb.devices[dhandle].conn_tries <= HID_HOST_MAX_CONN_RETRY) &&
+ (result == HCI_ERR_CONNECTION_TOUT || result == HCI_ERR_UNSPECIFIED ||
+ result == HCI_ERR_PAGE_TIMEOUT) ) {
+ hidh_conn_retry(dhandle);
+ } else
+#endif
+ {
+ reason = HID_L2CAP_CONN_FAIL | (UINT32) result ;
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
+ }
+ return;
+ }
+ /* receive Control Channel connect confirmation */
+ if (l2cap_cid == p_hcon->ctrl_cid) {
+ /* check security requirement */
+ p_hcon->conn_state = HID_CONN_STATE_SECURITY;
+ p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* In case disconnection occurs before security is completed, then set CLOSE_EVT reason code to "connection failure" */
+
+ btm_sec_mx_access_request (p_dev->addr, HID_PSM_CONTROL,
+ TRUE, BTM_SEC_PROTO_HID,
+ (p_dev->attr_mask & HID_SEC_REQUIRED) ? HID_SEC_CHN : HID_NOSEC_CHN,
+ &hidh_sec_check_complete_orig, p_dev);
+ } else {
+ p_hcon->conn_state = HID_CONN_STATE_CONFIG;
+ /* Send a Configuration Request. */
+ L2CA_ConfigReq (l2cap_cid, &hh_cb.l2cap_cfg);
+ HIDH_TRACE_EVENT ("HID-Host got Interrupt conn cnf, sent cfg req, CID: 0x%x", l2cap_cid);
+ }
+
+ return;
+}
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_config_ind
+**
+** Description This function processes the L2CAP configuration indication
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_config_ind (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+ UINT32 reason;
+
+ /* Find CCB based on CID */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd cfg ind, sent cfg cfm, CID: 0x%x", l2cap_cid);
+
+ /* Remember the remote MTU size */
+ if ((!p_cfg->mtu_present) || (p_cfg->mtu > HID_HOST_MTU)) {
+ p_hcon->rem_mtu_size = HID_HOST_MTU;
+ } else {
+ p_hcon->rem_mtu_size = p_cfg->mtu;
+ }
+
+ /* For now, always accept configuration from the other side */
+ p_cfg->flush_to_present = FALSE;
+ p_cfg->mtu_present = FALSE;
+ p_cfg->result = L2CAP_CFG_OK;
+
+ L2CA_ConfigRsp (l2cap_cid, p_cfg);
+
+ if (l2cap_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)) {
+ /* Connect interrupt channel */
+ p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
+ if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) {
+ HIDH_TRACE_WARNING ("HID-Host INTR Originate failed");
+ reason = HID_L2CAP_REQ_FAIL ;
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+ hidh_conn_disconnect (dhandle);
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
+ return;
+ } else {
+ /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */
+ p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
+ }
+ }
+ } else {
+ p_hcon->conn_flags |= HID_CONN_FLAGS_HIS_INTR_CFG_DONE;
+ }
+
+ /* If all configuration is complete, change state and tell management we are up */
+ 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;
+ /* Reset disconnect reason to success, as connection successful */
+ p_hcon->disc_reason = HID_SUCCESS;
+
+ hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_config_cfm
+**
+** Description This function processes the L2CAP configuration confirmation
+** event.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_config_cfm (UINT16 l2cap_cid, tL2CAP_CFG_INFO *p_cfg)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+ UINT32 reason;
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd cfg cfm, CID: 0x%x Result: %d", l2cap_cid, p_cfg->result);
+
+ /* Find CCB based on CID */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP cfg ind, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ /* If configuration failed, disconnect the channel(s) */
+ if (p_cfg->result != L2CAP_CFG_OK) {
+ hidh_conn_disconnect (dhandle);
+ reason = HID_L2CAP_CFG_FAIL | (UINT32) p_cfg->result ;
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
+ return;
+ }
+
+ if (l2cap_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)) {
+ /* Connect interrupt channel */
+ p_hcon->disc_reason = HID_L2CAP_CONN_FAIL; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
+ if ((p_hcon->intr_cid = L2CA_ConnectReq (HID_PSM_INTERRUPT, hh_cb.devices[dhandle].addr)) == 0) {
+ HIDH_TRACE_WARNING ("HID-Host INTR Originate failed");
+ reason = HID_L2CAP_REQ_FAIL ;
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+ hidh_conn_disconnect (dhandle);
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, reason, NULL ) ;
+ return;
+ } else {
+ /* Transition to the next appropriate state, waiting for connection confirm on interrupt channel. */
+ p_hcon->conn_state = HID_CONN_STATE_CONNECTING_INTR;
+ }
+ }
+ } else {
+ p_hcon->conn_flags |= HID_CONN_FLAGS_MY_INTR_CFG_DONE;
+ }
+
+ /* If all configuration is complete, change state and tell management we are up */
+ 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;
+ /* Reset disconnect reason to success, as connection successful */
+ p_hcon->disc_reason = HID_SUCCESS;
+
+ hh_cb.devices[dhandle].state = HID_DEV_CONNECTED;
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_OPEN, 0, NULL ) ;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_disconnect_ind
+**
+** Description This function handles a disconnect event from L2CAP. If
+** requested to, we ack the disconnect before dropping the CCB
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_disconnect_ind (UINT16 l2cap_cid, BOOLEAN ack_needed)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+ UINT16 disc_res = HCI_SUCCESS;
+ UINT16 hid_close_evt_reason;
+
+ /* Find CCB based on CID */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ if (ack_needed) {
+ L2CA_DisconnectRsp (l2cap_cid);
+ }
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc, CID: 0x%x", l2cap_cid);
+
+ 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)) {
+ hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+
+ if ( !ack_needed ) {
+ disc_res = btm_get_acl_disc_reason_code();
+ }
+
+#if (HID_HOST_MAX_CONN_RETRY > 0)
+ if ( (disc_res == HCI_ERR_CONNECTION_TOUT || disc_res == HCI_ERR_UNSPECIFIED) &&
+ (!(hh_cb.devices[dhandle].attr_mask & HID_RECONN_INIT)) &&
+ (hh_cb.devices[dhandle].attr_mask & HID_NORMALLY_CONNECTABLE)) {
+ hh_cb.devices[dhandle].conn_tries = 0;
+ hh_cb.devices[dhandle].conn.timer_entry.param = (UINT32) dhandle;
+ btu_start_timer (&(hh_cb.devices[dhandle].conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN);
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, disc_res, NULL);
+ } else
+#endif
+ {
+ /* Set reason code for HID_HDEV_EVT_CLOSE */
+ hid_close_evt_reason = p_hcon->disc_reason;
+
+ /* If we got baseband sent HCI_DISCONNECT_COMPLETE_EVT due to security failure, then set reason to HID_ERR_AUTH_FAILED */
+ if ((disc_res == HCI_ERR_AUTH_FAILURE) ||
+ (disc_res == HCI_ERR_KEY_MISSING) ||
+ (disc_res == HCI_ERR_HOST_REJECT_SECURITY) ||
+ (disc_res == HCI_ERR_PAIRING_NOT_ALLOWED) ||
+ (disc_res == HCI_ERR_UNIT_KEY_USED) ||
+ (disc_res == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) ||
+ (disc_res == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) ||
+ (disc_res == HCI_ERR_REPEATED_ATTEMPTS)) {
+ hid_close_evt_reason = HID_ERR_AUTH_FAILED;
+ }
+
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, hid_close_evt_reason, NULL ) ;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_disconnect_cfm
+**
+** Description This function handles a disconnect confirm event from L2CAP.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_disconnect_cfm (UINT16 l2cap_cid, UINT16 result)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+ UNUSED(result);
+
+ /* Find CCB based on CID */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP disc cfm, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP disc cfm, CID: 0x%x", l2cap_cid);
+
+ if (l2cap_cid == p_hcon->ctrl_cid) {
+ p_hcon->ctrl_cid = 0;
+ } else {
+ p_hcon->intr_cid = 0;
+ if (p_hcon->ctrl_cid) {
+ HIDH_TRACE_EVENT ("HID-Host Initiating L2CAP Ctrl disconnection");
+ L2CA_DisconnectReq (p_hcon->ctrl_cid);
+ }
+ }
+
+ if ((p_hcon->ctrl_cid == 0) && (p_hcon->intr_cid == 0)) {
+ hh_cb.devices[dhandle].state = HID_DEV_NO_CONN;
+ p_hcon->conn_state = HID_CONN_STATE_UNUSED;
+ // removes the device from list devices that host has to manage
+ if (hh_cb.devices[dhandle].delay_remove) {
+ hh_cb.devices[dhandle].in_use = FALSE;
+ hh_cb.devices[dhandle].delay_remove = FALSE;
+ hh_cb.devices[dhandle].attr_mask = 0;
+ }
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE, p_hcon->disc_reason, NULL ) ;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_cong_ind
+**
+** Description This function handles a congestion status event from L2CAP.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_cong_ind (UINT16 l2cap_cid, BOOLEAN congested)
+{
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+
+ /* Find CCB based on CID */
+ if ( (dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES ) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP congestion status, unknown CID: 0x%x", l2cap_cid);
+ return;
+ }
+
+ HIDH_TRACE_EVENT ("HID-Host Rcvd L2CAP congestion status, CID: 0x%x Cong: %d", l2cap_cid, congested);
+
+ if (congested) {
+ p_hcon->conn_flags |= HID_CONN_FLAGS_CONGESTED;
+ } else {
+ p_hcon->conn_flags &= ~HID_CONN_FLAGS_CONGESTED;
+
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function hidh_l2cif_data_ind
+**
+** Description This function is called when data is received from L2CAP.
+** if we are the originator of the connection, we are the SDP
+** client, and the received message is queued up for the client.
+**
+** If we are the destination of the connection, we are the SDP
+** server, so the message is passed to the server processing
+** function.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_l2cif_data_ind (UINT16 l2cap_cid, BT_HDR *p_msg)
+{
+ UINT8 *p_data = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT8 ttype, param, rep_type, evt;
+ UINT8 dhandle;
+ tHID_CONN *p_hcon = NULL;
+
+ HIDH_TRACE_DEBUG ("HID-Host hidh_l2cif_data_ind [l2cap_cid=0x%04x]", l2cap_cid);
+
+ /* Find CCB based on CID */
+ if ((dhandle = find_conn_by_cid(l2cap_cid)) < HID_HOST_MAX_DEVICES) {
+ p_hcon = &hh_cb.devices[dhandle].conn;
+ }
+
+ if (p_hcon == NULL) {
+ HIDH_TRACE_WARNING ("HID-Host Rcvd L2CAP data, unknown CID: 0x%x", l2cap_cid);
+ osi_free (p_msg);
+ return;
+ }
+
+
+ ttype = HID_GET_TRANS_FROM_HDR(*p_data);
+ param = HID_GET_PARAM_FROM_HDR(*p_data);
+ rep_type = param & HID_PAR_REP_TYPE_MASK;
+ p_data++;
+
+ /* Get rid of the data type */
+ p_msg->len--;
+ p_msg->offset++;
+
+ switch (ttype) {
+ case HID_TRANS_HANDSHAKE:
+ hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_HANDSHAKE, param, NULL);
+ osi_free (p_msg);
+ break;
+
+ case HID_TRANS_CONTROL:
+ switch (param) {
+ case HID_PAR_CONTROL_VIRTUAL_CABLE_UNPLUG:
+ hidh_conn_disconnect( dhandle ) ;
+ /* Device is unplugging from us. Tell USB */
+ hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_VC_UNPLUG, 0, NULL);
+ break;
+
+ default:
+ break;
+ }
+ osi_free (p_msg);
+ break;
+
+
+ case HID_TRANS_DATA:
+ evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
+ HID_HDEV_EVT_INTR_DATA : HID_HDEV_EVT_CTRL_DATA;
+ hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg);
+ break;
+
+ case HID_TRANS_DATAC:
+ evt = (hh_cb.devices[dhandle].conn.intr_cid == l2cap_cid) ?
+ HID_HDEV_EVT_INTR_DATC : HID_HDEV_EVT_CTRL_DATC;
+ hh_cb.callback(dhandle, hh_cb.devices[dhandle].addr, evt, rep_type, p_msg);
+ break;
+
+ default:
+ osi_free (p_msg);
+ break;
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function hidh_conn_snd_data
+**
+** Description This function is sends out data.
+**
+** Returns tHID_STATUS
+**
+*******************************************************************************/
+tHID_STATUS hidh_conn_snd_data (UINT8 dhandle, UINT8 trans_type, UINT8 param,
+ UINT16 data, UINT8 report_id, BT_HDR *buf)
+{
+ tHID_CONN *p_hcon = &hh_cb.devices[dhandle].conn;
+ BT_HDR *p_buf;
+ UINT8 *p_out;
+ UINT16 bytes_copied;
+ BOOLEAN seg_req = FALSE;
+ UINT16 data_size;
+ UINT16 cid;
+ UINT16 buf_size;
+ UINT8 use_data = 0 ;
+ BOOLEAN blank_datc = FALSE;
+
+ if (!BTM_IsAclConnectionUp(hh_cb.devices[dhandle].addr, BT_TRANSPORT_BR_EDR)) {
+ if (buf) {
+ osi_free ((void *)buf);
+ }
+ return ( HID_ERR_NO_CONNECTION );
+ }
+
+ if (p_hcon->conn_flags & HID_CONN_FLAGS_CONGESTED) {
+ if (buf) {
+ osi_free ((void *)buf);
+ }
+ return ( HID_ERR_CONGESTED );
+ }
+
+ switch ( trans_type ) {
+ case HID_TRANS_CONTROL:
+ case HID_TRANS_GET_REPORT:
+ case HID_TRANS_SET_REPORT:
+ case HID_TRANS_GET_PROTOCOL:
+ case HID_TRANS_SET_PROTOCOL:
+ case HID_TRANS_GET_IDLE:
+ case HID_TRANS_SET_IDLE:
+ cid = p_hcon->ctrl_cid;
+ buf_size = HID_CONTROL_BUF_SIZE;
+ break;
+ case HID_TRANS_DATA:
+ cid = p_hcon->intr_cid;
+ buf_size = HID_INTERRUPT_BUF_SIZE;
+ break;
+ default:
+ return (HID_ERR_INVALID_PARAM) ;
+ }
+
+ if ( trans_type == HID_TRANS_SET_IDLE ) {
+ use_data = 1;
+ } else if ( (trans_type == HID_TRANS_GET_REPORT) && (param & 0x08) ) {
+ use_data = 2;
+ }
+
+ do {
+ if ( buf == NULL || blank_datc ) {
+ if ((p_buf = (BT_HDR *)osi_malloc(buf_size)) == NULL) {
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ seg_req = FALSE;
+ data_size = 0;
+ bytes_copied = 0;
+ blank_datc = FALSE;
+ } else if ( (buf->len > (p_hcon->rem_mtu_size - 1))) {
+ if ((p_buf = (BT_HDR *)osi_malloc(buf_size)) == NULL) {
+ return (HID_ERR_NO_RESOURCES);
+ }
+
+ p_buf->offset = L2CAP_MIN_OFFSET;
+ seg_req = TRUE;
+ data_size = buf->len;
+ bytes_copied = p_hcon->rem_mtu_size - 1;
+ } else {
+ p_buf = buf ;
+ p_buf->offset -= 1;
+ seg_req = FALSE;
+ data_size = buf->len;
+ bytes_copied = buf->len;
+ }
+
+ p_out = (UINT8 *)(p_buf + 1) + p_buf->offset;
+ *p_out++ = HID_BUILD_HDR(trans_type, param);
+
+ /* If report ID required for this device */
+ if ( (trans_type == HID_TRANS_GET_REPORT) && (report_id != 0) ) {
+ *p_out = report_id;
+ data_size = bytes_copied = 1;
+ }
+
+
+ if (seg_req) {
+ memcpy (p_out, (((UINT8 *)(buf + 1)) + buf->offset), bytes_copied);
+ buf->offset += bytes_copied;
+ buf->len -= bytes_copied;
+ } else if ( use_data == 1) {
+ *(p_out + bytes_copied) = data & 0xff;
+ } else if ( use_data == 2 ) {
+ *(p_out + bytes_copied) = data & 0xff;
+ *(p_out + bytes_copied + 1) = (data >> 8) & 0xff ;
+ }
+
+ p_buf->len = bytes_copied + 1 + use_data;
+ data_size -= bytes_copied;
+
+ /* Send the buffer through L2CAP */
+ if (L2CA_DataWrite(cid, p_buf) == L2CAP_DW_FAILED) {
+ return (HID_ERR_CONGESTED);
+ }
+
+ if (data_size) {
+ trans_type = HID_TRANS_DATAC;
+ } else if ( bytes_copied == (p_hcon->rem_mtu_size - 1) ) {
+ trans_type = HID_TRANS_DATAC;
+ blank_datc = TRUE;
+ }
+
+ } while ((data_size != 0) || blank_datc ) ;
+
+ return (HID_SUCCESS);
+}
+/*******************************************************************************
+**
+** Function hidh_conn_initiate
+**
+** Description This function is called by the management to create a connection.
+**
+** Returns void
+**
+*******************************************************************************/
+tHID_STATUS hidh_conn_initiate (UINT8 dhandle)
+{
+ UINT8 service_id = BTM_SEC_SERVICE_HIDH_NOSEC_CTRL;
+ UINT32 mx_chan_id = HID_NOSEC_CHN;
+
+ tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
+
+ if ( p_dev->conn.conn_state != HID_CONN_STATE_UNUSED ) {
+ 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; /* Reset initial reason for CLOSE_EVT: Connection Attempt was made but failed */
+
+ /* We are the originator of this connection */
+ p_dev->conn.conn_flags = HID_CONN_FLAGS_IS_ORIG;
+
+ if (p_dev->attr_mask & HID_SEC_REQUIRED) {
+ service_id = BTM_SEC_SERVICE_HIDH_SEC_CTRL;
+ mx_chan_id = HID_SEC_CHN;
+ }
+ BTM_SetOutService (p_dev->addr, service_id, mx_chan_id);
+
+ /* Check if L2CAP started the connection process */
+ if ((p_dev->conn.ctrl_cid = L2CA_ConnectReq (HID_PSM_CONTROL, p_dev->addr)) == 0) {
+ HIDH_TRACE_WARNING ("HID-Host Originate failed");
+ hh_cb.callback( dhandle, hh_cb.devices[dhandle].addr, HID_HDEV_EVT_CLOSE,
+ HID_ERR_L2CAP_FAILED, NULL ) ;
+ } else {
+ /* Transition to the next appropriate state, waiting for connection confirm on control channel. */
+ p_dev->conn.conn_state = HID_CONN_STATE_CONNECTING_CTRL;
+ }
+
+ return ( HID_SUCCESS );
+}
+
+
+/*******************************************************************************
+**
+** Function find_conn_by_cid
+**
+** Description This function finds a connection control block based on CID
+**
+** Returns address of control block, or NULL if not found
+**
+*******************************************************************************/
+static UINT8 find_conn_by_cid (UINT16 cid)
+{
+ UINT8 xx;
+
+ for (xx = 0; xx < HID_HOST_MAX_DEVICES; xx++) {
+ if ((hh_cb.devices[xx].in_use) && (hh_cb.devices[xx].conn.conn_state != HID_CONN_STATE_UNUSED)
+ && ((hh_cb.devices[xx].conn.ctrl_cid == cid) || (hh_cb.devices[xx].conn.intr_cid == cid))) {
+ break;
+ }
+ }
+
+ return (xx);
+}
+
+void hidh_conn_dereg( void )
+{
+ L2CA_Deregister (HID_PSM_CONTROL);
+ L2CA_Deregister (HID_PSM_INTERRUPT);
+}
+
+/*******************************************************************************
+**
+** Function hidh_conn_retry
+**
+** Description This function is called to retry a failed connection.
+**
+** Returns void
+**
+*******************************************************************************/
+static void hidh_conn_retry( UINT8 dhandle )
+{
+ tHID_HOST_DEV_CTB *p_dev = &hh_cb.devices[dhandle];
+
+ p_dev->conn.conn_state = HID_CONN_STATE_UNUSED;
+ p_dev->conn.timer_entry.param = (UINT32) dhandle;
+#if (HID_HOST_REPAGE_WIN > 0)
+ btu_start_timer (&(p_dev->conn.timer_entry), BTU_TTYPE_HID_HOST_REPAGE_TO, HID_HOST_REPAGE_WIN);
+#else
+ hidh_proc_repage_timeout( &(p_dev->conn.timer_entry) );
+#endif
+}
+
+#endif // HID_HOST_INCLUDED
diff --git a/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h b/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h
new file mode 100644
index 00000000..38bb5978
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/hid/include/hid_conn.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *
+ * 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 HID connection internal definitions
+ *
+ ******************************************************************************/
+
+#ifndef HID_CONN_H
+#define HID_CONN_H
+
+#include "common/bt_defs.h"
+
+#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_DISCONNECTING_CTRL (7)
+#define HID_CONN_STATE_DISCONNECTING_INTR (8)
+
+ UINT8 conn_state;
+
+#define HID_CONN_FLAGS_IS_ORIG (0x01)
+#define HID_CONN_FLAGS_HIS_CTRL_CFG_DONE (0x02)
+#define HID_CONN_FLAGS_MY_CTRL_CFG_DONE (0x04)
+#define HID_CONN_FLAGS_HIS_INTR_CFG_DONE (0x08)
+#define HID_CONN_FLAGS_MY_INTR_CFG_DONE (0x10)
+#define HID_CONN_FLAGS_ALL_CONFIGURED (0x1E) /* All the config done */
+#define HID_CONN_FLAGS_CONGESTED (0x20)
+#define HID_CONN_FLAGS_INACTIVE (0x40)
+
+ UINT8 conn_flags;
+
+ UINT8 ctrl_id;
+ UINT16 ctrl_cid;
+ UINT16 intr_cid;
+ UINT16 rem_mtu_size;
+ UINT16 disc_reason; /* Reason for disconnecting (for HID_HDEV_EVT_CLOSE) */
+ TIMER_LIST_ENT timer_entry;
+} tHID_CONN;
+
+#define HID_SEC_CHN 1
+#define HID_NOSEC_CHN 2
+#define HIDD_SEC_CHN 3
+#define HIDD_NOSEC_CHN 4
+
+#endif ///BT_HID_INCLUDED == TRUE
+#endif
diff --git a/lib/bt/host/bluedroid/stack/hid/include/hid_int.h b/lib/bt/host/bluedroid/stack/hid/include/hid_int.h
new file mode 100644
index 00000000..b4208966
--- /dev/null
+++ b/lib/bt/host/bluedroid/stack/hid/include/hid_int.h
@@ -0,0 +1,144 @@
+/******************************************************************************
+ *
+ * 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 HID DEVICE internal definitions
+ *
+ ******************************************************************************/
+#ifndef HID_INT_H
+#define HID_INT_H
+
+#include "hid_conn.h"
+#include "stack/l2c_api.h"
+#if (BT_HID_INCLUDED == TRUE)
+
+#if (HID_HOST_INCLUDED == TRUE)
+#include "stack/hidh_api.h"
+enum { HID_DEV_NO_CONN, HID_DEV_CONNECTED };
+
+typedef struct per_device_ctb {
+ BOOLEAN in_use;
+ BOOLEAN delay_remove;
+ BD_ADDR addr; /* BD-Addr of the host device */
+ UINT16 attr_mask; /* 0x01- virtual_cable; 0x02- normally_connectable; 0x03- reconn_initiate;
+ 0x04- sdp_disable; */
+ UINT8 state; /* Device state if in HOST-KNOWN mode */
+ UINT8 conn_substate;
+ UINT8 conn_tries; /* Remembers to the number of connection attempts while CONNECTING */
+
+ tHID_CONN conn; /* L2CAP channel info */
+} tHID_HOST_DEV_CTB;
+
+typedef struct host_ctb {
+ tHID_HOST_DEV_CTB devices[HID_HOST_MAX_DEVICES];
+ tHID_HOST_DEV_CALLBACK *callback; /* Application callbacks */
+ tL2CAP_CFG_INFO l2cap_cfg;
+
+#define MAX_SERVICE_DB_SIZE 4000
+
+ BOOLEAN sdp_busy;
+ tHID_HOST_SDP_CALLBACK *sdp_cback;
+ tSDP_DISCOVERY_DB *p_sdp_db;
+ tHID_DEV_SDP_INFO sdp_rec;
+ BOOLEAN reg_flag;
+ UINT8 trace_level;
+} tHID_HOST_CTB;
+
+extern tHID_STATUS hidh_conn_snd_data(UINT8 dhandle, UINT8 trans_type, UINT8 param, \
+ UINT16 data, UINT8 rpt_id, BT_HDR *buf);
+extern tHID_STATUS hidh_conn_reg (void);
+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" {
+#endif
+
+/******************************************************************************
+ * Main Control Block
+ ******************************************************************************/
+
+#if HID_DYNAMIC_MEMORY == FALSE
+extern tHID_HOST_CTB hh_cb;
+#else
+extern tHID_HOST_CTB *hidh_cb_ptr;
+#define hh_cb (*hidh_cb_ptr)
+#endif
+
+#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
+
+/******************************************************************************
+ * 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 */