Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions host/usb/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ All notable changes to this component will be documented in this file.

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).

## [unreleased]

### Added

- Global suspend/resume (https://github.com/espressif/esp-usb/pull/275)

## [1.0.1] - 2025-10-16

### Fixed
Expand Down
29 changes: 29 additions & 0 deletions host/usb/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,35 @@ menu "USB-OTG"

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

config USB_HOST_RESUME_HOLD_MS
int "Resume hold delay in ms"
default 30
help
"The host may signal resume (TDRSMDN) at any time. It must send the resume signaling for at least
20 ms and then end the resume signaling.."
See USB 2.0 chapter 7.1.7.7 for more details.

The default value is set to 30 ms to be safe.

config USB_HOST_RESUME_RECOVERY_MS
int "Resume recovery delay in ms"
default 20
help
"The USB System Software must provide a 10 ms resume recovery time (TRSMRCY) during which it will
not attempt to access any device connected to the affected (just-activated) bus segment."
See the USB 2.0 chapter 7.1.7.7 for more details.

The default value is set to 20 ms to be safe.

config USB_HOST_SUSPEND_ENTRY_MS
int "Suspend entry delay in ms"
default 20
help
"The device must actually be suspended, drawing only suspend current from the bus after no more
than 10 ms of bus inactivity on all its ports." See the USB 2.0 chapter 7.1.7.6 for more details.

The default values is set to 20 ms to be safe.

endmenu #Root Hub configuration

config USB_HOST_HUBS_SUPPORTED
Expand Down
79 changes: 74 additions & 5 deletions host/usb/include/usb/usb_host.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,26 @@ typedef struct usb_host_client_handle_s *usb_host_client_handle_t;

#define USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS 0x01 /**< All clients have been deregistered from the USB Host Library */
#define USB_HOST_LIB_EVENT_FLAGS_ALL_FREE 0x02 /**< The USB Host Library has freed all devices */
#define USB_HOST_LIB_EVENT_FLAGS_AUTO_SUSPEND 0x04 /**< Timer for automatic suspend has expired */
Comment thread
roma-jam marked this conversation as resolved.

/**
* @brief The type event in a client event message
*/
typedef enum {
USB_HOST_CLIENT_EVENT_NEW_DEV, /**< A new device has been enumerated and added to the USB Host Library */
USB_HOST_CLIENT_EVENT_DEV_GONE, /**< A device opened by the client is now gone */
USB_HOST_CLIENT_EVENT_DEV_SUSPENDED, /**< A device opened by the client is now suspended */
USB_HOST_CLIENT_EVENT_DEV_RESUMED, /**< A device opened by the client is now resumed */
} usb_host_client_event_t;

/**
* @brief USB Host lib power management timer type
*/
typedef enum {
USB_HOST_LIB_PM_SUSPEND_ONE_SHOT, /**< USB Host lib power management -> Auto suspend one-shot timer */
USB_HOST_LIB_PM_SUSPEND_PERIODIC, /**< USB Host lib power management -> Auto suspend periodic timer */
} usb_host_lib_pm_t;

/**
* @brief Client event message
*
Expand All @@ -69,6 +80,9 @@ typedef struct {
struct {
usb_device_handle_t dev_hdl; /**< The handle of the device that was gone */
} dev_gone; /**< Gone device info */
struct {
usb_device_handle_t dev_hdl; /**< The handle of the device that was suspended/resumed */
} dev_suspend_resume; /**< Suspend/Resume device info */
};
} usb_host_client_event_msg_t;

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

// ---------------------- Callbacks ------------------------
Expand Down Expand Up @@ -243,6 +258,61 @@ esp_err_t usb_host_lib_info(usb_host_lib_info_t *info_ret);
*/
esp_err_t usb_host_lib_set_root_port_power(bool enable);

/**
* @brief Suspend the root port
*
* - The function checks, if a device is connected and if a transfer is submitted
* - Then halts and flushes all endpoints of all the connected devices and suspends the root port
* - Finally, it notifies all the clients which opened the device, that the device is now suspended
*
* @note Remote wakeup from device is not implemented yet
* @note The root port and the devices are not suspended immediately after returning from this function, this function
* only sets actions for devices and root port, which are handled by the usb_host_lib_handle_events() in separate task.
* @return
* - ESP_OK: Root port marked to be suspended, or already issuing a suspend sequence
* - ESP_ERR_NOT_FOUND: No device is connected
* - ESP_ERR_INVALID_STATE: Root port is not in correct state to be suspended
*/
esp_err_t usb_host_lib_root_port_suspend(void);

/**
* @brief Resume the root port from suspended state
*
* - The function resumes the root port from suspended state
* - Then resumes all the endpoints of all the connected devices
* - Finally, it notifies all the clients which opened the device, that the device is now resumed
*
* @note The root port and the devices are not resumed immediately after returning from this function, this function
* only sets actions for devices and root port, which are handled by the usb_host_lib_handle_events() in separate task.
* @return
* - ESP_OK: Root port marked to be resumed, or already issuing a resume sequence
* - ESP_ERR_NOT_FOUND: No device is connected
Comment thread
roma-jam marked this conversation as resolved.
* - ESP_ERR_INVALID_STATE: Root port is not in correct state to be resumed
*/
esp_err_t usb_host_lib_root_port_resume(void);

/**
* @brief Set auto power management timer
*
* - The function sets the auto suspend timer, used for global suspend of the root port
* - The timer is either one-shot or periodic
* - The timerexpires after the set period, only if there is no activity on the USB Bus
* - The timer resets (if enabled) every time, the usb_host_client_handle_events() handles any client events,
* or the usb_host_lib_handle_events() handles any host lib events, thus checking any activity on all the
* registered clients or inside the host lib
* - Once the timer expires, an auto_pm_timer_cb() is called, which delivers USB Host lib event flags
*
* @note set the timer interval to 0, to disable the timer (in case NO auto suspend functionality is required anymore)
* @note this function is not ISR safe
* @param[in] timer_type Power management timer type (periodic/one-shot)
* @param[in] timer_interval_ms Interval after which a usb_host lib event flag is delivered (0 to disable running timer)
* @return
* - ESP_OK: Timer set or stopped
* - ESP_ERR_INVALID_STATE: USB Host lib is not installed
* - ESP_FAIL: Timer was not configured correctly
*/
esp_err_t usb_host_lib_set_auto_pm(usb_host_lib_pm_t timer_type, size_t timer_interval_ms);

// ------------------------------------------------ Client Functions ---------------------------------------------------

/**
Expand Down Expand Up @@ -628,7 +698,7 @@ esp_err_t usb_host_transfer_free(usb_transfer_t *transfer);
* - ESP_ERR_INVALID_ARG: Invalid argument
* - ESP_ERR_NOT_FINISHED: Transfer already in-flight
* - ESP_ERR_NOT_FOUND: Endpoint address not found
* - ESP_ERR_INVALID_STATE: Endpoint pipe is not in a correct state to submit transfer
* - ESP_ERR_INVALID_STATE: Endpoint pipe or root port is not in a correct state to submit transfer, or to resume the root port
*/
esp_err_t usb_host_transfer_submit(usb_transfer_t *transfer);

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

Expand Down
37 changes: 34 additions & 3 deletions host/usb/private_include/hcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ typedef enum {
HCD_PORT_STATE_DISCONNECTED, /**< The port is powered but no device is connected */
HCD_PORT_STATE_DISABLED, /**< A device has connected to the port but has not been reset. SOF/keep alive are not being sent */
HCD_PORT_STATE_RESETTING, /**< The port is issuing a reset condition */
HCD_PORT_STATE_SUSPENDING, /**< The port is issuing a suspend condition */
HCD_PORT_STATE_SUSPENDED, /**< The port has been suspended. */
HCD_PORT_STATE_RESUMING, /**< The port is issuing a resume condition */
HCD_PORT_STATE_ENABLED, /**< The port has been enabled. SOF/keep alive are being sent */
Expand Down Expand Up @@ -348,6 +349,17 @@ esp_err_t hcd_port_recover(hcd_port_handle_t port_hdl);
*/
void *hcd_port_get_context(hcd_port_handle_t port_hdl);

/**
* @brief Check if all the HCD pipes routed through this port are idle
*
* @param[in] port_hdl Port handle
*
* @return
* - ESP_OK all HCD pipes are idle
* - ESP_ERR_INVALID_STATE port is not in correct state
* - ESP_ERR_NOT_FINISHED HCD pipes are still enqueued and processing
*/
esp_err_t hcd_port_check_all_pipes_idle(hcd_port_handle_t port_hdl);
// --------------------------------------------------- HCD Pipes -------------------------------------------------------

/**
Expand Down Expand Up @@ -499,18 +511,37 @@ hcd_pipe_event_t hcd_pipe_get_event(hcd_pipe_handle_t pipe_hdl);
/**
* @brief Enqueue an URB to a particular pipe
*
* The following conditions must be met before an URB can be enqueued:
* An URB can be either:
* - Enqueued to an active pipe and start processing right away, or
* - Deferred into a pending queue for later processing
*
* This depends on the pipe's state and a root port's state through which the pipe is routed.
*
* The following conditions must be met before an URB can be either enqueued or deferred:
* - The URB is properly initialized (data buffer and transfer length are set)
* - The URB must not already be enqueued
*
* If enqueueing an URB:
* - The pipe must be in the HCD_PIPE_STATE_ACTIVE state
* - The pipe's port must be in the HCD_PORT_STATE_ENABLED state
* - The pipe cannot be executing a command
*
* IF deferring an URB:
* - The pipe must be in the HCD_PIPE_STATE_HALTED state
* - The pipe's port must be in the HCD_PORT_STATE_SUSPENDED or HCD_PORT_STATE_RESUMING state
*
* Deferring of URBs is used, when a user submits a transfer while the root port is suspended (or is resuming) and the
* transfers can't be yet executed
* Deferred URBs will be put into a pipe's pending queue
* Deferred URBs will be executed automatically upon a pipe's clear command, which is a part of a global resume sequence
*
* @param[in] pipe_hdl Pipe handle
* @param[in] urb URB to enqueue
*
* @return
* - ESP_OK: URB enqueued successfully
* - ESP_ERR_INVALID_STATE: Conditions not met to enqueue URB
* - ESP_OK: URB enqueued, or deferred successfully
* - ESP_ERR_INVALID_STATE: Conditions not met to enqueue or defer URB
* - ESP_ERR_INVALID_SIZE: Invalid size of the URB
*/
esp_err_t hcd_urb_enqueue(hcd_pipe_handle_t pipe_hdl, urb_t *urb);

Expand Down
71 changes: 66 additions & 5 deletions host/usb/private_include/hub.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -33,15 +33,15 @@ typedef struct {
union {
struct {
unsigned int uid; /**< Unique device ID */
} connected; /**< HUB_EVENT_DEV_CONNECTED specific data */
} connected; /**< HUB_EVENT_CONNECTED specific data */

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

struct {
unsigned int uid; /**< Unique device ID */
} disconnected; /**< HUB_EVENT_DEV_DISCONNECTED specific data */
} disconnected; /**< HUB_EVENT_DISCONNECTED specific data */
};
} hub_event_data_t;

Expand Down Expand Up @@ -112,7 +112,7 @@ esp_err_t hub_uninstall(void);
* @note This function should only be called from the Host Library task
*
* @return
* - ESP_OK: Hub driver started successfully
* - ESP_OK: Root port has been powered on
* - ESP_ERR_INVALID_STATE: Hub driver is not installed, or root port is in other state than not powered
*/
esp_err_t hub_root_start(void);
Expand All @@ -123,11 +123,72 @@ esp_err_t hub_root_start(void);
* This will power OFF the root port
*
* @return
* - ESP_OK: Hub driver started successfully
* - ESP_OK: Root port has been powered off
* - ESP_ERR_INVALID_STATE: Hub driver is not installed, or root port is in not powered state
*/
esp_err_t hub_root_stop(void);

/**
* @brief Check if root port is in suspended state
*
* This will check root port state
*
* @return
* - true: Root port is in suspended state
* - false: Root port is in any other state
*/
bool hub_root_is_suspended(void);

/**
* @brief Check if the Hub driver's root port can be suspended
*
* This will check if all HCD pipes are idle and which state the root port is in
*
* @return
* - ESP_OK: Hub driver's root port can be suspended
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
* - ESP_ERR_NOT_FINISHED: HCD pipes routed through this port are still executing
* - ESP_ERR_TIMEOUT: Root port is already issuing a suspend command and is within a SUSPEND_ENTRY_MS timeout
* - ESP_ERR_NOT_ALLOWED: Root port is in other than enabled state
*/
esp_err_t hub_root_can_suspend(void);

/**
* @brief Check if the Hub driver's root port can be resumed
*
* @return
* - ESP_OK: Hub driver's root port can be resumed
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
* - ESP_ERR_TIMEOUT: Root port is already issuing a resume command and is within a RESUME_RECOVERY_MS,
* or a RESUME_HOLD_MS timeout
* - ESP_ERR_NOT_ALLOWED: Root port is in other than suspended state, or HCD port is in incorrect state
*/
esp_err_t hub_root_can_resume(void);

/**
* @brief Mark the Hub driver's root port as ready for suspend
*
* This will mark the root port, as ready to be suspended and and will be processed by the hub processing loop
*
* @return
* - ESP_OK: Hub driver suspended successfully
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
* - ESP_ERR_NOT_ALLOWED: Root port is in other than enabled state
*/
esp_err_t hub_root_mark_suspend(void);

/**
* @brief Mark the Hub driver's root port as ready for resume
*
* This will mark the root port, as ready to be resumed and and will be processed by the hub processing loop
*
* @return
* - ESP_OK: Hub driver resumed successfully
* - ESP_ERR_INVALID_STATE: Hub driver is not installed
* - ESP_ERR_NOT_ALLOWED: Root port is in other than suspended state
*/
esp_err_t hub_root_mark_resume(void);

/**
* @brief Indicate to the Hub driver that a device's port can be recycled
*
Expand Down
Loading
Loading