feat(ext_hub): Added External Hub driver

This commit is contained in:
Roman Leonov 2024-04-02 14:25:11 +02:00
parent 15013c978a
commit 548b03c69f
9 changed files with 2191 additions and 83 deletions

View File

@ -29,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

1598
components/usb/ext_hub.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,6 +19,10 @@
#include "hub.h"
#include "usb/usb_helpers.h"
#if ENABLE_USB_HUBS
#include "ext_hub.h"
#endif // ENABLE_USB_HUBS
/*
Implementation of the HUB driver that only supports the Root Hub with a single port. Therefore, we currently don't
implement the bare minimum to control the root HCD port.
@ -35,13 +39,21 @@ implement the bare minimum to control the root HCD port.
#define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_BALANCED
#endif
// Hub driver action flags. LISTED IN THE ORDER THEY SHOULD BE HANDLED IN within hub_process(). Some actions are mutually exclusive
#define HUB_DRIVER_FLAG_ACTION_ROOT_EVENT 0x01
#define HUB_DRIVER_FLAG_ACTION_PORT_REQ 0x02
#define PORT_REQ_DISABLE 0x01
#define PORT_REQ_RECOVER 0x02
/**
* @brief Hub driver action flags
*/
typedef enum {
HUB_DRIVER_ACTION_ROOT_EVENT = (1 << 0),
HUB_DRIVER_ACTION_ROOT_REQ = (1 << 1),
#if ENABLE_USB_HUBS
HUB_DRIVER_ACTION_EXT_HUB = (1 << 6),
HUB_DRIVER_ACTION_EXT_PORT = (1 << 7)
#endif // ENABLE_USB_HUBS
} hub_flag_action_t;
/**
* @brief Root port states
*/
@ -57,7 +69,6 @@ typedef enum {
* @brief Hub device tree node
*
* Object type of a node in the USB device tree that is maintained by the Hub driver
*
*/
struct dev_tree_node_s {
TAILQ_ENTRY(dev_tree_node_s) tailq_entry; /**< Entry for the device tree node object tailq */
@ -72,11 +83,11 @@ typedef struct {
struct {
union {
struct {
uint32_t actions: 8;
uint32_t reserved24: 24;
hub_flag_action_t actions: 8; /**< Hub actions */
uint32_t reserved24: 24; /**< Reserved */
};
uint32_t val; /**< Root port flag value */
} flags; /**< Root port flags */
uint32_t val; /**< Hub flag action value */
} flags; /**< Hub flags */
root_port_state_t root_port_state; /**< Root port state */
unsigned int port_reqs; /**< Root port request flag */
} dynamic; /**< Dynamic members. Require a critical section */
@ -145,7 +156,7 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port
*/
static esp_err_t new_dev_tree_node(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, usb_speed_t speed)
{
esp_err_t ret = ESP_FAIL;
esp_err_t ret;
unsigned int node_uid = p_hub_driver_obj->single_thread.next_uid;
dev_tree_node_t *dev_tree_node = heap_caps_calloc(1, sizeof(dev_tree_node_t), MALLOC_CAP_DEFAULT);
@ -165,6 +176,8 @@ static esp_err_t new_dev_tree_node(usb_device_handle_t parent_dev_hdl, uint8_t p
ret = usbh_devs_add(&params);
if (ret != ESP_OK) {
// USBH devs add could failed due to lack of free hcd channels
// TODO: IDF-10044 Hub should recover after running out of hcd channels
goto fail;
}
@ -293,12 +306,22 @@ static esp_err_t dev_tree_node_remove_by_parent(usb_device_handle_t parent_dev_h
static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port_event, void *user_arg, bool in_isr)
{
HUB_DRIVER_ENTER_CRITICAL_SAFE();
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_ROOT_EVENT;
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_ROOT_EVENT;
HUB_DRIVER_EXIT_CRITICAL_SAFE();
assert(in_isr); // Currently, this callback should only ever be called from an ISR context
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);
}
#ifdef ENABLE_USB_HUBS
static bool ext_hub_callback(bool in_isr, void *user_arg)
{
HUB_DRIVER_ENTER_CRITICAL_SAFE();
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_EXT_HUB;
HUB_DRIVER_EXIT_CRITICAL_SAFE();
return p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, in_isr, p_hub_driver_obj->constant.proc_req_cb_arg);
}
#endif // ENABLE_USB_HUBS
// ---------------------- Handlers -------------------------
static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
{
@ -344,7 +367,7 @@ reset_err:
case ROOT_PORT_STATE_DISABLED: // This occurred after the device has already been disabled
// Therefore, there's no device object to clean up, and we can go straight to port recovery
p_hub_driver_obj->dynamic.port_reqs |= PORT_REQ_RECOVER;
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_ROOT_REQ;
break;
case ROOT_PORT_STATE_ENABLED:
// There is an enabled (active) device. We need to indicate to USBH that the device is gone
@ -409,7 +432,7 @@ static esp_err_t root_port_recycle(void)
abort(); // Should never occur
break;
}
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_FLAG_ACTION_PORT_REQ;
p_hub_driver_obj->dynamic.flags.actions |= HUB_DRIVER_ACTION_ROOT_REQ;
HUB_DRIVER_EXIT_CRITICAL();
ESP_ERROR_CHECK(dev_tree_node_remove_by_parent(NULL, 0));
@ -434,7 +457,20 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
return ESP_ERR_NO_MEM;
}
#if ENABLE_USB_HUBS
// Install External HUB driver
ext_hub_config_t ext_hub_config = {
.proc_req_cb = ext_hub_callback,
.port_driver = NULL,
};
ret = ext_hub_install(&ext_hub_config);
if (ret != ESP_OK) {
goto err_ext_hub;
}
*client_ret = ext_hub_get_client();
#else
*client_ret = NULL;
#endif // ENABLE_USB_HUBS
// Install HCD port
hcd_port_config_t port_config = {
@ -474,6 +510,10 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
assign_err:
ESP_ERROR_CHECK(hcd_port_deinit(root_port_hdl));
err:
#if ENABLE_USB_HUBS
ext_hub_uninstall();
err_ext_hub:
#endif // ENABLE_USB_HUBS
heap_caps_free(hub_driver_obj);
return ret;
}
@ -487,6 +527,10 @@ esp_err_t hub_uninstall(void)
p_hub_driver_obj = NULL;
HUB_DRIVER_EXIT_CRITICAL();
#if ENABLE_USB_HUBS
ESP_ERROR_CHECK(ext_hub_uninstall());
#endif // ENABLE_USB_HUBS
ESP_ERROR_CHECK(hcd_port_deinit(hub_driver_obj->constant.root_port_hdl));
// Free Hub driver resources
heap_caps_free(hub_driver_obj);
@ -531,14 +575,19 @@ esp_err_t hub_port_recycle(usb_device_handle_t parent_dev_hdl, uint8_t parent_po
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
esp_err_t ret = ESP_FAIL;
esp_err_t ret;
if (parent_port_num == 0) {
ret = root_port_recycle();
} else {
ESP_LOGW(HUB_DRIVER_TAG, "Recycling External Port has not been implemented yet");
return ESP_ERR_NOT_SUPPORTED;
#if ENABLE_USB_HUBS
ext_hub_handle_t ext_hub_hdl = NULL;
ext_hub_get_handle(parent_dev_hdl, &ext_hub_hdl);
ret = ext_hub_port_recycle(ext_hub_hdl, parent_port_num);
#else
ESP_LOGW(HUB_DRIVER_TAG, "Recycling External Port is not available (External Hub support disabled)");
ret = ESP_ERR_NOT_SUPPORTED;
#endif // ENABLE_USB_HUBS
}
return ret;
@ -549,8 +598,7 @@ esp_err_t hub_port_reset(usb_device_handle_t parent_dev_hdl, uint8_t parent_port
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
esp_err_t ret = ESP_FAIL;
esp_err_t ret;
if (parent_port_num == 0) {
ret = hcd_port_command(p_hub_driver_obj->constant.root_port_hdl, HCD_PORT_CMD_RESET);
@ -559,13 +607,68 @@ esp_err_t hub_port_reset(usb_device_handle_t parent_dev_hdl, uint8_t parent_port
}
ret = dev_tree_node_reset_completed(NULL, 0);
} else {
ESP_LOGW(HUB_DRIVER_TAG, "Reset External Port has not been implemented yet");
return ESP_ERR_NOT_SUPPORTED;
#if ENABLE_USB_HUBS
ext_hub_handle_t ext_hub_hdl = NULL;
ext_hub_get_handle(parent_dev_hdl, &ext_hub_hdl);
ret = ext_hub_port_reset(ext_hub_hdl, parent_port_num);
#else
ESP_LOGW(HUB_DRIVER_TAG, "Resetting External Port is not available (External Hub support disabled)");
ret = ESP_ERR_NOT_SUPPORTED;
#endif // ENABLE_USB_HUBS
}
return ret;
}
esp_err_t hub_port_active(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num)
{
esp_err_t ret;
if (parent_port_num == 0) {
// Root port no need to be activated
ret = ESP_OK;
} else {
#if ENABLE_USB_HUBS
// External Hub port
ext_hub_handle_t ext_hub_hdl = NULL;
ext_hub_get_handle(parent_dev_hdl, &ext_hub_hdl);
ret = ext_hub_port_active(ext_hub_hdl, parent_port_num);
#else
ESP_LOGW(HUB_DRIVER_TAG, "Activating External Port is not available (External Hub support disabled)");
ret = ESP_ERR_NOT_SUPPORTED;
#endif // ENABLE_USB_HUBS
}
return ret;
}
#if ENABLE_USB_HUBS
esp_err_t hub_notify_new_dev(uint8_t dev_addr)
{
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
return ext_hub_new_dev(dev_addr);
}
esp_err_t hub_notify_dev_gone(uint8_t dev_addr)
{
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
return ext_hub_dev_gone(dev_addr);
}
esp_err_t hub_notify_all_free(void)
{
HUB_DRIVER_ENTER_CRITICAL();
HUB_DRIVER_CHECK_FROM_CRIT(p_hub_driver_obj != NULL, ESP_ERR_INVALID_STATE);
HUB_DRIVER_EXIT_CRITICAL();
return ext_hub_all_free();
}
#endif // ENABLE_USB_HUBS
esp_err_t hub_process(void)
{
HUB_DRIVER_ENTER_CRITICAL();
@ -574,10 +677,21 @@ esp_err_t hub_process(void)
HUB_DRIVER_EXIT_CRITICAL();
while (action_flags) {
if (action_flags & HUB_DRIVER_FLAG_ACTION_ROOT_EVENT) {
#if ENABLE_USB_HUBS
if (action_flags & HUB_DRIVER_ACTION_EXT_PORT) {
ESP_LOGW(HUB_DRIVER_TAG, "ext_port_process() has not been implemented yet");
/*
ESP_ERROR_CHECK(ext_port_process());
*/
}
if (action_flags & HUB_DRIVER_ACTION_EXT_HUB) {
ESP_ERROR_CHECK(ext_hub_process());
}
#endif // ENABLE_USB_HUBS
if (action_flags & HUB_DRIVER_ACTION_ROOT_EVENT) {
root_port_handle_events(p_hub_driver_obj->constant.root_port_hdl);
}
if (action_flags & HUB_DRIVER_FLAG_ACTION_PORT_REQ) {
if (action_flags & HUB_DRIVER_ACTION_ROOT_REQ) {
root_port_req(p_hub_driver_obj->constant.root_port_hdl);
}
@ -586,5 +700,6 @@ esp_err_t hub_process(void)
p_hub_driver_obj->dynamic.flags.actions = 0;
HUB_DRIVER_EXIT_CRITICAL();
}
return ESP_OK;
}

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

@ -0,0 +1,248 @@
/*
* 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);
} 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

@ -8,10 +8,15 @@
#include <stdlib.h>
#include <stdint.h>
#include "sdkconfig.h"
#include "esp_err.h"
#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
@ -138,6 +143,50 @@ esp_err_t hub_port_recycle(usb_device_handle_t parent_dev_hdl, uint8_t parent_po
*/
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);
#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
*

View File

@ -227,6 +227,11 @@ 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;
}
@ -311,9 +316,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,
@ -324,9 +335,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.parent_dev_hdl,
event_data->dev_free_data.port_num,
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: {
@ -378,6 +390,8 @@ static void enum_event_callback(enum_event_data_t *event_data, void *arg)
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;
@ -995,6 +1009,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;