Skip to content

Commit 3394856

Browse files
feat(hid_host): Add support for global suspend/resume
1 parent 4e6c17e commit 3394856

File tree

2 files changed

+185
-11
lines changed

2 files changed

+185
-11
lines changed

host/class/hid/usb_host_hid/hid_host.c

Lines changed: 174 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -91,6 +91,7 @@ typedef enum {
9191
HID_INTERFACE_STATE_READY, /**< HID Interface opened and ready to start transfer */
9292
HID_INTERFACE_STATE_ACTIVE, /**< HID Interface is in use */
9393
HID_INTERFACE_STATE_WAIT_USER_DELETION, /**< HID Interface wait user to be removed */
94+
HID_INTERFACE_STATE_SUSPENDED, /**< HID Interface (and the whole device) is suspended */
9495
HID_INTERFACE_STATE_MAX
9596
} hid_iface_state_t;
9697

@@ -550,6 +551,146 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
550551
return ESP_OK;
551552
}
552553

554+
#ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
555+
556+
/**
557+
* @brief Suspend interface
558+
*
559+
* @note endpoints are already halted and flushed when a global suspend is issues by the USB Host lib
560+
* @param[in] iface HID interface handle
561+
* @param[in] stop_ep Stop (halt and flush) endpoint
562+
*
563+
* @return esp_err_t
564+
*/
565+
static esp_err_t hid_host_suspend_interface(hid_iface_t *iface, bool stop_ep)
566+
{
567+
HID_RETURN_ON_INVALID_ARG(iface);
568+
HID_RETURN_ON_INVALID_ARG(iface->parent);
569+
570+
HID_RETURN_ON_FALSE(is_interface_in_list(iface),
571+
ESP_ERR_NOT_FOUND,
572+
"Interface handle not found");
573+
574+
HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_ACTIVE == iface->state),
575+
ESP_ERR_INVALID_STATE,
576+
"Interface wrong state");
577+
578+
// EP is usually stopped by usb_host_lib, in case of global suspend, thus no need to Halt->Flush EP again
579+
if (stop_ep) {
580+
HID_RETURN_ON_ERROR( usb_host_endpoint_halt(iface->parent->dev_hdl, iface->ep_in),
581+
"Unable to HALT EP");
582+
HID_RETURN_ON_ERROR( usb_host_endpoint_flush(iface->parent->dev_hdl, iface->ep_in),
583+
"Unable to FLUSH EP");
584+
// Don't clear EP, it must remain halted, when the device is in suspended state
585+
}
586+
587+
iface->state = HID_INTERFACE_STATE_SUSPENDED;
588+
589+
return ESP_OK;
590+
}
591+
592+
/**
593+
* @brief Resume interface
594+
*
595+
* @note endpoints are already cleared when a global resume is issues by the USB Host lib
596+
* @param[in] iface HID interface handle
597+
* @param[in] resume_ep Resume (clear) endpoint
598+
*
599+
* @return esp_err_t
600+
*/
601+
static esp_err_t hid_host_resume_interface(hid_iface_t *iface, bool resume_ep)
602+
{
603+
HID_RETURN_ON_INVALID_ARG(iface);
604+
HID_RETURN_ON_INVALID_ARG(iface->in_xfer);
605+
HID_RETURN_ON_INVALID_ARG(iface->parent);
606+
607+
HID_RETURN_ON_FALSE(is_interface_in_list(iface),
608+
ESP_ERR_NOT_FOUND,
609+
"Interface handle not found");
610+
611+
HID_RETURN_ON_FALSE ((HID_INTERFACE_STATE_SUSPENDED == iface->state),
612+
ESP_ERR_INVALID_STATE,
613+
"Interface wrong state");
614+
615+
// EP is usually cleared by usb_host_lib, in case of global suspend, thus no need to Clear an EP again
616+
if (resume_ep) {
617+
usb_host_endpoint_clear(iface->parent->dev_hdl, iface->ep_in);
618+
}
619+
620+
iface->state = HID_INTERFACE_STATE_ACTIVE;
621+
622+
// start data transfer
623+
return usb_host_transfer_submit(iface->in_xfer);
624+
}
625+
626+
/**
627+
* @brief Suspend device
628+
*
629+
* @param[in] dev_hdl USB Device handle
630+
*
631+
* @return esp_err_t
632+
*/
633+
static esp_err_t hid_host_device_suspended(usb_device_handle_t dev_hdl)
634+
{
635+
hid_device_t *hid_device = get_hid_device_by_handle(dev_hdl);
636+
HID_RETURN_ON_INVALID_ARG(hid_device);
637+
638+
HID_ENTER_CRITICAL();
639+
hid_iface_t *hid_iface_curr;
640+
hid_iface_t *hid_iface_next;
641+
// Go through list
642+
hid_iface_curr = STAILQ_FIRST(&s_hid_driver->hid_ifaces_tailq);
643+
while (hid_iface_curr != NULL) {
644+
hid_iface_next = STAILQ_NEXT(hid_iface_curr, tailq_entry);
645+
HID_EXIT_CRITICAL();
646+
647+
if (hid_iface_curr->parent && (hid_iface_curr->parent->dev_addr == hid_device->dev_addr)) {
648+
hid_host_suspend_interface(hid_iface_curr, false);
649+
hid_host_user_device_callback(hid_iface_curr, HID_HOST_DRIVER_EVENT_SUSPENDED);
650+
}
651+
HID_ENTER_CRITICAL();
652+
hid_iface_curr = hid_iface_next;
653+
}
654+
HID_EXIT_CRITICAL();
655+
656+
return ESP_OK;
657+
}
658+
659+
/**
660+
* @brief Resume device
661+
*
662+
* @param[in] dev_hdl USB Device handle
663+
*
664+
* @return esp_err_t
665+
*/
666+
static esp_err_t hid_host_device_resumed(usb_device_handle_t dev_hdl)
667+
{
668+
hid_device_t *hid_device = get_hid_device_by_handle(dev_hdl);
669+
HID_RETURN_ON_INVALID_ARG(hid_device);
670+
671+
HID_ENTER_CRITICAL();
672+
hid_iface_t *hid_iface_curr;
673+
hid_iface_t *hid_iface_next;
674+
// Go through list
675+
hid_iface_curr = STAILQ_FIRST(&s_hid_driver->hid_ifaces_tailq);
676+
while (hid_iface_curr != NULL) {
677+
hid_iface_next = STAILQ_NEXT(hid_iface_curr, tailq_entry);
678+
HID_EXIT_CRITICAL();
679+
680+
if (hid_iface_curr->parent && (hid_iface_curr->parent->dev_addr == hid_device->dev_addr)) {
681+
hid_host_resume_interface(hid_iface_curr, false);
682+
hid_host_user_device_callback(hid_iface_curr, HID_HOST_DRIVER_EVENT_RESUMED);
683+
}
684+
HID_ENTER_CRITICAL();
685+
hid_iface_curr = hid_iface_next;
686+
}
687+
HID_EXIT_CRITICAL();
688+
689+
return ESP_OK;
690+
}
691+
692+
#endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
693+
553694
/**
554695
* @brief USB Host Client's event callback
555696
*
@@ -558,10 +699,28 @@ static esp_err_t hid_host_device_disconnected(usb_device_handle_t dev_hdl)
558699
*/
559700
static void client_event_cb(const usb_host_client_event_msg_t *event, void *arg)
560701
{
561-
if (event->event == USB_HOST_CLIENT_EVENT_NEW_DEV) {
702+
switch (event->event) {
703+
case USB_HOST_CLIENT_EVENT_NEW_DEV:
704+
ESP_LOGD(TAG, "New device connected");
562705
hid_host_device_init_attempt(event->new_dev.address);
563-
} else if (event->event == USB_HOST_CLIENT_EVENT_DEV_GONE) {
706+
break;
707+
case USB_HOST_CLIENT_EVENT_DEV_GONE:
708+
ESP_LOGD(TAG, "Device suddenly disconnected");
564709
hid_host_device_disconnected(event->dev_gone.dev_hdl);
710+
break;
711+
#ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
712+
case USB_HOST_CLIENT_EVENT_DEV_SUSPENDED:
713+
ESP_LOGD(TAG, "Device suspended");
714+
hid_host_device_suspended(event->dev_suspend_resume.dev_hdl);
715+
break;
716+
case USB_HOST_CLIENT_EVENT_DEV_RESUMED:
717+
ESP_LOGD(TAG, "Device resumed");
718+
hid_host_device_resumed(event->dev_suspend_resume.dev_hdl);
719+
break;
720+
#endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
721+
default:
722+
ESP_LOGW(TAG, "Unrecognized USB Host client event");
723+
break;
565724
}
566725
}
567726

@@ -628,14 +787,19 @@ static esp_err_t hid_host_disable_interface(hid_iface_t *iface)
628787
ESP_ERR_NOT_FOUND,
629788
"Interface handle not found");
630789

631-
HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_ACTIVE == iface->state),
790+
HID_RETURN_ON_FALSE((HID_INTERFACE_STATE_ACTIVE == iface->state ||
791+
HID_INTERFACE_STATE_SUSPENDED == iface->state),
632792
ESP_ERR_INVALID_STATE,
633793
"Interface wrong state");
634794

635-
HID_RETURN_ON_ERROR( usb_host_endpoint_halt(iface->parent->dev_hdl, iface->ep_in),
636-
"Unable to HALT EP");
637-
HID_RETURN_ON_ERROR( usb_host_endpoint_flush(iface->parent->dev_hdl, iface->ep_in),
638-
"Unable to FLUSH EP");
795+
if (HID_INTERFACE_STATE_ACTIVE == iface->state) {
796+
HID_RETURN_ON_ERROR( usb_host_endpoint_halt(iface->parent->dev_hdl, iface->ep_in),
797+
"Unable to HALT EP");
798+
HID_RETURN_ON_ERROR( usb_host_endpoint_flush(iface->parent->dev_hdl, iface->ep_in),
799+
"Unable to FLUSH EP");
800+
}
801+
// If interface state is suspended, the EP is already flushed and halted, only clear the EP
802+
// If suspended, may return ESP_ERR_INVALID_STATE
639803
usb_host_endpoint_clear(iface->parent->dev_hdl, iface->ep_in);
640804

641805
iface->state = HID_INTERFACE_STATE_READY;
@@ -1204,15 +1368,15 @@ esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle)
12041368
hid_iface->dev_params.iface_num,
12051369
hid_iface->state);
12061370

1207-
if (HID_INTERFACE_STATE_ACTIVE == hid_iface->state) {
1371+
if (HID_INTERFACE_STATE_ACTIVE == hid_iface->state ||
1372+
HID_INTERFACE_STATE_SUSPENDED == hid_iface->state) {
12081373
HID_RETURN_ON_ERROR( hid_host_disable_interface(hid_iface),
12091374
"Unable to disable HID Interface");
12101375
}
12111376

12121377
if (HID_INTERFACE_STATE_READY == hid_iface->state) {
12131378
HID_RETURN_ON_ERROR( hid_host_interface_release_and_free_transfer(hid_iface),
12141379
"Unable to release HID Interface");
1215-
12161380
// If the device is closing by user before device detached we need to flush user callback here
12171381
free(hid_iface->report_desc);
12181382
hid_iface->report_desc = NULL;

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -11,6 +11,7 @@
1111
#include "esp_err.h"
1212
#include <freertos/FreeRTOS.h>
1313

14+
#include "usb/usb_host.h"
1415
#include "hid.h"
1516

1617
#ifdef __cplusplus
@@ -29,6 +30,11 @@ extern "C" {
2930
*/
3031
#define HID_STR_DESC_MAX_LENGTH 32
3132

33+
// For backward compatibility, remove after IDF 5.x EOL TODO IDF version
34+
#ifdef USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND
35+
#define HID_HOST_SUSPEND_RESUME_API_SUPPORTED
36+
#endif
37+
3238
typedef struct hid_interface *hid_host_device_handle_t; /**< Device Handle. Handle to a particular HID interface */
3339

3440
// ------------------------ USB HID Host events --------------------------------
@@ -37,6 +43,10 @@ typedef struct hid_interface *hid_host_device_handle_t; /**< Device Handle. H
3743
*/
3844
typedef enum {
3945
HID_HOST_DRIVER_EVENT_CONNECTED = 0x00, /**< HID Device has been found in connected USB device (at least one) */
46+
#ifdef HID_HOST_SUSPEND_RESUME_API_SUPPORTED
47+
HID_HOST_DRIVER_EVENT_SUSPENDED, /**< HID Device has been suspended */
48+
HID_HOST_DRIVER_EVENT_RESUMED, /**< HID Device has been resumed */
49+
#endif // HID_HOST_SUSPEND_RESUME_API_SUPPORTED
4050
} hid_host_driver_event_t;
4151

4252
/**

0 commit comments

Comments
 (0)