Merge branch 'feature/usb_host_hub_support_collective_backport_p2_v5.3' into 'release/v5.3'

feat(usb_host): Hub Support Collective backport part 2/3 (v5.3)

See merge request espressif/esp-idf!31365
This commit is contained in:
Jiang Jiang Jian 2024-09-23 13:40:01 +08:00
commit 7c137656aa
24 changed files with 4732 additions and 1033 deletions

View File

@ -14,6 +14,7 @@ set(priv_requires esp_driver_gpio esp_mm) # usb_phy driver relies on gpio drive
if(CONFIG_SOC_USB_OTG_SUPPORTED)
list(APPEND srcs "hcd_dwc.c"
"enum.c"
"hub.c"
"usb_helpers.c"
"usb_host.c"
@ -28,6 +29,10 @@ if(CONFIG_SOC_USB_OTG_SUPPORTED)
list(APPEND priv_includes "private_include")
endif()
if(CONFIG_USB_HOST_HUBS_SUPPORTED)
list(APPEND srcs "ext_hub.c")
endif()
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${include}
PRIV_INCLUDE_DIRS ${priv_includes}

View File

@ -46,73 +46,85 @@ menu "USB-OTG"
bool "Periodic OUT"
endchoice
menu "Root Hub configuration"
menu "Hub Driver Configuration"
config USB_HOST_DEBOUNCE_DELAY_MS
int "Debounce delay in ms"
default 250
menu "Root Port configuration"
config USB_HOST_DEBOUNCE_DELAY_MS
int "Debounce delay in ms"
default 250
help
On connection of a USB device, the USB 2.0 specification requires
a "debounce interval with a minimum duration of 100ms" to allow the connection to stabilize
(see USB 2.0 chapter 7.1.7.3 for more details).
During the debounce interval, no new connection/disconnection events are registered.
The default value is set to 250 ms to be safe.
config USB_HOST_RESET_HOLD_MS
int "Reset hold in ms"
default 30
help
The reset signaling can be generated on any Hub or Host Controller port by request from
the USB System Software. The USB 2.0 specification requires that "the reset signaling must
be driven for a minimum of 10ms" (see USB 2.0 chapter 7.1.7.5 for more details).
After the reset, the hub port will transition to the Enabled state (refer to Section 11.5).
The default value is set to 30 ms to be safe.
config USB_HOST_RESET_RECOVERY_MS
int "Reset recovery delay in ms"
default 30
help
After a port stops driving the reset signal, the USB 2.0 specification requires that
the "USB System Software guarantees a minimum of 10 ms for reset recovery" before the
attached device is expected to respond to data transfers (see USB 2.0 chapter 7.1.7.3 for
more details).
The device may ignore any data transfers during the recovery interval.
The default value is set to 30 ms to be safe.
config USB_HOST_SET_ADDR_RECOVERY_MS
int "SetAddress() recovery time in ms"
default 10
help
"After successful completion of the Status stage, the device is allowed a SetAddress()
recovery interval of 2 ms. At the end of this interval, the device must be able to accept
Setup packets addressed to the new address. Also, at the end of the recovery interval, the
device must not respond to tokens sent to the old address (unless, of course, the old and new
address is the same)." See USB 2.0 chapter 9.2.6.3 for more details.
The default value is set to 10 ms to be safe.
endmenu #Root Hub configuration
config USB_HOST_HUBS_SUPPORTED
bool "Support Hubs"
default n
help
On connection of a USB device, the USB 2.0 specification requires a "debounce interval with a minimum
duration of 100ms" to allow the connection to stabilize (see USB 2.0 chapter 7.1.7.3 for more details).
During the debounce interval, no new connection/disconnection events are registered.
Enables support of external Hubs.
The default value is set to 250 ms to be safe.
config USB_HOST_RESET_HOLD_MS
int "Reset hold in ms"
default 30
config USB_HOST_HUB_MULTI_LEVEL
depends on USB_HOST_HUBS_SUPPORTED
bool "Support multiple Hubs"
default y
help
The reset signaling can be generated on any Hub or Host Controller port by request from the USB System
Software. The USB 2.0 specification requires that "the reset signaling must be driven for a minimum of
10ms" (see USB 2.0 chapter 7.1.7.5 for more details). After the reset, the hub port will transition to
the Enabled state (refer to Section 11.5).
Enables support for connecting multiple Hubs simultaneously.
The default value is set to 30 ms to be safe.
config USB_HOST_RESET_RECOVERY_MS
int "Reset recovery delay in ms"
default 30
help
After a port stops driving the reset signal, the USB 2.0 specification requires that the "USB System
Software guarantees a minimum of 10 ms for reset recovery" before the attached device is expected to
respond to data transfers (see USB 2.0 chapter 7.1.7.3 for more details). The device may ignore any
data transfers during the recovery interval.
The default value is set to 30 ms to be safe.
config USB_HOST_SET_ADDR_RECOVERY_MS
int "SetAddress() recovery time in ms"
default 10
help
"After successful completion of the Status stage, the device is allowed a SetAddress() recovery
interval of 2 ms. At the end of this interval, the device must be able to accept Setup packets
addressed to the new address. Also, at the end of the recovery interval, the device must not respond to
tokens sent to the old address (unless, of course, the old and new address is the same)." See USB 2.0
chapter 9.2.6.3 for more details.
The default value is set to 10 ms to be safe.
endmenu #Root Hub configuration
endmenu #Hub Driver Configuration
config USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
bool "Enable enumeration filter callback"
default n
help
The enumeration filter callback is called before enumeration of each newly attached device. This callback
allows users to control whether a device should be enumerated, and what configuration number to use when
enumerating a device.
The enumeration filter callback is called before enumeration of each newly attached device.
This callback allows users to control whether a device should be enumerated, and what configuration
number to use when enumerating a device.
If enabled, the enumeration filter callback can be set via 'usb_host_config_t' when calling
'usb_host_install()'.
config USB_HOST_EXT_HUB_SUPPORT
depends on IDF_EXPERIMENTAL_FEATURES
bool "Support USB HUB (Experimental)"
default n
help
Feature is under development.
# Hidden or compatibility options
config USB_OTG_SUPPORTED
# Invisible config kept for compatibility

1379
components/usb/enum.c Normal file

File diff suppressed because it is too large Load Diff

1753
components/usb/ext_hub.c Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -66,7 +66,34 @@ typedef enum {
} usb_hub_port_feature_t;
/**
* @brief Size of a USB Hub Port Status and Hub Change results
* @brief USB Hub characteristics
*
* See USB 2.0 spec Table 11-13, offset 3
*/
#define USB_W_HUB_CHARS_PORT_PWR_CTRL_ALL (0) /**< All ports power control at once */
#define USB_W_HUB_CHARS_PORT_PWR_CTRL_INDV (1) /**< Individual port power control */
#define USB_W_HUB_CHARS_PORT_PWR_CTRL_NO (2) /**< No power switching */
#define USB_W_HUB_CHARS_PORT_OVER_CURR_ALL (0) /**< All ports Over-Current reporting */
#define USB_W_HUB_CHARS_PORT_OVER_CURR_INDV (1) /**< Individual port Over-current reporting */
#define USB_W_HUB_CHARS_PORT_OVER_CURR_NO (2) /**< No Over-current Protection support */
#define USB_W_HUB_CHARS_TTTT_8_BITS (0) /**< TT requires at most 8 FS bit times of inter transaction gap on a full-/low-speed downstream bus */
#define USB_W_HUB_CHARS_TTTT_16_BITS (1) /**< TT requires at most 16 FS bit times */
#define USB_W_HUB_CHARS_TTTT_24_BITS (2) /**< TT requires at most 24 FS bit times */
#define USB_W_HUB_CHARS_TTTT_32_BITS (3) /**< TT requires at most 32 FS bit times */
/**
* @brief USB Hub bDeviceProtocol
*/
#define USB_B_DEV_PROTOCOL_HUB_FS (0) /**< Full speed hub */
#define USB_B_DEV_PROTOCOL_HUB_HS_NO_TT (0) /**< Hi-speed hub without TT */
#define USB_B_DEV_PROTOCOL_HUB_HS_SINGLE_TT (1) /**< Hi-speed hub with single TT */
#define USB_B_DEV_PROTOCOL_HUB_HS_MULTI_TT (2) /**< Hi-speed hub with multiple TT */
#define USB_B_DEV_PROTOCOL_HUB_SS (3) /**< Super speed hub */
/**
* @brief USB Hub Port Status and Hub Change results size
*/
#define USB_PORT_STATUS_SIZE 4
@ -148,7 +175,17 @@ typedef struct {
uint8_t bDescLength; /**< Number of bytes in this descriptor, including this byte */
uint8_t bDescriptorType; /**< Descriptor Type, value: 29H for Hub descriptor */
uint8_t bNbrPorts; /**< Number of downstream facing ports that this Hub supports */
uint16_t wHubCharacteristics; /**< Logical Power Switching Mode, Compound Device, Over-current Protection Mode, TT Think Time, Port Indicators Supported */
union {
struct {
uint16_t power_switching: 2;
uint16_t compound: 1;
uint16_t ovr_current_protect: 2;
uint16_t tt_think_time: 2;
uint16_t indicator_support: 1;
uint16_t reserved: 8;
};
uint16_t val; /**< Hub Characteristics value */
} wHubCharacteristics; /**< Hub Characteristics */
uint8_t bPwrOn2PwrGood; /**< Time (in 2 ms intervals) from the time the power-on sequence begins on a port until power is good on that port */
uint8_t bHubContrCurrent; /**< Maximum current requirements of the Hub Controller electronics in mA. */
} __attribute__((packed)) usb_hub_descriptor_t;

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -101,6 +101,21 @@ typedef union {
} usb_setup_packet_t;
ESP_STATIC_ASSERT(sizeof(usb_setup_packet_t) == USB_SETUP_PACKET_SIZE, "Size of usb_setup_packet_t incorrect");
/**
* @brief Structure representing a USB device status
*
* See Figures 9-4 Information Returned by a GetStatus() Request to a Device of USB2.0 specification for more details
*/
typedef union {
struct {
uint16_t self_powered: 1; /**< 1 - Device is currently self-powered, 0 - bus powered */
uint16_t remote_wakeup: 1; /**< 1 - the ability of the device to signal remote wakeup is enabled, 0 - the ability of the device to signal remote wakeup is disabled. */
uint16_t reserved: 14; /**< reserved */
} USB_DESC_ATTR; /**< Packed */
uint16_t val; /**< Device status value */
} usb_device_status_t;
ESP_STATIC_ASSERT(sizeof(usb_device_status_t) == sizeof(uint16_t), "Size of usb_device_status_t incorrect");
/**
* @brief Bit masks belonging to the bmRequestType field of a setup packet
*/
@ -144,6 +159,19 @@ ESP_STATIC_ASSERT(sizeof(usb_setup_packet_t) == USB_SETUP_PACKET_SIZE, "Size of
#define USB_W_VALUE_DT_OTHER_SPEED_CONFIG 0x07
#define USB_W_VALUE_DT_INTERFACE_POWER 0x08
/**
* @brief Initializer for a GET_STATUS request
*
* Sets the address of a connected device
*/
#define USB_SETUP_PACKET_INIT_GET_STATUS(setup_pkt_ptr) ({ \
(setup_pkt_ptr)->bmRequestType = USB_BM_REQUEST_TYPE_DIR_IN | USB_BM_REQUEST_TYPE_TYPE_STANDARD | USB_BM_REQUEST_TYPE_RECIP_DEVICE; \
(setup_pkt_ptr)->bRequest = USB_B_REQUEST_GET_STATUS; \
(setup_pkt_ptr)->wValue = 0; \
(setup_pkt_ptr)->wIndex = 0; \
(setup_pkt_ptr)->wLength = 2; \
})
/**
* @brief Initializer for a SET_ADDRESS request
*

View File

@ -70,10 +70,19 @@ typedef struct usb_device_handle_s *usb_device_handle_t;
*/
typedef bool (*usb_host_enum_filter_cb_t)(const usb_device_desc_t *dev_desc, uint8_t *bConfigurationValue);
/**
* @brief Parent device information
*/
typedef struct {
usb_device_handle_t dev_hdl; /**< Device's parent handle */
uint8_t port_num; /**< Device's parent port number */
} usb_parent_dev_info_t;
/**
* @brief Basic information of an enumerated device
*/
typedef struct {
usb_parent_dev_info_t parent; /**< Device's parent information */
usb_speed_t speed; /**< Device's speed */
uint8_t dev_addr; /**< Device's address */
uint8_t bMaxPacketSize0; /**< The maximum packet size of the device's default endpoint */

View File

@ -0,0 +1,159 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "hcd.h"
#include "usbh.h"
#include "usb/usb_types_stack.h"
#include "usb/usb_types_ch9.h"
#ifdef __cplusplus
extern "C" {
#endif
// ---------------------- Settings & Configuration -----------------------------
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK 1
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
// -------------------------- Public Types -------------------------------------
// ---------------------------- Handles ----------------------------------------
/**
* @brief Handle of enumeration control object
*/
typedef struct enum_ctx_handle_s * enum_ctx_handle_t;
// ------------------------------ Events ---------------------------------------
/**
* @brief Event data object for Enumerator driver events
*/
typedef enum {
ENUM_EVENT_STARTED, /**< Enumeration of a device has started */
ENUM_EVENT_RESET_REQUIRED, /**< Enumerating device requires a reset */
ENUM_EVENT_COMPLETED, /**< Enumeration of a device has completed */
ENUM_EVENT_CANCELED, /**< Enumeration of a device was canceled */
} enum_event_t;
typedef struct {
enum_event_t event; /**< Enumerator driver event */
union {
struct {
unsigned int uid; /**< Device unique ID */
usb_device_handle_t parent_dev_hdl; /**< Parent of the enumerating device */
uint8_t parent_port_num; /**< Parent port number of the enumerating device */
} started; /**< ENUM_EVENT_STARTED specific data */
struct {
usb_device_handle_t parent_dev_hdl; /**< Parent of the enumerating device */
uint8_t parent_port_num; /**< Parent port number of the enumerating device */
} reset_req; /**< ENUM_EVENT_RESET_REQUIRED specific data */
struct {
usb_device_handle_t parent_dev_hdl; /**< Parent of the enumerating device */
uint8_t parent_port_num; /**< Parent port number of the enumerating device */
usb_device_handle_t dev_hdl; /**< Handle of the enumerating device */
uint8_t dev_addr; /**< Address of the enumerating device */
} complete; /**< ENUM_EVENT_COMPLETED specific data */
struct {
usb_device_handle_t parent_dev_hdl; /**< Parent of the enumerating device */
uint8_t parent_port_num; /**< Parent port number of the enumerating device */
} canceled; /**< ENUM_EVENT_CANCELED specific data */
};
} enum_event_data_t;
// ---------------------------- Callbacks --------------------------------------
/**
* @brief Callback used to indicate that the Enumerator has an event
*/
typedef void (*enum_event_cb_t)(enum_event_data_t *event_data, void *arg);
/**
* @brief Enum driver configuration
*/
typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */
enum_event_cb_t enum_event_cb; /**< Enum event callback */
void *enum_event_cb_arg; /**< Enum event callback argument */
#if ENABLE_ENUM_FILTER_CALLBACK
usb_host_enum_filter_cb_t enum_filter_cb; /**< Set device configuration callback */
void *enum_filter_cb_arg; /**< Set device configuration callback argument */
#endif // ENABLE_ENUM_FILTER_CALLBACK
} enum_config_t;
/**
* @brief Install Enumerator driver
*
* Entry:
* - USBH must already be installed
* - HUB must already be installed
*
* @param[in] enum_config Enumeration driver configuration
* @param[out] client_ret Unique pointer to identify Enum Driver as a USB Host client
* @return esp_err_t
*/
esp_err_t enum_install(enum_config_t *enum_config, void **client_ret);
/**
* @brief Uninstall Enumerator driver
*
* This must be called before uninstalling the HUB and USBH
*
* @return esp_err_t
*/
esp_err_t enum_uninstall(void);
/**
* @brief Start the enumeration process
*
* This will start the enumeration process for the device currently at address 0
*
* @param[in] uid Unique device ID
* @retval ESP_OK: Enumeration process started
* @retval ESP_ERR_NOT_FOUND: No device at address 0
*/
esp_err_t enum_start(unsigned int uid);
/**
* @brief Continue enumeration process
*
* This will continue the enumeration process. Typically called after the successful
* handling of a request from the Enumerator driver (such as ENUM_EVENT_RESET_REQUIRED)
*
* @param[in] uid Unique device ID
* @return esp_err_t
*/
esp_err_t enum_proceed(unsigned int uid);
/**
* @brief Cancel the enumeration process
*
* This will cancel enumeration process for device object under enumeration
*
* @return esp_err_t
*/
esp_err_t enum_cancel(unsigned int uid);
/**
* @brief Enumerator processing function
*
* Processing function that must be called repeatedly to process enumeration stages
*
* @return esp_err_t
*/
esp_err_t enum_process(void);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,249 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#include "hcd.h"
#include "usbh.h"
#include "usb/usb_types_stack.h"
#include "usb/usb_types_ch9.h"
#include "usb/usb_types_ch11.h"
#if CONFIG_USB_HOST_HUB_MULTI_LEVEL
#define ENABLE_MULTIPLE_HUBS 1
#endif // CONFIG_USB_HOST_HUB_MULTI_LEVEL
#ifdef __cplusplus
extern "C" {
#endif
// ----------------------------- Handles ---------------------------------------
typedef struct ext_hub_s *ext_hub_handle_t;
// ---------------------------- Callbacks --------------------------------------
/**
* @brief Callback used to indicate that the External Hub Driver requires process callback
* For Hub Driver only
*/
typedef bool (*ext_hub_cb_t)(bool in_isr, void *user_arg);
// ------------------------ External Port API typedefs -------------------------
/**
* @brief External Hub Port driver
*/
typedef struct {
esp_err_t (*new)(void *port_cfg, void **port_hdl);
esp_err_t (*reset)(void *port_hdl);
esp_err_t (*recycle)(void *port_hdl);
esp_err_t (*active)(void *port_hdl);
esp_err_t (*disable)(void *port_hdl);
esp_err_t (*gone)(void *port_hdl);
esp_err_t (*free)(void *port_hdl);
esp_err_t (*get_speed)(void *por_hdl, usb_speed_t *speed);
esp_err_t (*get_status)(void *port_hdl);
esp_err_t (*set_status)(void *port_hdl, const usb_port_status_t *status);
esp_err_t (*req_process)(void *port_hdl);
} ext_hub_port_driver_t;
/**
* @brief External Hub Driver configuration
*/
typedef struct {
ext_hub_cb_t proc_req_cb; /**< External Hub process callback */
void *proc_req_cb_arg; /**< External Hub process callback argument */
const ext_hub_port_driver_t* port_driver; /**< External Port Driver */
} ext_hub_config_t;
// ------------------------------ Driver ---------------------------------------
/**
* @brief Install External Hub Driver
*
* Entry:
* - should be called within Hub Driver
*
* @param[in] config External Hub driver configuration
* @return esp_err_t
*/
esp_err_t ext_hub_install(const ext_hub_config_t* config);
/**
* @brief Uninstall External Hub Driver
*
* Entry:
* - should be called within Hub Driver
*
* @return esp_err_t
*/
esp_err_t ext_hub_uninstall(void);
/**
* @brief External Hub Driver get client pointer
*
* Entry:
* - should be called within Hub Driver
*
* @param[in] config External Hub driver configuration
* @return Unique pointer to identify the External Hub as a USB Host client
*/
void *ext_hub_get_client(void);
// -------------------------- External Hub API ---------------------------------
/**
* @brief Get External Hub device handle by USBH device handle
*
* @param[in] dev_hdl USBH device handle
* @param[out] ext_hub_hdl External Hub device handle
* @return esp_err_t
*/
esp_err_t ext_hub_get_handle(usb_device_handle_t dev_hdl, ext_hub_handle_t *ext_hub_hdl);
/**
* @brief Add new device
*
* After attaching new device:
* - configure it's parameters (requesting hub descriptor)
*
* @param[in] dev_addr Device bus address
* @return esp_err_t
*/
esp_err_t ext_hub_new_dev(uint8_t dev_addr);
/**
* @brief Device gone
*
* After device were detached:
* - prepare the device to be freed
*
* @param[in] dev_addr Device bus address
* @return esp_err_t
*/
esp_err_t ext_hub_dev_gone(uint8_t dev_addr);
/**
* @brief Marks all devices to be freed
*
* Entry:
* - should be called within Hub Driver when USB Host library need to be uninstalled
*
* @param[in] dev_addr Device bus address
* @return esp_err_t
*/
esp_err_t ext_hub_all_free(void);
/**
* @brief The External Hub or Ports statuses change completed
*
* Enables Interrupt IN endpoint to get information about Hub or Ports statuses change
*
* @param[in] ext_hub_hdl External Hub device handle
* @return esp_err_t
*/
esp_err_t ext_hub_status_handle_complete(ext_hub_handle_t ext_hub_hdl);
/**
* @brief External Hub driver's process function
*
* External Hub driver process function that must be called repeatedly to process the driver's actions and events.
* If blocking, the caller can block on the notification callback of source USB_PROC_REQ_SOURCE_HUB
* to run this function.
*/
esp_err_t ext_hub_process(void);
// -------------------- External Hub - Port related ----------------------------
/**
* @brief Indicate to the External Hub driver that a device's port can be recycled
*
* The device connected to the port has been freed. The Hub driver can now
* recycle the port.
*
* @param[in] ext_hub_hdl External Hub handle
* @param[in] port_num Port number
* @retval ESP_OK: Success
*/
esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
/**
* @brief Indicate to the External Hub driver that a device's port need a reset
*
* @param[in] ext_hub_hdl External Hub handle
* @param[in] port_num Port number
* @retval ESP_OK: Success
*/
esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
/**
* @brief Indicate to the External Hub driver that a device's port has a device and device has been enumerated
*
* @param[in] ext_hub_hdl External Hub handle
* @param[in] port_num Port number
* @retval ESP_OK: Success
*/
esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
/**
* @brief Indicate to the External Hub driver that a device's port should be disabled
*
* @param[in] ext_hub_hdl External Hub handle
* @param[in] port_num Port number
* @retval ESP_OK: Success
*/
esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
/**
* @brief Returns device speed of the device, attached to the port
*
* @param[in] ext_hub_hdl External Hub handle
* @param[in] port_num Port number
* @param[out] speed Devices' speed
* @retval ESP_OK: Success
*/
esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, usb_speed_t *speed);
// --------------------------- USB Chapter 11 ----------------------------------
/**
* @brief Set Port Feature request
*
* @param[in] ext_hub_hdl External Hub device handle
* @param[in] port_num Port number
* @param[in] feature Port Feature to set
* @return esp_err_t
*/
esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature);
/**
* @brief Clear Port Feature request
*
* @param[in] ext_hub_hdl External Hub device handle
* @param[in] port_num Port number
* @param[in] feature Port Feature to clear
* @return esp_err_t
*/
esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature);
/**
* @brief Get Port Status request
*
* Sends the request to retrieve port status data.
* Actual port status data could be requested by calling ext_hub_get_port_status() after request completion.
*
* @param[in] ext_hub_hdl External Hub device handle
* @param[in] port_num Port number
* @return esp_err_t
*/
esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
#ifdef __cplusplus
}
#endif

View File

@ -13,10 +13,47 @@
#include "usb_private.h"
#include "usbh.h"
#if CONFIG_USB_HOST_HUBS_SUPPORTED
#define ENABLE_USB_HUBS 1
#endif // CONFIG_USB_HOST_HUBS_SUPPORTED
#ifdef __cplusplus
extern "C" {
#endif
// ----------------------------- Events ----------------------------------------
typedef enum {
HUB_EVENT_CONNECTED, /**< Device has been connected */
HUB_EVENT_RESET_COMPLETED, /**< Device reset completed */
HUB_EVENT_DISCONNECTED, /**< Device has been disconnected */
} hub_event_t;
typedef struct {
hub_event_t event; /**< HUB event ID */
union {
struct {
unsigned int uid; /**< Unique device ID */
} connected; /**< HUB_EVENT_DEV_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 */
};
} hub_event_data_t;
// ---------------------- Callbacks ------------------------
/**
* @brief Callback used to indicate that the USBH has an event
*
* @note This callback is called from within usbh_process()
*/
typedef void (*hub_event_cb_t)(hub_event_data_t *event_data, void *arg);
// ------------------------------------------------------ Types --------------------------------------------------------
/**
@ -25,9 +62,8 @@ extern "C" {
typedef struct {
usb_proc_req_cb_t proc_req_cb; /**< Processing request callback */
void *proc_req_cb_arg; /**< Processing request callback argument */
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
usb_host_enum_filter_cb_t enum_filter_cb; /**< Set device configuration callback */
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
hub_event_cb_t event_cb; /**< Hub event callback */
void *event_cb_arg; /**< Hub event callback argument */
} hub_config_t;
// ---------------------------------------------- Hub Driver Functions -------------------------------------------------
@ -65,6 +101,8 @@ esp_err_t hub_uninstall(void);
*
* This will power the root port ON
*
* @note This function should only be called from the Host Library task
*
* @return esp_err_t
*/
esp_err_t hub_root_start(void);
@ -81,19 +119,92 @@ esp_err_t hub_root_stop(void);
/**
* @brief Indicate to the Hub driver that a device's port can be recycled
*
* The device connected to the port has been freed. The Hub driver can now
* recycled the port.
* The device connected to the port has been freed. The Hub driver can now recycled the port
*
* @param dev_uid Device's unique ID
* @note This function should only be called from the Host Library task
*
* @param[in] parent_dev_hdl
* @param[in] parent_port_num
* @param[in] dev_uid Device's unique ID
* @return
* - ESP_OK: Success
*/
esp_err_t hub_port_recycle(unsigned int dev_uid);
esp_err_t hub_port_recycle(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, unsigned int dev_uid);
/**
* @brief Reset the port
*
* @note This function should only be called from the Host Library task
*
* @param[in] parent_dev_hdl
* @param[in] parent_port_num
* @return
* - ESP_OK: Success
*/
esp_err_t hub_port_reset(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num);
/**
* @brief Activate the port
*
* @note This function should only be called from the Host Library task
*
* @param[in] parent_dev_hdl Parent device handle (is used to get the External Hub handle)
* @param[in] parent_port_num Parent number (is used to specify the External Port)
* @return
* - ESP_OK: Success
*/
esp_err_t hub_port_active(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num);
/**
* @brief Disable the port
*
* @note This function should only be called from the Host Library task
*
* @param[in] parent_dev_hdl Parent device handle (is used to get the External Hub handle)
* @param[in] parent_port_num Parent number (is used to specify the External Port)
* @return
* @retval ESP_OK: Port has been disabled without error
* @retval ESP_ERR_INVALID_STATE: Port can't be disabled in current state
* @retval ESP_ERR_NOT_SUPPORTED: This function is not support by the selected port
*/
esp_err_t hub_port_disable(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num);
#if ENABLE_USB_HUBS
/**
* @brief Notify Hub driver that new device has been attached
*
* If device is has a HUB class, then it will be added as External Hub to Hub Driver.
*
* @param[in] dev_addr Device bus address
* @return
* - ESP_OK: Success
*/
esp_err_t hub_notify_new_dev(uint8_t dev_addr);
/**
* @brief Notify Hub driver that device has been removed
*
* If the device was an External Hub, then it will be removed from the Hub Driver.
*
* @param[in] dev_addr Device bus address
* @return
* - ESP_OK: Success
*/
esp_err_t hub_notify_dev_gone(uint8_t dev_addr);
/**
* @brief Notify Hub driver that all devices should be freed
*
* @return
* - ESP_OK: Success
*/
esp_err_t hub_notify_all_free(void);
#endif // ENABLE_USB_HUBS
/**
* @brief Hub driver's processing function
*
* Hub driver handling function that must be called repeatdly to process the Hub driver's events. If blocking, the
* Hub driver handling function that must be called repeatedly to process the Hub driver's events. If blocking, the
* caller can block on the notification callback of source USB_PROC_REQ_SOURCE_HUB to run this function.
*
* @return esp_err_t

View File

@ -58,6 +58,7 @@ typedef struct urb_s urb_t;
typedef enum {
USB_PROC_REQ_SOURCE_USBH = 0x01,
USB_PROC_REQ_SOURCE_HUB = 0x02,
USB_PROC_REQ_SOURCE_ENUM = 0x03
} usb_proc_req_source_t;
/**

View File

@ -59,6 +59,8 @@ typedef struct {
} dev_gone_data;
struct {
unsigned int dev_uid;
usb_device_handle_t parent_dev_hdl;
uint8_t port_num;
} dev_free_data;
};
} usbh_event_data_t;
@ -130,7 +132,18 @@ typedef struct {
void *event_cb_arg; /**< USBH event callback argument */
} usbh_config_t;
// -------------------------------------------- USBH Processing Functions ----------------------------------------------
/**
* @brief USBH device parameters used in usbh_devs_add()
*/
typedef struct {
unsigned int uid; /**< Unique ID assigned to the device */
usb_speed_t speed; /**< Device's speed */
hcd_port_handle_t root_port_hdl; /**< Handle of the port that the device is connected to */
usb_device_handle_t parent_dev_hdl; /**< Parent's device handle */
uint8_t parent_port_num; /**< Parent's port number */
} usbh_dev_params_t;
// ---------------------- USBH Processing Functions ----------------------------
/**
* @brief Installs the USBH driver
@ -169,7 +182,7 @@ esp_err_t usbh_uninstall(void);
*/
esp_err_t usbh_process(void);
// ---------------------------------------------- Device Pool Functions ------------------------------------------------
// ---------------------- Device Pool Functions --------------------------------
/**
* @brief Get the current number of devices
@ -206,12 +219,10 @@ esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *nu
* - Call usbh_dev_enum_lock() before enumerating the device via the various
* usbh_dev_set_...() functions.
*
* @param[in] uid Unique ID assigned to the device
* @param[in] dev_speed Device's speed
* @param[in] port_hdl Handle of the port that the device is connected to
* @param[in] params Device parameters, using for device creation
* @return esp_err_t
*/
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl);
esp_err_t usbh_devs_add(usbh_dev_params_t *params);
/**
* @brief Indicates to the USBH that a device is gone
@ -221,6 +232,18 @@ esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle
*/
esp_err_t usbh_devs_remove(unsigned int uid);
/**
* @brief Get a device's connection information
*
* @note Can be called without opening the device
*
* @param[in] uid Unique ID assigned to the device
* @param[out] parent_info Parent device handle
* @param[out] port_num Parent port number
* @return esp_err_t
*/
esp_err_t usbh_devs_get_parent_info(unsigned int uid, usb_parent_dev_info_t *parent_info);
/**
* @brief Mark that all devices should be freed at the next possible opportunity
*
@ -243,16 +266,6 @@ esp_err_t usbh_devs_mark_all_free(void);
*/
esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl);
/**
* @brief CLose a device
*
* Device can be opened by calling usbh_devs_open()
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl);
/**
* @brief Trigger a USBH_EVENT_NEW_DEV event for the device
*
@ -263,14 +276,23 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl);
*/
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl);
// ------------------------------------------------ Device Functions ---------------------------------------------------
// ------------------------ Device Functions -----------------------------------
// ----------------------- Getters -------------------------
/**
* @brief Close a device
*
* @note Callers of this function must have opened the device via usbh_devs_open()
* *
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl);
// ------------------------------ Getters --------------------------------------
/**
* @brief Get a device's address
*
* @note Can be called without opening the device
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @param[in] dev_hdl Device handle
* @param[out] dev_addr Device's address
@ -281,8 +303,10 @@ esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr);
/**
* @brief Get a device's information
*
* @note Callers of this function must have opened the device via usbh_devs_open()
* @note It is possible that the device has not been enumerated yet, thus some
* fields may be NULL.
*
* @param[in] dev_hdl Device handle
* @param[out] dev_info Device information
* @return esp_err_t
@ -292,7 +316,7 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
/**
* @brief Get a device's device descriptor
*
* - The device descriptor is cached when the device is created by the Hub driver
* The device descriptor is cached when the device is created by the Hub driver
*
* @note It is possible that the device has not been enumerated yet, thus the
* device descriptor could be NULL.
@ -305,6 +329,7 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
/**
* @brief Get a device's active configuration descriptor
*
* @note Callers of this function must have opened the device via usbh_devs_open()
* Simply returns a reference to the internally cached configuration descriptor
*
* @note It is possible that the device has not been enumerated yet, thus the
@ -315,11 +340,13 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
*/
esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config_desc_t **config_desc_ret);
// ----------------------- Setters -------------------------
// ------------------------------- Setters -------------------------------------
/**
* @brief Lock a device for enumeration
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* - A device's enumeration lock must be set before any of its enumeration fields
* (e.g., address, device/config descriptors) can be set/updated.
* - The caller must be the sole opener of the device (see 'usbh_devs_open()')
@ -333,6 +360,8 @@ esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl);
/**
* @brief Release a device's enumeration lock
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @param[in] dev_hdl Device handle
* @return esp_err_t
*/
@ -344,8 +373,11 @@ esp_err_t usbh_dev_enum_unlock(usb_device_handle_t dev_hdl);
* Typically called during enumeration after obtaining the first 8 bytes of the
* device's descriptor.
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle
* @param[in] wMaxPacketSize Maximum packet size
* @return esp_err_t
@ -355,13 +387,15 @@ esp_err_t usbh_dev_set_ep0_mps(usb_device_handle_t dev_hdl, uint16_t wMaxPacketS
/**
* @brief Set a device's address
*
* Typically called during enumeration after a SET_ADDRESSS request has be
* Typically called during enumeration after a SET_ADDRESS request has been
* sent to the device.
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
* @param[in] dev_hdl Device handle
* @param[in] dev_addr
* @param[in] dev_addr Device address to set
* @return esp_err_t
*/
esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
@ -372,8 +406,11 @@ esp_err_t usbh_dev_set_addr(usb_device_handle_t dev_hdl, uint8_t dev_addr);
* Typically called during enumeration after obtaining the device's descriptor
* via a GET_DESCRIPTOR request.
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle
* @param[in] device_desc Device descriptor to copy
* @return esp_err_t
@ -386,8 +423,11 @@ esp_err_t usbh_dev_set_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
* Typically called during enumeration after obtaining the device's configuration
* descriptor via a GET_DESCRIPTOR request.
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle
* @param[in] config_desc_full Configuration descriptor to copy
* @return esp_err_t
@ -400,8 +440,11 @@ esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config
* Typically called during enumeration after obtaining one of the device's string
* descriptor via a GET_DESCRIPTOR request.
*
* @note Callers of this function must have opened the device via usbh_devs_open()
*
* @note The device's enumeration lock must be set before calling this function
* (see 'usbh_dev_enum_lock()')
*
* @param[in] dev_hdl Device handle
* @param[in] str_desc String descriptor to copy
* @param[in] select Select string descriptor. 0/1/2 for Manufacturer/Product/Serial
@ -410,7 +453,7 @@ esp_err_t usbh_dev_set_config_desc(usb_device_handle_t dev_hdl, const usb_config
*/
esp_err_t usbh_dev_set_str_desc(usb_device_handle_t dev_hdl, const usb_str_desc_t *str_desc, int select);
// ----------------------------------------------- Endpoint Functions -------------------------------------------------
// ----------------------- Endpoint Functions ----------------------------------
/**
* @brief Allocate an endpoint on a device
@ -482,7 +525,7 @@ esp_err_t usbh_ep_command(usbh_ep_handle_t ep_hdl, usbh_ep_cmd_t command);
*/
void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl);
// ----------------------------------------------- Transfer Functions --------------------------------------------------
// ------------------------- Transfer Functions --------------------------------
/**
* @brief Submit a control transfer (URB) to a device

View File

@ -20,6 +20,7 @@ Warning: The USB Host Library API is still a beta version and may be subject to
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "hub.h"
#include "enum.h"
#include "usbh.h"
#include "hcd.h"
#include "esp_private/usb_phy.h"
@ -46,12 +47,9 @@ static portMUX_TYPE host_lock = portMUX_INITIALIZER_UNLOCKED;
} \
})
#define PROCESS_REQUEST_PENDING_FLAG_USBH 0x01
#define PROCESS_REQUEST_PENDING_FLAG_HUB 0x02
#ifdef CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define ENABLE_ENUM_FILTER_CALLBACK
#endif // CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK
#define PROCESS_REQUEST_PENDING_FLAG_USBH (1 << 0)
#define PROCESS_REQUEST_PENDING_FLAG_HUB (1 << 1)
#define PROCESS_REQUEST_PENDING_FLAG_ENUM (1 << 2)
#define SHORT_DESC_REQ_LEN 8
#define CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE
@ -112,7 +110,7 @@ struct client_s {
uint32_t val;
} flags;
uint32_t num_done_ctrl_xfer;
uint32_t opened_dev_addr_map;
uint32_t opened_dev_addr_map[4];
} dynamic;
// Mux protected members must be protected by host library the mux_lock when accessed
struct {
@ -152,7 +150,8 @@ typedef struct {
SemaphoreHandle_t event_sem;
SemaphoreHandle_t mux_lock;
usb_phy_handle_t phy_handle; // Will be NULL if host library is installed with skip_phy_setup
void *hub_client; // Pointer to Hub driver (acting as a client). Used to reroute completed USBH control transfers
void *enum_client; // Pointer to Enum driver (acting as a client). Used to reroute completed USBH control transfers
void *hub_client; // Pointer to External Hub driver (acting as a client). Used to reroute completed USBH control transfers. NULL, when External Hub Driver not available.
} constant;
} host_lib_t;
@ -164,26 +163,25 @@ const char *USB_HOST_TAG = "USB HOST";
static inline void _record_client_opened_device(client_t *client_obj, uint8_t dev_addr)
{
assert(dev_addr != 0);
client_obj->dynamic.opened_dev_addr_map |= (1 << (dev_addr - 1));
assert(dev_addr != 0 && dev_addr <= 127);
client_obj->dynamic.opened_dev_addr_map[dev_addr / 32] |= (uint32_t)(1 << (dev_addr % 32));
}
static inline void _clear_client_opened_device(client_t *client_obj, uint8_t dev_addr)
{
assert(dev_addr != 0);
client_obj->dynamic.opened_dev_addr_map &= ~(1 << (dev_addr - 1));
assert(dev_addr != 0 && dev_addr <= 127);
client_obj->dynamic.opened_dev_addr_map[dev_addr / 32] &= ~(uint32_t)(1 << (dev_addr % 32));
}
static inline bool _check_client_opened_device(client_t *client_obj, uint8_t dev_addr)
{
bool ret;
assert(dev_addr <= 127);
if (dev_addr != 0) {
ret = client_obj->dynamic.opened_dev_addr_map & (1 << (dev_addr - 1));
ret = client_obj->dynamic.opened_dev_addr_map[dev_addr / 32] & (uint32_t)(1 << (dev_addr % 32));
} else {
ret = false;
}
return ret;
}
@ -223,6 +221,19 @@ static bool _unblock_lib(bool in_isr)
return yield;
}
static inline bool _is_internal_client(void *client)
{
if (p_host_lib_obj->constant.enum_client && (client == p_host_lib_obj->constant.enum_client)) {
return true;
}
#if ENABLE_USB_HUBS
if (p_host_lib_obj->constant.hub_client && (client == p_host_lib_obj->constant.hub_client)) {
return true;
}
#endif // ENABLE_USB_HUBS
return false;
}
static void send_event_msg_to_clients(const usb_host_client_event_msg_t *event_msg, bool send_to_all, uint8_t opened_dev_addr)
{
// Lock client list
@ -267,6 +278,9 @@ static bool proc_req_callback(usb_proc_req_source_t source, bool in_isr, void *a
case USB_PROC_REQ_SOURCE_HUB:
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_HUB;
break;
case USB_PROC_REQ_SOURCE_ENUM:
p_host_lib_obj->dynamic.process_pending_flags |= PROCESS_REQUEST_PENDING_FLAG_ENUM;
break;
}
bool yield = _unblock_lib(in_isr);
HOST_EXIT_CRITICAL_SAFE();
@ -281,8 +295,8 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
assert(event_data->ctrl_xfer_data.urb != NULL);
assert(event_data->ctrl_xfer_data.urb->usb_host_client != NULL);
// Redistribute completed control transfers to the clients that submitted them
if (event_data->ctrl_xfer_data.urb->usb_host_client == p_host_lib_obj->constant.hub_client) {
// Redistribute to Hub driver. Simply call the transfer callback
if (_is_internal_client(event_data->ctrl_xfer_data.urb->usb_host_client)) {
// Simply call the transfer callback
event_data->ctrl_xfer_data.urb->transfer.callback(&event_data->ctrl_xfer_data.urb->transfer);
} else {
client_t *client_obj = (client_t *)event_data->ctrl_xfer_data.urb->usb_host_client;
@ -301,9 +315,15 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
.new_dev.address = event_data->new_dev_data.dev_addr,
};
send_event_msg_to_clients(&event_msg, true, 0);
#if ENABLE_USB_HUBS
hub_notify_new_dev(event_data->new_dev_data.dev_addr);
#endif // ENABLE_USB_HUBS
break;
}
case USBH_EVENT_DEV_GONE: {
#if ENABLE_USB_HUBS
hub_notify_dev_gone(event_data->new_dev_data.dev_addr);
#endif // ENABLE_USB_HUBS
// Prepare event msg, send only to clients that have opened the device
usb_host_client_event_msg_t event_msg = {
.event = USB_HOST_CLIENT_EVENT_DEV_GONE,
@ -314,7 +334,10 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
}
case USBH_EVENT_DEV_FREE: {
// Let the Hub driver know that the device is free and its port can be recycled
ESP_ERROR_CHECK(hub_port_recycle(event_data->dev_free_data.dev_uid));
// Port could be absent, no need to verify
hub_port_recycle(event_data->dev_free_data.parent_dev_hdl,
event_data->dev_free_data.port_num,
event_data->dev_free_data.dev_uid);
break;
}
case USBH_EVENT_ALL_FREE: {
@ -331,6 +354,55 @@ static void usbh_event_callback(usbh_event_data_t *event_data, void *arg)
}
}
static void hub_event_callback(hub_event_data_t *event_data, void *arg)
{
switch (event_data->event) {
case HUB_EVENT_CONNECTED:
// Start enumeration process
enum_start(event_data->connected.uid);
break;
case HUB_EVENT_RESET_COMPLETED:
ESP_ERROR_CHECK(enum_proceed(event_data->reset_completed.uid));
break;
case HUB_EVENT_DISCONNECTED:
// Cancel enumeration process
enum_cancel(event_data->disconnected.uid);
// We allow this to fail in case the device object was already freed
usbh_devs_remove(event_data->disconnected.uid);
break;
default:
abort(); // Should never occur
break;
}
}
static void enum_event_callback(enum_event_data_t *event_data, void *arg)
{
enum_event_t event = event_data->event;
switch (event) {
case ENUM_EVENT_STARTED:
// Enumeration process started
break;
case ENUM_EVENT_RESET_REQUIRED:
// Device may be gone, don't need to verify result
hub_port_reset(event_data->reset_req.parent_dev_hdl, event_data->reset_req.parent_port_num);
break;
case ENUM_EVENT_COMPLETED:
// Notify port that device completed enumeration
hub_port_active(event_data->complete.parent_dev_hdl, event_data->complete.parent_port_num);
// Propagate a new device event
ESP_ERROR_CHECK(usbh_devs_new_dev_event(event_data->complete.dev_hdl));
break;
case ENUM_EVENT_CANCELED:
hub_port_disable(event_data->canceled.parent_dev_hdl, event_data->canceled.parent_port_num);
break;
default:
abort(); // Should never occur
break;
}
}
// ------------------- Client Related ----------------------
static bool endpoint_callback(usbh_ep_handle_t ep_hdl, usbh_ep_event_t ep_event, void *user_arg, bool in_isr)
@ -388,6 +460,7 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
- USB PHY
- HCD
- USBH
- Enum
- Hub
*/
@ -429,18 +502,28 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
goto usbh_err;
}
#ifdef ENABLE_ENUM_FILTER_CALLBACK
if (config->enum_filter_cb == NULL) {
ESP_LOGW(USB_HOST_TAG, "User callback to set USB device configuration is enabled, but not used");
}
// Install Enumeration driver
enum_config_t enum_config = {
.proc_req_cb = proc_req_callback,
.proc_req_cb_arg = NULL,
.enum_event_cb = enum_event_callback,
.enum_event_cb_arg = NULL,
#if ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = config->enum_filter_cb,
.enum_filter_cb_arg = NULL,
#endif // ENABLE_ENUM_FILTER_CALLBACK
};
ret = enum_install(&enum_config, &host_lib_obj->constant.enum_client);
if (ret != ESP_OK) {
goto enum_err;
}
// Install Hub
hub_config_t hub_config = {
.proc_req_cb = proc_req_callback,
.proc_req_cb_arg = NULL,
#ifdef ENABLE_ENUM_FILTER_CALLBACK
.enum_filter_cb = config->enum_filter_cb,
#endif // ENABLE_ENUM_FILTER_CALLBACK
.event_cb = hub_event_callback,
.event_cb_arg = NULL,
};
ret = hub_install(&hub_config, &host_lib_obj->constant.hub_client);
if (ret != ESP_OK) {
@ -465,6 +548,8 @@ esp_err_t usb_host_install(const usb_host_config_t *config)
assign_err:
ESP_ERROR_CHECK(hub_uninstall());
hub_err:
ESP_ERROR_CHECK(enum_uninstall());
enum_err:
ESP_ERROR_CHECK(usbh_uninstall());
usbh_err:
ESP_ERROR_CHECK(hcd_uninstall());
@ -507,11 +592,13 @@ esp_err_t usb_host_uninstall(void)
/*
Uninstall each layer of the Host stack (listed below) from the highest layer to the lowest
- Hub
- Enum
- USBH
- HCD
- USB PHY
*/
ESP_ERROR_CHECK(hub_uninstall());
ESP_ERROR_CHECK(enum_uninstall());
ESP_ERROR_CHECK(usbh_uninstall());
ESP_ERROR_CHECK(hcd_uninstall());
// If the USB PHY was setup, then delete it
@ -558,6 +645,9 @@ esp_err_t usb_host_lib_handle_events(TickType_t timeout_ticks, uint32_t *event_f
if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_HUB) {
ESP_ERROR_CHECK(hub_process());
}
if (process_pending_flags & PROCESS_REQUEST_PENDING_FLAG_ENUM) {
ESP_ERROR_CHECK(enum_process());
}
ret = ESP_OK;
// Set timeout_ticks to 0 so that we can check for events again without blocking
@ -729,7 +819,10 @@ esp_err_t usb_host_client_deregister(usb_host_client_handle_t client_hdl)
client_obj->dynamic.flags.taking_mux ||
client_obj->dynamic.flags.num_intf_claimed != 0 ||
client_obj->dynamic.num_done_ctrl_xfer != 0 ||
client_obj->dynamic.opened_dev_addr_map != 0) {
client_obj->dynamic.opened_dev_addr_map[0] != 0 ||
client_obj->dynamic.opened_dev_addr_map[1] != 0 ||
client_obj->dynamic.opened_dev_addr_map[2] != 0 ||
client_obj->dynamic.opened_dev_addr_map[3] != 0) {
can_deregister = false;
} else {
can_deregister = true;
@ -863,7 +956,7 @@ esp_err_t usb_host_device_open(usb_host_client_handle_t client_hdl, uint8_t dev_
return ret;
already_opened:
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl));
ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
exit:
return ret;
}
@ -905,7 +998,7 @@ esp_err_t usb_host_device_close(usb_host_client_handle_t client_hdl, usb_device_
_clear_client_opened_device(client_obj, dev_addr);
HOST_EXIT_CRITICAL();
ESP_ERROR_CHECK(usbh_devs_close(dev_hdl));
ESP_ERROR_CHECK(usbh_dev_close(dev_hdl));
ret = ESP_OK;
exit:
xSemaphoreGive(p_host_lib_obj->constant.mux_lock);
@ -918,6 +1011,9 @@ esp_err_t usb_host_device_free_all(void)
HOST_CHECK_FROM_CRIT(p_host_lib_obj->dynamic.flags.num_clients == 0, ESP_ERR_INVALID_STATE); // All clients must have been deregistered
HOST_EXIT_CRITICAL();
esp_err_t ret;
#if ENABLE_USB_HUBS
hub_notify_all_free();
#endif // ENABLE_USB_HUBS
ret = usbh_devs_mark_all_free();
// If ESP_ERR_NOT_FINISHED is returned, caller must wait for USB_HOST_LIB_EVENT_FLAGS_ALL_FREE to confirm all devices are free
return ret;

View File

@ -40,78 +40,79 @@ typedef struct device_s device_t;
typedef struct {
struct {
usbh_ep_cb_t ep_cb;
void *ep_cb_arg;
hcd_pipe_handle_t pipe_hdl;
device_t *dev; // Pointer to the device object that this endpoint is contained in
const usb_ep_desc_t *ep_desc; // This just stores a pointer endpoint descriptor inside the device's "config_desc"
} constant;
usbh_ep_cb_t ep_cb; /**< Endpoint callback is called when transfer in complete or error occurred */
void *ep_cb_arg; /**< Endpoint callback argument */
hcd_pipe_handle_t pipe_hdl; /**< Endpoint HCD pipe handle */
device_t *dev; /**< Pointer to the device object that this endpoint is contained in */
const usb_ep_desc_t *ep_desc; /**< This just stores a pointer endpoint descriptor inside the device's "config_desc" */
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
} endpoint_t;
struct device_s {
// Dynamic members require a critical section
struct {
TAILQ_ENTRY(device_s) tailq_entry;
TAILQ_ENTRY(device_s) tailq_entry; /**< Entry for the device object tailq */
union {
struct {
uint32_t in_pending_list: 1;
uint32_t is_gone: 1; // Device is gone (disconnected or port error)
uint32_t waiting_free: 1; // Device object is awaiting to be freed
uint32_t enum_lock: 1; // Device is locked for enumeration. Enum information (e.g., address, device/config desc etc) may change
uint32_t reserved28: 28;
uint32_t in_pending_list: 1; /**< Device is in pending list */
uint32_t is_gone: 1; /**< Device is gone (disconnected or port error) */
uint32_t waiting_free: 1; /**< Device object is awaiting to be freed */
uint32_t enum_lock: 1; /**< Device is locked for enumeration. Enum information (e.g., address, device/config desc etc) may change */
uint32_t reserved28: 28; /**< Reserved */
};
uint32_t val;
uint32_t val; /**< Device flags value */
} flags;
uint32_t action_flags;
int num_ctrl_xfers_inflight;
usb_device_state_t state;
uint32_t open_count;
} dynamic;
// Mux protected members must be protected by the USBH mux_lock when accessed
uint32_t action_flags; /**< Device action flags */
int num_ctrl_xfers_inflight; /**< Amount of ongoing Control transfers */
usb_device_state_t state; /**< Device state */
uint32_t open_count; /**< Amount of clients which opened this device */
} dynamic; /**< Dynamic members. Require a critical section */
struct {
/*
- Endpoint object pointers for each possible non-default endpoint
- All OUT EPs are listed before IN EPs (i.e., EP_NUM_MIN OUT ... EP_NUM_MAX OUT ... EP_NUM_MIN IN ... EP_NUM_MAX)
*/
endpoint_t *endpoints[NUM_NON_DEFAULT_EP];
} mux_protected;
} mux_protected; /**< Mutex protected members. Must be protected by the USBH mux_lock when accessed */
// Constant members do not require a critical section
struct {
// Assigned on device allocation and remain constant for the device's lifetime
hcd_pipe_handle_t default_pipe;
hcd_port_handle_t port_hdl;
usb_speed_t speed;
unsigned int uid;
hcd_pipe_handle_t default_pipe; /**< Pipe handle for Control EP0 */
hcd_port_handle_t port_hdl; /**< HCD port handle */
usb_device_handle_t parent_dev_hdl; /**< Device's parent device handle. NULL if device is connected to the root port */
uint8_t parent_port_num; /**< Device's parent port number. 0 if device connected to the root port */
usb_speed_t speed; /**< Device's speed */
unsigned int uid; /**< Device's Unique ID */
/*
These fields are can only be changed when enum_lock is set, thus can be treated as constant
*/
uint8_t address;
usb_device_desc_t *desc;
usb_config_desc_t *config_desc;
usb_str_desc_t *str_desc_manu;
usb_str_desc_t *str_desc_product;
usb_str_desc_t *str_desc_ser_num;
} constant;
uint8_t address; /**< Device's bus address */
usb_device_desc_t *desc; /**< Device's descriptor pointer */
usb_config_desc_t *config_desc; /**< Device's configuration descriptor pointer. NULL if not configured. */
usb_str_desc_t *str_desc_manu; /**< Device's Manufacturer string descriptor pointer */
usb_str_desc_t *str_desc_product; /**< Device's Product string descriptor pointer */
usb_str_desc_t *str_desc_ser_num; /**< Device's Serial string descriptor pointer */
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
};
typedef struct {
// Dynamic members require a critical section
struct {
TAILQ_HEAD(tailhead_devs, device_s) devs_idle_tailq; // Tailq of all enum and configured devices
TAILQ_HEAD(tailhead_devs_cb, device_s) devs_pending_tailq; // Tailq of devices that need to have their cb called
} dynamic;
// Mux protected members must be protected by the USBH mux_lock when accessed
TAILQ_HEAD(tailhead_devs, device_s) devs_idle_tailq; /**< Tailq of all enum and configured devices */
TAILQ_HEAD(tailhead_devs_cb, device_s) devs_pending_tailq; /**< Tailq of devices that need to have their cb called */
} dynamic; /**< Dynamic members. Require a critical section */
struct {
uint8_t num_device; // Number of enumerated devices
} mux_protected;
// Constant members do no change after installation thus do not require a critical section
uint8_t num_device; /**< Current number of device objects */
} mux_protected; /**< Mutex protected members. Must be protected by the USBH mux_lock when accessed */
struct {
usb_proc_req_cb_t proc_req_cb;
void *proc_req_cb_arg;
usbh_event_cb_t event_cb;
void *event_cb_arg;
SemaphoreHandle_t mux_lock;
} constant;
usb_proc_req_cb_t proc_req_cb; /**< USB Host process request callback. Refer to proc_req_callback() in usb_host.c */
void *proc_req_cb_arg; /**< USB Host process request callback argument */
usbh_event_cb_t event_cb; /**< USBH event callback */
void *event_cb_arg; /**< USBH event callback argument */
SemaphoreHandle_t mux_lock; /**< Mutex for protected members */
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
} usbh_t;
static usbh_t *p_usbh_obj = NULL;
@ -147,7 +148,9 @@ static bool epN_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_
static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags);
// ----------------------------------------------------- Helpers -------------------------------------------------------
// -----------------------------------------------------------------------------
// ---------------------------- Helpers ----------------------------------------
// -----------------------------------------------------------------------------
static device_t *_find_dev_from_uid(unsigned int uid)
{
@ -305,7 +308,9 @@ static bool transfer_check_usb_compliance(usb_transfer_t *transfer, usb_transfer
return true;
}
// --------------------------------------------------- Allocation ------------------------------------------------------
// -----------------------------------------------------------------------------
// ----------------------------- Allocation ------------------------------------
// -----------------------------------------------------------------------------
static esp_err_t endpoint_alloc(device_t *dev_obj, const usb_ep_desc_t *ep_desc, usbh_ep_config_t *ep_config, endpoint_t **ep_obj_ret)
{
@ -358,10 +363,7 @@ static void endpoint_free(endpoint_t *ep_obj)
heap_caps_free(ep_obj);
}
static esp_err_t device_alloc(unsigned int uid,
usb_speed_t speed,
hcd_port_handle_t port_hdl,
device_t **dev_obj_ret)
static esp_err_t device_alloc(usbh_dev_params_t *params, device_t **dev_obj_ret)
{
device_t *dev_obj = heap_caps_calloc(1, sizeof(device_t), MALLOC_CAP_DEFAULT);
if (dev_obj == NULL) {
@ -375,22 +377,23 @@ static esp_err_t device_alloc(unsigned int uid,
.callback_arg = (void *)dev_obj,
.context = (void *)dev_obj,
.ep_desc = NULL, // No endpoint descriptor means we're allocating a pipe for EP0
.dev_speed = speed,
.dev_speed = params->speed,
.dev_addr = 0,
};
hcd_pipe_handle_t default_pipe_hdl;
ret = hcd_pipe_alloc(port_hdl, &pipe_config, &default_pipe_hdl);
ret = hcd_pipe_alloc(params->root_port_hdl, &pipe_config, &default_pipe_hdl);
if (ret != ESP_OK) {
goto err;
}
// Initialize device object
dev_obj->dynamic.state = USB_DEVICE_STATE_DEFAULT;
dev_obj->constant.default_pipe = default_pipe_hdl;
dev_obj->constant.port_hdl = port_hdl;
dev_obj->constant.speed = speed;
dev_obj->constant.uid = uid;
dev_obj->constant.port_hdl = params->root_port_hdl;
dev_obj->constant.parent_dev_hdl = params->parent_dev_hdl;
dev_obj->constant.parent_port_num = params->parent_port_num;
dev_obj->constant.speed = params->speed;
dev_obj->constant.uid = params->uid;
// Note: Enumeration related dev_obj->constant fields are initialized later using usbh_dev_set_...() functions
// Write-back device object
*dev_obj_ret = dev_obj;
ret = ESP_OK;
@ -407,11 +410,11 @@ static void device_free(device_t *dev_obj)
if (dev_obj == NULL) {
return;
}
// Device descriptor might not have been set yet
// Device descriptor might not have been allocated (in case of early enumeration failure)
if (dev_obj->constant.desc) {
heap_caps_free(dev_obj->constant.desc);
}
// Configuration descriptor might not have been set yet
// Configuration might not have been allocated (in case of early enumeration failure)
if (dev_obj->constant.config_desc) {
heap_caps_free(dev_obj->constant.config_desc);
}
@ -429,7 +432,9 @@ static void device_free(device_t *dev_obj)
heap_caps_free(dev_obj);
}
// ---------------------------------------------------- Callbacks ------------------------------------------------------
// -----------------------------------------------------------------------------
// -------------------------- Callbacks ----------------------------------------
// -----------------------------------------------------------------------------
static bool ep0_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_event, void *user_arg, bool in_isr)
{
@ -487,7 +492,9 @@ static bool epN_pipe_callback(hcd_pipe_handle_t pipe_hdl, hcd_pipe_event_t pipe_
in_isr);
}
// -------------------------------------------------- Event Related ----------------------------------------------------
// -----------------------------------------------------------------------------
// ------------------------- Event Related -------------------------------------
// -----------------------------------------------------------------------------
static bool _dev_set_actions(device_t *dev_obj, uint32_t action_flags)
{
@ -581,6 +588,8 @@ static inline void handle_free(device_t *dev_obj)
{
// Cache a copy of the device's address as we are about to free the device object
const unsigned int dev_uid = dev_obj->constant.uid;
usb_device_handle_t parent_dev_hdl = dev_obj->constant.parent_dev_hdl;
const uint8_t parent_port_num = dev_obj->constant.parent_port_num;
bool all_free;
ESP_LOGD(USBH_TAG, "Freeing device %d", dev_obj->constant.address);
@ -605,6 +614,8 @@ static inline void handle_free(device_t *dev_obj)
.event = USBH_EVENT_DEV_FREE,
.dev_free_data = {
.dev_uid = dev_uid,
.parent_dev_hdl = parent_dev_hdl,
.port_num = parent_port_num,
}
};
p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg);
@ -629,7 +640,9 @@ static inline void handle_prop_new_dev(device_t *dev_obj)
p_usbh_obj->constant.event_cb(&event_data, p_usbh_obj->constant.event_cb_arg);
}
// -------------------------------------------- USBH Processing Functions ----------------------------------------------
// -----------------------------------------------------------------------------
// ------------------------- USBH Processing Functions -------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_install(const usbh_config_t *usbh_config)
{
@ -766,7 +779,9 @@ esp_err_t usbh_process(void)
return ESP_OK;
}
// ---------------------------------------------- Device Pool Functions ------------------------------------------------
// -----------------------------------------------------------------------------
// ------------------------- Device Pool Functions -----------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_devs_num(int *num_devs_ret)
{
@ -825,14 +840,15 @@ esp_err_t usbh_devs_addr_list_fill(int list_len, uint8_t *dev_addr_list, int *nu
return ESP_OK;
}
esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle_t port_hdl)
esp_err_t usbh_devs_add(usbh_dev_params_t *params)
{
USBH_CHECK(port_hdl != NULL, ESP_ERR_INVALID_ARG);
USBH_CHECK(params != NULL, ESP_ERR_NOT_ALLOWED);
USBH_CHECK(params->root_port_hdl != NULL, ESP_ERR_INVALID_ARG);
esp_err_t ret;
device_t *dev_obj;
// Allocate a device object (initialized to address 0)
ret = device_alloc(uid, dev_speed, port_hdl, &dev_obj);
ret = device_alloc(params, &dev_obj);
if (ret != ESP_OK) {
return ret;
}
@ -842,7 +858,7 @@ esp_err_t usbh_devs_add(unsigned int uid, usb_speed_t dev_speed, hcd_port_handle
USBH_ENTER_CRITICAL();
// Check that there is not already a device with the same uid
if (_find_dev_from_uid(uid) != NULL) {
if (_find_dev_from_uid(params->uid) != NULL) {
ret = ESP_ERR_INVALID_ARG;
goto exit;
}
@ -860,6 +876,11 @@ exit:
USBH_EXIT_CRITICAL();
xSemaphoreGive(p_usbh_obj->constant.mux_lock);
if (ret != ESP_OK) {
// Free dev_obj for memory not to leak
device_free(dev_obj);
}
return ret;
}
@ -901,6 +922,27 @@ exit:
return ret;
}
esp_err_t usbh_devs_get_parent_info(unsigned int uid, usb_parent_dev_info_t *parent_info)
{
USBH_CHECK(parent_info, ESP_ERR_INVALID_ARG);
esp_err_t ret = ESP_FAIL;
device_t *dev_obj = NULL;
USBH_ENTER_CRITICAL();
dev_obj = _find_dev_from_uid(uid);
if (dev_obj == NULL) {
ret = ESP_ERR_NOT_FOUND;
goto exit;
} else {
parent_info->dev_hdl = dev_obj->constant.parent_dev_hdl;
parent_info->port_num = dev_obj->constant.parent_port_num;
ret = ESP_OK;
}
exit:
USBH_EXIT_CRITICAL();
return ret;
}
esp_err_t usbh_devs_mark_all_free(void)
{
USBH_ENTER_CRITICAL();
@ -971,7 +1013,30 @@ esp_err_t usbh_devs_open(uint8_t dev_addr, usb_device_handle_t *dev_hdl)
return ret;
}
esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl)
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl)
{
device_t *dev_obj = (device_t *)dev_hdl;
bool call_proc_req_cb = false;
USBH_ENTER_CRITICAL();
// Device must be in the configured state
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
USBH_EXIT_CRITICAL();
// Call the processing request callback
if (call_proc_req_cb) {
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
}
return ESP_OK;
}
// -----------------------------------------------------------------------------
// ---------------------------- Device Functions -------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_close(usb_device_handle_t dev_hdl)
{
USBH_CHECK(dev_hdl != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl;
@ -1000,28 +1065,9 @@ esp_err_t usbh_devs_close(usb_device_handle_t dev_hdl)
return ESP_OK;
}
esp_err_t usbh_devs_new_dev_event(usb_device_handle_t dev_hdl)
{
device_t *dev_obj = (device_t *)dev_hdl;
bool call_proc_req_cb = false;
USBH_ENTER_CRITICAL();
// Device must be in the configured state
USBH_CHECK_FROM_CRIT(dev_obj->dynamic.state == USB_DEVICE_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
call_proc_req_cb = _dev_set_actions(dev_obj, DEV_ACTION_PROP_NEW_DEV);
USBH_EXIT_CRITICAL();
// Call the processing request callback
if (call_proc_req_cb) {
p_usbh_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_USBH, false, p_usbh_obj->constant.proc_req_cb_arg);
}
return ESP_OK;
}
// ------------------------------------------------ Device Functions ---------------------------------------------------
// ----------------------- Getters -------------------------
// -----------------------------------------------------------------------------
// ---------------------------- Getters ----------------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr)
{
@ -1029,7 +1075,6 @@ esp_err_t usbh_dev_get_addr(usb_device_handle_t dev_hdl, uint8_t *dev_addr)
device_t *dev_obj = (device_t *)dev_hdl;
USBH_ENTER_CRITICAL();
USBH_CHECK_FROM_CRIT(dev_obj->constant.address > 0, ESP_ERR_INVALID_STATE);
*dev_addr = dev_obj->constant.address;
USBH_EXIT_CRITICAL();
@ -1041,6 +1086,8 @@ esp_err_t usbh_dev_get_info(usb_device_handle_t dev_hdl, usb_device_info_t *dev_
USBH_CHECK(dev_hdl != NULL && dev_info != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl;
dev_info->parent.dev_hdl = dev_obj->constant.parent_dev_hdl;
dev_info->parent.port_num = dev_obj->constant.parent_port_num;
dev_info->speed = dev_obj->constant.speed;
dev_info->dev_addr = dev_obj->constant.address;
// Device descriptor might not have been set yet
@ -1067,7 +1114,6 @@ esp_err_t usbh_dev_get_desc(usb_device_handle_t dev_hdl, const usb_device_desc_t
{
USBH_CHECK(dev_hdl != NULL && dev_desc_ret != NULL, ESP_ERR_INVALID_ARG);
device_t *dev_obj = (device_t *)dev_hdl;
*dev_desc_ret = dev_obj->constant.desc;
return ESP_OK;
}
@ -1082,7 +1128,9 @@ esp_err_t usbh_dev_get_config_desc(usb_device_handle_t dev_hdl, const usb_config
return ESP_OK;
}
// ----------------------- Setters -------------------------
// -----------------------------------------------------------------------------
// -------------------------------- Setters ------------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_enum_lock(usb_device_handle_t dev_hdl)
{
@ -1330,7 +1378,9 @@ err:
return ret;
}
// ----------------------------------------------- Endpoint Functions -------------------------------------------------
// -----------------------------------------------------------------------------
// ----------------------------- Endpoint Functions ----------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_ep_alloc(usb_device_handle_t dev_hdl, usbh_ep_config_t *ep_config, usbh_ep_handle_t *ep_hdl_ret)
{
@ -1460,7 +1510,9 @@ void *usbh_ep_get_context(usbh_ep_handle_t ep_hdl)
return hcd_pipe_get_context(ep_obj->constant.pipe_hdl);
}
// ----------------------------------------------- Transfer Functions --------------------------------------------------
// -----------------------------------------------------------------------------
// ------------------------ Transfer Functions ---------------------------------
// -----------------------------------------------------------------------------
esp_err_t usbh_dev_submit_ctrl_urb(usb_device_handle_t dev_hdl, urb_t *urb)
{

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -121,7 +121,9 @@ USB_DOCS = ['api-reference/peripherals/usb_device.rst',
'api-reference/peripherals/usb_host/usb_host_notes_design.rst',
'api-reference/peripherals/usb_host/usb_host_notes_dwc_otg.rst',
'api-reference/peripherals/usb_host/usb_host_notes_index.rst',
'api-reference/peripherals/usb_host/usb_host_notes_usbh.rst']
'api-reference/peripherals/usb_host/usb_host_notes_usbh.rst',
'api-reference/peripherals/usb_host/usb_host_notes_enum.rst',
'api-reference/peripherals/usb_host/usb_host_notes_ext_hub.rst']
I80_LCD_DOCS = ['api-reference/peripherals/lcd/i80_lcd.rst']
RGB_LCD_DOCS = ['api-reference/peripherals/lcd/rgb_lcd.rst']

View File

@ -0,0 +1,119 @@
USB Host Enumeration Driver (Enum)
==================================
Introduction
------------
The USB Host Enumeration Driver (henceforth referred to as Enum Driver) provides a software interface which abstracts away the USB device enumeration process. The Enum Driver provides a simple API to start, proceed, complete and cancel the enumeration of a particular device. Internally, the Enum Driver will handle all stages of the enumeration process such as requesting various descriptors and setting the device's configuration.
Requirements
------------
USB Specification Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Chapter 9.1.2 of the USB 2.0 specification outlines some actions, when a USB device is attached to a powered port.
The design of the Enum Driver takes into consideration following actions:
- **The hub performs the required reset processing for the port.** ``enum_config_t.enum_event_cb`` call with ``ENUM_EVENT_RESET_REQUIRED`` event used for Hub Driver notification.
- **The host assigns a unique address to the USB device.** The Enum Driver keeps the device address value and assigns a unique address to the USB device, moving the device to the Address state.
- **The host reads the device descriptor to determine what actual maximum data payload size this USB device's default pipe can use**. The Enum Driver reads the device descriptor to determine what actual maximum data payload size this USB device's default pipe can use.
- **The host reads the configuration information from the device by reading each configuration zero to n-1, where n is the number of configurations.** This requirement simplified to reading only one configuration information from the device by reading configuration with default number, which could be selected by ``enum_config_t.enum_filter_cb`` call.
- **The host assigns a configuration value to the device.** The Enum Driver assigns a configuration value to the device.
.. note::
Typically, most USB devices only contain a single configuration. Thus, the Enum Driver defaults to selecting the configuration with `bConfigurationValue = 1`.
If users would like to select a different configuration, the Enum Driver provides an enumeration filter callback feature (enabled via :ref:`CONFIG_USB_HOST_ENABLE_ENUM_FILTER_CALLBACK`). This callback is called during the enumeration process and allows users to decide what devices to enumerate, and which ``bConfigurationValue`` to use.
.. note::
For more detailed information about Bus enumeration, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 9.1.2 **Bus Enumeration**.
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
In addition to the USB 2.0 specification requirements, the Enum Driver also takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- Enum Driver must not instantiate any tasks/threads
- Enum Driver must be event driven, providing event callbacks and an event processing function
- Enum Driver must use only API from underlying layer (USBH)
Implementation & Usage
----------------------
Host Stack Interaction
^^^^^^^^^^^^^^^^^^^^^^
The Enum Driver takes place between USB Host layer and USBH layer with a possibility to select configuration with ``enum_config_t.enum_event_cb`` callback provided on Enum Driver installation.
Events & Processing
^^^^^^^^^^^^^^^^^^^
The Enum Driver is completely event driven and all event handling is done via the ``enum_process()`` function. The ``enum_config_t.proc_req_cb`` callback provided on Enum Driver installation will be called when processing is required. Typically, ``enum_process()`` will be called from a shared USB Host stack thread/task.
The Enum Driver exposes the following event callbacks:
- ``enum_event_cb_t`` used to indicate various events regarding an enumeration process. This callback is called from the context of ``enum_process()``
The Enum Driver exposes the following events:
- ``ENUM_EVENT_STARTED`` Enumeration process has been started
- ``ENUM_EVENT_RESET_REQUIRED`` Enumeration process requires device reset
- ``ENUM_EVENT_COMPLETED`` Enumeration process has been completed
- ``ENUM_EVENT_CANCELED`` Enumeration process has been canceled (due to internal error or via ``enum_cancel()`` function call)
Device Enumeration
^^^^^^^^^^^^^^^^^^
The USB device enumeration process implemented in the Enum Driver is mostly based same process in the `Windows USB stack <https://techcommunity.microsoft.com/t5/microsoft-usb-blog/how-does-usb-stack-enumerate-a-device/ba-p/270685>`__. The Enum Driver's enumeration process involves the following steps:
#. First device descriptor request (to obtain the MPS of EP0)
#. Second device reset (to workaround some non-compliant devices)
#. Set device address
#. Second device descriptor request (to obtain and store the full device descriptor)
#. Configuration descriptor request (to obtain the full configuration descriptor of selected configuration)
#. Language ID Table request (checks to see if en-US is supported)
#. Manufacturer string descriptor request
#. Product string descriptor request
#. Serial number string descriptor request
#. Set configuration request (sets the device to target configuration number)
.. note::
String descriptors are optional. If a device does not support string descriptors, these stages could be omitted.
Enumeration Stages
^^^^^^^^^^^^^^^^^^
The Enum Driver splits the enumeration process into multiple stages which are executed linearly. Depending on the connected device, some stages (such as fetching the string descriptors) can be skipped. When a stage completes, a call to the ``enum_config_t.proc_req_cb`` callback must be made to trigger a subsequent call of ``enum_process()``.The subsequent call of ``enum_process()`` will then select and execute the next stage of enumeration. Stage completion can trigger the ``enum_config_t.proc_req_cb`` callback in one of the following ways:
- Inside the control transfer completion callback (for stages that send a control transfer)
- Direct call to ``enum_config_t.proc_req_cb`` (for stages that don't need to wait for any event)
- Inside ``enum_proceed()`` (for stages that require some action to be carried out outside the Enum Driver)
Any control transfer made during enumeration is split into two stages, where the first stage executes the transfer and the second stage (suffixed with ``_CHECK``) will check the results of the transfers.
When requesting a variable length descriptors (e.g., configuration or string descriptors), the request is split into two control transfers. The first control transfer is fixed in length which only reads the header of the descriptor. The ``bLength`` field of the descriptor's header indicates the full length of the entire descriptor and is used to set the size of the second transfer which fetches the entire descriptor. As a result, any request for a variable length descriptor is split into four stages:
- Get short **ANY** descriptor (prefixed with ``GET_SHORT_...``)
- Check short **ANY** descriptor (prefixed with ``CHECK_SHORT_...``)
- Get full **ANY** descriptor (prefixed with ``GET_FULL_...``)
- Check full **ANY** descriptor (prefixed with ``CHECK_FULL_...``)
.. note::
Retrieving the Device Descriptor is an exception here because the second reset is taken place after retrieving short Device Descriptor.
Cancel Enumeration
^^^^^^^^^^^^^^^^^^
In some cases (such as a device disconnection), an ongoing enumeration process may need to be cancelled. An ongoing enumeration can be cancelled (regardless of its current stage) by calling ``enum_cancel()`` which will change the enumeration process's current stage to ``ENUM_STAGE_CANCEL``.
On the next call to ``enum_process``, the Enum Driver will execute the ``ENUM_STAGE_CANCEL`` which does the following:
- releases the device's enumeration lock.
- frees all resources related to the current device.
- propagates the ``ENUM_EVENT_CANCELED`` event.

View File

@ -0,0 +1,65 @@
USB Host External Hub Driver (Ext Hub)
======================================
Introduction
------------
The External Hub Driver (henceforth referred to as Ext Hub Driver)
Requirements
------------
USB Specification Requirements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Chapter 11 of the USB 2.0 specification outlines some aspects, when a USB Hub device is attached to a powered port.
The design of the Ext Driver takes into consideration the following:
- **Connectivity behavior**
- **Power management**
- **Device connect/disconnect detection**
- **Bus fault detection and recovery**
- **High-, full-, and low-speed device support**
.. note::
For more detailed information, please refer to `USB 2.0 Specification <https://www.usb.org/document-library/usb-20-specification>`_ > Chapter 11.1 **Overview**.
Host Stack Requirements
^^^^^^^^^^^^^^^^^^^^^^^
In addition to the USB 2.0 specification requirements, the Ext Hub Driver also takes into consideration the requirements set for the overall Host Stack (see :doc:`./usb_host_notes_design`):
- Ext Hub Driver must not instantiate any tasks/threads
- Ext Hub Driver must be event driven, providing event callbacks and an event processing function
- Ext Hub Driver must use only API from underlying layer (USBH)
Implementation & Usage
----------------------
Host Stack Interaction
^^^^^^^^^^^^^^^^^^^^^^
The Ext Hub Driver takes place between USB Host layer and USBH layer, next to the Hub Driver. The Hub Driver and the Ext Hub Driver were split into two Drivers to achieve the goal of logic distinguishing between root Hub and external Hub.
Device handling
^^^^^^^^^^^^^^^
The Ext Hub Driver can be installed via ``ext_hub_install()`` call and uninstalled via ``ext_hub_uninstall()`` call. After installation the Ext Hub provides the following APIs for external Hub addition and removal:
- ``ext_hub_new_dev()`` which will verify the device class (`HUB_CLASSCODE (09H)`) and, if the device has the Hub class, the Ext Hub Driver:
- allocates a new device object
- adds it to the external device pool
- starts the process of Hub configuration (retrieving Hub Descriptor, Device status and Hub status)
- ``ext_hub_dev_gone()`` which will verify the device in the Ext Hub Driver list and start the process of external Hub device removing.
Events & Processing
^^^^^^^^^^^^^^^^^^^
The Ext Hub Driver is completely event driven and all event handling is done via the ``ext_hub_process()`` function. The ``ext_hub_config_t.proc_req_cb`` callback provided on the Ext Hub Driver installation will be called when processing is required. Typically, ``ext_hub_process()`` will be called from the Hub Driver ``hub_process()`` processing function.
The Ext Hub Driver does not expose any event callback.

View File

@ -22,6 +22,8 @@ This document is split into the following sections:
usb_host_notes_arch
usb_host_notes_dwc_otg
usb_host_notes_usbh
usb_host_notes_enum
usb_host_notes_ext_hub
Todo:
@ -44,6 +46,10 @@ Features & Limitations
**The Host Stack currently supports the following notable features:**
.. only:: esp32p4
- Supports HS (High Speed)
- Supports FS (Full Speed) and LS (Low Speed) devices
- Supports all transfer types (Control, Bulk, Isochronous, and Interrupt)
- Automatically enumerates connected devices
@ -51,5 +57,4 @@ Features & Limitations
**The Host Stack currently has the following notable limitations:**
- No HS (High Speed) support
- No Hub support (currently only supports a single device)

View File

@ -0,0 +1 @@
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_enum.rst

View File

@ -0,0 +1 @@
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_ext_hub.rst

View File

@ -1,55 +1 @@
USB 主机维护者注意事项(简介)
==============================
:link_to_translation:`en:[English]`
本文档包含有关 USB 主机协议栈实现细节的信息,面向 USB 主机协议栈的维护者和第三方贡献者。USB 主机协议栈的用户请参考 :doc:`../usb_host`
.. warning::
USB 主机协议栈的实现细节属于私有 API因此除 USB 主机库外的所有层均不遵循 :ref:`ESP-IDF 版本简介 <versioning-scheme>`,即允许进行重大更改。
.. figure:: ../../../../_static/usb_host/stack-overview.png
:align: center
:alt: 主机协议栈层次结构图
本文档分为以下几个部分:
.. toctree::
:maxdepth: 1
usb_host_notes_design
usb_host_notes_arch
usb_host_notes_dwc_otg
usb_host_notes_usbh
待写章节:
- USB 主机维护者注意事项HAL 和 LL
- USB 主机维护者注意事项HCD
- USB 主机维护者注意事项Hub
- USB 主机维护者注意事项USB Host Library
.. -------------------------------------------------- Introduction -----------------------------------------------------
简介
----
ESP-IDF USB 主机协议栈允许 {IDF_TARGET_NAME} 作为 USB 主机运行,此时,{IDF_TARGET_NAME} 能够与各种 USB 设备通信。然而,大多数 USB 主机协议栈实现都不运行在嵌入式硬件上(即在电脑和手机端运行),因此,相对来说具有更多的资源(即,具有更高内存和 CPU 速度)。
ESP-IDF USB 主机协议栈(以下简称为主机协议栈)的实现考虑到了 {IDF_TARGET_NAME} 的嵌入式特性,这体现在主机协议栈设计的各个方面。
特性和局限性
^^^^^^^^^^^^
**主机协议栈目前支持以下显著特性:**
- 支持 FS全速和 LS低速设备
- 支持所有传输类型(控制传输、批量传输、同步传输和中断传输)
- 自动枚举已连接设备
- 允许多个类驱动程序(即 USB 主机库的客户端)同时运行并共享同一设备(即组合设备)
**主机协议栈目前存在以下显著局限:**
- 不支持 HS高速设备
- 不支持集线器(当前仅支持单个设备)
.. include:: ../../../../en/api-reference/peripherals/usb_host/usb_host_notes_index.rst

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/