From 2078dfe2932496101376cc7a68a224048b2afd56 Mon Sep 17 00:00:00 2001 From: liqigan Date: Tue, 7 Sep 2021 17:08:43 +0800 Subject: [PATCH] 1. update esp_hid component to use esp HID API 2. add esp_hidh_config_t::callback_arg --- components/esp_hid/CMakeLists.txt | 3 +- components/esp_hid/include/esp_hid_common.h | 15 + components/esp_hid/include/esp_hidd.h | 20 + components/esp_hid/include/esp_hidh.h | 109 +- components/esp_hid/private/bt_hidd.h | 33 + components/esp_hid/private/esp_bt_hh_api.h | 367 ----- components/esp_hid/private/esp_hidd_private.h | 25 + components/esp_hid/private/esp_hidh_private.h | 44 +- components/esp_hid/src/ble_hidd.c | 53 +- components/esp_hid/src/ble_hidh.c | 13 +- components/esp_hid/src/bt_hidd.c | 879 +++++++++++ components/esp_hid/src/bt_hidh.c | 1329 ++++++++++++----- components/esp_hid/src/esp_hid_common.c | 4 +- components/esp_hid/src/esp_hidd.c | 10 +- components/esp_hid/src/esp_hidh.c | 491 +++++- examples/bluetooth/esp_hid_device/README.md | 20 +- .../esp_hid_device/main/CMakeLists.txt | 1 - .../esp_hid_device/main/esp_hid_device_main.c | 296 +++- .../esp_hid_device/main/esp_hid_gap.c | 51 +- .../esp_hid_device/main/esp_hid_gap.h | 23 +- .../esp_hid_device/sdkconfig.defaults | 4 +- .../esp_hid_device/sdkconfig.defaults.esp32c3 | 16 +- .../esp_hid_device/sdkconfig.defaults.esp32s3 | 16 +- examples/bluetooth/esp_hid_host/README.md | 2 + .../esp_hid_host/main/CMakeLists.txt | 1 - .../bluetooth/esp_hid_host/main/esp_hid_gap.c | 54 +- .../bluetooth/esp_hid_host/main/esp_hid_gap.h | 23 +- .../esp_hid_host/main/esp_hid_host_main.c | 41 +- .../bluetooth/esp_hid_host/sdkconfig.defaults | 2 + .../esp_hid_host/sdkconfig.defaults.esp32c3 | 16 +- .../esp_hid_host/sdkconfig.defaults.esp32s3 | 16 +- 31 files changed, 2985 insertions(+), 992 deletions(-) create mode 100644 components/esp_hid/private/bt_hidd.h delete mode 100644 components/esp_hid/private/esp_bt_hh_api.h create mode 100644 components/esp_hid/src/bt_hidd.c diff --git a/components/esp_hid/CMakeLists.txt b/components/esp_hid/CMakeLists.txt index 890d5f575c..5209d0ce3c 100644 --- a/components/esp_hid/CMakeLists.txt +++ b/components/esp_hid/CMakeLists.txt @@ -10,7 +10,8 @@ if(CONFIG_BT_ENABLED) list(APPEND srcs "src/ble_hidd.c" "src/ble_hidh.c" - "src/bt_hidh.c") + "src/bt_hidh.c" + "src/bt_hidd.c") endif() endif() diff --git a/components/esp_hid/include/esp_hid_common.h b/components/esp_hid/include/esp_hid_common.h index 9fd7a2c637..d7582cbc84 100644 --- a/components/esp_hid/include/esp_hid_common.h +++ b/components/esp_hid/include/esp_hid_common.h @@ -126,6 +126,21 @@ typedef enum { ESP_HID_COD_MIN_MAX } esp_hid_cod_min_t; +/* HID transaction Types */ +typedef enum { + ESP_HID_TRANS_HANDSHAKE = 0, + ESP_HID_TRANS_CONTROL = 1, + ESP_HID_TRANS_GET_REPORT = 4, + ESP_HID_TRANS_SET_REPORT = 5, + ESP_HID_TRANS_GET_PROTOCOL = 6, + ESP_HID_TRANS_SET_PROTOCOL = 7, + ESP_HID_TRANS_GET_IDLE = 8, + ESP_HID_TRANS_SET_IDLE = 9, + ESP_HID_TRANS_DATA = 10, + ESP_HID_TRANS_DATAC = 11, + ESP_HID_TRANS_MAX +} esp_hid_trans_type_t; + /** * @brief HID report item structure */ diff --git a/components/esp_hid/include/esp_hidd.h b/components/esp_hid/include/esp_hidd.h index ef596c6b2b..3e1bfc2c06 100644 --- a/components/esp_hid/include/esp_hidd.h +++ b/components/esp_hid/include/esp_hidd.h @@ -53,11 +53,28 @@ typedef struct esp_hidd_dev_s esp_hidd_dev_t; * @brief HIDD callback parameters union */ typedef union { + /** + * @brief ESP_HIDD_START_EVENT + * @note Used only for Classic Bluetooth. + */ + struct { + esp_err_t status; /*!< HID device operation status */ + } start; /*!< HID callback param of ESP_HIDD_START_EVENT */ + + /** + * @brief ESP_HIDD_STOP_EVENT + * @note Used only for Classic Bluetooth. + */ + struct { + esp_err_t status; /*!< HID device operation status */ + } stop; /*!< HID callback param of ESP_HIDD_STOP_EVENT */ + /** * @brief ESP_HIDD_CONNECT_EVENT */ struct { esp_hidd_dev_t *dev; /*!< HID device structure */ + esp_err_t status; /*!< HID device operation status, used only for Classic Bluetooth */ } connect; /*!< HID callback param of ESP_HIDD_CONNECT_EVENT */ /** @@ -66,6 +83,7 @@ typedef union { struct { esp_hidd_dev_t *dev; /*!< HID device structure */ int reason; /*!< Indicate the reason of disconnection */ + esp_err_t status; /*!< HID device operation status, used only for Classic Bluetooth */ } disconnect; /*!< HID callback param of ESP_HIDD_DISCONNECT_EVENT */ /** @@ -90,6 +108,8 @@ typedef union { uint16_t length; /*!< data length */ uint8_t *data; /*!< The pointer to the data */ uint8_t map_index; /*!< HID config report map index */ + uint8_t trans_type; /*!< HID device feature transaction type, used only for Classic Bluetooth */ + uint8_t report_type; /*!< HID device feature report type, used only for Classic Bluetooth */ } feature; /*!< HID callback param of ESP_HIDD_FEATURE_EVENT */ /** diff --git a/components/esp_hid/include/esp_hidh.h b/components/esp_hid/include/esp_hidh.h index d3d1002103..d49f76e680 100644 --- a/components/esp_hid/include/esp_hidh.h +++ b/components/esp_hid/include/esp_hidh.h @@ -42,6 +42,8 @@ typedef enum { ESP_HIDH_INPUT_EVENT, /*!< Received HID device INPUT report */ ESP_HIDH_FEATURE_EVENT, /*!< Received HID device FEATURE report */ ESP_HIDH_CLOSE_EVENT, /*!< HID device closed */ + ESP_HIDH_START_EVENT, /*!< HID host stack started, used only for Classic Bluetooth */ + ESP_HIDH_STOP_EVENT, /*!< HID host stack stopped, used only for Classic Bluetooth */ ESP_HIDH_MAX_EVENT, /*!< HID events end marker */ } esp_hidh_event_t; @@ -49,11 +51,28 @@ typedef enum { * @brief HIDH callback parameters union */ typedef union { + /** + * @brief ESP_HIDH_START_EVENT + * @note Used only for Classic Bluetooth. + */ + struct { + esp_err_t status; /*!< HID host operation status */ + } start; /*!< HID callback param of ESP_HIDH_START_EVENT */ + + /** + * @brief ESP_HIDH_STOP_EVENT + * @note Used only for Classic Bluetooth. + */ + struct { + esp_err_t status; /*!< HID host operation status */ + } stop; /*!< HID callback param of ESP_HIDH_STOP_EVENT */ + /** * @brief ESP_HIDH_OPEN_EVENT */ struct { esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */ + esp_err_t status; /*!< HID host operation status, used only for Classic Bluetooth */ } open; /*!< HID callback param of ESP_HIDH_OPEN_EVENT */ /** @@ -62,6 +81,7 @@ typedef union { struct { esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device. */ int reason; /*!< Reason why the connection was closed. BLE Only */ + esp_err_t status; /*!< HID host operation status, used only for Classic Bluetooth */ } close; /*!< HID callback param of ESP_HIDH_CLOSE_EVENT */ /** @@ -70,6 +90,7 @@ typedef union { struct { esp_hidh_dev_t *dev; /*!< HID Remote bluetooth device */ uint8_t level; /*!< Battery Level (0-100%) */ + esp_err_t status; /*!< HID host operation status */ } battery; /*!< HID callback param of ESP_HIDH_BATTERY_EVENT */ /** @@ -80,7 +101,7 @@ typedef union { esp_hid_usage_t usage; /*!< HID report usage */ uint16_t report_id; /*!< HID report index */ uint16_t length; /*!< HID data length */ - uint8_t *data; /*!< The pointer to the HID data */ + uint8_t *data; /*!< The pointer to the HID data */ uint8_t map_index; /*!< HID report map index */ } input; /*!< HID callback param of ESP_HIDH_INPUT_EVENT */ @@ -92,8 +113,10 @@ typedef union { esp_hid_usage_t usage; /*!< HID report usage */ uint16_t report_id; /*!< HID report index */ uint16_t length; /*!< HID data length */ - uint8_t *data; /*!< The pointer to the HID data */ + uint8_t *data; /*!< The pointer to the HID data */ uint8_t map_index; /*!< HID report map index */ + esp_err_t status; /*!< HID host operation status, used only for Classic Bluetooth */ + esp_hid_trans_type_t trans_type; /*!< HID host feature transaction type, used only for Classic Bluetooth */ } feature; /*!< HID callback param of ESP_HIDH_FEATURE_EVENT */ } esp_hidh_event_data_t; @@ -101,6 +124,7 @@ typedef union { typedef struct { esp_event_handler_t callback; uint16_t event_stack_size; + void *callback_arg; } esp_hidh_config_t; /** @@ -136,6 +160,14 @@ esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev); */ esp_err_t esp_hidh_dev_free(esp_hidh_dev_t *dev); +/** + * @brief Check if the device still exists. + * @param dev : pointer to the device + * + * @return: true if exists + */ +bool esp_hidh_dev_exists(esp_hidh_dev_t *dev); + /** * @brief Send an OUTPUT report to the device * @param dev : pointer to the device @@ -173,6 +205,79 @@ esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t */ esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_len, uint8_t *data, size_t *length); +/** + * @brief Set_Report command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * @param map_index : index of the device report map + * @param report_id : id of the HID FEATURE report + * @param report_type : report type, defines in `esp_hid_common.h` + * @param data : pointer to the data to send + * @param length : length of the data to send + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_set_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, + uint8_t *data, size_t length); + +/** + * @brief Get_Report command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * @param map_index : index of the device report map + * @param report_id : id of the HID FEATURE report + * @param report_type : report type, defines in `esp_hid_common.h` + * @param max_len : size of the buffer that will hold the data + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_get_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, + size_t max_len); + +/** + * @brief Get_Idle Command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_get_idle(esp_hidh_dev_t *dev); + +/** + * @brief Set_Idle Command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * @param idle_time : idle_time + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time); + +/** + * @brief Get_Protocol Command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_get_protocol(esp_hidh_dev_t *dev); + +/** + * @brief Set_Protocol Command. + * @note For now, this function used only for Classic Bluetooth. + * + * @param dev : pointer to the device + * @param protocol_mode : protocol_mode + * + * @return: ESP_OK on success + */ +esp_err_t esp_hidh_dev_set_protocol(esp_hidh_dev_t *dev, uint8_t protocol_mode); + /** * @brief Dump the properties of HID Device to UART * @param dev : pointer to the HID Device diff --git a/components/esp_hid/private/bt_hidd.h b/components/esp_hid/private/bt_hidd.h new file mode 100644 index 0000000000..0ff874aea6 --- /dev/null +++ b/components/esp_hid/private/bt_hidd.h @@ -0,0 +1,33 @@ +// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#pragma once + +#include "esp_hidd.h" +#include "esp_err.h" +#include "esp_hid_common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#if CONFIG_BT_HID_DEVICE_ENABLED + +esp_err_t esp_bt_hidd_dev_init(esp_hidd_dev_t *dev, const esp_hid_device_config_t *config, esp_event_handler_t callback); + +#endif /* CONFIG_BT_HID_DEVICE_ENABLED */ + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_hid/private/esp_bt_hh_api.h b/components/esp_hid/private/esp_bt_hh_api.h deleted file mode 100644 index 0d438c28b3..0000000000 --- a/components/esp_hid/private/esp_bt_hh_api.h +++ /dev/null @@ -1,367 +0,0 @@ -/****************************************************************************** - * - * Copyright (C) 2005-2012 Broadcom Corporation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - ******************************************************************************/ - -#ifndef _ESP_BT_HH_API_H_ -#define _ESP_BT_HH_API_H_ - -#include "esp_err.h" -#include "esp_bt_defs.h" - -/***************************************************************************** -** Constants and Type Definitions -*****************************************************************************/ -#ifndef BTA_HH_DEBUG -#define BTA_HH_DEBUG TRUE -#endif - -#ifndef BTA_HH_SSR_MAX_LATENCY_DEF -#define BTA_HH_SSR_MAX_LATENCY_DEF 800 /* 500 ms*/ -#endif - -#ifndef BTA_HH_SSR_MIN_TOUT_DEF -#define BTA_HH_SSR_MIN_TOUT_DEF 2 -#endif - -/* defined the minimum offset */ -#define BTA_HH_MIN_OFFSET L2CAP_MIN_OFFSET+1 - -/* HID_HOST_MAX_DEVICES can not exceed 15 for th design of BTA HH */ -#define BTA_HH_IDX_INVALID 0xff -#define BTA_HH_MAX_KNOWN HID_HOST_MAX_DEVICES - -/* Security Service Levels [bit mask] (BTM_SetSecurityLevel) - ** Encryption should not be used without authentication - */ -#define BTM_SEC_NONE 0x0000 /* Nothing required */ -#define BTM_SEC_IN_AUTHORIZE 0x0001 /* Inbound call requires authorization */ -#define BTM_SEC_IN_AUTHENTICATE 0x0002 /* Inbound call requires authentication */ -#define BTM_SEC_IN_ENCRYPT 0x0004 /* Inbound call requires encryption */ -#define BTM_SEC_OUT_AUTHORIZE 0x0008 /* Outbound call requires authorization */ -#define BTM_SEC_OUT_AUTHENTICATE 0x0010 /* Outbound call requires authentication */ -#define BTM_SEC_OUT_ENCRYPT 0x0020 /* Outbound call requires encryption */ -#define BTM_SEC_MODE4_LEVEL4 0x0040 /* Secure Connections Only Mode */ -#define BTM_SEC_FORCE_MASTER 0x0100 /* Need to switch connection to be master */ -#define BTM_SEC_ATTEMPT_MASTER 0x0200 /* Try to switch connection to be master */ -#define BTM_SEC_FORCE_SLAVE 0x0400 /* Need to switch connection to be master */ -#define BTM_SEC_ATTEMPT_SLAVE 0x0800 /* Try to switch connection to be slave */ -#define BTM_SEC_IN_MITM 0x1000 /* inbound Do man in the middle protection */ -#define BTM_SEC_OUT_MITM 0x2000 /* outbound Do man in the middle protection */ -#define BTM_SEC_IN_MIN_16_DIGIT_PIN 0x4000 /* enforce a minimum of 16 digit for sec mode 2 */ - -/* Security Setting Mask */ -#define BTA_SEC_NONE BTM_SEC_NONE /* No security. */ -#define BTA_SEC_AUTHORIZE (BTM_SEC_IN_AUTHORIZE ) /* Authorization required (only needed for out going connection )*/ -#define BTA_SEC_AUTHENTICATE (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_OUT_AUTHENTICATE) /* Authentication required. */ -#define BTA_SEC_ENCRYPT (BTM_SEC_IN_ENCRYPT | BTM_SEC_OUT_ENCRYPT) /* Encryption required. */ -#define BTA_SEC_MODE4_LEVEL4 (BTM_SEC_MODE4_LEVEL4) /* Mode 4 level 4 service, i.e. incoming/outgoing MITM and P-256 encryption */ -#define BTA_SEC_MITM (BTM_SEC_IN_MITM | BTM_SEC_OUT_MITM) /* Man-In-The_Middle protection */ -#define BTA_SEC_IN_16_DIGITS (BTM_SEC_IN_MIN_16_DIGIT_PIN) /* Min 16 digit for pin code */ - -#if (defined BTA_HH_LE_INCLUDED && BTA_HH_LE_INCLUDED == TRUE) -/* GATT_MAX_PHY_CHANNEL can not exceed 14 for the design of BTA HH */ -#define BTA_HH_LE_MAX_KNOWN GATT_MAX_PHY_CHANNEL -#define BTA_HH_MAX_DEVICE (HID_HOST_MAX_DEVICES + GATT_MAX_PHY_CHANNEL) -#else -#define BTA_HH_MAX_DEVICE HID_HOST_MAX_DEVICES -#endif -/* invalid device handle */ -#define BTA_HH_INVALID_HANDLE 0xff - -#define BTA_HH_SSR_PARAM_INVALID HID_SSR_PARAM_INVALID - -/* id DI is not existing in remote device, vendor_id in tBTA_HH_DEV_DSCP_INFO will be set to 0xffff */ -#define BTA_HH_VENDOR_ID_INVALID 0xffff - -/* application ID(none-zero) for each type of device */ -#define BTA_HH_APP_ID_MI 1 -#define BTA_HH_APP_ID_KB 2 -#define BTA_HH_APP_ID_RMC 3 -#define BTA_HH_APP_ID_3DSG 4 -#define BTA_HH_APP_ID_JOY 5 -#define BTA_HH_APP_ID_GPAD 6 -#define BTA_HH_APP_ID_LE 0xff - -/* type of devices, bit mask */ -#define BTA_HH_DEVT_UNKNOWN 0x00 -#define BTA_HH_DEVT_JOS 0x01 /* joy stick */ -#define BTA_HH_DEVT_GPD 0x02 /* game pad */ -#define BTA_HH_DEVT_RMC 0x03 /* remote control */ -#define BTA_HH_DEVT_SED 0x04 /* sensing device */ -#define BTA_HH_DEVT_DGT 0x05 /* Digitizer tablet */ -#define BTA_HH_DEVT_CDR 0x06 /* card reader */ -#define BTA_HH_DEVT_KBD 0x10 /* keyboard */ -#define BTA_HH_DEVT_MIC 0x20 /* pointing device */ -#define BTA_HH_DEVT_COM 0x30 /* Combo keyboard/pointing */ -#define BTA_HH_DEVT_OTHER 0x80 -typedef uint8_t tBTA_HH_DEVT; - - -/* BTA HID Host callback events */ -#define BTA_HH_ENABLE_EVT 0 /* HH enabled */ -#define BTA_HH_DISABLE_EVT 1 /* HH disabled */ -#define BTA_HH_OPEN_EVT 2 /* connection opened */ -#define BTA_HH_CLOSE_EVT 3 /* connection closed */ -#define BTA_HH_GET_RPT_EVT 4 /* BTA_HhGetReport callback */ -#define BTA_HH_SET_RPT_EVT 5 /* BTA_HhSetReport callback */ -#define BTA_HH_GET_PROTO_EVT 6 /* BTA_GetProtoMode callback */ -#define BTA_HH_SET_PROTO_EVT 7 /* BTA_HhSetProtoMode callback */ -#define BTA_HH_GET_IDLE_EVT 8 /* BTA_HhGetIdle comes callback */ -#define BTA_HH_SET_IDLE_EVT 9 /* BTA_HhSetIdle finish callback */ -#define BTA_HH_GET_DSCP_EVT 10 /* Get report descriptor */ -#define BTA_HH_ADD_DEV_EVT 11 /* Add Device callback */ -#define BTA_HH_RMV_DEV_EVT 12 /* remove device finished */ -#define BTA_HH_VC_UNPLUG_EVT 13 /* virtually unplugged */ -#define BTA_HH_DATA_EVT 15 -#define BTA_HH_API_ERR_EVT 16 /* API error is caught */ -#define BTA_HH_UPDATE_SCPP_EVT 17 /* update scan paramter complete */ -typedef uint16_t tBTA_HH_EVT; - -/* type of protocol mode */ -#define BTA_HH_PROTO_RPT_MODE (0x00) -#define BTA_HH_PROTO_BOOT_MODE (0x01) -#define BTA_HH_PROTO_UNKNOWN (0xff) -typedef uint8_t tBTA_HH_PROTO_MODE; - -#define BTA_HH_VIRTUAL_CABLE HID_VIRTUAL_CABLE -#define BTA_HH_NORMALLY_CONNECTABLE HID_NORMALLY_CONNECTABLE -#define BTA_HH_RECONN_INIT HID_RECONN_INIT -#define BTA_HH_SDP_DISABLE HID_SDP_DISABLE -#define BTA_HH_BATTERY_POWER HID_BATTERY_POWER -#define BTA_HH_REMOTE_WAKE HID_REMOTE_WAKE -#define BTA_HH_SUP_TOUT_AVLBL HID_SUP_TOUT_AVLBL -#define BTA_HH_SEC_REQUIRED HID_SEC_REQUIRED -typedef uint16_t tBTA_HH_ATTR_MASK; - -enum { - BTA_HH_OK, - BTA_HH_HS_HID_NOT_READY, /* handshake error : device not ready */ - BTA_HH_HS_INVALID_RPT_ID, /* handshake error : invalid report ID */ - BTA_HH_HS_TRANS_NOT_SPT, /* handshake error : transaction not spt */ - BTA_HH_HS_INVALID_PARAM, /* handshake error : invalid paremter */ - BTA_HH_HS_ERROR, /* handshake error : unspecified HS error */ - BTA_HH_ERR, /* general BTA HH error */ - BTA_HH_ERR_SDP, /* SDP error */ - BTA_HH_ERR_PROTO, /* SET_Protocol error, only used in BTA_HH_OPEN_EVT callback */ - BTA_HH_ERR_DB_FULL, /* device database full error, used in BTA_HH_OPEN_EVT/BTA_HH_ADD_DEV_EVT */ - BTA_HH_ERR_TOD_UNSPT, /* type of device not supported */ - BTA_HH_ERR_NO_RES, /* out of system resources */ - BTA_HH_ERR_AUTH_FAILED, /* authentication fail */ - BTA_HH_ERR_HDL, - BTA_HH_ERR_SEC -}; -typedef uint8_t tBTA_HH_STATUS; - -enum { - BTA_HH_RPTT_RESRV, /* reserved */ - BTA_HH_RPTT_INPUT, /* input report */ - BTA_HH_RPTT_OUTPUT, /* output report */ - BTA_HH_RPTT_FEATURE /* feature report */ -}; -typedef uint8_t tBTA_HH_RPT_TYPE; - -/* HID_CONTROL operation code used in BTA_HhSendCtrl() -*/ -enum { - BTA_HH_CTRL_NOP = 0, /* mapping from BTE */ - BTA_HH_CTRL_HARD_RESET, /* hard reset */ - BTA_HH_CTRL_SOFT_RESET, /* soft reset */ - BTA_HH_CTRL_SUSPEND, /* enter suspend */ - BTA_HH_CTRL_EXIT_SUSPEND, /* exit suspend */ - BTA_HH_CTRL_VIRTUAL_CABLE_UNPLUG /* virtual unplug */ -}; -typedef uint8_t tBTA_HH_TRANS_CTRL_TYPE; - - -typedef struct desc_info { - uint16_t dl_len; - uint8_t *dsc_list; -} tBTA_HH_DEV_DESCR; - -/* Define the header of each buffer used in the Bluetooth stack. */ -typedef struct { - uint16_t event; - uint16_t len; - uint16_t offset; - uint16_t layer_specific; - uint8_t data[]; -} BT_HDR; - -#define BT_HDR_SIZE (sizeof (BT_HDR)) - -/* callback event data for BTA_HH_OPEN_EVT */ -typedef struct { - esp_bd_addr_t bda; /* HID device bd address */ - tBTA_HH_STATUS status; /* operation status */ - uint8_t handle; /* device handle */ - -} tBTA_HH_CONN; - -typedef tBTA_HH_CONN tBTA_HH_DEV_INFO; - -/* callback event data */ -typedef struct { - tBTA_HH_STATUS status; /* operation status */ - uint8_t handle; /* device handle */ -} tBTA_HH_CBDATA; - -/* report descriptor information */ -typedef struct { - uint16_t vendor_id; /* vendor ID */ - uint16_t product_id; /* product ID */ - uint16_t version; /* version */ - uint16_t ssr_max_latency;/* SSR max latency, BTA_HH_SSR_PARAM_INVALID if unknown */ - uint16_t ssr_min_tout; /* SSR min timeout, BTA_HH_SSR_PARAM_INVALID if unknown */ - uint8_t ctry_code; /*Country Code.*/ - tBTA_HH_DEV_DESCR descriptor; -} tBTA_HH_DEV_DSCP_INFO; - -/* handshake data */ -typedef struct { - tBTA_HH_STATUS status; /* handshake status */ - uint8_t handle; /* device handle */ - union { - tBTA_HH_PROTO_MODE proto_mode; /* GET_PROTO_EVT :protocol mode */ - BT_HDR *p_rpt_data;/* GET_RPT_EVT : report data */ - uint8_t idle_rate; /* GET_IDLE_EVT : idle rate */ - } rsp_data; - -} tBTA_HH_HSDATA; - -/* union of data associated with HD callback */ -typedef union { - tBTA_HH_DEV_INFO dev_info; /* BTA_HH_ADD_DEV_EVT, BTA_HH_RMV_DEV_EVT */ - tBTA_HH_CONN conn; /* BTA_HH_OPEN_EVT */ - tBTA_HH_CBDATA dev_status; /* BTA_HH_CLOSE_EVT, - BTA_HH_SET_PROTO_EVT - BTA_HH_SET_RPT_EVT - BTA_HH_SET_IDLE_EVT - BTA_HH_UPDATE_SCPP_EVT */ - - tBTA_HH_STATUS status; /* BTA_HH_ENABLE_EVT */ - tBTA_HH_DEV_DSCP_INFO dscp_info; /* BTA_HH_GET_DSCP_EVT */ - tBTA_HH_HSDATA hs_data; /* GET_ transaction callback - BTA_HH_GET_RPT_EVT - BTA_HH_GET_PROTO_EVT - BTA_HH_GET_IDLE_EVT */ -} tBTA_HH; - -/* BTA HH callback function */ -typedef void (tBTA_HH_CBACK) (tBTA_HH_EVT event, tBTA_HH *p_data); - -/***************************************************************************** -** External Function Declarations -*****************************************************************************/ -#ifdef __cplusplus -extern "C" -{ -#endif - -/* This function enable HID host and registers HID-Host with lower layers.*/ -extern void BTA_HhEnable(uint16_t sec_mask, tBTA_HH_CBACK *p_cback); - -/* This function is called when the host is about power down. */ -extern void BTA_HhDisable(void); - -/* This function is called to start an inquiry and read SDP record of responding devices; connect to a device if only one active HID device is found. */ -extern void BTA_HhOpen (esp_bd_addr_t dev_bda, uint8_t bd_type, tBTA_HH_PROTO_MODE mode, uint16_t sec_mask); - -/* This function disconnects the device. */ -extern void BTA_HhClose(uint8_t dev_handle); - -/* This function set the protocol mode at specified HID handle */ -extern void BTA_HhSetProtoMode(uint8_t handle, tBTA_HH_PROTO_MODE t_type); - -/* This function get the protocol mode of a specified HID device. */ -extern void BTA_HhGetProtoMode(uint8_t dev_handle); - -/* send SET_REPORT to device. */ -extern void BTA_HhSetReport(uint8_t dev_handle, tBTA_HH_RPT_TYPE r_type, BT_HDR *p_data); - -/* Send a GET_REPORT to HID device. */ -extern void BTA_HhGetReport(uint8_t dev_handle, tBTA_HH_RPT_TYPE r_type, uint8_t rpt_id, uint16_t buf_size); - -/* send SET_IDLE to device. */ -extern void BTA_HhSetIdle(uint8_t dev_handle, uint16_t idle_rate); - -/* Send a GET_IDLE to HID device. */ -extern void BTA_HhGetIdle(uint8_t dev_handle); - -/* Send HID_CONTROL request to a HID device. */ -extern void BTA_HhSendCtrl(uint8_t dev_handle, tBTA_HH_TRANS_CTRL_TYPE c_type); - -/* Send DATA transaction to a HID device. */ -extern void BTA_HhSendData(uint8_t dev_handle, esp_bd_addr_t dev_bda, BT_HDR *p_buf); - -/* Get report descriptor of the device */ -extern void BTA_HhGetDscpInfo(uint8_t dev_handle); - -/* Add a virtually cabled device into HID-Host device list to manage and assign a device handle for future API call, host applciation call this API at start-up to initialize its virtually cabled devices. */ -extern void BTA_HhAddDev(esp_bd_addr_t bda, tBTA_HH_ATTR_MASK attr_mask, uint8_t sub_class, uint8_t app_id, tBTA_HH_DEV_DSCP_INFO dscp_info); - -/* Remove a device from the HID host devices list. */ -extern void BTA_HhRemoveDev(uint8_t dev_handle ); - -enum { - BTA_HH_MOD_CTRL_KEY, - BTA_HH_MOD_SHFT_KEY, - BTA_HH_MOD_ALT_KEY, - BTA_HH_MOD_GUI_KEY, - BTA_HH_MOD_MAX_KEY -}; - -/* parsed boot mode keyboard report */ -typedef struct { - uint8_t this_char[6]; /* virtual key code */ - bool mod_key[BTA_HH_MOD_MAX_KEY];/* ctrl, shift, Alt, GUI */ - bool caps_lock; /* is caps locked */ - bool num_lock; /* is Num key pressed */ -} tBTA_HH_KEYBD_RPT; - -/* parsed boot mode mouse report */ -typedef struct { - uint8_t mouse_button; /* mouse button is clicked */ - int8_t delta_x; /* displacement x */ - int8_t delta_y; /* displacement y */ -} tBTA_HH_MICE_RPT; - -enum { - BTA_HH_KEYBD_RPT_ID = 1, - BTA_HH_MOUSE_RPT_ID -}; -typedef uint8_t tBTA_HH_BOOT_RPT_ID; - -/* parsed Boot report */ -typedef struct { - tBTA_HH_BOOT_RPT_ID dev_type; /* type of device report */ - union { - tBTA_HH_KEYBD_RPT keybd_rpt; /* keyboard report */ - tBTA_HH_MICE_RPT mice_rpt; /* mouse report */ - } data_rpt; -} tBTA_HH_BOOT_RPT; - -/* This utility function parse a boot mode report. */ -extern void BTA_HhParseBootRpt(tBTA_HH_BOOT_RPT *p_data, uint8_t *p_report, uint16_t report_len); - - - - -#ifdef __cplusplus -} -#endif - -#endif /* _ESP_BT_HH_API_H_ */ diff --git a/components/esp_hid/private/esp_hidd_private.h b/components/esp_hid/private/esp_hidd_private.h index 0b947e3ee9..abb76efc11 100644 --- a/components/esp_hid/private/esp_hidd_private.h +++ b/components/esp_hid/private/esp_hidd_private.h @@ -22,12 +22,37 @@ extern "C" { #endif +typedef union { + struct { + uint16_t notify_enable : 1; + uint16_t indicate_enable : 1; + uint16_t reserved : 14; + }; + uint16_t value; +} hidd_le_ccc_value_t; + +typedef struct { + uint8_t map_index; // the index of the report map + uint8_t report_id; // the id of the report + uint8_t report_type; // input, output or feature + uint8_t protocol_mode; // boot or report + esp_hid_usage_t usage; // generic, keyboard, mouse, joystick or gamepad + uint16_t value_len; // maximum len of value by report map + // used by gatts + uint8_t index; // index of the value in the gatts attr db + uint16_t handle; // obtained once all attributes are registered + uint16_t ccc_handle; // obtained once all attributes are registered + hidd_le_ccc_value_t ccc; // notifications and/or indications enabled +} hidd_report_item_t; + struct esp_hidd_dev_s { void *dev; esp_hid_transport_t transport; bool (*connected) (void *dev); esp_err_t (*deinit) (void *dev); + esp_err_t (*disconnect) (void *dev); + esp_err_t (*virtual_unplug) (void *dev); esp_err_t (*battery_set) (void *dev, uint8_t level); esp_err_t (*input_set) (void *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length); esp_err_t (*feature_set) (void *dev, size_t map_index, size_t report_id, uint8_t *data, size_t length); diff --git a/components/esp_hid/private/esp_hidh_private.h b/components/esp_hid/private/esp_hidh_private.h index a052d06809..1e5ac5afbe 100644 --- a/components/esp_hid/private/esp_hidh_private.h +++ b/components/esp_hid/private/esp_hidh_private.h @@ -24,6 +24,7 @@ #include "freertos/semphr.h" #include "esp_event.h" #include "sys/queue.h" +#include "esp_timer.h" #ifdef __cplusplus extern "C" { @@ -54,10 +55,18 @@ struct esp_hidh_dev_s { esp_hid_device_config_t config; esp_hid_usage_t usage; - esp_hid_transport_t transport; //BT, BLE or USB - bool connected; //we have all required data to communicate - bool opened; //we opened the device manually, else the device connected to us - int status; //status of the last command + esp_hid_transport_t transport; //BT, BLE or USB + esp_hid_trans_type_t trans_type; //indicate what transaction is going on, new transaction only be allowed after the previous done + esp_timer_handle_t trans_timer; //transactiion timer + uint8_t report_type; //Get_Report tansaction report_type + uint8_t report_id; //Get_Report tansaction report_id + uint8_t protocol_mode; //device protocol mode + bool connected; //we have all required data to communicate + bool opened; //we opened the device manually, else the device connected to us + bool added; //If lower layer has added the device + bool is_orig; //If host initiate the connection + bool in_use; //If false, it will be deleted from the devices list. + int status; //status of the last command size_t reports_len; esp_hidh_dev_report_t *reports; @@ -66,10 +75,16 @@ struct esp_hidh_dev_s { size_t tmp_len; xSemaphoreHandle semaphore; + xSemaphoreHandle mutex; esp_err_t (*close) (esp_hidh_dev_t *dev); esp_err_t (*report_write) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len); esp_err_t (*report_read) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len); + esp_err_t (*set_report) (esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len); + esp_err_t (*get_idle) (esp_hidh_dev_t *dev); + esp_err_t (*set_idle) (esp_hidh_dev_t *dev, uint8_t idle_time); + esp_err_t (*get_protocol) (esp_hidh_dev_t *dev); + esp_err_t (*set_protocol) (esp_hidh_dev_t *dev, uint8_t protocol_mode); void (*dump) (esp_hidh_dev_t *dev, FILE *fp); #if CONFIG_BLUEDROID_ENABLED @@ -80,10 +95,10 @@ struct esp_hidh_dev_s { #if CONFIG_BT_HID_HOST_ENABLED struct { esp_bt_cod_t cod; - int handle; - uint8_t sub_class; - uint8_t app_id; - uint16_t attr_mask; + uint8_t handle; + // uint8_t sub_class; + // uint8_t app_id; + // uint16_t attr_mask; } bt; #endif /* CONFIG_BT_HID_HOST_ENABLED */ #if CONFIG_GATTC_ENABLE @@ -105,17 +120,24 @@ esp_hidh_dev_t *esp_hidh_dev_malloc(void); #if CONFIG_BLUEDROID_ENABLED esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda); //BT/BLE -esp_hidh_dev_t *esp_hidh_dev_get_by_handle(int handle); //BT Only +esp_hidh_dev_t *esp_hidh_dev_get_by_handle(uint8_t handle); //Classic Bluetooth Only esp_hidh_dev_t *esp_hidh_dev_get_by_conn_id(uint16_t conn_id); //BLE Only #endif /* CONFIG_BLUEDROID_ENABLED */ +esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_type_proto(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t protocol_mode); esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_and_type(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type); +esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_len_and_proto(esp_hidh_dev_t *dev, size_t len, int protocol_mode); esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_id_and_proto(esp_hidh_dev_t *dev, size_t report_id, int protocol_mode); +esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_proto_and_data(esp_hidh_dev_t *dev, int protocol_mode, + size_t len, const uint8_t *data, bool *has_report_id); esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_handle(esp_hidh_dev_t *dev, uint16_t handle); //BLE Only - - void esp_hidh_process_event_data_handler(void *event_handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data); +void esp_hidh_dev_lock(esp_hidh_dev_t *dev); +void esp_hidh_dev_unlock(esp_hidh_dev_t *dev); +void esp_hidh_dev_wait(esp_hidh_dev_t *dev); +void esp_hidh_dev_send(esp_hidh_dev_t *dev); +esp_err_t esp_hidh_dev_free_inner(esp_hidh_dev_t *dev); #ifdef __cplusplus } #endif diff --git a/components/esp_hid/src/ble_hidd.c b/components/esp_hid/src/ble_hidd.c index 69061ae4dc..85b49bcf22 100644 --- a/components/esp_hid/src/ble_hidd.c +++ b/components/esp_hid/src/ble_hidd.c @@ -38,6 +38,8 @@ static const char *TAG = "BLE_HIDD"; /// Length of Boot Report Char. Value Maximal Length #define HIDD_LE_BOOT_REPORT_MAX_LEN (8) +typedef hidd_report_item_t hidd_le_report_item_t; + /* * UUIDs * */ @@ -126,30 +128,6 @@ enum { HIDD_LE_IDX_NB, }; -/* Client Characteristic Configuration value structure */ -typedef union { - struct { - uint16_t notify_enable: 1; - uint16_t indicate_enable: 1; - uint16_t reserved: 14; - }; - uint16_t value; -} hidd_le_ccc_value_t; - -typedef struct { - uint8_t map_index; //the index of the report map - uint8_t report_id; //the id of the report - uint8_t report_type; //input, output or feature - uint8_t protocol_mode; //boot or report - esp_hid_usage_t usage; //generic, keyboard, mouse, joystick or gamepad - uint16_t value_len; //maximum len of value by report map - //used by gatts - uint8_t index; //index of the value in the gatts attr db - uint16_t handle; //obtained once all attributes are registered - uint16_t ccc_handle; //obtained once all attributes are registered - hidd_le_ccc_value_t ccc; //notifications and/or indications enabled -} hidd_le_report_item_t; - typedef struct { esp_gatt_if_t gatt_if; uint16_t handle; @@ -872,8 +850,14 @@ static esp_err_t esp_ble_hidd_dev_input_set(void *devp, size_t index, size_t id, if (!dev || s_dev != dev) { return ESP_FAIL; } + if (!dev->connected) { - ESP_LOGE(TAG, "Device Not Connected: %d", index); + ESP_LOGE(TAG, "%s Device Not Connected", __func__); + return ESP_FAIL; + } + + if (index >= dev->devices_len) { + ESP_LOGE(TAG, "%s index out of range[0-%d]", __func__, dev->devices_len - 1); return ESP_FAIL; } @@ -898,6 +882,17 @@ static esp_err_t esp_ble_hidd_dev_feature_set(void *devp, size_t index, size_t i if (!dev || s_dev != dev) { return ESP_FAIL; } + + if (!dev->connected) { + ESP_LOGE(TAG, "%s Device Not Connected", __func__); + return ESP_FAIL; + } + + if (index >= dev->devices_len) { + ESP_LOGE(TAG, "%s index out of range[0-%d]", __func__, dev->devices_len - 1); + return ESP_FAIL; + } + hidd_le_report_item_t *p_rpt; if ((p_rpt = get_report_by_id_and_type(dev, id, ESP_HID_REPORT_TYPE_FEATURE)) != NULL) { ret = esp_ble_gatts_set_attr_value(p_rpt->handle, length, data); @@ -941,9 +936,11 @@ static esp_err_t esp_ble_hidd_dev_event_handler_unregister(void *devp, esp_event static void ble_hidd_dev_free(void) { - ble_hid_free_config(s_dev); - free(s_dev); - s_dev = NULL; + if (s_dev) { + ble_hid_free_config(s_dev); + free(s_dev); + s_dev = NULL; + } } esp_err_t esp_ble_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback) diff --git a/components/esp_hid/src/ble_hidh.c b/components/esp_hid/src/ble_hidh.c index 731ac75e7a..6116384ee7 100644 --- a/components/esp_hid/src/ble_hidh.c +++ b/components/esp_hid/src/ble_hidh.c @@ -534,13 +534,16 @@ void esp_hidh_gattc_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gatt } else { dev->connected = false; dev->status = p_data->close.status; + // free the device in the wrapper event handler + dev->in_use = false; if (event_loop_handle) { esp_hidh_event_data_t p = {0}; p.close.dev = dev; p.close.reason = p_data->close.reason; + p.close.status = ESP_OK; esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_CLOSE_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); } else { - esp_hidh_dev_free(dev); + esp_hidh_dev_free_inner(dev); } } break; @@ -659,7 +662,7 @@ esp_err_t esp_ble_hidh_init(const esp_hidh_config_t *config) ret = esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, esp_hidh_process_event_data_handler, NULL); ret |= esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, config->callback, - NULL); + config->callback_arg); } while (0); if (ret != ESP_OK) { @@ -706,6 +709,7 @@ esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t add return NULL; } + dev->in_use = true; dev->transport = ESP_HID_TRANSPORT_BLE; memcpy(dev->bda, bda, sizeof(esp_bd_addr_t)); dev->ble.address_type = address_type; @@ -713,7 +717,7 @@ esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t add ret = esp_ble_gattc_open(hid_gattc_if, dev->bda, dev->ble.address_type, true); if (ret) { - esp_hidh_dev_free(dev); + esp_hidh_dev_free_inner(dev); ESP_LOGE(TAG, "esp_ble_gattc_open failed: %d", ret); return NULL; } @@ -721,7 +725,7 @@ esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t add if (dev->ble.conn_id < 0) { ret = dev->status; ESP_LOGE(TAG, "dev open failed! status: 0x%x", dev->status); - esp_hidh_dev_free(dev); + esp_hidh_dev_free_inner(dev); return NULL; } @@ -734,6 +738,7 @@ esp_hidh_dev_t *esp_ble_hidh_dev_open(esp_bd_addr_t bda, esp_ble_addr_type_t add if (event_loop_handle) { esp_hidh_event_data_t p = {0}; + p.open.status = ESP_OK; p.open.dev = dev; esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); } diff --git a/components/esp_hid/src/bt_hidd.c b/components/esp_hid/src/bt_hidd.c new file mode 100644 index 0000000000..0af5424d08 --- /dev/null +++ b/components/esp_hid/src/bt_hidd.c @@ -0,0 +1,879 @@ +// Copyright 2017-2019 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "bt_hidd.h" + +#if CONFIG_BT_HID_DEVICE_ENABLED +#include "esp_bt.h" +#include "esp_bt_defs.h" +#include "esp_bt_main.h" +#include "esp_hidd.h" +#include "esp_hidd_api.h" +#include "esp_hidd_private.h" +#include "esp_log.h" +#include "osi/mutex.h" +#include "string.h" + +/* Values for service_type */ +#define NO_TRAFFIC 0 +#define BEST_EFFORT 1 +#define GUARANTEED 2 + +static const char *TAG = "BT_HIDD"; + +typedef struct { + esp_hid_raw_report_map_t reports_map; + uint8_t reports_len; + hidd_report_item_t *reports; +} hidd_dev_map_t; + +typedef struct { + esp_hidd_dev_t *dev; + esp_event_loop_handle_t event_loop_handle; + esp_hid_device_config_t config; + uint16_t appearance; + bool registered; + bool connected; + esp_bd_addr_t remote_bda; + uint8_t bat_level; // 0 - 100 - battery percentage + uint8_t control; // 0x00 suspend, 0x01 suspend off + uint8_t protocol_mode; // 0x00 boot, 0x01 report + hidd_dev_map_t *devices; + uint8_t devices_len; +} esp_bt_hidd_dev_t; + +typedef struct +{ + osi_mutex_t mutex; + esp_bt_hidd_dev_t *dev; + esp_hidd_app_param_t app_param; + esp_hidd_qos_param_t in_qos; + esp_hidd_qos_param_t out_qos; +} hidd_param_t; + +static hidd_param_t s_hidd_param = {0}; +#define is_init() (s_hidd_param.dev != NULL) +#define UNUSED(x) (void)(x) + +static esp_err_t bt_hidd_get_status(esp_hidd_status_t status) +{ + esp_err_t ret = ESP_OK; + switch (status) { + case ESP_HIDD_SUCCESS: + ret = ESP_OK; + break; + case ESP_HIDD_NO_RES: + ret = ESP_ERR_NO_MEM; + break; + default: + ret = ESP_FAIL; + break; + } + return ret; +} + +static esp_err_t bt_hidd_init_config(esp_bt_hidd_dev_t *dev, const esp_hid_device_config_t *config) +{ + if (config->report_maps == NULL || config->report_maps_len == 0 || config->report_maps_len > 1) { + return ESP_ERR_INVALID_ARG; + } + memset((uint8_t *)(&dev->config), 0, sizeof(esp_hid_device_config_t)); + dev->config.vendor_id = config->vendor_id; + dev->config.product_id = config->product_id; + dev->config.version = config->version; + if (config->device_name != NULL) { + dev->config.device_name = strdup(config->device_name); + } + if (config->manufacturer_name != NULL) { + dev->config.manufacturer_name = strdup(config->manufacturer_name); + } + if (config->serial_number != NULL) { + dev->config.serial_number = strdup(config->serial_number); + } + dev->appearance = ESP_HID_APPEARANCE_GENERIC; + + if (config->report_maps_len) { + dev->devices = (hidd_dev_map_t *)malloc(config->report_maps_len * sizeof(hidd_dev_map_t)); + if (dev->devices == NULL) { + ESP_LOGE(TAG, "devices malloc(%d) failed", config->report_maps_len); + return ESP_ERR_NO_MEM; + } + memset(dev->devices, 0, config->report_maps_len * sizeof(hidd_dev_map_t)); + dev->devices_len = config->report_maps_len; + for (uint8_t d = 0; d < dev->devices_len; d++) { + + //raw report map + uint8_t *map = (uint8_t *)malloc(config->report_maps[d].len); + if (map == NULL) { + ESP_LOGE(TAG, "report map malloc(%d) failed", config->report_maps[d].len); + return ESP_ERR_NO_MEM; + } + memcpy(map, config->report_maps[d].data, config->report_maps[d].len); + + dev->devices[d].reports_map.data = (const uint8_t *)map; + dev->devices[d].reports_map.len = config->report_maps[d].len; + + esp_hid_report_map_t *rmap = esp_hid_parse_report_map(config->report_maps[d].data, config->report_maps[d].len); + if (rmap == NULL) { + ESP_LOGE(TAG, "hid_parse_report_map[%d](%d) failed", d, config->report_maps[d].len); + return ESP_FAIL; + } + dev->appearance = rmap->appearance; + dev->devices[d].reports_len = rmap->reports_len; + dev->devices[d].reports = (hidd_report_item_t *)malloc(rmap->reports_len * sizeof(hidd_report_item_t)); + if (dev->devices[d].reports == NULL) { + ESP_LOGE(TAG, "reports malloc(%d) failed", rmap->reports_len * sizeof(hidd_report_item_t)); + free(rmap); + return ESP_ERR_NO_MEM; + } + for (uint8_t r = 0; r < rmap->reports_len; r++) { + dev->devices[d].reports[r].map_index = d; + dev->devices[d].reports[r].report_id = rmap->reports[r].report_id; + dev->devices[d].reports[r].protocol_mode = rmap->reports[r].protocol_mode; + dev->devices[d].reports[r].report_type = rmap->reports[r].report_type; + dev->devices[d].reports[r].usage = rmap->reports[r].usage; + dev->devices[d].reports[r].value_len = rmap->reports[r].value_len; + } + free(rmap->reports); + free(rmap); + } + } + + return ESP_OK; +} + +static hidd_report_item_t *get_report_by_idx_id_type(esp_bt_hidd_dev_t *dev, size_t index, uint8_t id, uint8_t type) +{ + hidd_report_item_t *rpt = NULL; + if (index >= dev->devices_len) { + ESP_LOGE(TAG, "index out of range[0-%d]", dev->devices_len - 1); + return NULL; + } + for (uint8_t i = 0; i < dev->devices[index].reports_len; i++) { + rpt = &dev->devices[index].reports[i]; + if (rpt->report_id == id && rpt->report_type == type && rpt->protocol_mode == dev->protocol_mode) { + return rpt; + } + } + return NULL; +} + +static hidd_report_item_t *get_report_by_id_and_type(esp_bt_hidd_dev_t *dev, uint8_t id, uint8_t type, uint8_t *index) +{ + hidd_report_item_t *rpt = NULL; + for (uint8_t idx = 0; idx < dev->devices_len; idx++) { + for (uint8_t i = 0; i < dev->devices[idx].reports_len; i++) { + rpt = &dev->devices[idx].reports[i]; + if (rpt->report_id == id && rpt->report_type == type && rpt->protocol_mode == dev->protocol_mode) { + if (index) { + *index = idx; + } + return rpt; + } + } + } + return NULL; +} + +static esp_err_t bt_hid_free_config(esp_bt_hidd_dev_t *dev) +{ + for (uint8_t d = 0; d < dev->devices_len; d++) { + free((void *)dev->devices[d].reports); + free((void *)dev->devices[d].reports_map.data); + } + + free((void *)dev->devices); + free((void *)dev->config.device_name); + free((void *)dev->config.manufacturer_name); + free((void *)dev->config.serial_number); + if (dev->event_loop_handle != NULL) { + esp_event_loop_delete(dev->event_loop_handle); + dev->event_loop_handle = NULL; + } + return ESP_OK; +} + +static void bt_hidd_dev_free(void) +{ + if (s_hidd_param.dev) { + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + bt_hid_free_config(s_hidd_param.dev); + free(s_hidd_param.dev); + s_hidd_param.dev = NULL; + osi_mutex_unlock(&s_hidd_param.mutex); + osi_mutex_free(&s_hidd_param.mutex); + } +} + +static void build_default_in_qos(esp_hidd_qos_param_t *in_qos, uint32_t value_len) +{ + + if (value_len > 0) { + in_qos->service_type = GUARANTEED; + in_qos->token_rate = value_len * 100; + in_qos->token_bucket_size = value_len; + in_qos->peak_bandwidth = value_len * 100; + in_qos->access_latency = 10; + in_qos->delay_variation = 10; + } else { + memset(in_qos, 0, sizeof(esp_hidd_qos_param_t)); + } +} + +static void build_default_out_qos(esp_hidd_qos_param_t *out_qos, uint32_t value_len) +{ + if (value_len > 0) { + out_qos->service_type = GUARANTEED; + out_qos->token_rate = value_len * 100; + out_qos->token_bucket_size = value_len; + out_qos->peak_bandwidth = value_len * 100; + out_qos->access_latency = 10; + out_qos->delay_variation = 10; + } else { + memset(out_qos, 0, sizeof(esp_hidd_qos_param_t)); + } +} + +static uint8_t get_subclass_by_appearance(uint16_t appearance) +{ + uint8_t ret = ESP_HID_CLASS_UNKNOWN; + switch (appearance) { + case ESP_HID_APPEARANCE_KEYBOARD: + ret = ESP_HID_CLASS_KBD; + break; + case ESP_HID_APPEARANCE_MOUSE: + ret = ESP_HID_CLASS_MIC; + break; + case ESP_HID_APPEARANCE_JOYSTICK: + ret = ESP_HID_CLASS_JOS; + break; + case ESP_HID_APPEARANCE_GAMEPAD: + ret = ESP_HID_CLASS_GPD; + break; + default: + ret = ESP_HID_CLASS_UNKNOWN; + break; + } + return ret; +} + +static uint32_t get_value_len_by_type_protocol(esp_bt_hidd_dev_t *dev, uint8_t report_type, uint8_t protocol_mode) +{ + uint32_t value_len = 0; + hidd_report_item_t *rpt = NULL; + for (uint8_t d = 0; d < dev->devices_len; d++) { + for (uint8_t i = 0; i < dev->devices[d].reports_len; i++) { + rpt = &dev->devices[d].reports[i]; + if (rpt->report_type == report_type && rpt->protocol_mode == dev->protocol_mode) { + value_len += rpt->value_len; + } + } + } + return value_len; +} + +static void bt_hidd_init_app(void) +{ + esp_hid_device_config_t *p_config = &s_hidd_param.dev->config; + s_hidd_param.app_param.name = p_config->device_name; + s_hidd_param.app_param.description = p_config->device_name; + s_hidd_param.app_param.provider = p_config->manufacturer_name; + s_hidd_param.app_param.subclass = get_subclass_by_appearance(s_hidd_param.dev->appearance); + s_hidd_param.app_param.desc_list = (uint8_t *)s_hidd_param.dev->devices[0].reports_map.data; + s_hidd_param.app_param.desc_list_len = s_hidd_param.dev->devices[0].reports_map.len; +} + +static void bt_hidd_init_qos(void) +{ + uint32_t value_len = 0; + value_len = + get_value_len_by_type_protocol(s_hidd_param.dev, ESP_HID_REPORT_TYPE_INPUT, s_hidd_param.dev->protocol_mode); + build_default_in_qos(&s_hidd_param.in_qos, value_len); + + value_len = + get_value_len_by_type_protocol(s_hidd_param.dev, ESP_HID_REPORT_TYPE_INPUT, s_hidd_param.dev->protocol_mode); + build_default_out_qos(&s_hidd_param.out_qos, value_len); +} + +static bool esp_bt_hidd_dev_connected(void *devp) +{ + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + bool ret = true; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device profile is uninit"); + ret = false; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = false; + break; + } + } while(0); + if (ret) { + ret = dev->connected; + } + osi_mutex_unlock(&s_hidd_param.mutex); + return ret; +} + +static esp_err_t esp_bt_hidd_dev_deinit(void *devp) +{ + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + osi_mutex_unlock(&s_hidd_param.mutex); + ESP_LOGE(TAG, "HID device profile already uninitialized"); + return ESP_OK; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + } + } while(0); + osi_mutex_unlock(&s_hidd_param.mutex); + + if (ret == ESP_OK) { + ret = esp_bt_hid_device_deinit(); + } + return ret; +} + +static esp_err_t esp_bt_hidd_dev_disconnect(void *devp) +{ + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit"); + ret = ESP_FAIL; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + break; + } + + if (!dev->connected) { + osi_mutex_unlock(&s_hidd_param.mutex); + ESP_LOGW(TAG, "already disconnected"); + return ESP_OK; + } + } while(0); + osi_mutex_unlock(&s_hidd_param.mutex); + + if (ret == ESP_OK) { + ret = esp_bt_hid_device_disconnect(); + } + return ret; +} + +static esp_err_t esp_bt_hidd_dev_battery_set(void *devp, uint8_t level) +{ + UNUSED(devp); + UNUSED(level); + ESP_LOGW(TAG, "Not implement yet!"); + return ESP_OK; +} + +static esp_err_t esp_bt_hidd_dev_input_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length) +{ + hidd_report_item_t *p_rpt; + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit"); + ret = ESP_FAIL; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + break; + } + + if (!dev->connected) { + ESP_LOGE(TAG, "HID device not connected!"); + ret = ESP_FAIL; + break; + } + + p_rpt = get_report_by_idx_id_type(dev, index, id, ESP_HID_REPORT_TYPE_INPUT); + if (p_rpt == NULL) { + ESP_LOGE(TAG, "HID device not connected!"); + ret = ESP_FAIL; + break; + } + + if (length > p_rpt->value_len) { + ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len); + ret = ESP_FAIL; + break; + } + } while(0); + osi_mutex_unlock(&s_hidd_param.mutex); + + if (ret == ESP_OK) { + ret = esp_bt_hid_device_send_report(ESP_HIDD_REPORT_TYPE_INTRDATA, id, length, data); + } + return ret; +} + +static esp_err_t esp_bt_hidd_dev_feature_set(void *devp, size_t index, size_t id, uint8_t *data, size_t length) +{ + hidd_report_item_t *p_rpt; + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit"); + ret = ESP_FAIL; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + break; + } + + if (!dev->connected) { + ESP_LOGE(TAG, "HID device not connected!"); + ret = ESP_FAIL; + break; + } + + p_rpt = get_report_by_idx_id_type(dev, index, id, ESP_HID_REPORT_TYPE_FEATURE); + if (p_rpt == NULL) { + ESP_LOGE(TAG, "HID device not connected!"); + ret = ESP_FAIL; + break; + } + + if (length > p_rpt->value_len) { + ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len); + ret = ESP_FAIL; + break; + } + } while(0); + osi_mutex_unlock(&s_hidd_param.mutex); + + if (ret == ESP_OK) { + ret = esp_bt_hid_device_send_report(ESP_HID_REPORT_TYPE_FEATURE, id, length, data); + } + return ret; +} + +static esp_err_t esp_bt_hidd_dev_event_handler_register(void *devp, esp_event_handler_t callback, esp_hidd_event_t event) +{ + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit"); + ret = ESP_FAIL; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + break; + } + + ret = esp_event_handler_register_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback, dev->dev); + } while (0); + osi_mutex_unlock(&s_hidd_param.mutex); + return ret; +} + +static esp_err_t esp_bt_hidd_dev_event_handler_unregister(void *devp, esp_event_handler_t callback, esp_hidd_event_t event) +{ + esp_bt_hidd_dev_t *dev = (esp_bt_hidd_dev_t *)devp; + esp_err_t ret = ESP_OK; + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + do { + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit"); + ret = ESP_FAIL; + break; + } + + if (s_hidd_param.dev != dev) { + ESP_LOGE(TAG, "Wrong HID device provided"); + ret = ESP_FAIL; + break; + } + ret = esp_event_handler_unregister_with(dev->event_loop_handle, ESP_HIDD_EVENTS, event, callback); + } while (0); + osi_mutex_unlock(&s_hidd_param.mutex); + return ret; +} + +void bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) +{ + esp_hidd_event_data_t cb_param = {0}; + esp_hidd_event_data_t *p_cb_param = NULL; + size_t event_data_size = 0; + uint8_t map_index = 0; + hidd_report_item_t *p_rpt = NULL; + + if (!is_init()) { + ESP_LOGE(TAG, "HID device is uninit, event(%d)", event); + return; + } + + switch (event) { + case ESP_HIDD_INIT_EVT: { + if (param->init.status == ESP_HIDD_SUCCESS) { + ESP_LOGD(TAG, "Setting hid parameters in_qos:%d, out_qos:%d", s_hidd_param.in_qos.token_bucket_size, + s_hidd_param.out_qos.token_bucket_size); + esp_bt_hid_device_register_app(&s_hidd_param.app_param, &s_hidd_param.in_qos, &s_hidd_param.out_qos); + } else { + ESP_LOGE(TAG, "Init hidd failed (%d)!", param->init.status); + cb_param.start.status = bt_hidd_get_status(param->init.status); + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, &cb_param, + sizeof(esp_hidd_event_data_t), portMAX_DELAY); + + bt_hidd_dev_free(); + } + break; + } + case ESP_HIDD_DEINIT_EVT: { + cb_param.stop.status = bt_hidd_get_status(param->deinit.status); + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_STOP_EVENT, &cb_param, + sizeof(esp_hidd_event_data_t), portMAX_DELAY); + if (param->deinit.status == ESP_HIDD_SUCCESS) { + bt_hidd_dev_free(); + } else { + ESP_LOGE(TAG, "Deinit hidd failed (%d)!", param->deinit.status); + } + break; + } + case ESP_HIDD_REGISTER_APP_EVT: { + if (param->register_app.status == ESP_HIDD_SUCCESS) { + ESP_LOGD(TAG, "Setting hid parameters success!"); + if (param->register_app.in_use && param->register_app.bd_addr != NULL) { + ESP_LOGI(TAG, "Start virtual cable plug!"); + esp_bt_hid_device_connect(param->register_app.bd_addr); + } + + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + s_hidd_param.dev->registered = true; + osi_mutex_unlock(&s_hidd_param.mutex); + + cb_param.start.status = bt_hidd_get_status(param->init.status); + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_START_EVENT, &cb_param, + sizeof(esp_hidd_event_data_t), portMAX_DELAY); + } else { + ESP_LOGE(TAG, "Setting hid parameters failed (%d), now deint!", param->register_app.status); + esp_bt_hid_device_deinit(); + } + break; + } + case ESP_HIDD_UNREGISTER_APP_EVT: { + break; + } + case ESP_HIDD_OPEN_EVT: { + if (param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTING) { + break; + } + if (param->open.status == ESP_HIDD_SUCCESS && param->open.conn_status == ESP_HIDD_CONN_STATE_CONNECTED) { + ESP_LOGI(TAG, "Connected to %02x:%02x:%02x:%02x:%02x:%02x", param->open.bd_addr[0], param->open.bd_addr[1], + param->open.bd_addr[2], param->open.bd_addr[3], param->open.bd_addr[4], param->open.bd_addr[5]); + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + s_hidd_param.dev->connected = true; + memcpy(s_hidd_param.dev->remote_bda, param->open.bd_addr, ESP_BD_ADDR_LEN); + osi_mutex_unlock(&s_hidd_param.mutex); + } else { + ESP_LOGE(TAG, "Connect failed (%d)!", param->open.status); + } + cb_param.connect.status = bt_hidd_get_status(param->open.status); + cb_param.connect.dev = s_hidd_param.dev->dev; + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_CONNECT_EVENT, &cb_param, + sizeof(esp_hidd_event_data_t), portMAX_DELAY); + break; + } + case ESP_HIDD_CLOSE_EVT: { + if (param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTING) { + break; + } + if (param->close.status == ESP_HIDD_SUCCESS && param->close.conn_status == ESP_HIDD_CONN_STATE_DISCONNECTED) { + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + s_hidd_param.dev->connected = false; + memset(s_hidd_param.dev->remote_bda, 0, ESP_BD_ADDR_LEN); + osi_mutex_unlock(&s_hidd_param.mutex); + } else { + ESP_LOGE(TAG, "Disconnect failed (%d)!", param->close.status); + } + cb_param.disconnect.status = bt_hidd_get_status(param->close.status); + cb_param.disconnect.dev = s_hidd_param.dev->dev; + cb_param.disconnect.reason = param->close.status; + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_DISCONNECT_EVENT, &cb_param, + sizeof(esp_hidd_event_data_t), portMAX_DELAY); + break; + } + case ESP_HIDD_SEND_REPORT_EVT: + break; + case ESP_HIDD_REPORT_ERR_EVT: + break; + case ESP_HIDD_GET_REPORT_EVT: { + uint8_t *data_ptr = NULL; + p_rpt = get_report_by_id_and_type((esp_bt_hidd_dev_t *)s_hidd_param.dev->dev, param->get_report.report_id, + param->get_report.report_type, &map_index); + if (p_rpt == NULL) { + ESP_LOGE(TAG, "Can not find report!"); + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID); + break; + } + if (param->get_report.buffer_size > p_rpt->value_len) { + ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len); + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM); + break; + } + + event_data_size = sizeof(esp_hidd_event_data_t); + if (param->get_report.buffer_size) { + event_data_size += 2; + } + + if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) { + ESP_LOGE(TAG, "%s malloc event data failed!", __func__); + break; + } + memset(p_cb_param, 0, event_data_size); + p_cb_param->feature.dev = s_hidd_param.dev->dev; + p_cb_param->feature.trans_type = ESP_HID_TRANS_GET_REPORT; + p_cb_param->feature.report_type = param->get_report.report_type; + p_cb_param->feature.report_id = p_rpt->report_id; + p_cb_param->feature.usage = p_rpt->usage; + p_cb_param->feature.length = param->get_report.buffer_size ? 2 : 0; + p_cb_param->feature.data = ((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t); + p_cb_param->feature.map_index = map_index; + if (param->get_report.buffer_size) { + data_ptr = ((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t); + *data_ptr++ = (uint8_t)param->get_report.buffer_size; + *data_ptr++ = (uint8_t)(param->get_report.buffer_size >> 8); + } + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_FEATURE_EVENT, p_cb_param, + event_data_size, portMAX_DELAY); + break; + } + case ESP_HIDD_SET_REPORT_EVT: { + p_rpt = get_report_by_id_and_type((esp_bt_hidd_dev_t *)s_hidd_param.dev->dev, param->set_report.report_id, + param->set_report.report_type, &map_index); + if (p_rpt == NULL) { + ESP_LOGE(TAG, "Can not find report!"); + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_REP_ID); + break; + } + if (param->set_report.len > p_rpt->value_len) { + ESP_LOGE(TAG, "Data size over %d!", p_rpt->value_len); + esp_bt_hid_device_report_error(ESP_HID_PAR_HANDSHAKE_RSP_ERR_INVALID_PARAM); + break; + } + + event_data_size = sizeof(esp_hidd_event_data_t); + if (param->set_report.len && param->set_report.data) { + event_data_size += param->set_report.len; + } + + if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) { + ESP_LOGE(TAG, "%s malloc event data failed!", __func__); + break; + } + memset(p_cb_param, 0, event_data_size); + p_cb_param->feature.dev = s_hidd_param.dev->dev; + p_cb_param->feature.trans_type = ESP_HID_TRANS_SET_REPORT; + p_cb_param->feature.report_type = param->set_report.report_type; + p_cb_param->feature.report_id = p_rpt->report_id; + p_cb_param->feature.usage = p_rpt->usage; + p_cb_param->feature.length = param->set_report.len; + p_cb_param->feature.data = param->set_report.data; + p_cb_param->feature.map_index = map_index; + if (param->set_report.len && param->set_report.data) { + memcpy(((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t), param->set_report.data, + param->set_report.len); + } + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_FEATURE_EVENT, p_cb_param, + event_data_size, portMAX_DELAY); + break; + } + case ESP_HIDD_SET_PROTOCOL_EVT: { + if (param->set_protocol.protocol_mode != ESP_HIDD_UNSUPPORTED_MODE) { + if (s_hidd_param.dev->protocol_mode == param->set_protocol.protocol_mode) { + break; + } + osi_mutex_lock(&s_hidd_param.mutex, OSI_MUTEX_MAX_TIMEOUT); + s_hidd_param.dev->protocol_mode = param->set_protocol.protocol_mode; + osi_mutex_unlock(&s_hidd_param.mutex); + cb_param.protocol_mode.dev = s_hidd_param.dev->dev; + cb_param.protocol_mode.protocol_mode = s_hidd_param.dev->protocol_mode; + cb_param.protocol_mode.map_index = 0; + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_PROTOCOL_MODE_EVENT, + &cb_param, sizeof(esp_hidd_event_data_t), portMAX_DELAY); + } else { + ESP_LOGE(TAG, "Unsupported protocol mode!"); + break; + } + break; + } + case ESP_HIDD_INTR_DATA_EVT: { + p_rpt = get_report_by_id_and_type((esp_bt_hidd_dev_t *)s_hidd_param.dev->dev, param->intr_data.report_id, + ESP_HID_REPORT_TYPE_OUTPUT, &map_index); + if (p_rpt == NULL) { + ESP_LOGE(TAG, "Can not find report!"); + break; + } + + event_data_size = sizeof(esp_hidd_event_data_t); + if (param->intr_data.len && param->intr_data.data) { + event_data_size += param->intr_data.len; + } + + if ((p_cb_param = (esp_hidd_event_data_t *)malloc(event_data_size)) == NULL) { + ESP_LOGE(TAG, "%s malloc event data failed!", __func__); + break; + } + memset(p_cb_param, 0, event_data_size); + p_cb_param->output.dev = s_hidd_param.dev->dev; + p_cb_param->output.report_id = p_rpt->report_id; + p_cb_param->output.usage = p_rpt->usage; + p_cb_param->output.length = param->intr_data.len; + p_cb_param->output.data = param->intr_data.data; + p_cb_param->output.map_index = map_index; + if (param->intr_data.len && param->intr_data.data) { + memcpy(((uint8_t *)p_cb_param) + sizeof(esp_hidd_event_data_t), param->intr_data.data, + param->intr_data.len); + } + esp_event_post_to(s_hidd_param.dev->event_loop_handle, ESP_HIDD_EVENTS, ESP_HIDD_OUTPUT_EVENT, p_cb_param, + event_data_size, portMAX_DELAY); + break; + } + default: + break; + } + + if (p_cb_param) { + free(p_cb_param); + p_cb_param = NULL; + } +} + +esp_err_t esp_bt_hidd_dev_init(esp_hidd_dev_t *dev_p, const esp_hid_device_config_t *config, esp_event_handler_t callback) +{ + esp_err_t ret = ESP_OK; + if (dev_p == NULL || config == NULL) { + return ESP_ERR_INVALID_ARG; + } + + if (is_init()) { + ESP_LOGE(TAG, "HID device profile already initialized"); + return ESP_FAIL; + } + + osi_mutex_new(&s_hidd_param.mutex); + if (s_hidd_param.mutex == NULL) { + ESP_LOGE(TAG, "HID device mutex could not be allocated"); + return ESP_ERR_NO_MEM; + } + + s_hidd_param.dev = (esp_bt_hidd_dev_t *)calloc(1, sizeof(esp_bt_hidd_dev_t)); + if (s_hidd_param.dev == NULL) { + ESP_LOGE(TAG, "HID device could not be allocated"); + return ESP_ERR_NO_MEM; + } + + //[1] Reset the hid device target environment + s_hidd_param.dev->connected = false; + s_hidd_param.dev->registered = false; + s_hidd_param.dev->bat_level = 100; + s_hidd_param.dev->control = ESP_HID_CONTROL_EXIT_SUSPEND; + s_hidd_param.dev->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT; + s_hidd_param.dev->event_loop_handle = NULL; + s_hidd_param.dev->dev = dev_p; + + esp_event_loop_args_t event_task_args = { + .queue_size = 5, + .task_name = "bt_hidd_events", + .task_priority = uxTaskPriorityGet(NULL), + .task_stack_size = 2048, + .task_core_id = tskNO_AFFINITY + }; + ret = esp_event_loop_create(&event_task_args, &s_hidd_param.dev->event_loop_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "HID device event loop could not be created"); + bt_hidd_dev_free(); + return ret; + } + + //[2] parse hid descriptor + ret = bt_hidd_init_config(s_hidd_param.dev, config); + if (ret != ESP_OK) { + bt_hidd_dev_free(); + return ret; + } + + //[3] configure hidd app param and qos param + bt_hidd_init_app(); + bt_hidd_init_qos(); + + //[4] implement the interface + dev_p->dev = s_hidd_param.dev; + dev_p->connected = esp_bt_hidd_dev_connected; + dev_p->disconnect = esp_bt_hidd_dev_disconnect; + dev_p->deinit = esp_bt_hidd_dev_deinit; + dev_p->battery_set = esp_bt_hidd_dev_battery_set; + dev_p->input_set = esp_bt_hidd_dev_input_set; + dev_p->feature_set = esp_bt_hidd_dev_feature_set; + dev_p->event_handler_register = esp_bt_hidd_dev_event_handler_register; + dev_p->event_handler_unregister = esp_bt_hidd_dev_event_handler_unregister; + + ret = esp_bt_hidd_dev_event_handler_register(s_hidd_param.dev, esp_hidd_process_event_data_handler, ESP_EVENT_ANY_ID); + if (ret != ESP_OK) { + bt_hidd_dev_free(); + return ret; + } + + if (callback != NULL) { + ret = esp_bt_hidd_dev_event_handler_register(s_hidd_param.dev, callback, ESP_EVENT_ANY_ID); + if (ret != ESP_OK) { + bt_hidd_dev_free(); + return ret; + } + } + + ret = esp_bt_hid_device_register_callback(bt_hidd_cb); + ret |= esp_bt_hid_device_init(); + if (ret != ESP_OK) { + bt_hidd_dev_free(); + return ret; + } + + return ret; +} +#endif /* CONFIG_BT_HID_DEVICE_ENABLED */ diff --git a/components/esp_hid/src/bt_hidh.c b/components/esp_hid/src/bt_hidh.c index d30ecffbcb..ed0ab3bc3f 100644 --- a/components/esp_hid/src/bt_hidh.c +++ b/components/esp_hid/src/bt_hidh.c @@ -21,202 +21,711 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" - -#include "esp_bt_hh_api.h" +#include "osi/fixed_queue.h" +#include "string.h" +#include "esp_hidh_api.h" static const char *TAG = "BT_HIDH"; -static esp_event_loop_handle_t event_loop_handle; +// element of connection queue +typedef struct { + esp_hidh_dev_t* dev; +} conn_item_t; -static const char *s_bta_hh_evt_names[] = {"ENABLE", "DISABLE", "OPEN", "CLOSE", "GET_RPT", "SET_RPT", "GET_PROTO", "SET_PROTO", "GET_IDLE", "SET_IDLE", "GET_DSCP", "ADD_DEV", "RMV_DEV", "VC_UNPLUG", "DATA", "API_ERR", "UPDATE_SCPP"}; -static const char *s_bta_hh_status_names[] = {"OK", "HS_HID_NOT_READY", "HS_INVALID_RPT_ID", "HS_TRANS_NOT_SPT", "HS_INVALID_PARAM", "HS_ERROR", "ERR", "ERR_SDP", "ERR_PROTO", "ERR_DB_FULL", "ERR_TOD_UNSPT", "ERR_NO_RES", "ERR_AUTH_FAILED", "ERR_HDL", "ERR_SEC"}; +typedef struct { + fixed_queue_t *connection_queue; /* Queue of connection */ + esp_event_loop_handle_t event_loop_handle; +} hidh_local_param_t; -static inline void WAIT_DEV(esp_hidh_dev_t *dev) +static hidh_local_param_t hidh_local_param; +#define TRANS_TO 1000000 // us +#define is_init() (hidh_local_param.event_loop_handle != NULL) + +#define get_protocol_mode(mode) (mode) ? "BOOT" : "REPORT" +static const char *s_esp_hh_evt_names[] = {"INIT", "DEINIT", "OPEN", "CLOSE", "GET_RPT", "SET_RPT", "GET_PROTO", "SET_PROTO", "GET_IDLE", "SET_IDLE", "GET_DSCP", "ADD_DEV", "RMV_DEV", "VC_UNPLUG", "DATA", "DATA_IND", "SET_INFO"}; +static const char *s_esp_hh_status_names[] = {"OK", + "HS_HID_NOT_READY", + "HS_INVALID_RPT_ID", + "HS_TRANS_NOT_SPT", + "HS_INVALID_PARAM", + "HS_ERROR", + "ERR", + "ERR_SDP", + "ERR_PROTO", + "ERR_DB_FULL", + "ERR_TOD_UNSPT", + "ERR_NO_RES", + "ERR_AUTH_FAILED", + "ERR_HDL", + "ERR_SEC", + "BUSY", + "NO_DATA", + "NEED_INIT", + "NEED_DEINIT", + "NO_CONNECTION"}; + +static esp_hidh_dev_t *hidh_dev_ctor(esp_bd_addr_t bda); + +static char *get_trans_type_str(esp_hid_trans_type_t trans_type) { - xSemaphoreTake(dev->semaphore, portMAX_DELAY); + switch (trans_type) { + case ESP_HID_TRANS_HANDSHAKE: + return "TRANS_HANDSHAKE"; + case ESP_HID_TRANS_CONTROL: + return "TRANS_CONTROL"; + case ESP_HID_TRANS_GET_REPORT: + return "TRANS_GET_REPORT"; + case ESP_HID_TRANS_SET_REPORT: + return "TRANS_SET_REPORT"; + case ESP_HID_TRANS_GET_PROTOCOL: + return "TRANS_GET_PROTOCOL"; + case ESP_HID_TRANS_SET_PROTOCOL: + return "TRANS_SET_PROTOCOL"; + case ESP_HID_TRANS_GET_IDLE: + return "TRANS_GET_IDLE"; + case ESP_HID_TRANS_SET_IDLE: + return "TRANS_SET_IDLE"; + case ESP_HID_TRANS_DATA: + return "TRANS_DATA"; + case ESP_HID_TRANS_DATAC: + return "TRANS_DATAC"; + case ESP_HID_TRANS_MAX: + return "TRANS_MAX"; + default: + return "UNKOWN"; + } } -static inline void SEND_DEV(esp_hidh_dev_t *dev) +static esp_err_t bt_hidh_get_status(esp_hidh_status_t status) { - xSemaphoreGive(dev->semaphore); + esp_err_t ret = ESP_OK; + switch (status) { + case ESP_HIDH_OK: + ret = ESP_OK; + break; + case ESP_HIDH_ERR_NO_RES: + ret = ESP_ERR_NO_MEM; + break; + default: + ret = ESP_FAIL; + break; + } + return ret; } -static void bta_hh_cb(tBTA_HH_EVT event, tBTA_HH *p_data) +static void utl_freebuf(void **p) { - static esp_hidh_dev_t *descr_dev = NULL; + if (*p != NULL) { + free(*p); + *p = NULL; + } +} + +static void transaction_timeout_handler(void *arg) +{ + esp_hidh_dev_t *dev = (esp_hidh_dev_t *)arg; + if (dev != NULL && esp_hidh_dev_exists(dev)) { + ESP_LOGW(TAG, "transaction timeout!"); + esp_hidh_dev_lock(dev); + dev->trans_type = ESP_HID_TRANS_MAX; + dev->report_id = 0; + dev->report_type = 0; + esp_hidh_dev_unlock(dev); + } +} + +static inline void set_trans(esp_hidh_dev_t *dev, esp_hid_trans_type_t trans_type) +{ + dev->trans_type = trans_type; + if (dev->trans_timer == NULL) { + esp_timer_create_args_t config = { + .callback = &transaction_timeout_handler, + .arg = (void *)dev, + .name = "hid_trans" + }; + if (esp_timer_create(&config, &dev->trans_timer) != ESP_OK) { + ESP_LOGE(TAG, "create trans timer failed! trans:%s", get_trans_type_str(trans_type)); + return; + } + } + if (!esp_timer_is_active(dev->trans_timer) && esp_timer_start_once(dev->trans_timer, TRANS_TO) != ESP_OK) { + ESP_LOGE(TAG, "set trans timer failed! trans:%s", get_trans_type_str(trans_type)); + } +} + +static inline void reset_trans(esp_hidh_dev_t *dev) +{ + esp_hidh_dev_lock(dev); + dev->trans_type = ESP_HID_TRANS_MAX; + dev->report_id = 0; + dev->report_type = 0; + if (dev->trans_timer) { + esp_timer_stop(dev->trans_timer); + } + esp_hidh_dev_unlock(dev); +} + +static inline bool is_trans_done(esp_hidh_dev_t *dev) +{ + bool ret = (dev->trans_type == ESP_HID_TRANS_MAX); + return ret; +} + +static void free_local_param(void) +{ + if (hidh_local_param.event_loop_handle) { + esp_event_loop_delete(hidh_local_param.event_loop_handle); + } + + if (hidh_local_param.connection_queue) { + fixed_queue_free(hidh_local_param.connection_queue, free); + } +} + +static void open_failed_cb(esp_hidh_dev_t *dev, esp_hidh_status_t status, esp_hidh_event_data_t *p, + size_t event_data_size) +{ + p->open.status = bt_hidh_get_status(status); + p->open.dev = dev; + if (dev != NULL) { + esp_hidh_dev_lock(dev); + if (dev->connected) { + esp_bt_hid_host_disconnect(dev->bda); + } else { + dev->in_use = false; + } + esp_hidh_dev_unlock(dev); + } + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, p, event_data_size, + portMAX_DELAY); +} + +static void esp_hh_cb(esp_hidh_cb_event_t event, esp_hidh_cb_param_t *param) +{ + conn_item_t *conn_item = NULL; esp_hidh_dev_t *dev = NULL; + esp_hidh_dev_report_t *report = NULL; + bool has_report_id = false; + size_t data_len = 0; + uint8_t *p_data = NULL; + esp_hidh_event_data_t p = {0}; + esp_hidh_event_data_t *p_param = NULL; + size_t event_data_size = sizeof(esp_hidh_event_data_t); + switch (event) { - case BTA_HH_ENABLE_EVT: { - if (p_data->status) { - ESP_LOGE(TAG, "ENABLE ERROR: %s", s_bta_hh_status_names[p_data->status]); + case ESP_HIDH_INIT_EVT: { + p.start.status = bt_hidh_get_status(param->init.status); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_START_EVENT, &p, + event_data_size, portMAX_DELAY); + if (param->init.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "ENABLE ERROR: %s", s_esp_hh_status_names[param->init.status]); + free_local_param(); } - } break; - case BTA_HH_OPEN_EVT: { - dev = esp_hidh_dev_get_by_handle(p_data->conn.handle); - if (dev == NULL) { - ESP_LOGE(TAG, "OPEN ERROR: Device Not Found"); - return; - } - dev->status = p_data->conn.status; - memcpy(dev->bda, p_data->conn.bda, sizeof(esp_bd_addr_t)); - if (dev->status == BTA_HH_OK) { - descr_dev = dev; - BTA_HhGetDscpInfo(dev->bt.handle); + break; + } + case ESP_HIDH_DEINIT_EVT: { + p.stop.status = bt_hidh_get_status(param->deinit.status); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_STOP_EVENT, &p, + event_data_size, portMAX_DELAY); + if (param->deinit.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "DISABLE ERROR: %s", s_esp_hh_status_names[param->deinit.status]); } else { - ESP_LOGE(TAG, "OPEN ERROR: %s", s_bta_hh_status_names[dev->status]); - if (dev->opened) { - SEND_DEV(dev); - } else { - esp_hidh_dev_free(dev); - } + free_local_param(); + } + break; + } + case ESP_HIDH_OPEN_EVT: { + if (param->open.conn_status == ESP_HIDH_CONN_STATE_CONNECTING) { + // ignore this conn_status + break; } - } break; - case BTA_HH_GET_DSCP_EVT: { - ESP_LOGV(TAG, "DESCRIPTOR: PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x, REPORT_LEN: %u", p_data->dscp_info.product_id, p_data->dscp_info.vendor_id, p_data->dscp_info.version, p_data->dscp_info.descriptor.dl_len); - if (descr_dev == NULL) { - ESP_LOGE(TAG, "Device Not Found"); - return; - } - dev = descr_dev; - dev->config.product_id = p_data->dscp_info.product_id; - dev->config.vendor_id = p_data->dscp_info.vendor_id; - dev->config.version = p_data->dscp_info.version; - - - dev->config.report_maps_len = 1; - dev->config.report_maps = (esp_hid_raw_report_map_t *)malloc(dev->config.report_maps_len * sizeof(esp_hid_raw_report_map_t)); - if (dev->config.report_maps == NULL) { - ESP_LOGE(TAG, "malloc report maps failed"); - return; - } - - dev->config.report_maps[0].data = (uint8_t *)malloc(p_data->dscp_info.descriptor.dl_len); - if (dev->config.report_maps[0].data == NULL) { - ESP_LOGE(TAG, "Malloc Report Map Failed"); - dev->status = BTA_HH_ERR_NO_RES; - } else { - dev->config.report_maps[0].len = p_data->dscp_info.descriptor.dl_len; - memcpy((uint8_t *)dev->config.report_maps[0].data, p_data->dscp_info.descriptor.dsc_list, dev->config.report_maps[0].len); - //generate reports - - if (dev->config.report_maps[0].len && dev->config.report_maps[0].data) { - esp_hid_report_map_t *map; - esp_hidh_dev_report_t *report; - esp_hid_report_item_t *r; - map = esp_hid_parse_report_map(dev->config.report_maps[0].data, dev->config.report_maps[0].len); - if (map) { - if (dev->usage == 0) { - dev->usage = map->usage; - } - dev->connected = true; - dev->reports = NULL; - for (uint8_t i = 0; i < map->reports_len; i++) { - r = &map->reports[i]; - report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t)); - if (report == NULL) { - ESP_LOGE(TAG, "Malloc Report Failed"); - dev->status = BTA_HH_ERR_NO_RES; - dev->connected = false; + do { + dev = esp_hidh_dev_get_by_bda(param->open.bd_addr); + if (dev == NULL) { + if (param->open.is_orig) { + ESP_LOGE(TAG, "OPEN ERROR: Device Not Found"); + param->open.status = ESP_HIDH_NO_CONNECTION; + break; + } else { + ESP_LOGD(TAG, "incoming device connect"); + if (param->open.status == ESP_HIDH_OK) { + if ((dev = hidh_dev_ctor(param->open.bd_addr)) == NULL) { + ESP_LOGE(TAG, "%s create device failed!", __func__); + param->open.status = ESP_HIDH_ERR_NO_RES; break; } - report->map_index = 0; - report->protocol_mode = r->protocol_mode; - report->report_type = r->report_type; - report->report_id = r->report_id; - report->value_len = r->value_len; - report->usage = r->usage; - report->next = dev->reports; - dev->reports = report; + esp_hidh_dev_lock(dev); + dev->opened = false; // not opened by ourself + dev->is_orig = false; + esp_hidh_dev_unlock(dev); } - dev->reports_len = map->reports_len; - free(map->reports); - free(map); - map = NULL; - } else { - ESP_LOGE(TAG, "Parse Report Map Failed"); - dev->status = BTA_HH_ERR; } } + if (param->open.status != ESP_HIDH_OK) { + break; + } + esp_hidh_dev_lock(dev); + dev->connected = true; + dev->bt.handle = param->open.handle; + esp_hidh_dev_unlock(dev); + conn_item = malloc(sizeof(conn_item_t)); + if (conn_item == NULL) { + ESP_LOGE(TAG, "conn_item malloc failed!"); + param->open.status = ESP_HIDH_ERR_NO_RES; + break; + } + conn_item->dev = dev; + bool ret = fixed_queue_enqueue(hidh_local_param.connection_queue, conn_item, FIXED_QUEUE_MAX_TIMEOUT); + assert(ret == true); + } while (0); + + if (param->open.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "OPEN ERROR: %s", s_esp_hh_status_names[param->open.status]); + open_failed_cb(dev, param->open.status, &p, event_data_size); } - descr_dev = NULL; - if (dev->status == BTA_HH_OK) { - BTA_HhAddDev(dev->bda, dev->bt.attr_mask, dev->bt.sub_class, dev->bt.app_id, p_data->dscp_info); - } else { - ESP_LOGE(TAG, "Read Report Map Failed, status: %s", s_bta_hh_status_names[dev->status]); - if (dev->opened) { - SEND_DEV(dev); + + if (dev != NULL) { + esp_hidh_dev_lock(dev); + dev->status = param->open.status; + esp_hidh_dev_unlock(dev); + } + break; + } + case ESP_HIDH_GET_DSCP_EVT: { + do { + ESP_LOGV(TAG, "DESCRIPTOR: PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x, REPORT_LEN: %u", + param->dscp.product_id, param->dscp.vendor_id, param->dscp.version, param->dscp.dl_len); + if ((conn_item = (conn_item_t *)fixed_queue_dequeue(hidh_local_param.connection_queue, + FIXED_QUEUE_MAX_TIMEOUT)) == NULL) { + ESP_LOGE(TAG, "No pending connect device!"); + param->dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + dev = conn_item->dev; + utl_freebuf((void **)&conn_item); + // in case the dev has been freed + if (!esp_hidh_dev_exists(dev)) { + ESP_LOGE(TAG, "Device Not Found"); + dev = NULL; + param->dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + // check if connected + esp_hidh_dev_lock(dev); + if (!dev->connected) { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "Connection has been released!"); + param->dscp.status = ESP_HIDH_NO_CONNECTION; + break; + } + // check if get descriptor failed + if (param->dscp.status != ESP_HIDH_OK) { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "GET_DSCP ERROR: %s", s_esp_hh_status_names[param->dscp.status]); + break; + } + dev->added = param->dscp.added; + dev->config.product_id = param->dscp.product_id; + dev->config.vendor_id = param->dscp.vendor_id; + dev->config.version = param->dscp.version; + + dev->config.report_maps_len = 1; + dev->config.report_maps = + (esp_hid_raw_report_map_t *)malloc(dev->config.report_maps_len * sizeof(esp_hid_raw_report_map_t)); + if (dev->config.report_maps == NULL) { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "malloc report maps failed"); + param->dscp.status = ESP_HIDH_ERR_NO_RES; + break; + } + + dev->config.report_maps[0].data = (uint8_t *)malloc(param->dscp.dl_len); + if (dev->config.report_maps[0].data == NULL) { + ESP_LOGE(TAG, "Malloc Report Map Failed"); + param->dscp.status = ESP_HIDH_ERR_NO_RES; } else { - esp_hidh_dev_free(dev); + dev->config.report_maps[0].len = param->dscp.dl_len; + memcpy((uint8_t *)dev->config.report_maps[0].data, param->dscp.dsc_list, + dev->config.report_maps[0].len); + // generate reports + + if (dev->config.report_maps[0].len && dev->config.report_maps[0].data) { + esp_hid_report_map_t *map; + esp_hidh_dev_report_t *report; + esp_hid_report_item_t *r; + map = esp_hid_parse_report_map(dev->config.report_maps[0].data, dev->config.report_maps[0].len); + if (map) { + if (dev->usage == 0) { + dev->usage = map->usage; + } + dev->reports = NULL; + for (uint8_t i = 0; i < map->reports_len; i++) { + r = &map->reports[i]; + report = (esp_hidh_dev_report_t *)malloc(sizeof(esp_hidh_dev_report_t)); + if (report == NULL) { + ESP_LOGE(TAG, "Malloc Report Failed"); + param->dscp.status = ESP_HIDH_ERR_NO_RES; + break; + } + report->map_index = 0; + report->protocol_mode = r->protocol_mode; + report->report_type = r->report_type; + report->report_id = r->report_id; + report->value_len = r->value_len; + report->usage = r->usage; + report->next = dev->reports; + dev->reports = report; + } + dev->reports_len = map->reports_len; + free(map->reports); + free(map); + map = NULL; + } else { + ESP_LOGE(TAG, "Parse Report Map Failed"); + param->dscp.status = ESP_HIDH_ERR; + } + } + } + esp_hidh_dev_unlock(dev); + } while (0); + + if (param->dscp.status != ESP_HIDH_OK) { + open_failed_cb(dev, param->dscp.status, &p, event_data_size); + } + + if (dev != NULL) { + esp_hidh_dev_lock(dev); + dev->status = param->dscp.status; + // if has been added by lower layer, tell up layer + if (dev->status == ESP_HIDH_OK && dev->connected && dev->added) { + p.open.status = bt_hidh_get_status(ESP_HIDH_OK); + p.open.dev = dev; + esp_hidh_dev_unlock(dev); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, + event_data_size, portMAX_DELAY); + } else { + esp_hidh_dev_unlock(dev); } } - } break; - case BTA_HH_ADD_DEV_EVT: { - ESP_LOGV(TAG, "ADD_DEV: BDA: " ESP_BD_ADDR_STR ", handle: %d, status: %s", ESP_BD_ADDR_HEX(p_data->dev_info.bda), p_data->dev_info.handle, s_bta_hh_status_names[p_data->dev_info.status]); - dev = esp_hidh_dev_get_by_handle(p_data->conn.handle); - if (dev == NULL) { - ESP_LOGE(TAG, "Device Not Found"); - return; - } - dev->status = p_data->conn.status; - if (dev->status == BTA_HH_OK) { - esp_hidh_event_data_t p; - p.open.dev = dev; - esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); - } else { - ESP_LOGE(TAG, "Device Add Failed, status: %s", s_bta_hh_status_names[dev->status]); - } - if (dev->opened) { - SEND_DEV(dev); - } else if (dev->status != BTA_HH_OK) { - esp_hidh_dev_free(dev); - } - } break; - case BTA_HH_CLOSE_EVT: { - ESP_LOGV(TAG, "CLOSE: handle: %d, status: %s", p_data->dev_status.handle, s_bta_hh_status_names[p_data->dev_status.status]); - dev = esp_hidh_dev_get_by_handle(p_data->dev_status.handle); - if (dev == NULL) { - ESP_LOGE(TAG, "Device Not Found"); - return; - } - dev->status = p_data->dev_status.status; - esp_hidh_event_data_t p; - p.close.dev = dev; - p.close.reason = 0; - esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_CLOSE_EVENT, &p, sizeof(esp_hidh_event_data_t), portMAX_DELAY); - } break; - case BTA_HH_SET_RPT_EVT: { - dev = esp_hidh_dev_get_by_handle(p_data->dev_status.handle); - if (dev == NULL) { - ESP_LOGE(TAG, "SET_RPT ERROR: hDevice Not Found"); - return; - } - if (p_data->dev_status.status) { - ESP_LOGE(TAG, "SET_RPT ERROR: handle: %d, status: %s", p_data->dev_status.handle, s_bta_hh_status_names[p_data->dev_status.status]); - } - dev->status = p_data->dev_status.status; - SEND_DEV(dev); - } break; - case BTA_HH_GET_RPT_EVT: { - dev = esp_hidh_dev_get_by_handle(p_data->hs_data.handle); - if (dev == NULL) { - ESP_LOGE(TAG, "Device Not Found"); - return; - } - if (p_data->hs_data.status) { - ESP_LOGE(TAG, "GET_RPT ERROR: handle: %d, status: %s", p_data->hs_data.handle, s_bta_hh_status_names[p_data->hs_data.status]); - } - dev->status = p_data->hs_data.status; - BT_HDR *rpt = p_data->hs_data.rsp_data.p_rpt_data; - dev->tmp = rpt->data + rpt->offset; - dev->tmp_len = rpt->len; - SEND_DEV(dev); - } break; - default: - ESP_LOGV(TAG, "BTA_HH EVENT: %s", s_bta_hh_evt_names[event]); break; } + case ESP_HIDH_ADD_DEV_EVT: { + ESP_LOGV(TAG, "ADD_DEV: BDA: " ESP_BD_ADDR_STR ", handle: %d, status: %s", + ESP_BD_ADDR_HEX(param->add_dev.bd_addr), param->add_dev.handle, + s_esp_hh_status_names[param->add_dev.status]); + do { + dev = esp_hidh_dev_get_by_handle(param->add_dev.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "Device Not Found"); + param->add_dev.status = ESP_HIDH_NO_CONNECTION; + break; + } + esp_hidh_dev_lock(dev); + dev->added = param->add_dev.status == ESP_HIDH_OK ? true : false; + esp_hidh_dev_unlock(dev); + } while(0); + + if (param->add_dev.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "ADD_DEV ERROR: %s", s_esp_hh_status_names[param->add_dev.status]); + open_failed_cb(dev, param->add_dev.status, &p, event_data_size); + } + if (dev != NULL) { + esp_hidh_dev_lock(dev); + dev->status = param->add_dev.status; + if (dev->status == ESP_HIDH_OK && dev->connected && dev->added) { + p.open.status = bt_hidh_get_status(ESP_HIDH_OK); + p.open.dev = dev; + esp_hidh_dev_unlock(dev); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_OPEN_EVENT, &p, + event_data_size, portMAX_DELAY); + } else { + esp_hidh_dev_unlock(dev); + } + } + break; + } + case ESP_HIDH_CLOSE_EVT: { + if (param->close.conn_status == ESP_HIDH_CONN_STATE_DISCONNECTING) { + // ignore this conn_status + break; + } + ESP_LOGV(TAG, "CLOSE: handle: %d, status: %s", param->close.handle, s_esp_hh_status_names[param->close.status]); + do { + dev = esp_hidh_dev_get_by_handle(param->close.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "Device Not Found"); + param->close.status = ESP_HIDH_NO_CONNECTION; + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->close.status; + if (dev->connected) { + dev->connected = false; + } + // free the device in the wrapper event handler + dev->in_use = false; + esp_hidh_dev_unlock(dev); + } while(0); + + if (param->close.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "CLOSE ERROR: %s", s_esp_hh_status_names[param->close.status]); + } + p.close.dev = dev; + p.close.status = bt_hidh_get_status(param->close.status); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_CLOSE_EVENT, &p, + event_data_size, portMAX_DELAY); + + break; + } + case ESP_HIDH_SET_RPT_EVT: { + if (param->set_rpt.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "SET_RPT ERROR: handle: %d, status: %s", param->set_rpt.handle, + s_esp_hh_status_names[param->set_rpt.status]); + } + dev = esp_hidh_dev_get_by_handle(param->set_rpt.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "SET_RPT ERROR: Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->set_rpt.status; + p.feature.dev = dev; + esp_hidh_dev_unlock(dev); + p.feature.status = bt_hidh_get_status(param->set_rpt.status); + p.feature.trans_type = ESP_HID_TRANS_SET_REPORT; + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, &p, + event_data_size, portMAX_DELAY); + reset_trans(dev); + break; + } + case ESP_HIDH_GET_RPT_EVT: { + if (param->get_rpt.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "GET_RPT ERROR: handle: %d, status: %s", param->get_rpt.handle, + s_esp_hh_status_names[param->get_rpt.status]); + } else if (param->get_rpt.len > 0 && param->get_rpt.data) { + event_data_size += param->get_rpt.len; + } + dev = esp_hidh_dev_get_by_handle(param->get_rpt.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "GET_RPT ERROR: Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->get_rpt.status; + if ((p_param = (esp_hidh_event_data_t *)malloc(event_data_size)) != NULL) { + memset(p_param, 0, event_data_size); + p_param->feature.dev = dev; + p_param->feature.status = bt_hidh_get_status(param->get_rpt.status); + p_param->feature.trans_type = ESP_HID_TRANS_GET_REPORT; + if (param->get_rpt.status == ESP_HIDH_OK && param->get_rpt.len > 0 && param->get_rpt.data) { + if (dev->report_id) { + data_len = param->get_rpt.len - 1; + p_data = (uint8_t *)param->get_rpt.data + 1; + } else { + data_len = param->get_rpt.len; + p_data = (uint8_t *)param->get_rpt.data; + } + memcpy(((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t), p_data, data_len); + p_param->feature.length = data_len; + p_param->feature.data = p_data; + p_param->feature.report_id = dev->report_id; + esp_hidh_dev_unlock(dev); + } + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, p_param, + event_data_size, portMAX_DELAY); + } else { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "GET_RPT ERROR: malloc event data failed!"); + } + reset_trans(dev); + break; + } + case ESP_HIDH_GET_IDLE_EVT:{ + if (param->get_idle.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "GET_IDLE ERROR: handle: %d, status: %s", param->get_idle.handle, + s_esp_hh_status_names[param->get_idle.status]); + } else { + event_data_size += 1; + } + dev = esp_hidh_dev_get_by_handle(param->get_idle.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "GET_IDLE ERROR: Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->get_idle.status; + if ((p_param = (esp_hidh_event_data_t *)malloc(event_data_size)) != NULL) { + memset(p_param, 0, event_data_size); + p_param->feature.dev = dev; + p_param->feature.status = bt_hidh_get_status(param->get_idle.status); + p_param->feature.trans_type = ESP_HID_TRANS_GET_IDLE; + if (param->get_idle.status == ESP_HIDH_OK) { + *(((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t)) = param->get_idle.idle_rate; + p_param->feature.length = 1; + p_param->feature.data = ((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t); + } + esp_hidh_dev_unlock(dev); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, p_param, + event_data_size, portMAX_DELAY); + } else { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "GET_IDLE ERROR: malloc event data failed!"); + } + reset_trans(dev); + break; + } + case ESP_HIDH_SET_IDLE_EVT: { + if (param->set_idle.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "SET_IDLE ERROR: handle: %d, status: %s", param->set_idle.handle, + s_esp_hh_status_names[param->set_idle.status]); + } + dev = esp_hidh_dev_get_by_handle(param->set_idle.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "SET_IDLE ERROR: Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->set_idle.status; + p.feature.dev = dev; + esp_hidh_dev_unlock(dev); + p.feature.status = bt_hidh_get_status(param->set_idle.status); + p.feature.trans_type = ESP_HID_TRANS_SET_IDLE; + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, &p, + event_data_size, portMAX_DELAY); + reset_trans(dev); + break; + } + case ESP_HIDH_GET_PROTO_EVT: { + if (param->get_proto.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "GET_PROTO ERROR: handle: %d, status: %s", param->get_proto.handle, + s_esp_hh_status_names[param->get_proto.status]); + } else { + event_data_size += 1; + } + dev = esp_hidh_dev_get_by_handle(param->get_proto.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "GET_PROTO ERROR: Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->get_proto.status; + if ((p_param = (esp_hidh_event_data_t *)malloc(event_data_size)) != NULL) { + memset(p_param, 0, event_data_size); + p_param->feature.dev = dev; + p_param->feature.status = bt_hidh_get_status(param->get_proto.status); + p_param->feature.trans_type = ESP_HID_TRANS_GET_PROTOCOL; + if (param->get_proto.status == ESP_HIDH_OK) { + dev->protocol_mode = param->get_proto.proto_mode; // update the device protocol mode + *(((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t)) = param->get_proto.proto_mode; + p_param->feature.length = 1; + p_param->feature.data = ((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t); + } + esp_hidh_dev_unlock(dev); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, p_param, + event_data_size, portMAX_DELAY); + } else { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "GET_PROTO ERROR: malloc event data failed!"); + } + reset_trans(dev); + break; + } + case ESP_HIDH_SET_PROTO_EVT: { + if (param->set_proto.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "SET_PROTO ERROR: handle: %d, status: %s", param->set_proto.handle, + s_esp_hh_status_names[param->set_proto.status]); + } + dev = esp_hidh_dev_get_by_handle(param->set_proto.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "Device Not Found"); + break; + } + esp_hidh_dev_lock(dev); + dev->status = param->set_proto.status; + p.feature.dev = dev; + esp_hidh_dev_unlock(dev); + p.feature.status = bt_hidh_get_status(param->set_proto.status); + p.feature.trans_type = ESP_HID_TRANS_SET_PROTOCOL; + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, &p, + event_data_size, portMAX_DELAY); + reset_trans(dev); + break; + } + case ESP_HIDH_DATA_IND_EVT: { + if (param->data_ind.status != ESP_HIDH_OK) { + ESP_LOGE(TAG, "DATA_IND ERROR: handle: %d, status: %s", param->data_ind.handle, + s_esp_hh_status_names[param->data_ind.status]); + } + dev = esp_hidh_dev_get_by_handle(param->data_ind.handle); + if (dev == NULL) { + ESP_LOGE(TAG, "Device Not Found: handle %u", param->data_ind.handle); + break; + } + esp_hidh_dev_lock(dev); + if (param->data_ind.len > 0 && param->data_ind.data != NULL) { + event_data_size += param->data_ind.len; + if (param->data_ind.proto_mode == ESP_HID_PROTOCOL_MODE_BOOT) { + // first data is report id + if (param->data_ind.data[0]) { + report = esp_hidh_dev_get_input_report_by_len_and_proto(dev, param->data_ind.len, + ESP_HID_PROTOCOL_MODE_BOOT); + has_report_id = true; + } else { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "report_id=0 in boot mode!"); + break; + } + } else { + report = esp_hidh_dev_get_input_report_by_proto_and_data( + dev, ESP_HID_PROTOCOL_MODE_REPORT, param->data_ind.len, param->data_ind.data, &has_report_id); + } + if (report == NULL) { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "Not find report handle: %d mode: %s", param->data_ind.handle, + param->data_ind.proto_mode == ESP_HID_PROTOCOL_MODE_REPORT ? "REPORT" : "BOOT"); + break; + } + if ((p_param = (esp_hidh_event_data_t *)malloc(event_data_size)) == NULL) { + esp_hidh_dev_unlock(dev); + ESP_LOGE(TAG, "DATA_IND ERROR: malloc event data failed!"); + break; + } + memset(p_param, 0, event_data_size); + p_param->input.dev = dev; + p_param->input.usage = report->usage; + if (has_report_id) { + data_len = param->data_ind.len - 1; + p_data = (uint8_t *)param->data_ind.data + 1; + p_param->input.report_id = *(uint8_t *)param->data_ind.data; + } else { + data_len = param->data_ind.len; + p_data = (uint8_t *)param->data_ind.data; + p_param->input.report_id = report->report_id; + } + memcpy(((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t), p_data, data_len); + p_param->input.length = data_len; + p_param->input.data = p_data; + esp_hidh_dev_unlock(dev); + esp_event_post_to(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, p_param, + event_data_size, portMAX_DELAY); + break; + } + esp_hidh_dev_unlock(dev); + break; + } + case ESP_HIDH_DATA_EVT: + break; + default: + ESP_LOGV(TAG, "BTA_HH EVENT: %s", s_esp_hh_evt_names[event]); + break; + } + + if (p_param) { + free(p_param); + p_param = NULL; + } } /* @@ -225,91 +734,244 @@ static void bta_hh_cb(tBTA_HH_EVT event, tBTA_HH *p_data) static esp_err_t esp_bt_hidh_dev_close(esp_hidh_dev_t *dev) { - BTA_HhClose(dev->bt.handle); - return ESP_OK; + esp_err_t ret = ESP_OK; + do { + if (dev == NULL) { + ret = ESP_ERR_INVALID_ARG; + break; + } + if (!dev->connected) { + ESP_LOGW(TAG, "%s hdl:0x%02x not connected", __func__, dev->bt.handle); + ret = ESP_ERR_INVALID_STATE; + break; + } + ret = esp_bt_hid_host_disconnect(dev->bda); + } while (0); + return ret; } -static esp_err_t esp_bt_hidh_dev_report_write(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t len) +static esp_err_t esp_bt_hidh_dev_report_write(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, + int report_type, uint8_t *data, size_t len) { - esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type); - if (!report) { - ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id); - return ESP_FAIL; - } - if (len > report->value_len) { - ESP_LOGE(TAG, "%s report %d takes maximum %d bytes. you have provided %d", esp_hid_report_type_str(report_type), report_id, report->value_len, len); - return ESP_FAIL; - } + esp_err_t ret = ESP_OK; + uint8_t *p_data = NULL; + do { + esp_hidh_dev_report_t *report = + esp_hidh_dev_get_report_by_id_type_proto(dev, map_index, report_id, report_type, dev->protocol_mode); + if (!report) { + ESP_LOGE(TAG, "mode:%s report:%s id:%d not found", get_protocol_mode(dev->protocol_mode), + esp_hid_report_type_str(report_type), report_id); + ret = ESP_FAIL; + break; + } + if (len > report->value_len) { + ESP_LOGE(TAG, "%s report %d takes maximum %d bytes. you have provided %d", + esp_hid_report_type_str(report_type), report_id, report->value_len, len); + ret = ESP_FAIL; + break; + } -#define BT_HDR_HID_DATA_OFFSET 14 //this equals to L2CAP_MIN_OFFSET + 1 (1 byte to hold the HID transaction header) + if (report_type != ESP_HID_REPORT_TYPE_OUTPUT) { + ESP_LOGE(TAG, + "Only OUTPUT type data can be send on interrupt channel.\n" \ + "You have provided %s, try Set_Report!", + esp_hid_report_type_str(report_type)); + ret = ESP_FAIL; + break; + } - uint8_t *pbuf_data; - BT_HDR *p_buf = (BT_HDR *)malloc((uint16_t) (len + 1 + BT_HDR_HID_DATA_OFFSET + sizeof(BT_HDR))); - - if (p_buf == NULL) { - ESP_LOGE(TAG, "Could not allocate BT_HDR buffer"); - return ESP_ERR_NO_MEM; - } - - p_buf->len = len + 1; - p_buf->offset = BT_HDR_HID_DATA_OFFSET; - - pbuf_data = (uint8_t *) (p_buf + 1) + p_buf->offset; - pbuf_data[0] = report_id; - memcpy(pbuf_data + 1, data, len); - - if (report_type == ESP_HID_REPORT_TYPE_OUTPUT) { - p_buf->layer_specific = BTA_HH_RPTT_OUTPUT; - BTA_HhSendData(dev->bt.handle, dev->bda, p_buf); - } else { - BTA_HhSetReport(dev->bt.handle, report_type, p_buf); - WAIT_DEV(dev); - } - if (dev->status) { - ESP_LOGE(TAG, "Write %s: %s", esp_hid_report_type_str(report_type), s_bta_hh_status_names[dev->status]); - return ESP_FAIL; - } - return ESP_OK; + if (report_id) { + if ((p_data = malloc(len + 1)) == NULL) { + ESP_LOGE(TAG, "%s malloc failed!", __func__); + ret = ESP_FAIL; + break; + } + *p_data = report_id; + memcpy(p_data + 1, data, len); + data = p_data; + len = len + 1; + } + ret = esp_bt_hid_host_send_data(dev->bda, data, len); + } while (0); + return ret; } -static esp_err_t esp_bt_hidh_dev_report_read(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, size_t max_length, uint8_t *value, size_t *value_len) +static esp_err_t esp_bt_hidh_dev_set_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, + int report_type, uint8_t *data, size_t len) { - esp_hidh_dev_report_t *report = esp_hidh_dev_get_report_by_id_and_type(dev, map_index, report_id, report_type); - if (!report) { - ESP_LOGE(TAG, "%s report %d not found", esp_hid_report_type_str(report_type), report_id); - return ESP_FAIL; - } - BTA_HhGetReport(dev->bt.handle, report_type, report_id, max_length); - if (xSemaphoreTake(dev->semaphore, 500 / portTICK_PERIOD_MS) != pdTRUE) { - ESP_LOGE(TAG, "Read Timeout %s", esp_hid_report_type_str(report_type)); - return ESP_FAIL; - } - if (dev->status) { - ESP_LOGE(TAG, "Read %s: %s", esp_hid_report_type_str(report_type), s_bta_hh_status_names[dev->status]); - return ESP_FAIL; - } - if (report_id) { - dev->tmp++; - dev->tmp_len--; - } - if (dev->tmp_len > max_length) { - dev->tmp_len = max_length; - } - *value_len = dev->tmp_len; - memcpy(value, dev->tmp, dev->tmp_len); - return ESP_OK; + esp_err_t ret = ESP_OK; + uint8_t *p_data = NULL; + esp_hidh_dev_report_t *report = NULL; + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + report = esp_hidh_dev_get_report_by_id_type_proto(dev, map_index, report_id, report_type, dev->protocol_mode); + if (!report) { + ESP_LOGE(TAG, "mode:%s report:%s id:%d not found", get_protocol_mode(dev->protocol_mode), + esp_hid_report_type_str(report_type), report_id); + ret = ESP_FAIL; + break; + } + if (len > report->value_len) { + ESP_LOGE(TAG, "%s report %d takes maximum %d bytes. you have provided %d", + esp_hid_report_type_str(report_type), report_id, report->value_len, len); + ret = ESP_FAIL; + break; + } + + if (report_id) { + if ((p_data = malloc(len + 1)) == NULL) { + ESP_LOGE(TAG, "%s malloc failed!", __func__); + ret = ESP_FAIL; + break; + } + *p_data = report_id; + memcpy(p_data + 1, data, len); + data = p_data; + len = len + 1; + } + ret = esp_bt_hid_host_set_report(dev->bda, report_type, data, len); + if (ret == ESP_OK) { + set_trans(dev, ESP_HID_TRANS_SET_REPORT); + } + } while (0); + return ret; +} + +static esp_err_t esp_bt_hidh_dev_report_read(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, + size_t max_length, uint8_t *value, size_t *value_len) +{ + esp_err_t ret = ESP_OK; + esp_hidh_dev_report_t *report = NULL; + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + report = esp_hidh_dev_get_report_by_id_type_proto(dev, map_index, report_id, report_type, dev->protocol_mode); + if (!report) { + ESP_LOGE(TAG, "mode:%s report:%s id:%d not found", get_protocol_mode(dev->protocol_mode), + esp_hid_report_type_str(report_type), report_id); + ret = ESP_FAIL; + break; + } + ret = esp_bt_hid_host_get_report(dev->bda, report_type, report_id, max_length); + if (ret == ESP_OK) { + dev->trans_type = ESP_HID_TRANS_GET_REPORT; + dev->report_id = report_id; + dev->report_type = report_type; + } + } while (0); + return ret; +} + +static esp_err_t esp_bt_hidh_dev_get_idle(esp_hidh_dev_t *dev) +{ + esp_err_t ret = ESP_OK; + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + if (!dev->connected) { + ESP_LOGW(TAG, "%s hdl:0x%02x not connected", __func__, dev->bt.handle); + ret = ESP_ERR_INVALID_STATE; + break; + } + ret = esp_bt_hid_host_get_idle(dev->bda); + if (ret == ESP_OK) { + set_trans(dev, ESP_HID_TRANS_GET_IDLE); + } + } while(0); + + return ret; +} + +static esp_err_t esp_bt_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time) +{ + esp_err_t ret = ESP_OK; + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + if (!dev->connected) { + ESP_LOGW(TAG, "%s hdl:0x%02x not connected", __func__, dev->bt.handle); + ret = ESP_ERR_INVALID_STATE; + break; + } + ret = esp_bt_hid_host_set_idle(dev->bda, idle_time); + if (ret == ESP_OK) { + set_trans(dev, ESP_HID_TRANS_SET_IDLE); + } + } while(0); + + return ret; +} + +static esp_err_t esp_bt_hidh_dev_get_protocol(esp_hidh_dev_t *dev) +{ + esp_err_t ret = ESP_OK; + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + if (!dev->connected) { + ESP_LOGW(TAG, "%s hdl:0x%02x not connected", __func__, dev->bt.handle); + ret = ESP_ERR_INVALID_STATE; + break; + } + ret = esp_bt_hid_host_get_protocol(dev->bda); + if (ret == ESP_OK) { + set_trans(dev, ESP_HID_TRANS_GET_PROTOCOL); + } + } while(0); + + return ret; +} + +static esp_err_t esp_bt_hidh_dev_set_protocol(esp_hidh_dev_t *dev, uint8_t protocol_mode) +{ + esp_err_t ret = ESP_OK; + + do { + if (!is_trans_done(dev)) { + ESP_LOGE(TAG, "Pending previous tansaction %s done, try later!", get_trans_type_str(dev->trans_type)); + ret = ESP_FAIL; + break; + } + if (!dev->connected) { + ESP_LOGW(TAG, "%s hdl:0x%02x not connected", __func__, dev->bt.handle); + ret = ESP_ERR_INVALID_STATE; + break; + } + ret = esp_bt_hid_host_set_protocol(dev->bda, protocol_mode); + if (ret == ESP_OK) { + set_trans(dev, ESP_HID_TRANS_SET_PROTOCOL); + } + } while(0); + + return ret; } static void esp_bt_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp) { - fprintf(fp, "BDA:" ESP_BD_ADDR_STR ", Status: %s, Connected: %s, Handle: %d, Usage: %s\n", ESP_BD_ADDR_HEX(dev->bda), s_bta_hh_status_names[dev->status], dev->connected ? "YES" : "NO", dev->bt.handle, esp_hid_usage_str(dev->usage)); + fprintf(fp, "BDA:" ESP_BD_ADDR_STR ", Status: %s, Connected: %s, Handle: %d, Usage: %s\n", ESP_BD_ADDR_HEX(dev->bda), s_esp_hh_status_names[dev->status], dev->connected ? "YES" : "NO", dev->bt.handle, esp_hid_usage_str(dev->usage)); fprintf(fp, "Name: %s, Manufacturer: %s, Serial Number: %s\n", dev->config.device_name ? dev->config.device_name : "", dev->config.manufacturer_name ? dev->config.manufacturer_name : "", dev->config.serial_number ? dev->config.serial_number : ""); fprintf(fp, "PID: 0x%04x, VID: 0x%04x, VERSION: 0x%04x\n", dev->config.product_id, dev->config.vendor_id, dev->config.version); fprintf(fp, "Report Map Length: %d\n", dev->config.report_maps[0].len); esp_hidh_dev_report_t *report = dev->reports; while (report) { fprintf(fp, " %8s %7s %6s, ID: %3u, Length: %3u\n", - esp_hid_usage_str(report->usage), esp_hid_report_type_str(report->report_type), esp_hid_protocol_mode_str(report->protocol_mode), + esp_hid_usage_str(report->usage), esp_hid_report_type_str(report->report_type), get_protocol_mode(report->protocol_mode), report->report_id, report->value_len); report = report->next; } @@ -317,155 +979,108 @@ static void esp_bt_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp) esp_err_t esp_bt_hidh_init(const esp_hidh_config_t *config) { + esp_err_t ret = ESP_OK; if (config == NULL) { ESP_LOGE(TAG, "Config is NULL"); - return ESP_FAIL; + return ESP_ERR_INVALID_ARG; } esp_event_loop_args_t event_task_args = { .queue_size = 5, .task_name = "esp_bt_hidh_events", .task_priority = uxTaskPriorityGet(NULL), - .task_stack_size = config->event_stack_size > 0 ? config->event_stack_size : 2048, + .task_stack_size = config->event_stack_size > 0 ? config->event_stack_size : 4096, .task_core_id = tskNO_AFFINITY }; - esp_err_t ret = esp_event_loop_create(&event_task_args, &event_loop_handle); + + do { + if ((hidh_local_param.connection_queue = fixed_queue_new(QUEUE_SIZE_MAX)) == NULL) { + ESP_LOGE(TAG, "connection_queue create failed!"); + ret = ESP_FAIL; + break; + } + ret = esp_event_loop_create(&event_task_args, &hidh_local_param.event_loop_handle); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "esp_event_loop_create failed!"); + ret = ESP_FAIL; + break; + } + ret = esp_event_handler_register_with(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, + esp_hidh_process_event_data_handler, NULL); + ret |= esp_event_handler_register_with(hidh_local_param.event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, + config->callback, config->callback_arg); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "event_loop register failed!"); + ret = ESP_FAIL; + break; + } + ret = esp_bt_hid_host_register_callback(esp_hh_cb); + ret |= esp_bt_hid_host_init(); + } while (0); + if (ret != ESP_OK) { - ESP_LOGE(TAG, "esp_event_loop_create failed!"); - return ret; + free_local_param(); } - esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, - esp_hidh_process_event_data_handler, NULL); - esp_event_handler_register_with(event_loop_handle, ESP_HIDH_EVENTS, ESP_EVENT_ANY_ID, config->callback, NULL); - BTA_HhEnable(0, bta_hh_cb); - return ESP_OK; + return ret; } esp_err_t esp_bt_hidh_deinit(void) { - if (event_loop_handle) { - esp_event_loop_delete(event_loop_handle); + esp_err_t ret = esp_bt_hid_host_deinit(); + return ret; +} + +static esp_hidh_dev_t *hidh_dev_ctor(esp_bd_addr_t bda) +{ + esp_hidh_dev_t *dev = NULL; + dev = esp_hidh_dev_malloc(); + if (dev == NULL) { + return NULL; } - BTA_HhDisable(); - return ESP_OK; + dev->in_use = true; + dev->transport = ESP_HID_TRANSPORT_BT; + dev->trans_type = ESP_HID_TRANS_MAX; + dev->trans_timer = NULL; + dev->protocol_mode = ESP_HID_PROTOCOL_MODE_REPORT; // device default protocol mode + dev->connected = false; + dev->opened = true; + dev->added = false; + dev->is_orig = true; + dev->reports = NULL; + dev->reports_len = 0; + dev->tmp = NULL; + dev->tmp_len = 0; + memcpy(dev->bda, bda, sizeof(esp_bd_addr_t)); + dev->bt.handle = 0xff; + + dev->close = esp_bt_hidh_dev_close; + dev->report_write = esp_bt_hidh_dev_report_write; + dev->report_read = esp_bt_hidh_dev_report_read; + dev->set_report = esp_bt_hidh_dev_set_report; + dev->get_idle = esp_bt_hidh_dev_get_idle; + dev->set_idle = esp_bt_hidh_dev_set_idle; + dev->get_protocol = esp_bt_hidh_dev_get_protocol; + dev->set_protocol = esp_bt_hidh_dev_set_protocol; + dev->dump = esp_bt_hidh_dev_dump; + + return dev; } esp_hidh_dev_t *esp_bt_hidh_dev_open(esp_bd_addr_t bda) { - esp_hidh_dev_t *dev = esp_hidh_dev_malloc(); + esp_hidh_dev_t *dev = esp_hidh_dev_get_by_bda(bda); if (dev == NULL) { - ESP_LOGE(TAG, "malloc esp_hidh_dev_t failed"); - return NULL; + if ((dev = hidh_dev_ctor(bda)) == NULL) { + ESP_LOGE(TAG, "%s create device failed!", __func__); + return NULL; + } + } else { + ESP_LOGW(TAG, "device has opened, connected: %d", dev->connected); } - dev->transport = ESP_HID_TRANSPORT_BT; - memcpy(dev->bda, bda, sizeof(esp_bd_addr_t)); - dev->bt.handle = -1; - - dev->opened = true; - BTA_HhOpen(dev->bda, 0, BTA_HH_PROTO_RPT_MODE, (BTA_SEC_AUTHENTICATE | BTA_SEC_ENCRYPT)); - WAIT_DEV(dev); - if (dev->status != BTA_HH_OK) { - esp_hidh_dev_free(dev); - return NULL; + if (!dev->connected) { + esp_bt_hid_host_connect(dev->bda); } - dev->close = esp_bt_hidh_dev_close; - dev->report_write = esp_bt_hidh_dev_report_write; - dev->report_read = esp_bt_hidh_dev_report_read; - dev->dump = esp_bt_hidh_dev_dump; return dev; } -/* - * BlueDroid BT HIDH Stack Callbacks - * */ - -/* This callback function is executed by BTA_HH when data is received on an interrupt channel. */ -void bta_hh_co_data(uint8_t handle, uint8_t *p_rpt, uint16_t len, tBTA_HH_PROTO_MODE mode, uint8_t sub_class, uint8_t country_code, esp_bd_addr_t bda, uint8_t app_id) -{ - if (len < 2) { - ESP_LOGE(TAG, "Not Enough Data"); - return; - } - esp_hidh_dev_t *dev = NULL; - esp_hidh_dev_report_t *report = NULL; - dev = esp_hidh_dev_get_by_handle(handle); - if (dev == NULL) { - ESP_LOGE(TAG, "Device Not Found: handle %u", handle); - return; - } - report = esp_hidh_dev_get_input_report_by_id_and_proto(dev, p_rpt[0], mode ? ESP_HID_PROTOCOL_MODE_BOOT : ESP_HID_PROTOCOL_MODE_REPORT); - if (report == NULL) { - ESP_LOGE(TAG, "Report Not Found: %d mode: %s", p_rpt[0], mode ? "BOOT" : "REPORT"); - return; - } - if (len != (report->value_len + 1)) { - ESP_LOGW(TAG, "Wrong Data Len: %u != %u", len, (report->value_len + 1)); - } - - if (event_loop_handle) { - esp_hidh_event_data_t *p_param = NULL; - size_t event_data_size = sizeof(esp_hidh_event_data_t); - - if (len > 1 && p_rpt) { - event_data_size += (len - 1); - } - - if ((p_param = (esp_hidh_event_data_t *)malloc(event_data_size)) == NULL) { - ESP_LOGE(TAG, "%s malloc event data failed!", __func__); - return; - } - memset(p_param, 0, event_data_size); - if (len > 1 && p_rpt) { - memcpy(((uint8_t *)p_param) + sizeof(esp_hidh_event_data_t), p_rpt + 1, len - 1); - } - if (report->report_type == ESP_HID_REPORT_TYPE_FEATURE) { - p_param->feature.dev = dev; - p_param->feature.report_id = report->report_id; - p_param->feature.usage = report->usage; - p_param->feature.data = p_rpt + 1; - p_param->feature.length = len - 1; - esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_FEATURE_EVENT, p_param, event_data_size, portMAX_DELAY); - } else { - p_param->input.dev = dev; - p_param->input.report_id = report->report_id; - p_param->input.usage = report->usage; - p_param->input.data = p_rpt + 1; - p_param->input.length = len - 1; - esp_event_post_to(event_loop_handle, ESP_HIDH_EVENTS, ESP_HIDH_INPUT_EVENT, p_param, event_data_size, portMAX_DELAY); - } - - if (p_param) { - free(p_param); - p_param = NULL; - } - } -} - -/* This callback function is executed by BTA_HH when connection is opened, and application may do some device specific initialization. */ -void bta_hh_co_open(uint8_t handle, uint8_t sub_class, uint16_t attr_mask, uint8_t app_id) -{ - esp_hidh_dev_t *dev = NULL; - dev = esp_hidh_dev_get_by_handle(-1); - if (dev == NULL) { - ESP_LOGI(TAG, "Device Not Found? It's probably a reconnect."); - dev = esp_hidh_dev_malloc(); - if (dev == NULL) { - ESP_LOGE(TAG, "DEV Malloc Failed"); - return; - } - dev->transport = ESP_HID_TRANSPORT_BT; - dev->close = esp_bt_hidh_dev_close; - dev->report_write = esp_bt_hidh_dev_report_write; - dev->report_read = esp_bt_hidh_dev_report_read; - dev->dump = esp_bt_hidh_dev_dump; - } - dev->bt.attr_mask = attr_mask; - dev->bt.app_id = app_id; - dev->bt.sub_class = sub_class; - dev->bt.handle = handle; -} - -/* This callback function is executed by BTA_HH when connection is closed, and device specific finalization may be needed. */ -void bta_hh_co_close(uint8_t dev_handle, uint8_t app_id) {} - #endif /* CONFIG_BT_HID_HOST_ENABLED */ diff --git a/components/esp_hid/src/esp_hid_common.c b/components/esp_hid/src/esp_hid_common.c index 9d1f4167f4..e6d0cd8544 100644 --- a/components/esp_hid/src/esp_hid_common.c +++ b/components/esp_hid/src/esp_hid_common.c @@ -103,7 +103,8 @@ static int handle_report(hid_report_params_t *report, bool first) } item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; - item.value_len = 8; + item.report_id = 0x01; + item.value_len = 9; if (add_report(map, &item) != 0) { return -1; } @@ -142,6 +143,7 @@ static int handle_report(hid_report_params_t *report, bool first) } item.protocol_mode = ESP_HID_PROTOCOL_MODE_BOOT; + item.report_id = 0x02; item.value_len = 4; if (add_report(map, &item) != 0) { return -1; diff --git a/components/esp_hid/src/esp_hidd.c b/components/esp_hid/src/esp_hidd.c index 972c486407..56b9c23a5a 100644 --- a/components/esp_hid/src/esp_hidd.c +++ b/components/esp_hid/src/esp_hidd.c @@ -20,11 +20,14 @@ #include "ble_hidd.h" #endif /* CONFIG_GATTS_ENABLE */ +#if CONFIG_BT_HID_DEVICE_ENABLED +#include "bt_hidd.h" +#endif /* CONFIG_BT_HID_DEVICE_ENABLED */ + ESP_EVENT_DEFINE_BASE(ESP_HIDD_EVENTS); esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_transport_t transport, esp_event_handler_t callback, esp_hidd_dev_t **dev_out) { - esp_err_t ret = ESP_OK; esp_hidd_dev_t *dev = (esp_hidd_dev_t *)calloc(1, sizeof(esp_hidd_dev_t)); if (dev == NULL) { @@ -37,6 +40,11 @@ esp_err_t esp_hidd_dev_init(const esp_hid_device_config_t *config, esp_hid_trans ret = esp_ble_hidd_dev_init(dev, config, callback); break; #endif /* CONFIG_GATTS_ENABLE */ +#if CONFIG_BT_HID_DEVICE_ENABLED + case ESP_HID_TRANSPORT_BT: + ret = esp_bt_hidd_dev_init(dev, config, callback); + break; +#endif /* CONFIG_BT_HID_DEVICE_ENABLED */ default: ret = ESP_FAIL; break; diff --git a/components/esp_hid/src/esp_hidh.c b/components/esp_hid/src/esp_hidh.c index 74b3b95619..484092c885 100644 --- a/components/esp_hid/src/esp_hidh.c +++ b/components/esp_hid/src/esp_hidh.c @@ -21,13 +21,16 @@ #include "esp_event_base.h" ESP_EVENT_DEFINE_BASE(ESP_HIDH_EVENTS); +#define ESP_HIDH_DELAY_FREE_TO 100000 // us static const char *TAG = "ESP_HIDH"; static esp_hidh_dev_head_t s_esp_hidh_devices; - +static esp_timer_handle_t s_esp_hidh_timer; static xSemaphoreHandle s_esp_hidh_devices_semaphore = NULL; +static void esp_hidh_dev_delay_free(void *arg); + static inline void lock_devices(void) { if (s_esp_hidh_devices_semaphore != NULL) { @@ -42,7 +45,12 @@ static inline void unlock_devices(void) } } -static bool esp_hidh_dev_exists(esp_hidh_dev_t *dev) + +/* + * Public Functions + * */ + +bool esp_hidh_dev_exists(esp_hidh_dev_t *dev) { if (dev == NULL) { return false; @@ -59,10 +67,6 @@ static bool esp_hidh_dev_exists(esp_hidh_dev_t *dev) return false; } -/* - * Public Functions - * */ - esp_err_t esp_hidh_init(const esp_hidh_config_t *config) { esp_err_t err = ESP_FAIL; @@ -76,12 +80,25 @@ esp_err_t esp_hidh_init(const esp_hidh_config_t *config) return err; } - s_esp_hidh_devices_semaphore = xSemaphoreCreateBinary(); + TAILQ_INIT(&s_esp_hidh_devices); + + esp_timer_create_args_t timer_config = { + .callback = &esp_hidh_dev_delay_free, + .arg = NULL, + .name = "hidh_timer" + }; + + if ((err = esp_timer_create(&timer_config, &s_esp_hidh_timer)) != ESP_OK) { + ESP_LOGE(TAG, "%s create timer failed!", __func__); + return err; + } + + s_esp_hidh_devices_semaphore = xSemaphoreCreateMutex(); if (s_esp_hidh_devices_semaphore == NULL) { ESP_LOGE(TAG, "xSemaphoreCreateMutex failed!"); return err; } - + // unlock_devices(); err = ESP_OK; #if CONFIG_BT_HID_HOST_ENABLED @@ -96,12 +113,11 @@ esp_err_t esp_hidh_init(const esp_hidh_config_t *config) } #endif /* CONFIG_GATTC_ENABLE */ - if (err == ESP_OK) { - TAILQ_INIT(&s_esp_hidh_devices); - unlock_devices(); - } else { + if (err != ESP_OK) { vSemaphoreDelete(s_esp_hidh_devices_semaphore); s_esp_hidh_devices_semaphore = NULL; + esp_timer_delete(s_esp_hidh_timer); + s_esp_hidh_timer = NULL; } return err; @@ -115,6 +131,11 @@ esp_err_t esp_hidh_deinit(void) return err; } + if (esp_timer_is_active(s_esp_hidh_timer)) { + ESP_LOGE(TAG, "Busy, try again later!"); + return ESP_ERR_NOT_FINISHED; + } + if (!TAILQ_EMPTY(&s_esp_hidh_devices)) { ESP_LOGE(TAG, "Please disconnect all devices first!"); return err; @@ -138,6 +159,8 @@ esp_err_t esp_hidh_deinit(void) TAILQ_INIT(&s_esp_hidh_devices); vSemaphoreDelete(s_esp_hidh_devices_semaphore); s_esp_hidh_devices_semaphore = NULL; + esp_timer_delete(s_esp_hidh_timer); + s_esp_hidh_timer = NULL; } return err; } @@ -166,156 +189,317 @@ esp_hidh_dev_t *esp_hidh_dev_open(esp_bd_addr_t bda, esp_hid_transport_t transpo esp_err_t esp_hidh_dev_close(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_FAIL; + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->close(dev); + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; } - return dev->close(dev); + return ret; } void esp_hidh_dev_dump(esp_hidh_dev_t *dev, FILE *fp) { - if (!esp_hidh_dev_exists(dev)) { - return; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + dev->dump(dev, fp); + esp_hidh_dev_unlock(dev); } - dev->dump(dev, fp); } esp_err_t esp_hidh_dev_output_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_FAIL; + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_OUTPUT, value, value_len); + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; } - return dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_OUTPUT, value, value_len); + return ret; } esp_err_t esp_hidh_dev_feature_set(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, uint8_t *value, size_t value_len) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_FAIL; + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, value, value_len); + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; } - return dev->report_write(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, value, value_len); + return ret; } esp_err_t esp_hidh_dev_feature_get(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, size_t max_length, uint8_t *value, size_t *value_len) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_FAIL; + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->report_read(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, max_length, value, value_len); + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; } - return dev->report_read(dev, map_index, report_id, ESP_HID_REPORT_TYPE_FEATURE, max_length, value, value_len); + return ret; +} + +esp_err_t esp_hidh_dev_set_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t *data, size_t length) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + if (dev->set_report) { + ret = dev->set_report(dev, map_index, report_id, report_type, data, length); + } else { + ret = ESP_ERR_NOT_SUPPORTED; + } + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; +} + +esp_err_t esp_hidh_dev_get_report(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, + size_t max_len) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->report_read(dev, map_index, report_id, report_type, max_len, NULL, NULL); + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; +} + +esp_err_t esp_hidh_dev_get_idle(esp_hidh_dev_t *dev) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + if (dev->get_idle) { + ret = dev->get_idle(dev); + } else { + ret = ESP_ERR_NOT_SUPPORTED; + } + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; +} + +esp_err_t esp_hidh_dev_set_idle(esp_hidh_dev_t *dev, uint8_t idle_time) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + if (dev->set_idle) { + ret = dev->set_idle(dev, idle_time); + } else { + ret = ESP_ERR_NOT_SUPPORTED; + } + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; +} + +esp_err_t esp_hidh_dev_get_protocol(esp_hidh_dev_t *dev) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + if (dev->get_protocol) { + ret = dev->get_protocol(dev); + } else { + ret = ESP_ERR_NOT_SUPPORTED; + } + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; +} + +esp_err_t esp_hidh_dev_set_protocol(esp_hidh_dev_t *dev, uint8_t protocol_mode) +{ + esp_err_t ret = ESP_OK; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + if (dev->set_protocol) { + ret = dev->set_protocol(dev, protocol_mode); + } else { + ret = ESP_ERR_NOT_SUPPORTED; + } + esp_hidh_dev_unlock(dev); + } else { + ret = ESP_FAIL; + } + return ret; } const uint8_t *esp_hidh_dev_bda_get(esp_hidh_dev_t *dev) { + uint8_t *ret = NULL; #if CONFIG_BLUEDROID_ENABLED if (esp_hidh_dev_exists(dev)) { - return dev->bda; + esp_hidh_dev_lock(dev); + ret = dev->bda; + esp_hidh_dev_unlock(dev); } #endif /* CONFIG_BLUEDROID_ENABLED */ - return NULL; + return ret; } esp_hid_transport_t esp_hidh_dev_transport_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_HID_TRANSPORT_MAX; + esp_hid_transport_t ret = ESP_HID_TRANSPORT_MAX; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->transport; + esp_hidh_dev_unlock(dev); } - return dev->transport; + return ret; } const esp_hid_device_config_t *esp_hidh_dev_config_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return NULL; + esp_hid_device_config_t *ret = NULL; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = &dev->config; + esp_hidh_dev_unlock(dev); } - return &dev->config; + return ret; } const char *esp_hidh_dev_name_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return NULL; + const char * ret = NULL; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->config.device_name ? dev->config.device_name : ""; + esp_hidh_dev_unlock(dev); } - return dev->config.device_name ? dev->config.device_name : ""; + return ret; } const char *esp_hidh_dev_manufacturer_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return NULL; + const char *ret = NULL; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->config.manufacturer_name ? dev->config.manufacturer_name : ""; + esp_hidh_dev_unlock(dev); } - return dev->config.manufacturer_name ? dev->config.manufacturer_name : ""; + return ret; } const char *esp_hidh_dev_serial_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return NULL; + const char *ret = NULL; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->config.serial_number ? dev->config.serial_number : ""; + esp_hidh_dev_unlock(dev); } - return dev->config.serial_number ? dev->config.serial_number : ""; + return ret; } uint16_t esp_hidh_dev_vendor_id_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return 0; + uint16_t ret = 0; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->config.vendor_id; + esp_hidh_dev_unlock(dev); } - return dev->config.vendor_id; + return ret; } uint16_t esp_hidh_dev_product_id_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return 0; + uint16_t ret = 0; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->config.product_id; + esp_hidh_dev_unlock(dev); } - return dev->config.product_id; + return ret; } uint16_t esp_hidh_dev_version_get(esp_hidh_dev_t *dev) { + uint16_t ret = 0; if (!esp_hidh_dev_exists(dev)) { - return 0; + esp_hidh_dev_lock(dev); + ret = dev->config.version; + esp_hidh_dev_unlock(dev); } - return dev->config.version; + return ret; } esp_hid_usage_t esp_hidh_dev_usage_get(esp_hidh_dev_t *dev) { - if (!esp_hidh_dev_exists(dev)) { - return ESP_HID_USAGE_GENERIC; + esp_hid_usage_t ret = ESP_HID_USAGE_GENERIC; + if (esp_hidh_dev_exists(dev)) { + esp_hidh_dev_lock(dev); + ret = dev->usage; + esp_hidh_dev_unlock(dev); } - return dev->usage; + return ret; } esp_err_t esp_hidh_dev_reports_get(esp_hidh_dev_t *dev, size_t *num_reports, esp_hid_report_item_t **reports) { + esp_err_t ret = 0; + esp_hid_report_item_t *r = NULL; + if (!esp_hidh_dev_exists(dev)) { return ESP_FAIL; } - esp_hid_report_item_t *r = (esp_hid_report_item_t *)malloc(sizeof(esp_hid_report_item_t) * dev->reports_len); - if (r == NULL) { - return ESP_FAIL; - } - - esp_hidh_dev_report_t *dr = dev->reports; - for (uint8_t i = 0; i < dev->reports_len; i++) { - if (dr == NULL) { - //error - free(r); - return ESP_FAIL; + esp_hidh_dev_lock(dev); + do { + r = (esp_hid_report_item_t *)malloc(sizeof(esp_hid_report_item_t) * dev->reports_len); + if (r == NULL) { + ret = ESP_FAIL; + break; } - r[i].map_index = dr->map_index; - r[i].protocol_mode = dr->protocol_mode; - r[i].usage = dr->usage; - r[i].report_id = dr->report_id; - r[i].report_type = dr->report_type; - r[i].value_len = dr->value_len; - dr = dr->next; - } - *reports = r; - *num_reports = dev->reports_len; - return ESP_OK; + esp_hidh_dev_report_t *dr = dev->reports; + for (uint8_t i = 0; i < dev->reports_len; i++) { + if (dr == NULL) { + // error + free(r); + ret = ESP_FAIL; + goto error_; + } + r[i].map_index = dr->map_index; + r[i].protocol_mode = dr->protocol_mode; + r[i].usage = dr->usage; + r[i].report_id = dr->report_id; + r[i].report_type = dr->report_type; + r[i].value_len = dr->value_len; + + dr = dr->next; + } + *reports = r; + *num_reports = dev->reports_len; + } while (0); +error_:; + esp_hidh_dev_unlock(dev); + + return ret; } esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, esp_hid_raw_report_map_t **maps) @@ -323,8 +507,10 @@ esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, es if (!esp_hidh_dev_exists(dev)) { return ESP_FAIL; } + esp_hidh_dev_lock(dev); *num_maps = dev->config.report_maps_len; *maps = dev->config.report_maps; + esp_hidh_dev_unlock(dev); return ESP_OK; } @@ -333,6 +519,37 @@ esp_err_t esp_hidh_dev_report_maps_get(esp_hidh_dev_t *dev, size_t *num_maps, es * Private Functions * */ +/** + * `lock_devices()` only protect the devices list, this mutex protect the single deivce instance. + */ +inline void esp_hidh_dev_lock(esp_hidh_dev_t *dev) +{ + if (dev && dev->mutex != NULL) { + xSemaphoreTake(dev->mutex, portMAX_DELAY); + } +} + +inline void esp_hidh_dev_unlock(esp_hidh_dev_t *dev) +{ + if (dev && dev->mutex != NULL) { + xSemaphoreGive(dev->mutex); + } +} + +inline void esp_hidh_dev_wait(esp_hidh_dev_t *dev) +{ + if (dev && dev->semaphore != NULL) { + xSemaphoreTake(dev->semaphore, portMAX_DELAY); + } +} + +inline void esp_hidh_dev_send(esp_hidh_dev_t *dev) +{ + if (dev && dev->semaphore != NULL) { + xSemaphoreGive(dev->semaphore); + } +} + esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_handle(esp_hidh_dev_t *dev, uint16_t handle) { esp_hidh_dev_report_t *r = dev->reports; @@ -345,6 +562,19 @@ esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_handle(esp_hidh_dev_t *dev, ui return NULL; } +esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_type_proto(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type, uint8_t protocol_mode) +{ + esp_hidh_dev_report_t *r = dev->reports; + while (r) { + if (r->map_index == map_index && r->report_type == report_type && r->report_id == report_id && + r->protocol_mode == protocol_mode) { + return r; + } + r = r->next; + } + return NULL; +} + esp_hidh_dev_report_t *esp_hidh_dev_get_report_by_id_and_type(esp_hidh_dev_t *dev, size_t map_index, size_t report_id, int report_type) { esp_hidh_dev_report_t *r = dev->reports; @@ -369,11 +599,68 @@ esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_id_and_proto(esp_hidh_de return NULL; } +esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_len_and_proto(esp_hidh_dev_t *dev, size_t len, int protocol_mode) +{ + esp_hidh_dev_report_t *r = dev->reports; + while (r) { + if (r->value_len == len && (r->report_type & 1) && r->protocol_mode == protocol_mode) { + return r; + } + r = r->next; + } + return NULL; +} + +/** + * If no Report ID item tags are present in the Report descriptor, it + * can be assumed that only one Input, Output, and Feature report structure exists + * and together they represent all of the device’s data. + */ +esp_hidh_dev_report_t *esp_hidh_dev_get_input_report_by_proto_and_data(esp_hidh_dev_t *dev, int protocol_mode, + size_t len, const uint8_t *data, bool *has_report_id) +{ + esp_hidh_dev_report_t *r = dev->reports; + *has_report_id = false; + // first, assume data not include report id + while (r) { + if (r->value_len == len && r->report_id == 0 && (r->report_type & 1) && + r->protocol_mode == protocol_mode) { + *has_report_id = false; + break; + } + r = r->next; + } + // indicate data include report id + if (r == NULL) { + if (*data == 0) { + ESP_LOGE(TAG, "data not include report id!"); + *has_report_id = false; + return NULL; + } + r = dev->reports; + while (r) { + if (r->value_len == len + 1 && r->report_id == *data && (r->report_type & 1) && + r->protocol_mode == protocol_mode) { + *has_report_id = true; + break; + } + r = r->next; + } + } + return r; +} + static void esp_hidh_dev_resources_free(esp_hidh_dev_t *dev) { + esp_hidh_dev_lock(dev); if (dev->semaphore) { vSemaphoreDelete(dev->semaphore); } + if (dev->trans_timer) { + esp_timer_stop(dev->trans_timer); + esp_timer_delete(dev->trans_timer); + dev->trans_timer = NULL; + } free((void *)dev->config.device_name); free((void *)dev->config.manufacturer_name); free((void *)dev->config.serial_number); @@ -387,6 +674,10 @@ static void esp_hidh_dev_resources_free(esp_hidh_dev_t *dev) dev->reports = dev->reports->next; free(r); } + esp_hidh_dev_unlock(dev); + if (dev->mutex) { + vSemaphoreDelete(dev->mutex); + } free(dev); } @@ -405,6 +696,13 @@ esp_hidh_dev_t *esp_hidh_dev_malloc() return NULL; } + dev->mutex = xSemaphoreCreateMutex(); + if (dev->mutex == NULL) { + ESP_LOGE(TAG, "malloc mutex failed"); + esp_hidh_dev_resources_free(dev); + return NULL; + } + lock_devices(); TAILQ_INSERT_TAIL(&s_esp_hidh_devices, dev, devices); unlock_devices(); @@ -412,7 +710,16 @@ esp_hidh_dev_t *esp_hidh_dev_malloc() return dev; } +/** + * The `dev` is allocated by the internal function, and it should also be freed by the internal function. So, when the + * user call this function, it will do nothing. + */ esp_err_t esp_hidh_dev_free(esp_hidh_dev_t *dev) +{ + return ESP_OK; +} + +esp_err_t esp_hidh_dev_free_inner(esp_hidh_dev_t *dev) { esp_err_t ret = ESP_FAIL; @@ -440,6 +747,20 @@ esp_err_t esp_hidh_dev_free(esp_hidh_dev_t *dev) return ret; } +static void esp_hidh_dev_delay_free(void *arg) +{ + esp_hidh_dev_t *d = NULL; + esp_hidh_dev_t *next = NULL; + lock_devices(); + TAILQ_FOREACH_SAFE(d, &s_esp_hidh_devices, devices, next) { + if (!d->in_use) { + TAILQ_REMOVE(&s_esp_hidh_devices, d, devices); + esp_hidh_dev_resources_free(d); + } + } + unlock_devices(); +} + #if CONFIG_BLUEDROID_ENABLED esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda) { @@ -455,7 +776,7 @@ esp_hidh_dev_t *esp_hidh_dev_get_by_bda(esp_bd_addr_t bda) return NULL; } -esp_hidh_dev_t *esp_hidh_dev_get_by_handle(int handle) +esp_hidh_dev_t *esp_hidh_dev_get_by_handle(uint8_t handle) { #if CONFIG_BT_HID_HOST_ENABLED esp_hidh_dev_t * d = NULL; @@ -508,6 +829,20 @@ void esp_hidh_process_event_data_handler(void *event_handler_arg, esp_event_base param->feature.data = (uint8_t *)param + sizeof(esp_hidh_event_data_t); } break; + case ESP_HIDH_OPEN_EVENT: + if (param->open.status != ESP_OK) { + if (s_esp_hidh_timer && !esp_timer_is_active(s_esp_hidh_timer) && + esp_timer_start_once(s_esp_hidh_timer, ESP_HIDH_DELAY_FREE_TO) != ESP_OK) { + ESP_LOGE(TAG, "%s set hidh timer failed!", __func__); + } + } + break; + case ESP_HIDH_CLOSE_EVENT: + if (s_esp_hidh_timer && !esp_timer_is_active(s_esp_hidh_timer) && + esp_timer_start_once(s_esp_hidh_timer, ESP_HIDH_DELAY_FREE_TO) != ESP_OK) { + ESP_LOGE(TAG, "%s set hidh timer failed!", __func__); + } + break; default: break; } diff --git a/examples/bluetooth/esp_hid_device/README.md b/examples/bluetooth/esp_hid_device/README.md index 8b5492a22a..c637a3eaa5 100644 --- a/examples/bluetooth/esp_hid_device/README.md +++ b/examples/bluetooth/esp_hid_device/README.md @@ -1,5 +1,23 @@ | Supported Targets | ESP32 | | ----------------- | ----- | -# ESP-IDF BLE HID Device Demo +# ESP-IDF BT/BLE HID Device Demo +This demo use APIs which esp_hid component provided to create a BT, BLE or Bluetooth dual mode hid device. Users can choose mode by setting `HID_DEV_MODE`. + +The BT hid device plays as a mouse. When the connection is successfully established, users can follow the usage below to operate the 'mouse'. +``` +######################################################################## +BT hid mouse demo usage: +You can input these value to simulate mouse: 'q', 'w', 'e', 'a', 's', 'd', 'h' +q -- click the left key +w -- move up +e -- click the right key +a -- move left +s -- move down +d -- move right +h -- show the help +######################################################################## +``` + +The BLE hid device plays as a remote control. When the connection is successfully established, the remote control will set volume up and down periodically. diff --git a/examples/bluetooth/esp_hid_device/main/CMakeLists.txt b/examples/bluetooth/esp_hid_device/main/CMakeLists.txt index 3e360f79a3..7fd7a52f0e 100644 --- a/examples/bluetooth/esp_hid_device/main/CMakeLists.txt +++ b/examples/bluetooth/esp_hid_device/main/CMakeLists.txt @@ -1,6 +1,5 @@ set(srcs "esp_hid_device_main.c" "esp_hid_gap.c") - set(include_dirs ".") idf_component_register(SRCS "${srcs}" diff --git a/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c b/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c index d9a84119a6..e0f592e587 100644 --- a/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c +++ b/examples/bluetooth/esp_hid_device/main/esp_hid_device_main.c @@ -18,9 +18,11 @@ #include "nvs_flash.h" #include "esp_bt.h" #include "esp_bt_defs.h" +#if CONFIG_BT_BLE_ENABLED #include "esp_gap_ble_api.h" #include "esp_gatts_api.h" #include "esp_gatt_defs.h" +#endif #include "esp_bt_main.h" #include "esp_bt_device.h" @@ -29,6 +31,16 @@ static const char *TAG = "HID_DEV_DEMO"; +typedef struct +{ + xTaskHandle task_hdl; + esp_hidd_dev_t *hid_dev; + uint8_t protocol_mode; + uint8_t *buffer; +} local_param_t; + +#if CONFIG_BT_BLE_ENABLED +static local_param_t s_ble_hid_param = {0}; const unsigned char hidapiReportMap[] = { //8 bytes input, 8 bytes feature 0x06, 0x00, 0xFF, // Usage Page (Vendor Defined 0xFF00) 0x0A, 0x00, 0x01, // Usage (0x0100) @@ -111,7 +123,7 @@ const unsigned char mediaReportMap[] = { 0xC0, // End Collection }; -static esp_hid_raw_report_map_t report_maps[] = { +static esp_hid_raw_report_map_t ble_report_maps[] = { { .data = hidapiReportMap, .len = sizeof(hidapiReportMap) @@ -122,20 +134,17 @@ static esp_hid_raw_report_map_t report_maps[] = { } }; -static esp_hid_device_config_t hid_config = { +static esp_hid_device_config_t ble_hid_config = { .vendor_id = 0x16C0, .product_id = 0x05DF, .version = 0x0100, .device_name = "ESP BLE HID2", .manufacturer_name = "Espressif", .serial_number = "1234567890", - .report_maps = report_maps, + .report_maps = ble_report_maps, .report_maps_len = 2 }; -static esp_hidd_dev_t *hid_dev = NULL; -static bool dev_connected = false; - #define HID_CC_RPT_MUTE 1 #define HID_CC_RPT_POWER 2 #define HID_CC_RPT_LAST 3 @@ -283,14 +292,48 @@ void esp_hidd_send_consumer_value(uint8_t key_cmd, bool key_pressed) break; } } - esp_hidd_dev_input_set(hid_dev, 1, HID_RPT_ID_CC_IN, buffer, HID_CC_IN_RPT_LEN); + esp_hidd_dev_input_set(s_ble_hid_param.hid_dev, 1, HID_RPT_ID_CC_IN, buffer, HID_CC_IN_RPT_LEN); return; } -static void hidd_event_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) +void ble_hid_demo_task(void *pvParameters) +{ + static bool send_volum_up = false; + while (1) { + ESP_LOGI(TAG, "Send the volume"); + if (send_volum_up) { + esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, true); + vTaskDelay(100 / portTICK_PERIOD_MS); + esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, false); + } else { + esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, true); + vTaskDelay(100 / portTICK_PERIOD_MS); + esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, false); + } + send_volum_up = !send_volum_up; + vTaskDelay(2000 / portTICK_PERIOD_MS); + } +} + +void ble_hid_task_start_up(void) +{ + xTaskCreate(ble_hid_demo_task, "ble_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, + &s_ble_hid_param.task_hdl); +} + +void ble_hid_task_shut_down(void) +{ + if (s_ble_hid_param.task_hdl) { + vTaskDelete(s_ble_hid_param.task_hdl); + s_ble_hid_param.task_hdl = NULL; + } +} + +static void ble_hidd_event_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) { esp_hidd_event_t event = (esp_hidd_event_t)id; esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data; + static const char *TAG = "HID_DEV_BLE"; switch (event) { case ESP_HIDD_START_EVENT: { @@ -300,7 +343,7 @@ static void hidd_event_callback(void *handler_args, esp_event_base_t base, int32 } case ESP_HIDD_CONNECT_EVENT: { ESP_LOGI(TAG, "CONNECT"); - dev_connected = true;//todo: this should be on auth_complete (in GAP) + ble_hid_task_start_up();//todo: this should be on auth_complete (in GAP) break; } case ESP_HIDD_PROTOCOL_MODE_EVENT: { @@ -323,7 +366,7 @@ static void hidd_event_callback(void *handler_args, esp_event_base_t base, int32 } case ESP_HIDD_DISCONNECT_EVENT: { ESP_LOGI(TAG, "DISCONNECT: %s", esp_hid_disconnect_reason_str(esp_hidd_dev_transport_get(param->disconnect.dev), param->disconnect.reason)); - dev_connected = false; + ble_hid_task_shut_down(); esp_hid_ble_gap_adv_start(); break; } @@ -336,31 +379,204 @@ static void hidd_event_callback(void *handler_args, esp_event_base_t base, int32 } return; } +#endif -void hid_demo_task(void *pvParameters) +#if CONFIG_BT_HID_DEVICE_ENABLED +static local_param_t s_bt_hid_param = {0}; +const unsigned char mouseReportMap[] = { + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x02, // USAGE (Mouse) + 0xa1, 0x01, // COLLECTION (Application) + + 0x09, 0x01, // USAGE (Pointer) + 0xa1, 0x00, // COLLECTION (Physical) + + 0x05, 0x09, // USAGE_PAGE (Button) + 0x19, 0x01, // USAGE_MINIMUM (Button 1) + 0x29, 0x03, // USAGE_MAXIMUM (Button 3) + 0x15, 0x00, // LOGICAL_MINIMUM (0) + 0x25, 0x01, // LOGICAL_MAXIMUM (1) + 0x95, 0x03, // REPORT_COUNT (3) + 0x75, 0x01, // REPORT_SIZE (1) + 0x81, 0x02, // INPUT (Data,Var,Abs) + 0x95, 0x01, // REPORT_COUNT (1) + 0x75, 0x05, // REPORT_SIZE (5) + 0x81, 0x03, // INPUT (Cnst,Var,Abs) + + 0x05, 0x01, // USAGE_PAGE (Generic Desktop) + 0x09, 0x30, // USAGE (X) + 0x09, 0x31, // USAGE (Y) + 0x09, 0x38, // USAGE (Wheel) + 0x15, 0x81, // LOGICAL_MINIMUM (-127) + 0x25, 0x7f, // LOGICAL_MAXIMUM (127) + 0x75, 0x08, // REPORT_SIZE (8) + 0x95, 0x03, // REPORT_COUNT (3) + 0x81, 0x06, // INPUT (Data,Var,Rel) + + 0xc0, // END_COLLECTION + 0xc0 // END_COLLECTION +}; + +static esp_hid_raw_report_map_t bt_report_maps[] = { + { + .data = mouseReportMap, + .len = sizeof(mouseReportMap) + }, +}; + +static esp_hid_device_config_t bt_hid_config = { + .vendor_id = 0x16C0, + .product_id = 0x05DF, + .version = 0x0100, + .device_name = "ESP BT HID1", + .manufacturer_name = "Espressif", + .serial_number = "1234567890", + .report_maps = bt_report_maps, + .report_maps_len = 1 +}; + +// send the buttons, change in x, and change in y +void send_mouse(uint8_t buttons, char dx, char dy, char wheel) { - static bool send_volum_up = false; + static uint8_t buffer[4] = {0}; + buffer[0] = buttons; + buffer[1] = dx; + buffer[2] = dy; + buffer[3] = wheel; + esp_hidd_dev_input_set(s_bt_hid_param.hid_dev, 0, 0, buffer, 4); +} + +void bt_hid_demo_task(void *pvParameters) +{ + static const char* help_string = "########################################################################\n"\ + "BT hid mouse demo usage:\n"\ + "You can input these value to simulate mouse: 'q', 'w', 'e', 'a', 's', 'd', 'h'\n"\ + "q -- click the left key\n"\ + "w -- move up\n"\ + "e -- click the right key\n"\ + "a -- move left\n"\ + "s -- move down\n"\ + "d -- move right\n"\ + "h -- show the help\n"\ + "########################################################################\n"; + printf("%s\n", help_string); + char c; while (1) { - if (dev_connected) { - ESP_LOGI(TAG, "Send the volume"); - if (send_volum_up) { - esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, true); - vTaskDelay(100 / portTICK_PERIOD_MS); - esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_UP, false); - } else { - esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, true); - vTaskDelay(100 / portTICK_PERIOD_MS); - esp_hidd_send_consumer_value(HID_CONSUMER_VOLUME_DOWN, false); - } - send_volum_up = !send_volum_up; + c = fgetc(stdin); + switch (c) { + case 'q': + send_mouse(1, 0, 0, 0); + break; + case 'w': + send_mouse(0, 0, -10, 0); + break; + case 'e': + send_mouse(2, 0, 0, 0); + break; + case 'a': + send_mouse(0, -10, 0, 0); + break; + case 's': + send_mouse(0, 0, 10, 0); + break; + case 'd': + send_mouse(0, 10, 0, 0); + break; + case 'h': + printf("%s\n", help_string); + break; + default: + break; } - vTaskDelay(2000 / portTICK_PERIOD_MS); + vTaskDelay(10 / portTICK_PERIOD_MS); } } +void bt_hid_task_start_up(void) +{ + xTaskCreate(bt_hid_demo_task, "bt_hid_demo_task", 2 * 1024, NULL, configMAX_PRIORITIES - 3, &s_bt_hid_param.task_hdl); + return; +} + +void bt_hid_task_shut_down(void) +{ + if (s_bt_hid_param.task_hdl) { + vTaskDelete(s_bt_hid_param.task_hdl); + s_bt_hid_param.task_hdl = NULL; + } +} + +static void bt_hidd_event_callback(void *handler_args, esp_event_base_t base, int32_t id, void *event_data) +{ + esp_hidd_event_t event = (esp_hidd_event_t)id; + esp_hidd_event_data_t *param = (esp_hidd_event_data_t *)event_data; + static const char *TAG = "HID_DEV_BT"; + + switch (event) { + case ESP_HIDD_START_EVENT: { + if (param->start.status == ESP_OK) { + ESP_LOGI(TAG, "START OK"); + ESP_LOGI(TAG, "Setting to connectable, discoverable"); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(TAG, "START failed!"); + } + break; + } + case ESP_HIDD_CONNECT_EVENT: { + if (param->connect.status == ESP_OK) { + ESP_LOGI(TAG, "CONNECT OK"); + ESP_LOGI(TAG, "Setting to non-connectable, non-discoverable"); + esp_bt_gap_set_scan_mode(ESP_BT_NON_CONNECTABLE, ESP_BT_NON_DISCOVERABLE); + bt_hid_task_start_up(); + } else { + ESP_LOGE(TAG, "CONNECT failed!"); + } + break; + } + case ESP_HIDD_PROTOCOL_MODE_EVENT: { + ESP_LOGI(TAG, "PROTOCOL MODE[%u]: %s", param->protocol_mode.map_index, param->protocol_mode.protocol_mode ? "REPORT" : "BOOT"); + break; + } + case ESP_HIDD_OUTPUT_EVENT: { + ESP_LOGI(TAG, "OUTPUT[%u]: %8s ID: %2u, Len: %d, Data:", param->output.map_index, esp_hid_usage_str(param->output.usage), param->output.report_id, param->output.length); + ESP_LOG_BUFFER_HEX(TAG, param->output.data, param->output.length); + break; + } + case ESP_HIDD_FEATURE_EVENT: { + ESP_LOGI(TAG, "FEATURE[%u]: %8s ID: %2u, Len: %d, Data:", param->feature.map_index, esp_hid_usage_str(param->feature.usage), param->feature.report_id, param->feature.length); + ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length); + break; + } + case ESP_HIDD_DISCONNECT_EVENT: { + if (param->disconnect.status == ESP_OK) { + ESP_LOGI(TAG, "DISCONNECT OK"); + bt_hid_task_shut_down(); + ESP_LOGI(TAG, "Setting to connectable, discoverable again"); + esp_bt_gap_set_scan_mode(ESP_BT_CONNECTABLE, ESP_BT_GENERAL_DISCOVERABLE); + } else { + ESP_LOGE(TAG, "DISCONNECT failed!"); + } + break; + } + case ESP_HIDD_STOP_EVENT: { + ESP_LOGI(TAG, "STOP"); + break; + } + default: + break; + } + return; +} +#endif + void app_main(void) { esp_err_t ret; +#if HID_DEV_MODE == HIDD_IDLE_MODE + ESP_LOGE(TAG, "Please turn on BT HID device or BLE!"); + return; +#endif ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); @@ -368,23 +584,33 @@ void app_main(void) } ESP_ERROR_CHECK( ret ); -#if CONFIG_BT_CLASSIC_ENABLED - ret = esp_hid_gap_init(ESP_BT_MODE_BTDM); -#else - ret = esp_hid_gap_init(ESP_BT_MODE_BLE); -#endif - + ESP_LOGI(TAG, "setting hid gap, mode:%d", HID_DEV_MODE); + ret = esp_hid_gap_init(HID_DEV_MODE); ESP_ERROR_CHECK( ret ); - ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, hid_config.device_name); +#if CONFIG_BT_BLE_ENABLED + ret = esp_hid_ble_gap_adv_init(ESP_HID_APPEARANCE_GENERIC, ble_hid_config.device_name); ESP_ERROR_CHECK( ret ); if ((ret = esp_ble_gatts_register_callback(esp_hidd_gatts_event_handler)) != ESP_OK) { ESP_LOGE(TAG, "GATTS register callback failed: %d", ret); return; } + ESP_LOGI(TAG, "setting ble device"); + ESP_ERROR_CHECK( + esp_hidd_dev_init(&ble_hid_config, ESP_HID_TRANSPORT_BLE, ble_hidd_event_callback, &s_ble_hid_param.hid_dev)); +#endif - ESP_ERROR_CHECK( esp_hidd_dev_init(&hid_config, ESP_HID_TRANSPORT_BLE, hidd_event_callback, &hid_dev) ); - xTaskCreate(&hid_demo_task, "hid_task", 2048, NULL, 2, NULL); - +#if CONFIG_BT_HID_DEVICE_ENABLED + ESP_LOGI(TAG, "setting device name"); + esp_bt_dev_set_device_name(bt_hid_config.device_name); + ESP_LOGI(TAG, "setting cod major, peripheral"); + esp_bt_cod_t cod; + cod.major = ESP_BT_COD_MAJOR_DEV_PERIPHERAL; + esp_bt_gap_set_cod(cod, ESP_BT_SET_COD_MAJOR_MINOR); + vTaskDelay(1000 / portTICK_PERIOD_MS); + ESP_LOGI(TAG, "setting bt device"); + ESP_ERROR_CHECK( + esp_hidd_dev_init(&bt_hid_config, ESP_HID_TRANSPORT_BT, bt_hidd_event_callback, &s_bt_hid_param.hid_dev)); +#endif } diff --git a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c index a75f6a1fea..466434b120 100644 --- a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c +++ b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.c @@ -71,6 +71,7 @@ const char *bt_gap_evt_str(uint8_t event) return bt_gap_evt_names[event]; } +#if CONFIG_BT_BLE_ENABLED const char *esp_ble_key_type_str(esp_ble_key_type_t key_type) { const char *key_str = NULL; @@ -109,6 +110,7 @@ const char *esp_ble_key_type_str(esp_ble_key_type_t key_type) } return key_str; } +#endif /* CONFIG_BT_BLE_ENABLED */ void esp_hid_scan_results_free(esp_hid_scan_result_t *results) { @@ -123,6 +125,7 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results) } } +#if (CONFIG_BT_HID_DEVICE_ENABLED || CONFIG_BT_BLE_ENABLED) static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results) { esp_hid_scan_result_t *r = results; @@ -134,8 +137,9 @@ static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_r } return NULL; } +#endif /* (CONFIG_BT_HID_DEVICE_ENABLED || CONFIG_BT_BLE_ENABLED) */ -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_DEVICE_ENABLED static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi) { esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results); @@ -189,6 +193,7 @@ static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid } #endif +#if CONFIG_BT_BLE_ENABLED static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi) { if (find_scan_result(bda, ble_scan_results)) { @@ -222,6 +227,7 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type ble_scan_results = r; num_ble_scan_results++; } +#endif /* CONFIG_BT_BLE_ENABLED */ void print_uuid(esp_bt_uuid_t *uuid) { @@ -239,7 +245,7 @@ void print_uuid(esp_bt_uuid_t *uuid) } } -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_DEVICE_ENABLED static void handle_bt_device_result(struct disc_res_param *disc_res) { GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda)); @@ -330,6 +336,7 @@ static void handle_bt_device_result(struct disc_res_param *disc_res) } #endif +#if CONFIG_BT_BLE_ENABLED static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst) { @@ -375,8 +382,9 @@ static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst) add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi); } } +#endif /* CONFIG_BT_BLE_ENABLED */ -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_DEVICE_ENABLED /* * BT GAP * */ @@ -398,6 +406,9 @@ static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_para case ESP_BT_GAP_KEY_NOTIF_EVT: ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey); break; + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode); + break; default: ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event)); break; @@ -408,7 +419,7 @@ static esp_err_t init_bt_gap(void) { esp_err_t ret; esp_bt_sp_param_t param_type = ESP_BT_SP_IOCAP_MODE; - esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_IO; + esp_bt_io_cap_t iocap = ESP_BT_IO_CAP_NONE; esp_bt_gap_set_security_param(param_type, &iocap, sizeof(uint8_t)); /* * Set default parameters for Legacy Pairing @@ -446,6 +457,7 @@ static esp_err_t start_bt_scan(uint32_t seconds) } #endif +#if CONFIG_BT_BLE_ENABLED /* * BLE GAP * */ @@ -667,6 +679,7 @@ esp_err_t esp_hid_ble_gap_adv_start(void) }; return esp_ble_gap_start_advertising(&hidd_adv_params); } +#endif /* CONFIG_BT_BLE_ENABLED */ /* * CONTROLLER INIT @@ -676,15 +689,16 @@ static esp_err_t init_low_level(uint8_t mode) { esp_err_t ret; esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_IDF_TARGET_ESP32 + bt_cfg.mode = mode; +#endif +#if CONFIG_BT_HID_DEVICE_ENABLED if (mode & ESP_BT_MODE_CLASSIC_BT) { - bt_cfg.mode = mode; bt_cfg.bt_max_acl_conn = 3; bt_cfg.bt_max_sync_conn = 3; } else #endif { - ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT); if (ret) { ESP_LOGE(TAG, "esp_bt_controller_mem_release failed: %d", ret); @@ -714,7 +728,7 @@ static esp_err_t init_low_level(uint8_t mode) ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret); return ret; } -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_DEVICE_ENABLED if (mode & ESP_BT_MODE_CLASSIC_BT) { ret = init_bt_gap(); if (ret) { @@ -722,19 +736,17 @@ static esp_err_t init_low_level(uint8_t mode) } } #endif - +#if CONFIG_BT_BLE_ENABLED if (mode & ESP_BT_MODE_BLE) { ret = init_ble_gap(); if (ret) { return ret; } } +#endif /* CONFIG_BT_BLE_ENABLED */ return ret; } - - - esp_err_t esp_hid_gap_init(uint8_t mode) { esp_err_t ret; @@ -781,16 +793,21 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul return ESP_FAIL; } +#if CONFIG_BT_BLE_ENABLED if (start_ble_scan(seconds) == ESP_OK) { -#if CONFIG_BT_CLASSIC_ENABLED - if (start_bt_scan(seconds) == ESP_OK) { - WAIT_BT_CB(); - } -#endif WAIT_BLE_CB(); } else { return ESP_FAIL; } +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if CONFIG_BT_HID_DEVICE_ENABLED + if (start_bt_scan(seconds) == ESP_OK) { + WAIT_BT_CB(); + } else { + return ESP_FAIL; + } +#endif *num_results = num_bt_scan_results + num_ble_scan_results; *results = bt_scan_results; diff --git a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.h b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.h index f2fb240161..524fbc4ec9 100644 --- a/examples/bluetooth/esp_hid_device/main/esp_hid_gap.h +++ b/examples/bluetooth/esp_hid_device/main/esp_hid_gap.h @@ -15,17 +15,36 @@ #ifndef _ESP_HID_GAP_H_ #define _ESP_HID_GAP_H_ +#define HIDD_IDLE_MODE 0x00 +#define HIDD_BLE_MODE 0x01 +#define HIDD_BT_MODE 0x02 +#define HIDD_BTDM_MODE 0x03 + +#if CONFIG_BT_HID_DEVICE_ENABLED +#if CONFIG_BT_BLE_ENABLED +#define HID_DEV_MODE HIDD_BTDM_MODE +#else +#define HID_DEV_MODE HIDD_BT_MODE +#endif +#elif CONFIG_BT_BLE_ENABLED +#define HID_DEV_MODE HIDD_BLE_MODE +#else +#define HID_DEV_MODE HIDD_IDLE_MODE +#endif + #include "esp_err.h" #include "esp_log.h" #include "esp_bt.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_hid_common.h" +#if CONFIG_BT_BLE_ENABLED #include "esp_gattc_api.h" #include "esp_gatt_defs.h" #include "esp_gap_ble_api.h" -#include "esp_gap_bt_api.h" -#include "esp_hid_common.h" +#endif #ifdef __cplusplus extern "C" { diff --git a/examples/bluetooth/esp_hid_device/sdkconfig.defaults b/examples/bluetooth/esp_hid_device/sdkconfig.defaults index ff04ba6104..780c4c3639 100644 --- a/examples/bluetooth/esp_hid_device/sdkconfig.defaults +++ b/examples/bluetooth/esp_hid_device/sdkconfig.defaults @@ -3,4 +3,6 @@ CONFIG_BTDM_CTRL_MODE_BTDM=y CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y -CONFIG_BT_HID_HOST_ENABLED=y +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_HID_ENABLED=y +CONFIG_BT_HID_DEVICE_ENABLED=y diff --git a/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32c3 b/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32c3 index 7a475bc657..5355912f04 100644 --- a/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32c3 +++ b/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32c3 @@ -1,14 +1,4 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_CMAKE=y -CONFIG_IDF_TARGET_ARCH_RISCV=y -CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 - -# -# Bluetooth -# +CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y diff --git a/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32s3 b/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32s3 index 7a475bc657..5355912f04 100644 --- a/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32s3 +++ b/examples/bluetooth/esp_hid_device/sdkconfig.defaults.esp32s3 @@ -1,14 +1,4 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_CMAKE=y -CONFIG_IDF_TARGET_ARCH_RISCV=y -CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 - -# -# Bluetooth -# +CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y diff --git a/examples/bluetooth/esp_hid_host/README.md b/examples/bluetooth/esp_hid_host/README.md index 36207932a9..5c26b96866 100644 --- a/examples/bluetooth/esp_hid_host/README.md +++ b/examples/bluetooth/esp_hid_host/README.md @@ -2,3 +2,5 @@ | ----------------- | ----- | # ESP-IDF BT/BLE HID Host Demo + +This demo use APIs which esp_hid component provided to create a Bluetooth dual mode hid host. After the program is started, the HID host will scan the surrounding Bluetooth HID device, and try to connect to the last device which has been scanned. When the connection is successfully established, the HID host will dump the HID device information, and can receive the data sent by the HID device. diff --git a/examples/bluetooth/esp_hid_host/main/CMakeLists.txt b/examples/bluetooth/esp_hid_host/main/CMakeLists.txt index 6d8582450b..178d1bfa74 100644 --- a/examples/bluetooth/esp_hid_host/main/CMakeLists.txt +++ b/examples/bluetooth/esp_hid_host/main/CMakeLists.txt @@ -1,6 +1,5 @@ set(srcs "esp_hid_host_main.c" "esp_hid_gap.c") - set(include_dirs ".") idf_component_register(SRCS "${srcs}" diff --git a/examples/bluetooth/esp_hid_host/main/esp_hid_gap.c b/examples/bluetooth/esp_hid_host/main/esp_hid_gap.c index 2d61e75a3e..5f446a0a91 100644 --- a/examples/bluetooth/esp_hid_host/main/esp_hid_gap.c +++ b/examples/bluetooth/esp_hid_host/main/esp_hid_gap.c @@ -24,8 +24,11 @@ static const char *TAG = "ESP_HID_GAP"; // uncomment to print all devices that were seen during a scan -#define GAP_DBG_PRINTF(...) //printf(__VA_ARGS__) -//static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"}; +#define GAP_DBG_PRINTF(...) printf(__VA_ARGS__) + +#if CONFIG_BT_HID_HOST_ENABLED +static const char * gap_bt_prop_type_names[5] = {"","BDNAME","COD","RSSI","EIR"}; +#endif static esp_hid_scan_result_t *bt_scan_results = NULL; static size_t num_bt_scan_results = 0; @@ -71,6 +74,7 @@ const char *bt_gap_evt_str(uint8_t event) return bt_gap_evt_names[event]; } +#if CONFIG_BT_BLE_ENABLED const char *esp_ble_key_type_str(esp_ble_key_type_t key_type) { const char *key_str = NULL; @@ -109,6 +113,7 @@ const char *esp_ble_key_type_str(esp_ble_key_type_t key_type) } return key_str; } +#endif /* CONFIG_BT_BLE_ENABLED */ void esp_hid_scan_results_free(esp_hid_scan_result_t *results) { @@ -123,6 +128,7 @@ void esp_hid_scan_results_free(esp_hid_scan_result_t *results) } } +#if (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED) static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_result_t *results) { esp_hid_scan_result_t *r = results; @@ -134,8 +140,9 @@ static esp_hid_scan_result_t *find_scan_result(esp_bd_addr_t bda, esp_hid_scan_r } return NULL; } +#endif /* (CONFIG_BT_HID_HOST_ENABLED || CONFIG_BT_BLE_ENABLED) */ -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_HOST_ENABLED static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid_t *uuid, uint8_t *name, uint8_t name_len, int rssi) { esp_hid_scan_result_t *r = find_scan_result(bda, bt_scan_results); @@ -189,6 +196,7 @@ static void add_bt_scan_result(esp_bd_addr_t bda, esp_bt_cod_t *cod, esp_bt_uuid } #endif +#if CONFIG_BT_BLE_ENABLED static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type, uint16_t appearance, uint8_t *name, uint8_t name_len, int rssi) { if (find_scan_result(bda, ble_scan_results)) { @@ -222,6 +230,7 @@ static void add_ble_scan_result(esp_bd_addr_t bda, esp_ble_addr_type_t addr_type ble_scan_results = r; num_ble_scan_results++; } +#endif /* CONFIG_BT_BLE_ENABLED */ void print_uuid(esp_bt_uuid_t *uuid) { @@ -239,7 +248,7 @@ void print_uuid(esp_bt_uuid_t *uuid) } } -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_HOST_ENABLED static void handle_bt_device_result(struct disc_res_param *disc_res) { GAP_DBG_PRINTF("BT : " ESP_BD_ADDR_STR, ESP_BD_ADDR_HEX(disc_res->bda)); @@ -330,6 +339,7 @@ static void handle_bt_device_result(struct disc_res_param *disc_res) } #endif +#if CONFIG_BT_BLE_ENABLED static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst) { @@ -375,8 +385,9 @@ static void handle_ble_device_result(struct ble_scan_result_evt_param *scan_rst) add_ble_scan_result(scan_rst->bda, scan_rst->ble_addr_type, appearance, adv_name, adv_name_len, scan_rst->rssi); } } +#endif /* CONFIG_BT_BLE_ENABLED */ -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_HOST_ENABLED /* * BT GAP * */ @@ -398,6 +409,9 @@ static void bt_gap_event_handler(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_para case ESP_BT_GAP_KEY_NOTIF_EVT: ESP_LOGI(TAG, "BT GAP KEY_NOTIF passkey:%d", param->key_notif.passkey); break; + case ESP_BT_GAP_MODE_CHG_EVT: + ESP_LOGI(TAG, "BT GAP MODE_CHG_EVT mode:%d", param->mode_chg.mode); + break; default: ESP_LOGV(TAG, "BT GAP EVENT %s", bt_gap_evt_str(event)); break; @@ -446,6 +460,7 @@ static esp_err_t start_bt_scan(uint32_t seconds) } #endif +#if CONFIG_BT_BLE_ENABLED /* * BLE GAP * */ @@ -667,6 +682,7 @@ esp_err_t esp_hid_ble_gap_adv_start(void) }; return esp_ble_gap_start_advertising(&hidd_adv_params); } +#endif /* CONFIG_BT_BLE_ENABLED */ /* * CONTROLLER INIT @@ -676,9 +692,11 @@ static esp_err_t init_low_level(uint8_t mode) { esp_err_t ret; esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT(); -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_IDF_TARGET_ESP32 + bt_cfg.mode = mode; +#endif +#if CONFIG_BT_HID_HOST_ENABLED if (mode & ESP_BT_MODE_CLASSIC_BT) { - bt_cfg.mode = mode; bt_cfg.bt_max_acl_conn = 3; bt_cfg.bt_max_sync_conn = 3; } else @@ -713,7 +731,7 @@ static esp_err_t init_low_level(uint8_t mode) ESP_LOGE(TAG, "esp_bluedroid_enable failed: %d", ret); return ret; } -#if CONFIG_BT_CLASSIC_ENABLED +#if CONFIG_BT_HID_HOST_ENABLED if (mode & ESP_BT_MODE_CLASSIC_BT) { ret = init_bt_gap(); if (ret) { @@ -721,18 +739,17 @@ static esp_err_t init_low_level(uint8_t mode) } } #endif +#if CONFIG_BT_BLE_ENABLED if (mode & ESP_BT_MODE_BLE) { ret = init_ble_gap(); if (ret) { return ret; } } +#endif /* CONFIG_BT_BLE_ENABLED */ return ret; } - - - esp_err_t esp_hid_gap_init(uint8_t mode) { esp_err_t ret; @@ -779,16 +796,21 @@ esp_err_t esp_hid_scan(uint32_t seconds, size_t *num_results, esp_hid_scan_resul return ESP_FAIL; } +#if CONFIG_BT_BLE_ENABLED if (start_ble_scan(seconds) == ESP_OK) { -#if CONFIG_BT_CLASSIC_ENABLED - if (start_bt_scan(seconds) == ESP_OK) { - WAIT_BT_CB(); - } -#endif WAIT_BLE_CB(); } else { return ESP_FAIL; } +#endif /* CONFIG_BT_BLE_ENABLED */ + +#if CONFIG_BT_HID_HOST_ENABLED + if (start_bt_scan(seconds) == ESP_OK) { + WAIT_BT_CB(); + } else { + return ESP_FAIL; + } +#endif *num_results = num_bt_scan_results + num_ble_scan_results; *results = bt_scan_results; diff --git a/examples/bluetooth/esp_hid_host/main/esp_hid_gap.h b/examples/bluetooth/esp_hid_host/main/esp_hid_gap.h index f2fb240161..d28156741e 100644 --- a/examples/bluetooth/esp_hid_host/main/esp_hid_gap.h +++ b/examples/bluetooth/esp_hid_host/main/esp_hid_gap.h @@ -15,17 +15,36 @@ #ifndef _ESP_HID_GAP_H_ #define _ESP_HID_GAP_H_ +#define HIDH_IDLE_MODE 0x00 +#define HIDH_BLE_MODE 0x01 +#define HIDH_BT_MODE 0x02 +#define HIDH_BTDM_MODE 0x03 + +#if CONFIG_BT_HID_HOST_ENABLED +#if CONFIG_BT_BLE_ENABLED +#define HID_HOST_MODE HIDH_BTDM_MODE +#else +#define HID_HOST_MODE HIDH_BT_MODE +#endif +#elif CONFIG_BT_BLE_ENABLED +#define HID_HOST_MODE HIDH_BLE_MODE +#else +#define HID_HOST_MODE HIDH_IDLE_MODE +#endif + #include "esp_err.h" #include "esp_log.h" #include "esp_bt.h" #include "esp_bt_defs.h" #include "esp_bt_main.h" +#include "esp_gap_bt_api.h" +#include "esp_hid_common.h" +#if CONFIG_BT_BLE_ENABLED #include "esp_gattc_api.h" #include "esp_gatt_defs.h" #include "esp_gap_ble_api.h" -#include "esp_gap_bt_api.h" -#include "esp_hid_common.h" +#endif #ifdef __cplusplus extern "C" { diff --git a/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c b/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c index a6a3663d2a..deb70ba145 100644 --- a/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c +++ b/examples/bluetooth/esp_hid_host/main/esp_hid_host_main.c @@ -36,9 +36,13 @@ void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void * switch (event) { case ESP_HIDH_OPEN_EVENT: { - const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev); - ESP_LOGI(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev)); - esp_hidh_dev_dump(param->open.dev, stdout); + if (param->open.status == ESP_OK) { + const uint8_t *bda = esp_hidh_dev_bda_get(param->open.dev); + ESP_LOGI(TAG, ESP_BD_ADDR_STR " OPEN: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->open.dev)); + esp_hidh_dev_dump(param->open.dev, stdout); + } else { + ESP_LOGE(TAG, " OPEN failed!"); + } break; } case ESP_HIDH_BATTERY_EVENT: { @@ -54,15 +58,15 @@ void hidh_callback(void *handler_args, esp_event_base_t base, int32_t id, void * } case ESP_HIDH_FEATURE_EVENT: { const uint8_t *bda = esp_hidh_dev_bda_get(param->feature.dev); - ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, param->feature.length); + ESP_LOGI(TAG, ESP_BD_ADDR_STR " FEATURE: %8s, MAP: %2u, ID: %3u, Len: %d", ESP_BD_ADDR_HEX(bda), + esp_hid_usage_str(param->feature.usage), param->feature.map_index, param->feature.report_id, + param->feature.length); ESP_LOG_BUFFER_HEX(TAG, param->feature.data, param->feature.length); break; } case ESP_HIDH_CLOSE_EVENT: { const uint8_t *bda = esp_hidh_dev_bda_get(param->close.dev); - ESP_LOGI(TAG, ESP_BD_ADDR_STR " CLOSE: '%s' %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev), esp_hid_disconnect_reason_str(esp_hidh_dev_transport_get(param->close.dev), param->close.reason)); - //MUST call this function to free all allocated memory by this device - esp_hidh_dev_free(param->close.dev); + ESP_LOGI(TAG, ESP_BD_ADDR_STR " CLOSE: %s", ESP_BD_ADDR_HEX(bda), esp_hidh_dev_name_get(param->close.dev)); break; } default: @@ -88,11 +92,15 @@ void hid_demo_task(void *pvParameters) printf(" %s: " ESP_BD_ADDR_STR ", ", (r->transport == ESP_HID_TRANSPORT_BLE) ? "BLE" : "BT ", ESP_BD_ADDR_HEX(r->bda)); printf("RSSI: %d, ", r->rssi); printf("USAGE: %s, ", esp_hid_usage_str(r->usage)); +#if CONFIG_BT_BLE_ENABLED if (r->transport == ESP_HID_TRANSPORT_BLE) { cr = r; printf("APPEARANCE: 0x%04x, ", r->ble.appearance); printf("ADDR_TYPE: '%s', ", ble_addr_type_str(r->ble.addr_type)); - } else { + } +#endif /* CONFIG_BT_BLE_ENABLED */ +#if CONFIG_BT_HID_HOST_ENABLED + if (r->transport == ESP_HID_TRANSPORT_BT) { cr = r; printf("COD: %s[", esp_hid_cod_major_str(r->bt.cod.major)); esp_hid_cod_minor_print(r->bt.cod.minor, stdout); @@ -100,6 +108,7 @@ void hid_demo_task(void *pvParameters) print_uuid(&r->bt.uuid); printf(", "); } +#endif /* CONFIG_BT_HID_HOST_ENABLED */ printf("NAME: %s ", r->name ? r->name : ""); printf("\n"); r = r->next; @@ -117,21 +126,25 @@ void hid_demo_task(void *pvParameters) void app_main(void) { esp_err_t ret; +#if HID_HOST_MODE == HIDH_IDLE_MODE + ESP_LOGE(TAG, "Please turn on BT HID host or BLE!"); + return; +#endif ret = nvs_flash_init(); if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { ESP_ERROR_CHECK(nvs_flash_erase()); ret = nvs_flash_init(); } ESP_ERROR_CHECK( ret ); -#if CONFIG_BT_CLASSIC_ENABLED - ESP_ERROR_CHECK( esp_hid_gap_init(ESP_BT_MODE_BTDM) ); -#else - ESP_ERROR_CHECK( esp_hid_gap_init(ESP_BT_MODE_BLE) ); -#endif + ESP_LOGI(TAG, "setting hid gap, mode:%d", HID_HOST_MODE); + ESP_ERROR_CHECK( esp_hid_gap_init(HID_HOST_MODE) ); +#if CONFIG_BT_BLE_ENABLED ESP_ERROR_CHECK( esp_ble_gattc_register_callback(esp_hidh_gattc_event_handler) ); +#endif /* CONFIG_BT_BLE_ENABLED */ esp_hidh_config_t config = { .callback = hidh_callback, - .event_stack_size = 4096 + .event_stack_size = 4096, + .callback_arg = NULL, }; ESP_ERROR_CHECK( esp_hidh_init(&config) ); diff --git a/examples/bluetooth/esp_hid_host/sdkconfig.defaults b/examples/bluetooth/esp_hid_host/sdkconfig.defaults index ff04ba6104..f2f81495e3 100644 --- a/examples/bluetooth/esp_hid_host/sdkconfig.defaults +++ b/examples/bluetooth/esp_hid_host/sdkconfig.defaults @@ -3,4 +3,6 @@ CONFIG_BTDM_CTRL_MODE_BTDM=y CONFIG_BTDM_CTRL_HCI_MODE_VHCI=y CONFIG_BT_BLUEDROID_ENABLED=y CONFIG_BT_CLASSIC_ENABLED=y +CONFIG_BT_BLE_ENABLED=y +CONFIG_BT_HID_ENABLED=y CONFIG_BT_HID_HOST_ENABLED=y diff --git a/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32c3 b/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32c3 index 7a475bc657..5355912f04 100644 --- a/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32c3 +++ b/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32c3 @@ -1,14 +1,4 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_CMAKE=y -CONFIG_IDF_TARGET_ARCH_RISCV=y -CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 - -# -# Bluetooth -# +CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y diff --git a/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32s3 b/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32s3 index 7a475bc657..5355912f04 100644 --- a/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32s3 +++ b/examples/bluetooth/esp_hid_host/sdkconfig.defaults.esp32s3 @@ -1,14 +1,4 @@ -# -# Automatically generated file. DO NOT EDIT. -# Espressif IoT Development Framework (ESP-IDF) Project Configuration -# -CONFIG_IDF_CMAKE=y -CONFIG_IDF_TARGET_ARCH_RISCV=y -CONFIG_IDF_TARGET="esp32c3" -CONFIG_IDF_TARGET_ESP32C3=y -CONFIG_IDF_FIRMWARE_CHIP_ID=0x0005 - -# -# Bluetooth -# +CONFIG_BT_ENABLED=y +CONFIG_BT_BLUEDROID_ENABLED=y +CONFIG_BT_BLE_ENABLED=y CONFIG_BT_BLE_42_FEATURES_SUPPORTED=y