mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(ext_hub): Added External Hub driver
This commit is contained in:
parent
15013c978a
commit
548b03c69f
@ -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}
|
||||
|
@ -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
1598
components/usb/ext_hub.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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(¶ms);
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
|
248
components/usb/private_include/ext_hub.h
Normal file
248
components/usb/private_include/ext_hub.h
Normal 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
|
@ -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
|
||||
*
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user