Skip to content

Commit d6209d7

Browse files
feat(usb_host): Global suspend/resume
- Suspend/Resume of the root port (global) - Auto resume by transfer submit feature - No remote wake-up feature - Closes espressif/esp-idf#14805
1 parent bf10872 commit d6209d7

26 files changed

+3196
-89
lines changed

host/usb/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this component will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7+
## [unreleased]
8+
9+
### Added
10+
11+
- Global suspend/resume (https://github.com/espressif/esp-usb/pull/275)
12+
713
## [1.0.1] - 2025-10-16
814

915
### Fixed

host/usb/Kconfig

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,35 @@ menu "USB-OTG"
9797

9898
The default value is set to 10 ms to be safe.
9999

100+
config USB_HOST_RESUME_HOLD_MS
101+
int "Resume hold delay in ms"
102+
default 30
103+
help
104+
"The host may signal resume (TDRSMDN) at any time. It must send the resume signaling for at least
105+
20 ms and then end the resume signaling.."
106+
See USB 2.0 chapter 7.1.7.7 for more details.
107+
108+
The default value is set to 30 ms to be safe.
109+
110+
config USB_HOST_RESUME_RECOVERY_MS
111+
int "Resume recovery delay in ms"
112+
default 20
113+
help
114+
"The USB System Software must provide a 10 ms resume recovery time (TRSMRCY) during which it will
115+
not attempt to access any device connected to the affected (just-activated) bus segment."
116+
See the USB 2.0 chapter 7.1.7.7 for more details.
117+
118+
The default value is set to 20 ms to be safe.
119+
120+
config USB_HOST_SUSPEND_ENTRY_MS
121+
int "Suspend entry delay in ms"
122+
default 20
123+
help
124+
"The device must actually be suspended, drawing only suspend current from the bus after no more
125+
than 10 ms of bus inactivity on all its ports." See the USB 2.0 chapter 7.1.7.6 for more details.
126+
127+
The default values is set to 20 ms to be safe.
128+
100129
endmenu #Root Hub configuration
101130

102131
config USB_HOST_HUBS_SUPPORTED

host/usb/include/usb/usb_host.h

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,26 @@ typedef struct usb_host_client_handle_s *usb_host_client_handle_t;
4040

4141
#define USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS 0x01 /**< All clients have been deregistered from the USB Host Library */
4242
#define USB_HOST_LIB_EVENT_FLAGS_ALL_FREE 0x02 /**< The USB Host Library has freed all devices */
43+
#define USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND 0x04 /**< Timer for automatic suspend has expired */
4344

4445
/**
4546
* @brief The type event in a client event message
4647
*/
4748
typedef enum {
4849
USB_HOST_CLIENT_EVENT_NEW_DEV, /**< A new device has been enumerated and added to the USB Host Library */
4950
USB_HOST_CLIENT_EVENT_DEV_GONE, /**< A device opened by the client is now gone */
51+
USB_HOST_CLIENT_EVENT_DEV_SUSPENDED, /**< A device opened by the client is now suspended */
52+
USB_HOST_CLIENT_EVENT_DEV_RESUMED, /**< A device opened by the client is now resumed */
5053
} usb_host_client_event_t;
5154

55+
/**
56+
* @brief USB Host lib power management timer type
57+
*/
58+
typedef enum {
59+
USB_HOST_LIB_PM_SUSPEND_ONE_SHOT, /**< USB Host lib power management -> Auto suspend one-shot timer */
60+
USB_HOST_LIB_PM_SUSPEND_PERIODIC, /**< USB Host lib power management -> Auto suspend periodic timer */
61+
} usb_host_lib_pm_t;
62+
5263
/**
5364
* @brief Client event message
5465
*
@@ -69,6 +80,9 @@ typedef struct {
6980
struct {
7081
usb_device_handle_t dev_hdl; /**< The handle of the device that was gone */
7182
} dev_gone; /**< Gone device info */
83+
struct {
84+
usb_device_handle_t dev_hdl; /**< The handle of the device that was suspended/resumed */
85+
} dev_suspend_resume; /**< Suspend/Resume device info */
7286
};
7387
} usb_host_client_event_msg_t;
7488

@@ -78,8 +92,9 @@ typedef struct {
7892
* @brief Current information about the USB Host Library obtained via usb_host_lib_info()
7993
*/
8094
typedef struct {
81-
int num_devices; /**< Current number of connected (and enumerated) devices */
82-
int num_clients; /**< Current number of registered clients */
95+
int num_devices; /**< Current number of connected (and enumerated) devices */
96+
int num_clients; /**< Current number of registered clients */
97+
bool root_port_suspended; /**< Current status of the root port (suspended/resumed) */
8398
} usb_host_lib_info_t;
8499

85100
// ---------------------- Callbacks ------------------------
@@ -243,6 +258,61 @@ esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret);
243258
*/
244259
esp_err_t usb_host_lib_set_root_port_power(bool enable);
245260

261+
/**
262+
* @brief Suspend the root port
263+
*
264+
* - The function checks, if a device is connected and if a transfer is submitted
265+
* - Then halts and flushes all endpoints of all the connected devices and suspends the root port
266+
* - Finally, it notifies all the clients which opened the device, that the device is now suspended
267+
*
268+
* @note Remote wakeup from device is not implemented yet
269+
* @note The root port and the devices are not suspended immediately after returning from this function, this function
270+
* only sets actions for devices and root port, which are handled by the usb_host_lib_handle_events() in separate task.
271+
* @return
272+
* - ESP_OK: Root port marked to be suspended, or already issuing a suspend sequence
273+
* - ESP_ERR_NOT_FOUND: No device is connected
274+
* - ESP_ERR_INVALID_STATE: Root port is not in correct state to be suspended
275+
*/
276+
esp_err_t usb_host_lib_root_port_suspend(void);
277+
278+
/**
279+
* @brief Resume the root port from suspended state
280+
*
281+
* - The function resumes the root port from suspended state
282+
* - Then resumes all the endpoints of all the connected devices
283+
* - Finally, it notifies all the clients which opened the device, that the device is now resumed
284+
*
285+
* @note The root port and the devices are not resumed immediately after returning from this function, this function
286+
* only sets actions for devices and root port, which are handled by the usb_host_lib_handle_events() in separate task.
287+
* @return
288+
* - ESP_OK: Root port marked to be resumed, or already issuing a resume sequence
289+
* - ESP_ERR_NOT_FOUND: No device is connected
290+
* - ESP_ERR_INVALID_STATE: Root port is not in correct state to be resumed
291+
*/
292+
esp_err_t usb_host_lib_root_port_resume(void);
293+
294+
/**
295+
* @brief Set auto power management timer
296+
*
297+
* - The function sets the auto suspend timer, used for global suspend of the root port
298+
* - The timer is either one-shot or periodic
299+
* - The timerexpires after the set period, only if there is no activity on the USB Bus
300+
* - The timer resets (if enabled) every time, the usb_host_client_handle_events() handles any client events,
301+
* or the usb_host_lib_handle_events() handles any host lib events, thus checking any activity on all the
302+
* registered clients or inside the host lib
303+
* - Once the timer expires, an auto_pm_timer_cb() is called, which delivers USB Host lib event flags
304+
*
305+
* @note set the timer interval to 0, to disable the timer (in case NO auto suspend functionality is required anymore)
306+
* @note this function is not ISR safe
307+
* @param[in] timer_type Power management timer type (periodic/one-shot)
308+
* @param[in] timer_interval_ms Interval after which a usb_host lib event flag is delivered (0 to disable running timer)
309+
* @return
310+
* - ESP_OK: Timer set or stopped
311+
* - ESP_ERR_INVALID_STATE: USB Host lib is not installed
312+
* - ESP_FAIL: Timer was not configured correctly
313+
*/
314+
esp_err_t usb_host_lib_set_auto_pm(usb_host_lib_pm_t timer_type, size_t timer_interval_ms);
315+
246316
// ------------------------------------------------ Client Functions ---------------------------------------------------
247317

248318
/**
@@ -628,7 +698,7 @@ esp_err_t usb_host_transfer_free(usb_transfer_t *transfer);
628698
* - ESP_ERR_INVALID_ARG: Invalid argument
629699
* - ESP_ERR_NOT_FINISHED: Transfer already in-flight
630700
* - ESP_ERR_NOT_FOUND: Endpoint address not found
631-
* - ESP_ERR_INVALID_STATE: Endpoint pipe is not in a correct state to submit transfer
701+
* - ESP_ERR_INVALID_STATE: Endpoint pipe or root port is not in a correct state to submit transfer, or to resume the root port
632702
*/
633703
esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer);
634704

@@ -647,8 +717,7 @@ esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer);
647717
* - ESP_OK: Control transfer submitted successfully
648718
* - ESP_ERR_INVALID_ARG: Invalid argument
649719
* - ESP_ERR_NOT_FINISHED: Transfer already in-flight
650-
* - ESP_ERR_NOT_FOUND: Endpoint address not found
651-
* - ESP_ERR_INVALID_STATE: Endpoint pipe is not in a correct state to submit transfer
720+
* - ESP_ERR_INVALID_STATE: Endpoint pipe or root port is not in a correct state to submit transfer, or to resume the root port
652721
*/
653722
esp_err_t usb_host_transfer_submit_control(usb_host_client_handle_t client_hdl, usb_transfer_t *transfer);
654723

host/usb/private_include/hcd.h

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ typedef enum {
3434
HCD_PORT_STATE_DISCONNECTED, /**< The port is powered but no device is connected */
3535
HCD_PORT_STATE_DISABLED, /**< A device has connected to the port but has not been reset. SOF/keep alive are not being sent */
3636
HCD_PORT_STATE_RESETTING, /**< The port is issuing a reset condition */
37+
HCD_PORT_STATE_SUSPENDING, /**< The port is issuing a suspend condition */
3738
HCD_PORT_STATE_SUSPENDED, /**< The port has been suspended. */
3839
HCD_PORT_STATE_RESUMING, /**< The port is issuing a resume condition */
3940
HCD_PORT_STATE_ENABLED, /**< The port has been enabled. SOF/keep alive are being sent */
@@ -348,6 +349,17 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl);
348349
*/
349350
void *hcd_port_get_context(hcd_port_handle_t port_hdl);
350351

352+
/**
353+
* @brief Check if all the HCD pipes routed through this port are idle
354+
*
355+
* @param[in] port_hdl Port handle
356+
*
357+
* @return
358+
* - ESP_OK all HCD pipes are idle
359+
* - ESP_ERR_INVALID_STATE port is not in correct state
360+
* - ESP_ERR_NOT_FINISHED HCD pipes are still enqueued and processing
361+
*/
362+
esp_err_t hcd_port_check_all_pipes_idle(hcd_port_handle_t port_hdl);
351363
// --------------------------------------------------- HCD Pipes -------------------------------------------------------
352364

353365
/**
@@ -499,18 +511,37 @@ hcd_pipe_event_t hcd_pipe_get_event(hcd_pipe_handle_t pipe_hdl);
499511
/**
500512
* @brief Enqueue an URB to a particular pipe
501513
*
502-
* The following conditions must be met before an URB can be enqueued:
514+
* An URB can be either:
515+
* - Enqueued to an active pipe and start processing right away, or
516+
* - Deferred into a pending queue for later processing
517+
*
518+
* This depends on the pipe's state and a root port's state through which the pipe is routed.
519+
*
520+
* The following conditions must be met before an URB can be either enqueued or deferred:
503521
* - The URB is properly initialized (data buffer and transfer length are set)
504522
* - The URB must not already be enqueued
523+
*
524+
* If enqueueing an URB:
505525
* - The pipe must be in the HCD_PIPE_STATE_ACTIVE state
526+
* - The pipe's port must be in the HCD_PORT_STATE_ENABLED state
506527
* - The pipe cannot be executing a command
507528
*
529+
* IF deferring an URB:
530+
* - The pipe must be in the HCD_PIPE_STATE_HALTED state
531+
* - The pipe's port must be in the HCD_PORT_STATE_SUSPENDED or HCD_PORT_STATE_RESUMING state
532+
*
533+
* Deferring of URBs is used, when a user submits a transfer while the root port is suspended (or is resuming) and the
534+
* transfers can't be yet executed
535+
* Deferred URBs will be put into a pipe's pending queue
536+
* Deferred URBs will be executed automatically upon a pipe's clear command, which is a part of a global resume sequence
537+
*
508538
* @param[in] pipe_hdl Pipe handle
509539
* @param[in] urb URB to enqueue
510540
*
511541
* @return
512-
* - ESP_OK: URB enqueued successfully
513-
* - ESP_ERR_INVALID_STATE: Conditions not met to enqueue URB
542+
* - ESP_OK: URB enqueued, or deferred successfully
543+
* - ESP_ERR_INVALID_STATE: Conditions not met to enqueue or defer URB
544+
* - ESP_ERR_INVALID_SIZE: Invalid size of the URB
514545
*/
515546
esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb);
516547

host/usb/private_include/hub.h

Lines changed: 66 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -33,15 +33,15 @@ typedef struct {
3333
union {
3434
struct {
3535
unsigned int uid; /**< Unique device ID */
36-
} connected; /**< HUB_EVENT_DEV_CONNECTED specific data */
36+
} connected; /**< HUB_EVENT_CONNECTED specific data */
3737

3838
struct {
3939
unsigned int uid; /**< Unique device ID */
4040
} reset_completed; /**< HUB_EVENT_RESET_COMPLETED specific data */
4141

4242
struct {
4343
unsigned int uid; /**< Unique device ID */
44-
} disconnected; /**< HUB_EVENT_DEV_DISCONNECTED specific data */
44+
} disconnected; /**< HUB_EVENT_DISCONNECTED specific data */
4545
};
4646
} hub_event_data_t;
4747

@@ -112,7 +112,7 @@ esp_err_t hub_uninstall(void);
112112
* @note This function should only be called from the Host Library task
113113
*
114114
* @return
115-
* - ESP_OK: Hub driver started successfully
115+
* - ESP_OK: Root port has been powered on
116116
* - ESP_ERR_INVALID_STATE: Hub driver is not installed, or root port is in other state than not powered
117117
*/
118118
esp_err_t hub_root_start(void);
@@ -123,11 +123,72 @@ esp_err_t hub_root_start(void);
123123
* This will power OFF the root port
124124
*
125125
* @return
126-
* - ESP_OK: Hub driver started successfully
126+
* - ESP_OK: Root port has been powered off
127127
* - ESP_ERR_INVALID_STATE: Hub driver is not installed, or root port is in not powered state
128128
*/
129129
esp_err_t hub_root_stop(void);
130130

131+
/**
132+
* @brief Check if root port is in suspended state
133+
*
134+
* This will check root port state
135+
*
136+
* @return
137+
* - true: Root port is in suspended state
138+
* - false: Root port is in any other state
139+
*/
140+
bool hub_root_is_suspended(void);
141+
142+
/**
143+
* @brief Check if the Hub driver's root port can be suspended
144+
*
145+
* This will check if all HCD pipes are idle and which state the root port is in
146+
*
147+
* @return
148+
* - ESP_OK: Hub driver's root port can be suspended
149+
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
150+
* - ESP_ERR_NOT_FINISHED: HCD pipes routed through this port are still executing
151+
* - ESP_ERR_TIMEOUT: Root port is already issuing a suspend command and is within a SUSPEND_ENTRY_MS timeout
152+
* - ESP_ERR_NOT_ALLOWED: Root port is in other than enabled state
153+
*/
154+
esp_err_t hub_root_can_suspend(void);
155+
156+
/**
157+
* @brief Check if the Hub driver's root port can be resumed
158+
*
159+
* @return
160+
* - ESP_OK: Hub driver's root port can be resumed
161+
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
162+
* - ESP_ERR_TIMEOUT: Root port is already issuing a resume command and is within a RESUME_RECOVERY_MS,
163+
* or a RESUME_HOLD_MS timeout
164+
* - ESP_ERR_NOT_ALLOWED: Root port is in other than suspended state, or HCD port is in incorrect state
165+
*/
166+
esp_err_t hub_root_can_resume(void);
167+
168+
/**
169+
* @brief Mark the Hub driver's root port as ready for suspend
170+
*
171+
* This will mark the root port, as ready to be suspended and and will be processed by the hub processing loop
172+
*
173+
* @return
174+
* - ESP_OK: Hub driver suspended successfully
175+
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
176+
* - ESP_ERR_NOT_ALLOWED: Root port is in other than enabled state
177+
*/
178+
esp_err_t hub_root_mark_suspend(void);
179+
180+
/**
181+
* @brief Mark the Hub driver's root port as ready for resume
182+
*
183+
* This will mark the root port, as ready to be resumed and and will be processed by the hub processing loop
184+
*
185+
* @return
186+
* - ESP_OK: Hub driver resumed successfully
187+
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
188+
* - ESP_ERR_NOT_ALLOWED: Root port is in other than suspended state
189+
*/
190+
esp_err_t hub_root_mark_resume(void);
191+
131192
/**
132193
* @brief Indicate to the Hub driver that a device's port can be recycled
133194
*

0 commit comments

Comments
 (0)