Skip to content

Commit a27f4e7

Browse files
feat(hid_host): Start HID Device from suspended state
1 parent cfb9e08 commit a27f4e7

File tree

4 files changed

+58
-59
lines changed

4 files changed

+58
-59
lines changed

host/class/hid/usb_host_hid/hid_host.c

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,11 @@ static esp_err_t hid_host_resume_interface(hid_iface_t *iface, bool resume_ep)
609609
ESP_ERR_NOT_FOUND,
610610
"Interface handle not found");
611611

612+
if (HID_INTERFACE_STATE_ACTIVE == iface->state) {
613+
// Interface already auto-resumed by hid_host_device_start(), return early and continue to resume event delivery
614+
return ESP_OK;
615+
}
616+
612617
HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED == iface->state),
613618
ESP_ERR_INVALID_STATE,
614619
"Interface wrong state");
@@ -618,14 +623,19 @@ static esp_err_t hid_host_resume_interface(hid_iface_t *iface, bool resume_ep)
618623
usb_host_endpoint_clear(iface->parent->dev_hdl, iface->ep_in);
619624
}
620625

626+
// Use the last device state before the device went to suspended state as the current state
621627
iface->state = iface->last_state;
622628

623629
if (iface->in_xfer == NULL) {
624630
return ESP_OK;
625631
}
626632

627-
// start data transfer
628-
HID_RETURN_ON_ERROR( usb_host_transfer_submit(iface->in_xfer), "Unable to start data transfer");
633+
// If the last state before the device went to suspended state was active state, start the data transfer
634+
if (iface->last_state == HID_INTERFACE_STATE_ACTIVE) {
635+
// start data transfer
636+
HID_RETURN_ON_ERROR( usb_host_transfer_submit(iface->in_xfer), "Unable to start data transfer");
637+
}
638+
629639
return ESP_OK;
630640
}
631641

@@ -653,8 +663,15 @@ static esp_err_t hid_host_device_suspended(usb_device_handle_t dev_hdl)
653663
HID_EXIT_CRITICAL();
654664

655665
if (hid_iface_curr->parent && (hid_iface_curr->parent->dev_addr == hid_device->dev_addr)) {
656-
hid_host_suspend_interface(hid_iface_curr, false);
657-
hid_host_user_interface_callback(hid_iface_curr, HID_HOST_INTERFACE_EVENT_SUSPENDED);
666+
esp_err_t ret = hid_host_suspend_interface(hid_iface_curr, false);
667+
668+
// Make sure the device is connected and the interface is found otherwise don't deliver suspend event
669+
if (ret != ESP_ERR_NOT_FOUND) {
670+
671+
// We will deliver the suspend event, if the hid_host_suspend_interface fails with other errors,
672+
// as the usb_host_lib has already suspended the root port anyway
673+
hid_host_user_interface_callback(hid_iface_curr, HID_HOST_INTERFACE_EVENT_SUSPENDED);
674+
}
658675
}
659676
HID_ENTER_CRITICAL();
660677
hid_iface_curr = hid_iface_next;
@@ -688,8 +705,15 @@ static esp_err_t hid_host_device_resumed(usb_device_handle_t dev_hdl)
688705
HID_EXIT_CRITICAL();
689706

690707
if (hid_iface_curr->parent && (hid_iface_curr->parent->dev_addr == hid_device->dev_addr)) {
691-
hid_host_resume_interface(hid_iface_curr, false);
692-
hid_host_user_interface_callback(hid_iface_curr, HID_HOST_INTERFACE_EVENT_RESUMED);
708+
esp_err_t ret = hid_host_resume_interface(hid_iface_curr, false);
709+
710+
// Make sure the device is connected and the interface is found otherwise don't deliver resume event
711+
if (ret != ESP_ERR_NOT_FOUND) {
712+
713+
// We will deliver the resume event, if the hid_host_resume_interface fails with other errors,
714+
// as the usb_host_lib has already resumed the root port anyway
715+
hid_host_user_interface_callback(hid_iface_curr, HID_HOST_INTERFACE_EVENT_RESUMED);
716+
}
693717
}
694718
HID_ENTER_CRITICAL();
695719
hid_iface_curr = hid_iface_next;
@@ -1487,7 +1511,7 @@ esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle)
14871511
ESP_ERR_NOT_FOUND,
14881512
"Interface handle not found");
14891513

1490-
HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_READY == iface->state),
1514+
HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_READY == iface->state || HID_INTERFACE_STATE_SUSPENDED == iface->state),
14911515
ESP_ERR_INVALID_STATE,
14921516
"Interface wrong state");
14931517

@@ -1511,6 +1535,15 @@ esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle)
15111535

15121536
HID_RETURN_ON_INVALID_ARG(iface);
15131537

1538+
HID_RETURN_ON_FALSE(is_interface_in_list(iface), ESP_ERR_NOT_FOUND, "Interface handle not found");
1539+
1540+
if (iface->state == HID_INTERFACE_STATE_SUSPENDED) {
1541+
// If interface is suspended, mark the last state as READY,
1542+
// as if the interface was stopped before entering suspended state
1543+
iface->last_state = HID_INTERFACE_STATE_READY;
1544+
return ESP_OK;
1545+
}
1546+
15141547
return hid_host_disable_interface(iface);
15151548
}
15161549

host/class/hid/usb_host_hid/include/usb/hid_host.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ esp_err_t hid_host_device_get_raw_input_report_data(hid_host_device_handle_t hid
204204
*
205205
* Calls a callback when the HID Interface event has occurred.
206206
*
207+
* @note Can be called on device in suspended state, it will effectively resume and start the device
207208
* @param[in] hid_dev_handle HID Device handle
208209
* @return esp_err_t
209210
*/
@@ -212,6 +213,8 @@ esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle);
212213
/**
213214
* @brief HID Host stop device
214215
*
216+
* @note Can be called on device in suspended state, it will effectively prevent the device from starting automatically
217+
* in case the device was started before entering suspended state
215218
* @param[in] hid_dev_handle HID Device handle
216219
*
217220
* @return esp_err_t

host/class/hid/usb_host_hid/test_app/main/test_hid_basic.c

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
#include "test_hid_basic.h"
2424
#include "hid_mock_device.h"
2525

26+
#define TEST_EVENT_WAIT_MS 500 // Time to expect driver or device event
27+
2628
// Global variable to verify user arg passing through callbacks
2729
static uint32_t user_arg_value = 0x8A53E0A4; // Just a constant random number
2830

@@ -1030,7 +1032,6 @@ TEST_CASE("auto_suspend_timer", "[hid_host]")
10301032
vQueueDelete(hid_host_test_event_queue);
10311033
hid_host_test_event_queue = NULL;
10321034
}
1033-
10341035
/**
10351036
* @brief Resume by transfer submit
10361037
*
@@ -1041,6 +1042,8 @@ TEST_CASE("auto_suspend_timer", "[hid_host]")
10411042
* - Install USB Host lib, Install HID driver, open device and start device
10421043
* - Manually suspend the root port, expect suspend event
10431044
* - Issue a CTRL transfer to the device, expect the root port to be resumed, expect resume event
1045+
* - Manually suspend the root port, expect suspend event
1046+
* - Start the device, expect the root port to be resumed, expect resume event
10441047
* - Teardown
10451048
*/
10461049
TEST_CASE("resume_by_transfer_submit", "[hid_host]")
@@ -1073,6 +1076,17 @@ TEST_CASE("resume_by_transfer_submit", "[hid_host]")
10731076
expected_event.interface_evt.event = HID_HOST_INTERFACE_EVENT_RESUMED;
10741077
hid_host_test_expect_event(&expected_event, expect_event_ticks);
10751078

1079+
// Suspend the root port manually
1080+
TEST_ASSERT_EQUAL(ESP_OK, usb_host_lib_root_port_suspend());
1081+
expected_event.event_group = HID_INTERFACE_EVENT;
1082+
expected_event.interface_evt.event = HID_HOST_INTERFACE_EVENT_SUSPENDED;
1083+
hid_host_test_expect_event(&expected_event, expect_event_ticks);
1084+
1085+
// Auto resume the device by calling device start
1086+
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(global_hdl));
1087+
expected_event.interface_evt.event = HID_HOST_INTERFACE_EVENT_RESUMED;
1088+
hid_host_test_expect_event(&expected_event, expect_event_ticks);
1089+
10761090
// Tear down test
10771091
test_hid_teardown();
10781092
vQueueDelete(hid_host_test_event_queue);

host/class/hid/usb_host_hid/test_app/main/test_hid_err_handling.c

Lines changed: 0 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -281,56 +281,6 @@ static void test_open_suspended_device(void)
281281
test_event_queue = NULL;
282282
}
283283

284-
/**
285-
* @brief Start suspended device
286-
*
287-
* Purpose:
288-
* - Test HID Host reaction to starting an opened device, which is in suspended state
289-
*
290-
* Procedure:
291-
* - Install USB Host lib, Install HID driver, wait for device connection, open the device
292-
* - Suspend the root port and fail to start a device
293-
* - Resume the root port, teardown
294-
*/
295-
static void test_start_suspended_device(void)
296-
{
297-
ESP_LOGI(TAG, "Start suspended device");
298-
test_event_queue = xQueueCreate(4, sizeof(test_event_queue_t));
299-
TEST_ASSERT_NOT_NULL(test_event_queue);
300-
301-
test_hid_setup(test_hid_host_event_callback_open_event, HID_TEST_EVENT_HANDLE_IN_DRIVER);
302-
303-
// Make sure connect events are delivered from both devices
304-
test_event_queue_t queue_item_1, queue_item_2;
305-
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(test_event_queue, &queue_item_1, pdMS_TO_TICKS(500)));
306-
TEST_ASSERT_EQUAL(pdTRUE, xQueueReceive(test_event_queue, &queue_item_2, pdMS_TO_TICKS(500)));
307-
308-
const hid_host_device_config_t dev_config = {
309-
.callback = test_hid_host_interface_event_close,
310-
.callback_arg = NULL
311-
};
312-
313-
// Open devices normally
314-
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(queue_item_1.hid_device_handle, &dev_config));
315-
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(queue_item_2.hid_device_handle, &dev_config));
316-
317-
printf("Issue suspend\n");
318-
TEST_ASSERT_EQUAL(ESP_OK, usb_host_lib_root_port_suspend());
319-
vTaskDelay(pdMS_TO_TICKS(100));
320-
321-
// Try to start devices with the root port suspended
322-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_device_start(queue_item_1.hid_device_handle));
323-
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_device_start(queue_item_2.hid_device_handle));
324-
325-
printf("Issue resume\n");
326-
TEST_ASSERT_EQUAL(ESP_OK, usb_host_lib_root_port_resume());
327-
vTaskDelay(pdMS_TO_TICKS(100));
328-
329-
// Teardown
330-
test_hid_teardown();
331-
vQueueDelete(test_event_queue);
332-
test_event_queue = NULL;
333-
}
334284
#endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
335285

336286
// ----------------------- Public --------------------------
@@ -351,6 +301,5 @@ TEST_CASE("error_handling", "[hid_host]")
351301
test_uninstall_hid_driver_while_device_is_present();
352302
#ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
353303
test_open_suspended_device();
354-
test_start_suspended_device();
355304
#endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
356305
}

0 commit comments

Comments
 (0)