From ad43b04898c19d38f47b00639877418b7e1b5749 Mon Sep 17 00:00:00 2001 From: liqigan Date: Fri, 18 Nov 2022 16:11:15 +0800 Subject: [PATCH] fix HID device can not remove virtually cabled device without a connection Closes https://github.com/espressif/esp-idf/issues/10107 --- .../bluedroid/api/include/api/esp_hidd_api.h | 133 ++++++++++-------- .../bt/host/bluedroid/bta/hd/bta_hd_act.c | 17 ++- .../bt/host/bluedroid/bta/hd/bta_hd_main.c | 2 +- .../bluedroid/btc/profile/std/hid/btc_hd.c | 37 ++++- .../btc/profile/std/include/btc_hd.h | 2 +- .../bt_hid_mouse_device/main/main.c | 10 +- 6 files changed, 137 insertions(+), 64 deletions(-) diff --git a/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h index ad3ed97ebae..53e92a08d98 100644 --- a/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h +++ b/components/bt/host/bluedroid/api/include/api/esp_hidd_api.h @@ -259,123 +259,144 @@ typedef union { } esp_hidd_cb_param_t; /** - * @brief HID device callback function type. - * @param event: Event type - * @param param: Point to callback parameter, currently is union type + * @brief HID device callback function type. + * @param event: Event type + * @param param: Point to callback parameter, currently is union type */ typedef void (*esp_hd_cb_t)(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param); /** - * @brief This function is called to init callbacks with HID device module. + * @brief This function is called to init callbacks with HID device module. * - * @param[in] callback: pointer to the init callback function. + * @param[in] callback: pointer to the init callback function. * * @return - * - ESP_OK: success - * - other: failed + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_register_callback(esp_hd_cb_t callback); /** - * @brief This function initializes HIDD. This function should be called after esp_bluedroid_enable and - * esp_bluedroid_init success, and should be called after esp_bt_hid_device_register_callback. - * When the operation is complete the callback function will be called with ESP_HIDD_INIT_EVT. + * @brief Initializes HIDD interface. This function should be called after esp_bluedroid_init() and + * esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_register_callback. + * When the operation is complete, the callback function will be called with ESP_HIDD_INIT_EVT. * * @return - * - ESP_OK: success - * - other: failed + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_init(void); /** - * @brief This function de-initializes HIDD interface. This function should be called after esp_bluedroid_enable() and - * esp_bluedroid_init() success, and should be called after esp_bt_hid_device_init(). When the operation is complete the callback - * function will be called with ESP_HIDD_DEINIT_EVT. + * @brief De-initializes HIDD interface. This function should be called after esp_bluedroid_init() and + * esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). When the + * operation is complete, the callback function will be called with ESP_HIDD_DEINIT_EVT. * - * @return - ESP_OK: success - * - other: failed + * @return + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_deinit(void); /** - * @brief Registers HIDD parameters with SDP and sets l2cap Quality of Service. This function should be called after - * esp_bluedroid_enable and esp_bluedroid_init success, and must be done after esp_bt_hid_device_init. When the operation is complete the callback - * function will be called with ESP_HIDD_REGISTER_APP_EVT. + * @brief Registers HIDD parameters with SDP and sets l2cap Quality of Service. This function should be + * called after esp_bluedroid_init() and esp_bluedroid_enable() success, and should be called after + * esp_bt_hid_device_init(). When the operation is complete, the callback function will be called + * with ESP_HIDD_REGISTER_APP_EVT. * - * @param[in] app_param: HIDD parameters - * @param[in] in_qos: incoming QoS parameters - * @param[in] out_qos: outgoing QoS parameters + * @param[in] app_param: HIDD parameters + * @param[in] in_qos: incoming QoS parameters + * @param[in] out_qos: outgoing QoS parameters * - * @return - ESP_OK: success - * - other: failed + * @return + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_register_app(esp_hidd_app_param_t *app_param, esp_hidd_qos_param_t *in_qos, esp_hidd_qos_param_t *out_qos); /** - * @brief Removes HIDD parameters from SDP and resets l2cap Quality of Service. This function should be called after esp_bluedroid_enable and - * esp_bluedroid_init success, and should be called after esp_bt_hid_device_init. When the operation is complete the callback - * function will be called with ESP_HIDD_UNREGISTER_APP_EVT. + * @brief Removes HIDD parameters from SDP and resets l2cap Quality of Service. This function should be + * called after esp_bluedroid_init() and esp_bluedroid_enable() success, and should be called after + * esp_bt_hid_device_init(). When the operation is complete, the callback function will be called + * with ESP_HIDD_UNREGISTER_APP_EVT. * - * @return - ESP_OK: success - * - other: failed + * @return + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_unregister_app(void); /** - * @brief This function connects HIDD interface to connected bluetooth device, if not done already. When the operation is complete the callback - * function will be called with ESP_HIDD_OPEN_EVT. + * @brief Connects to the peer HID Host with virtual cable. This function should be called after + * esp_bluedroid_init() and esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). + * When the operation is complete, the callback function will be called with ESP_HIDD_OPEN_EVT. * - * @param[in] bd_addr: Remote host bluetooth device address. + * @param[in] bd_addr: Remote host bluetooth device address. * * @return - * - ESP_OK: success - * - other: failed + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_connect(esp_bd_addr_t bd_addr); /** - * @brief This function disconnects HIDD interface. When the operation is complete the callback - * function will be called with ESP_HIDD_CLOSE_EVT. + * @brief Disconnects from the currently connected HID Host. This function should be called after + * esp_bluedroid_init() and esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). + * When the operation is complete, the callback function will be called with ESP_HIDD_CLOSE_EVT. + * + * @note The disconnect operation will not remove the virtually cabled device. If the connect request from the + * different HID Host, it will reject the request. * * @return - * - ESP_OK: success - * - other: failed + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_disconnect(void); /** - * @brief Send HIDD report. When the operation is complete the callback - * function will be called with ESP_HIDD_SEND_REPORT_EVT. + * @brief Sends HID report to the currently connected HID Host. This function should be called after + * esp_bluedroid_init() and esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). + * When the operation is complete, the callback function will be called with ESP_HIDD_SEND_REPORT_EVT. * - * @param[in] type: type of report - * @param[in] id: report id as defined by descriptor - * @param[in] len: length of report - * @param[in] data: report data + * @param[in] type: type of report + * @param[in] id: report id as defined by descriptor + * @param[in] len: length of report + * @param[in] data: report data * * @return - * - ESP_OK: success - * - other: failed + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_send_report(esp_hidd_report_type_t type, uint8_t id, uint16_t len, uint8_t *data); /** - * @brief Sends HIDD handshake with error info for invalid set_report. When the operation is complete the callback - * function will be called with ESP_HIDD_REPORT_ERR_EVT. + * @brief Sends HID Handshake with error info for invalid set_report to the currently connected HID Host. + * This function should be called after esp_bluedroid_init() and esp_bluedroid_enable() success, and + * should be called after esp_bt_hid_device_init(). When the operation is complete, the callback + * function will be called with ESP_HIDD_REPORT_ERR_EVT. * - * @param[in] error: type of error + * @param[in] error: type of error * - * @return - ESP_OK: success - * - other: failed + * @return + * - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_report_error(esp_hidd_handshake_error_t error); /** - * @brief Unplug virtual cable of HIDD. When the operation is complete the callback - * function will be called with ESP_HIDD_VC_UNPLUG_EVT. + * @brief Remove the virtually cabled device. This function should be called after esp_bluedroid_init() + * and esp_bluedroid_enable() success, and should be called after esp_bt_hid_device_init(). When the + * operation is complete, the callback function will be called with ESP_HIDD_VC_UNPLUG_EVT. + * + * @note If the connection exists, then HID Device will send a `VIRTUAL_CABLE_UNPLUG` control command to + * the peer HID Host, and the connection will be destroyed. If the connection does not exist, then HID + * Device will only unplug on it's single side. Once the unplug operation is success, the related + * pairing and bonding information will be removed, then the HID Device can accept connection request + * from the different HID Host, * - * @return - ESP_OK: success - * - other: failed + * @return - ESP_OK: success + * - other: failed */ esp_err_t esp_bt_hid_device_virtual_cable_unplug(void); diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_act.c b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c index addb585a1d0..8f655d73fe2 100644 --- a/components/bt/host/bluedroid/bta/hd/bta_hd_act.c +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_act.c @@ -422,13 +422,28 @@ extern void bta_hd_report_error_act(tBTA_HD_DATA *p_data) extern void bta_hd_vc_unplug_act(UNUSED_ATTR tBTA_HD_DATA *p_data) { tHID_STATUS ret; + tBTA_HD cback_data = {0}; + BD_ADDR plugged_addr = {0}; APPL_TRACE_API("%s", __func__); bta_hd_cb.vc_unplug = TRUE; ret = HID_DevVirtualCableUnplug(); - if (ret != HID_SUCCESS) { + if (ret == HID_ERR_NO_CONNECTION) { + /* This is a local VUP without connection, set the vc_unplug to FALSE */ + bta_hd_cb.vc_unplug = FALSE; + APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); + if (HID_DevGetDevice(&plugged_addr) == HID_SUCCESS) { + HID_DevUnplugDevice(plugged_addr); + } + APPL_TRACE_DEBUG("%s local VUP, remove bda: %02x:%02x:%02x:%02x:%02x:%02x", __func__, plugged_addr[0], + plugged_addr[1], plugged_addr[2], plugged_addr[3], plugged_addr[4], plugged_addr[5]); + cback_data.conn.status = BTA_HD_OK; + cback_data.conn.conn_status = BTA_HD_CONN_STATE_DISCONNECTED; + bta_hd_cb.p_cback(BTA_HD_VC_UNPLUG_EVT, &cback_data); + return; + } else if (ret != HID_SUCCESS) { APPL_TRACE_WARNING("%s: HID_DevVirtualCableUnplug returned %d", __func__, ret); } diff --git a/components/bt/host/bluedroid/bta/hd/bta_hd_main.c b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c index 4400b3e1728..2dd086cf2dc 100644 --- a/components/bt/host/bluedroid/bta/hd/bta_hd_main.c +++ b/components/bt/host/bluedroid/bta/hd/bta_hd_main.c @@ -116,7 +116,7 @@ const uint8_t bta_hd_st_idle[][BTA_HD_NUM_COLS] = { /* BTA_HD_API_REMOVE_DEVICE_EVT */ {BTA_HD_REMOVE_DEVICE_ACT, BTA_HD_IDLE_ST}, /* BTA_HD_API_SEND_REPORT_EVT */ {BTA_HD_SEND_REPORT_ACT, BTA_HD_IDLE_ST}, /* BTA_HD_API_REPORT_ERROR_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, - /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, + /* BTA_HD_API_VC_UNPLUG_EVT */ {BTA_HD_VC_UNPLUG_ACT, BTA_HD_IDLE_ST}, /* BTA_HD_INT_OPEN_EVT */ {BTA_HD_OPEN_ACT, BTA_HD_CONN_ST}, /* BTA_HD_INT_CLOSE_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, /* BTA_HD_INT_INTR_DATA_EVT */ {BTA_HD_IGNORE, BTA_HD_IDLE_ST}, diff --git a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c index 90bc298ac21..b14de4833d7 100644 --- a/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c +++ b/components/bt/host/bluedroid/btc/profile/std/hid/btc_hd.c @@ -254,6 +254,12 @@ static void btc_hd_deinit(void) break; } + if (btc_hd_cb.status == BTC_HD_DISABLING) { + BTC_TRACE_ERROR("%s is disabling, try later!", __func__); + ret = ESP_HIDD_BUSY; + break; + } + btc_hd_cb.service_dereg_active = FALSE; btc_hd_cb.status = BTC_HD_DISABLING; // unresgister app will also relase the connection @@ -411,6 +417,13 @@ static void btc_hd_connect(BD_ADDR bd_addr) ret = ESP_HIDD_NEED_REG; break; } + + if (btc_hd_cb.status == BTC_HD_CONNECTED) { + BTC_TRACE_ERROR("%s: already connect to the other HID host!", __func__); + ret = ESP_HIDD_NO_RES; + break; + } + BTA_HdConnect(bd_addr); } while (0); @@ -448,6 +461,13 @@ static void btc_hd_disconnect(void) ret = ESP_HIDD_NEED_REG; break; } + + if (btc_hd_cb.status != BTC_HD_CONNECTED) { + BTC_TRACE_ERROR("%s: already disconnected!", __func__); + ret = ESP_HIDD_NO_CONNECTION; + break; + } + BTA_HdDisconnect(); } while (0); @@ -721,6 +741,7 @@ void btc_hd_cb_handler(btc_msg_t *msg) // break; // } // btc_storage_set_hidd((bt_bdaddr_t *)&p_data->conn.bda); + btc_hd_cb.status = BTC_HD_CONNECTED; } param.open.status = p_data->conn.status; param.open.conn_status = p_data->conn.conn_status; @@ -736,6 +757,11 @@ void btc_hd_cb_handler(btc_msg_t *msg) btc_hd_cb.forced_disc = FALSE; break; } + + if (btc_hd_cb.status == BTC_HD_CONNECTED) { + btc_hd_cb.status = BTC_HD_ENABLED; + } + param.close.status = p_data->conn.status; param.close.conn_status = p_data->conn.conn_status; btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); @@ -782,9 +808,14 @@ void btc_hd_cb_handler(btc_msg_t *msg) BTC_TRACE_DEBUG("%s: Only removing HID data as some other profiles connected", __func__); btc_hd_remove_device(*bd_addr); } - param.close.status = p_data->conn.status; - param.close.conn_status = p_data->conn.conn_status; - btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + + if (btc_hd_cb.status == BTC_HD_CONNECTED) { + btc_hd_cb.status = BTC_HD_ENABLED; + param.close.status = p_data->conn.status; + param.close.conn_status = p_data->conn.conn_status; + btc_hd_cb_to_app(ESP_HIDD_CLOSE_EVT, ¶m); + } + param.vc_unplug.status = p_data->conn.status; param.vc_unplug.conn_status = p_data->conn.conn_status; btc_hd_cb_to_app(ESP_HIDD_VC_UNPLUG_EVT, ¶m); diff --git a/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h index a2b92cf7ffa..4292e648c24 100644 --- a/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h +++ b/components/bt/host/bluedroid/btc/profile/std/include/btc_hd.h @@ -39,7 +39,7 @@ typedef enum { BTC_HD_UNPLUG_EVT, } BTC_HD_EVT; -typedef enum { BTC_HD_DISABLED = 0, BTC_HD_ENABLED, BTC_HD_DISABLING } BTC_HD_STATUS; +typedef enum { BTC_HD_DISABLED = 0, BTC_HD_ENABLED, BTC_HD_CONNECTED, BTC_HD_DISABLING } BTC_HD_STATUS; /* BTIF-HD control block */ typedef struct { diff --git a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c index e40300d2c83..b9ff3cd5f8d 100644 --- a/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c +++ b/examples/bluetooth/bluedroid/classic_bt/bt_hid_mouse_device/main/main.c @@ -312,8 +312,14 @@ void esp_bt_hidd_cb(esp_hidd_cb_event_t event, esp_hidd_cb_param_t *param) } break; case ESP_HIDD_SEND_REPORT_EVT: - ESP_LOGI(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d", param->send_report.report_id, - param->send_report.report_type); + if (param->send_report.status == ESP_HIDD_SUCCESS) { + ESP_LOGI(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d", param->send_report.report_id, + param->send_report.report_type); + } else { + ESP_LOGE(TAG, "ESP_HIDD_SEND_REPORT_EVT id:0x%02x, type:%d, status:%d, reason:%d", + param->send_report.report_id, param->send_report.report_type, param->send_report.status, + param->send_report.reason); + } break; case ESP_HIDD_REPORT_ERR_EVT: ESP_LOGI(TAG, "ESP_HIDD_REPORT_ERR_EVT");