mirror of
https://github.com/espressif/esp-idf.git
synced 2025-07-30 18:57:19 +02:00
Merge branch 'feature/usb_host_ext_hub_collective_backport_v5.4' into 'release/v5.4'
feat(usb_host): External Hub collective backport to v5.4 See merge request espressif/esp-idf!37266
This commit is contained in:
1
Kconfig
1
Kconfig
@ -678,5 +678,4 @@ mainmenu "Espressif IoT Development Framework Configuration"
|
|||||||
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
|
- CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH
|
||||||
- CONFIG_ESP_WIFI_EAP_TLS1_3
|
- CONFIG_ESP_WIFI_EAP_TLS1_3
|
||||||
- CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
|
- CONFIG_ESP_WIFI_ENABLE_ROAMING_APP
|
||||||
- CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS
|
|
||||||
- CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
- CONFIG_USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
||||||
|
@ -115,13 +115,6 @@ menu "USB-OTG"
|
|||||||
menu "Downstream Port configuration"
|
menu "Downstream Port configuration"
|
||||||
depends on USB_HOST_HUBS_SUPPORTED
|
depends on USB_HOST_HUBS_SUPPORTED
|
||||||
|
|
||||||
config USB_HOST_EXT_PORT_SUPPORT_LS
|
|
||||||
depends on IDF_EXPERIMENTAL_FEATURES
|
|
||||||
bool "Support LS"
|
|
||||||
default n
|
|
||||||
help
|
|
||||||
Enables support of Low-speed devices, connected through the external Hub.
|
|
||||||
|
|
||||||
config USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
config USB_HOST_EXT_PORT_RESET_ATTEMPTS
|
||||||
depends on IDF_EXPERIMENTAL_FEATURES
|
depends on IDF_EXPERIMENTAL_FEATURES
|
||||||
# Invisible config option
|
# Invisible config option
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -29,7 +29,7 @@ typedef enum {
|
|||||||
EXT_HUB_STATE_ATTACHED, /**< Device attached, but not state is unknown (no: Hub Descriptor, Device status and Hub status) */
|
EXT_HUB_STATE_ATTACHED, /**< Device attached, but not state is unknown (no: Hub Descriptor, Device status and Hub status) */
|
||||||
EXT_HUB_STATE_CONFIGURED, /**< Device attached and configured (has Hub Descriptor, Device status and Hub status were requested )*/
|
EXT_HUB_STATE_CONFIGURED, /**< Device attached and configured (has Hub Descriptor, Device status and Hub status were requested )*/
|
||||||
EXT_HUB_STATE_SUSPENDED, /**< Device suspended */
|
EXT_HUB_STATE_SUSPENDED, /**< Device suspended */
|
||||||
EXT_HUB_STATE_FAILED /**< Device has internal error */
|
EXT_HUB_STATE_RELEASED, /**< Device released and its ports are not available */
|
||||||
} ext_hub_state_t;
|
} ext_hub_state_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,10 +53,10 @@ typedef enum {
|
|||||||
EXT_HUB_STAGE_CHECK_HUB_DESCRIPTOR, /**< Device received the Hub Descriptor and requires its' handling */
|
EXT_HUB_STAGE_CHECK_HUB_DESCRIPTOR, /**< Device received the Hub Descriptor and requires its' handling */
|
||||||
EXT_HUB_STAGE_GET_HUB_STATUS, /**< Device requests Hub Status. For more details, refer to 11.24.2.6 Get Hub Status of usb_20 */
|
EXT_HUB_STAGE_GET_HUB_STATUS, /**< Device requests Hub Status. For more details, refer to 11.24.2.6 Get Hub Status of usb_20 */
|
||||||
EXT_HUB_STAGE_CHECK_HUB_STATUS, /**< Device received the Hub Status and requires its' handling */
|
EXT_HUB_STAGE_CHECK_HUB_STATUS, /**< Device received the Hub Status and requires its' handling */
|
||||||
// Stages, don't required response handling
|
// Stages, don't required get stage handling
|
||||||
EXT_HUB_STAGE_PORT_FEATURE, /**< Device completed the Port Feature class-specific request (Set Feature or Clear Feature). For more details, refer to 11.24.2 Class-specific Requests of usb_20 */
|
EXT_HUB_STAGE_CHECK_PORT_FEATURE, /**< Device completed the Port Feature class-specific request (Set Feature or Clear Feature). For more details, refer to 11.24.2 Class-specific Requests of usb_20 */
|
||||||
EXT_HUB_STAGE_PORT_STATUS_REQUEST, /**< Device completed the Port Get Status class-specific request. For more details, refer to 11.24.2 Class-specific Requests of usb_20 */
|
EXT_HUB_STAGE_CHECK_PORT_STATUS, /**< Device completed the Port Get Status class-specific request. For more details, refer to 11.24.2 Class-specific Requests of usb_20 */
|
||||||
EXT_HUB_STAGE_FAILURE /**< Device has internal error and requires handling */
|
EXT_HUB_STAGE_ERROR /**< Device has internal error and requires handling */
|
||||||
} ext_hub_stage_t;
|
} ext_hub_stage_t;
|
||||||
|
|
||||||
const char *const ext_hub_stage_strings[] = {
|
const char *const ext_hub_stage_strings[] = {
|
||||||
@ -76,15 +76,14 @@ const char *const ext_hub_stage_strings[] = {
|
|||||||
* @brief Device action flags
|
* @brief Device action flags
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
DEV_ACTION_EP0_COMPLETE = (1 << 1), /**< Device complete one of stages, requires handling */
|
DEV_ACTION_EP0_COMPLETE = (1 << 1), /**< Device's Control EP transfer completed */
|
||||||
DEV_ACTION_EP1_FLUSH = (1 << 2), /**< Device's Interrupt EP needs to be flushed */
|
DEV_ACTION_EP1_FLUSH = (1 << 2), /**< Device's Interrupt EP needs to be flushed */
|
||||||
DEV_ACTION_EP1_DEQUEUE = (1 << 3), /**< Device's Interrupt EP needs to be dequeued */
|
DEV_ACTION_EP1_DEQUEUE = (1 << 3), /**< Device's Interrupt EP needs to be dequeued */
|
||||||
DEV_ACTION_EP1_CLEAR = (1 << 4), /**< Device's Interrupt EP needs to be cleared */
|
DEV_ACTION_EP1_CLEAR = (1 << 4), /**< Device's Interrupt EP needs to be cleared */
|
||||||
DEV_ACTION_REQ = (1 << 5), /**< Device has new actions and required handling */
|
DEV_ACTION_REQ = (1 << 5), /**< Device has new actions and requires handling */
|
||||||
DEV_ACTION_ERROR = (1 << 6), /**< Device encounters an error */
|
DEV_ACTION_ERROR = (1 << 6), /**< Device encounters an error */
|
||||||
DEV_ACTION_GONE = (1 << 7), /**< Device was gone */
|
DEV_ACTION_RELEASE = (1 << 7), /**< Device should be released */
|
||||||
DEV_ACTION_RELEASE = (1 << 8), /**< Device was released */
|
DEV_ACTION_FREE = (1 << 8), /**< Device should be freed */
|
||||||
DEV_ACTION_FREE = (1 << 9), /**< Device should be freed */
|
|
||||||
} dev_action_t;
|
} dev_action_t;
|
||||||
|
|
||||||
typedef struct ext_hub_s ext_hub_dev_t;
|
typedef struct ext_hub_s ext_hub_dev_t;
|
||||||
@ -146,7 +145,7 @@ typedef struct {
|
|||||||
struct {
|
struct {
|
||||||
ext_hub_cb_t proc_req_cb; /**< Process callback */
|
ext_hub_cb_t proc_req_cb; /**< Process callback */
|
||||||
void *proc_req_cb_arg; /**< Process callback argument */
|
void *proc_req_cb_arg; /**< Process callback argument */
|
||||||
const ext_hub_port_driver_t* port_driver; /**< External Port Driver */
|
const ext_port_driver_api_t* port_driver; /**< External Port Driver */
|
||||||
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
||||||
} ext_hub_driver_t;
|
} ext_hub_driver_t;
|
||||||
|
|
||||||
@ -301,14 +300,13 @@ static bool _device_set_actions(ext_hub_dev_t *ext_hub_dev, uint32_t action_flag
|
|||||||
// Move device form idle device list to callback device list
|
// Move device form idle device list to callback device list
|
||||||
TAILQ_REMOVE(&p_ext_hub_driver->dynamic.ext_hubs_tailq, ext_hub_dev, dynamic.tailq_entry);
|
TAILQ_REMOVE(&p_ext_hub_driver->dynamic.ext_hubs_tailq, ext_hub_dev, dynamic.tailq_entry);
|
||||||
TAILQ_INSERT_TAIL(&p_ext_hub_driver->dynamic.ext_hubs_pending_tailq, ext_hub_dev, dynamic.tailq_entry);
|
TAILQ_INSERT_TAIL(&p_ext_hub_driver->dynamic.ext_hubs_pending_tailq, ext_hub_dev, dynamic.tailq_entry);
|
||||||
ext_hub_dev->dynamic.action_flags |= action_flags;
|
|
||||||
ext_hub_dev->dynamic.flags.in_pending_list = 1;
|
ext_hub_dev->dynamic.flags.in_pending_list = 1;
|
||||||
call_proc_req_cb = true;
|
call_proc_req_cb = true;
|
||||||
} else {
|
} else {
|
||||||
// The device is already on the callback list, thus a processing request is already pending.
|
// The device is already on the callback list, thus a processing request is already pending.
|
||||||
ext_hub_dev->dynamic.action_flags |= action_flags;
|
|
||||||
call_proc_req_cb = false;
|
call_proc_req_cb = false;
|
||||||
}
|
}
|
||||||
|
ext_hub_dev->dynamic.action_flags |= action_flags;
|
||||||
return call_proc_req_cb;
|
return call_proc_req_cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,7 +315,8 @@ static esp_err_t device_enable_int_ep(ext_hub_dev_t *ext_hub_dev)
|
|||||||
ESP_LOGD(EXT_HUB_TAG, "[%d] Enable EP IN", ext_hub_dev->constant.dev_addr);
|
ESP_LOGD(EXT_HUB_TAG, "[%d] Enable EP IN", ext_hub_dev->constant.dev_addr);
|
||||||
esp_err_t ret = usbh_ep_enqueue_urb(ext_hub_dev->constant.ep_in_hdl, ext_hub_dev->constant.in_urb);
|
esp_err_t ret = usbh_ep_enqueue_urb(ext_hub_dev->constant.ep_in_hdl, ext_hub_dev->constant.in_urb);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Failed to submit in urb: %s", esp_err_to_name(ret));
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Failed to submit in urb: %s", ext_hub_dev->constant.dev_addr, esp_err_to_name(ret));
|
||||||
|
device_error(ext_hub_dev);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -369,7 +368,7 @@ static void device_status_change_handle(ext_hub_dev_t *ext_hub_dev, const uint8_
|
|||||||
assert(i < ext_hub_dev->single_thread.maxchild); // Port should be in range
|
assert(i < ext_hub_dev->single_thread.maxchild); // Port should be in range
|
||||||
assert(p_ext_hub_driver->constant.port_driver); // Port driver call should be valid
|
assert(p_ext_hub_driver->constant.port_driver); // Port driver call should be valid
|
||||||
// Request Port status to handle changes
|
// Request Port status to handle changes
|
||||||
p_ext_hub_driver->constant.port_driver->get_status(ext_hub_dev->constant.ports[i]);
|
ESP_ERROR_CHECK(p_ext_hub_driver->constant.port_driver->get_status(ext_hub_dev->constant.ports[i]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -384,7 +383,9 @@ static void device_error(ext_hub_dev_t *ext_hub_dev)
|
|||||||
bool call_proc_req_cb = false;
|
bool call_proc_req_cb = false;
|
||||||
|
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_ERROR);
|
ext_hub_dev->dynamic.flags.waiting_release = 1;
|
||||||
|
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_ERROR |
|
||||||
|
DEV_ACTION_RELEASE);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
if (call_proc_req_cb) {
|
if (call_proc_req_cb) {
|
||||||
@ -395,7 +396,7 @@ static void device_error(ext_hub_dev_t *ext_hub_dev)
|
|||||||
static esp_err_t device_port_new(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
|
static esp_err_t device_port_new(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx)
|
||||||
{
|
{
|
||||||
ext_port_config_t port_config = {
|
ext_port_config_t port_config = {
|
||||||
.ext_hub_hdl = (ext_hub_handle_t) ext_hub_dev,
|
.context = (void*) ext_hub_dev,
|
||||||
.parent_dev_hdl = ext_hub_dev->constant.dev_hdl,
|
.parent_dev_hdl = ext_hub_dev->constant.dev_hdl,
|
||||||
.parent_port_num = port_idx + 1,
|
.parent_port_num = port_idx + 1,
|
||||||
.port_power_delay_ms = ext_hub_dev->constant.hub_desc->bPwrOn2PwrGood * 2,
|
.port_power_delay_ms = ext_hub_dev->constant.hub_desc->bPwrOn2PwrGood * 2,
|
||||||
@ -462,45 +463,72 @@ exit:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t device_release_ports(ext_hub_dev_t *ext_hub_dev)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
// Mark all ports as gone
|
||||||
|
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
|
||||||
|
// Only for ports, that were created
|
||||||
|
if (ext_hub_dev->constant.ports[i] != NULL) {
|
||||||
|
// Mark port as gone
|
||||||
|
ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]);
|
||||||
|
if (ret == ESP_OK) {
|
||||||
|
// Port doesn't have a device and can be recycled right now
|
||||||
|
ESP_ERROR_CHECK(device_port_free(ext_hub_dev, i));
|
||||||
|
} else if (ret == ESP_ERR_NOT_FINISHED) {
|
||||||
|
// Port has a device and will be recycled after USBH device will be released by all clients and freed
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port is gone", ext_hub_dev->constant.dev_addr, i + 1);
|
||||||
|
// Not an error case, but it's good to notify this with the error message
|
||||||
|
// TODO: IDF-12173 remove the error, instead set up the flag and verify the flag while recycle
|
||||||
|
ret = ESP_OK;
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s",
|
||||||
|
ext_hub_dev->constant.dev_addr, i + 1, esp_err_to_name(ret));
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port was not created", ext_hub_dev->constant.dev_addr, i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void device_release(ext_hub_dev_t *ext_hub_dev)
|
static void device_release(ext_hub_dev_t *ext_hub_dev)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
|
||||||
|
|
||||||
ESP_LOGD(EXT_HUB_TAG, "[%d] Device release", ext_hub_dev->constant.dev_addr);
|
ESP_LOGD(EXT_HUB_TAG, "[%d] Device release", ext_hub_dev->constant.dev_addr);
|
||||||
|
|
||||||
// Release IN EP
|
|
||||||
ESP_ERROR_CHECK(usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_HALT));
|
|
||||||
|
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
|
assert(ext_hub_dev->dynamic.flags.waiting_release); // Device should waiting the release
|
||||||
ext_hub_dev->dynamic.flags.is_gone = 1;
|
ext_hub_dev->dynamic.flags.is_gone = 1;
|
||||||
ext_hub_dev->dynamic.flags.waiting_release = 0;
|
ext_hub_dev->dynamic.flags.waiting_release = 0;
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
if (ext_hub_dev->single_thread.state >= EXT_HUB_STATE_CONFIGURED) {
|
// Release IN EP
|
||||||
// Hub device was configured and has a descriptor
|
ESP_ERROR_CHECK(usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_HALT));
|
||||||
assert(ext_hub_dev->constant.hub_desc != NULL);
|
|
||||||
assert(p_ext_hub_driver->constant.port_driver);
|
switch (ext_hub_dev->single_thread.state) {
|
||||||
// Mark all ports as gone
|
case EXT_HUB_STATE_ATTACHED:
|
||||||
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
|
// Device has no configured ports, release the USBH device object
|
||||||
if (ext_hub_dev->constant.ports[i]) {
|
ESP_LOGD(EXT_HUB_TAG, "[%d] Release USBH device object", ext_hub_dev->constant.dev_addr);
|
||||||
ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]);
|
ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl));
|
||||||
if (ret == ESP_OK) {
|
break;
|
||||||
// Port doesn't have a device and can be recycled right now
|
case EXT_HUB_STATE_CONFIGURED:
|
||||||
ret = device_port_free(ext_hub_dev, i);
|
case EXT_HUB_STATE_SUSPENDED:
|
||||||
if (ret != ESP_OK) {
|
assert(ext_hub_dev->constant.hub_desc != NULL); // Device should have a Hub descriptor
|
||||||
// Hub runs into an error state
|
assert(p_ext_hub_driver->constant.port_driver); // Port driver should be available
|
||||||
// TODO: IDF-10057 Hub handling error
|
|
||||||
}
|
// Release ports if device has them
|
||||||
} else if (ret == ESP_ERR_NOT_FINISHED) {
|
if (ext_hub_dev->constant.hub_desc->bNbrPorts) {
|
||||||
// Port has a device and will be recycled after USBH device will be released by all clients and freed
|
ESP_ERROR_CHECK(device_release_ports(ext_hub_dev));
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port is gone", ext_hub_dev->constant.dev_addr, i + 1);
|
|
||||||
} else {
|
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s",
|
|
||||||
ext_hub_dev->constant.dev_addr, i + 1, esp_err_to_name(ret));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Should never occur
|
||||||
|
abort();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ext_hub_dev->single_thread.state = EXT_HUB_STATE_RELEASED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t device_alloc_desc(ext_hub_dev_t *ext_hub_hdl, const usb_hub_descriptor_t *hub_desc)
|
static esp_err_t device_alloc_desc(ext_hub_dev_t *ext_hub_hdl, const usb_hub_descriptor_t *hub_desc)
|
||||||
@ -520,6 +548,9 @@ static esp_err_t device_alloc_desc(ext_hub_dev_t *ext_hub_hdl, const usb_hub_des
|
|||||||
|
|
||||||
static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_dev)
|
static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_dev)
|
||||||
{
|
{
|
||||||
|
EXT_HUB_CHECK(config != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
EXT_HUB_CHECK(config->dev_addr != 0, ESP_ERR_NOT_ALLOWED);
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
urb_t *ctrl_urb = NULL;
|
urb_t *ctrl_urb = NULL;
|
||||||
urb_t *in_urb = NULL;
|
urb_t *in_urb = NULL;
|
||||||
@ -537,7 +568,8 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d
|
|||||||
ext_hub_dev_t *hub_dev = heap_caps_calloc(1, sizeof(ext_hub_dev_t), MALLOC_CAP_DEFAULT);
|
ext_hub_dev_t *hub_dev = heap_caps_calloc(1, sizeof(ext_hub_dev_t), MALLOC_CAP_DEFAULT);
|
||||||
|
|
||||||
if (hub_dev == NULL) {
|
if (hub_dev == NULL) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Unable to allocate device");
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Unable to allocate device",
|
||||||
|
config->dev_addr);
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
@ -545,13 +577,16 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d
|
|||||||
// Allocate Control transfer URB
|
// Allocate Control transfer URB
|
||||||
ctrl_urb = urb_alloc(sizeof(usb_setup_packet_t) + EXT_HUB_CTRL_TRANSFER_MAX_DATA_LEN, 0);
|
ctrl_urb = urb_alloc(sizeof(usb_setup_packet_t) + EXT_HUB_CTRL_TRANSFER_MAX_DATA_LEN, 0);
|
||||||
if (ctrl_urb == NULL) {
|
if (ctrl_urb == NULL) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Unable to allocate Control URB");
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Unable to allocate Control URB",
|
||||||
|
config->dev_addr);
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto ctrl_urb_fail;
|
goto ctrl_urb_fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->ep_in_desc->wMaxPacketSize > EXT_HUB_MAX_STATUS_BYTES_SIZE) {
|
if (config->ep_in_desc->wMaxPacketSize > EXT_HUB_MAX_STATUS_BYTES_SIZE) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "wMaxPacketSize=%d is not supported", config->ep_in_desc->wMaxPacketSize);
|
ESP_LOGE(EXT_HUB_TAG, "[%d] wMaxPacketSize=%d is not supported",
|
||||||
|
config->dev_addr,
|
||||||
|
config->ep_in_desc->wMaxPacketSize);
|
||||||
ret = ESP_ERR_NOT_SUPPORTED;
|
ret = ESP_ERR_NOT_SUPPORTED;
|
||||||
goto in_urb_fail;
|
goto in_urb_fail;
|
||||||
}
|
}
|
||||||
@ -559,7 +594,8 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d
|
|||||||
in_urb = urb_alloc(config->ep_in_desc->wMaxPacketSize, 0);
|
in_urb = urb_alloc(config->ep_in_desc->wMaxPacketSize, 0);
|
||||||
// Allocate Interrupt transfer URB
|
// Allocate Interrupt transfer URB
|
||||||
if (in_urb == NULL) {
|
if (in_urb == NULL) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Unable to allocate Interrupt URB");
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Unable to allocate Interrupt URB",
|
||||||
|
config->dev_addr);
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto in_urb_fail;
|
goto in_urb_fail;
|
||||||
}
|
}
|
||||||
@ -576,7 +612,9 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d
|
|||||||
|
|
||||||
ret = usbh_ep_alloc(config->dev_hdl, &ep_config, &ep_hdl);
|
ret = usbh_ep_alloc(config->dev_hdl, &ep_config, &ep_hdl);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Endpoint allocation failure: %s", esp_err_to_name(ret));
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Interrupt EP allocation failure: %s",
|
||||||
|
config->dev_addr,
|
||||||
|
esp_err_to_name(ret));
|
||||||
goto ep_fail;
|
goto ep_fail;
|
||||||
}
|
}
|
||||||
// Configure Control transfer URB
|
// Configure Control transfer URB
|
||||||
@ -678,15 +716,22 @@ static esp_err_t device_configure(ext_hub_dev_t *ext_hub_dev)
|
|||||||
ESP_LOGD(EXT_HUB_TAG, "\tPower on to power good time: %dms", hub_desc->bPwrOn2PwrGood * 2);
|
ESP_LOGD(EXT_HUB_TAG, "\tPower on to power good time: %dms", hub_desc->bPwrOn2PwrGood * 2);
|
||||||
ESP_LOGD(EXT_HUB_TAG, "\tMaximum current: %d mA", hub_desc->bHubContrCurrent);
|
ESP_LOGD(EXT_HUB_TAG, "\tMaximum current: %d mA", hub_desc->bHubContrCurrent);
|
||||||
|
|
||||||
// Create External Port flexible array
|
if (hub_desc->bNbrPorts == 0) {
|
||||||
|
ESP_LOGW(EXT_HUB_TAG, "[%d] Device doesn't have any ports", ext_hub_dev->constant.dev_addr);
|
||||||
|
// Nothing to configure, keep the device in EXT_HUB_STATE_ATTACHED
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device has external ports
|
||||||
|
// Create flexible array for port object pointers
|
||||||
ext_hub_dev->constant.ports = heap_caps_calloc(ext_hub_dev->constant.hub_desc->bNbrPorts, sizeof(ext_port_hdl_t), MALLOC_CAP_DEFAULT);
|
ext_hub_dev->constant.ports = heap_caps_calloc(ext_hub_dev->constant.hub_desc->bNbrPorts, sizeof(ext_port_hdl_t), MALLOC_CAP_DEFAULT);
|
||||||
if (ext_hub_dev->constant.ports == NULL) {
|
if (ext_hub_dev->constant.ports == NULL) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Ports list allocation error");
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Ports list allocation error", ext_hub_dev->constant.dev_addr);
|
||||||
ret = ESP_ERR_NO_MEM;
|
ret = ESP_ERR_NO_MEM;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create port and add it to pending list
|
// Create each port object
|
||||||
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
|
for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) {
|
||||||
ext_hub_dev->constant.ports[i] = NULL;
|
ext_hub_dev->constant.ports[i] = NULL;
|
||||||
ret = device_port_new(ext_hub_dev, i);
|
ret = device_port_new(ext_hub_dev, i);
|
||||||
@ -807,6 +852,13 @@ exit:
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// -------------------------- Device handling ---------------------------------
|
// -------------------------- Device handling ---------------------------------
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
static void handle_error(ext_hub_dev_t *ext_hub_dev)
|
||||||
|
{
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Device is not working properly, wait device removal",
|
||||||
|
ext_hub_dev->constant.dev_addr);
|
||||||
|
// Force change the stage
|
||||||
|
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
static bool handle_hub_descriptor(ext_hub_dev_t *ext_hub_dev)
|
static bool handle_hub_descriptor(ext_hub_dev_t *ext_hub_dev)
|
||||||
{
|
{
|
||||||
@ -815,13 +867,13 @@ static bool handle_hub_descriptor(ext_hub_dev_t *ext_hub_dev)
|
|||||||
usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
||||||
const usb_hub_descriptor_t *hub_desc = (const usb_hub_descriptor_t *)(ctrl_xfer->data_buffer + sizeof(usb_setup_packet_t));
|
const usb_hub_descriptor_t *hub_desc = (const usb_hub_descriptor_t *)(ctrl_xfer->data_buffer + sizeof(usb_setup_packet_t));
|
||||||
|
|
||||||
if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
ESP_LOG_BUFFER_HEXDUMP(EXT_HUB_TAG, ctrl_xfer->data_buffer, ctrl_xfer->actual_num_bytes, ESP_LOG_VERBOSE);
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Bad transfer status %d: stage=%d", ctrl_xfer->status, ext_hub_dev->single_thread.stage);
|
|
||||||
|
if (hub_desc->bDescriptorType != USB_CLASS_DESCRIPTOR_TYPE_HUB) {
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Hub Descriptor has wrong bDescriptorType", ext_hub_dev->constant.dev_addr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_LOG_BUFFER_HEXDUMP(EXT_HUB_TAG, ctrl_xfer->data_buffer, ctrl_xfer->actual_num_bytes, ESP_LOG_VERBOSE);
|
|
||||||
|
|
||||||
ret = device_alloc_desc(ext_hub_dev, hub_desc);
|
ret = device_alloc_desc(ext_hub_dev, hub_desc);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
pass = false;
|
pass = false;
|
||||||
@ -886,7 +938,8 @@ static void handle_port_feature(ext_hub_dev_t *ext_hub_dev)
|
|||||||
|
|
||||||
assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts);
|
assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts);
|
||||||
assert(p_ext_hub_driver->constant.port_driver);
|
assert(p_ext_hub_driver->constant.port_driver);
|
||||||
p_ext_hub_driver->constant.port_driver->req_process(ext_hub_dev->constant.ports[port_idx]);
|
// [TODO: IDF-12174] Revisit the External Hub Driver to ensure consistent error handling.
|
||||||
|
ESP_ERROR_CHECK(p_ext_hub_driver->constant.port_driver->req_process(ext_hub_dev->constant.ports[port_idx]));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_port_status(ext_hub_dev_t *ext_hub_dev)
|
static void handle_port_status(ext_hub_dev_t *ext_hub_dev)
|
||||||
@ -899,7 +952,8 @@ static void handle_port_status(ext_hub_dev_t *ext_hub_dev)
|
|||||||
|
|
||||||
assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts);
|
assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts);
|
||||||
assert(p_ext_hub_driver->constant.port_driver);
|
assert(p_ext_hub_driver->constant.port_driver);
|
||||||
p_ext_hub_driver->constant.port_driver->set_status(ext_hub_dev->constant.ports[port_idx], new_status);
|
// [TODO: IDF-12174] Revisit the External Hub Driver to ensure consistent error handling.
|
||||||
|
ESP_ERROR_CHECK(p_ext_hub_driver->constant.port_driver->set_status(ext_hub_dev->constant.ports[port_idx], new_status));
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool device_control_request(ext_hub_dev_t *ext_hub_dev)
|
static bool device_control_request(ext_hub_dev_t *ext_hub_dev)
|
||||||
@ -942,7 +996,8 @@ static bool device_control_response_handling(ext_hub_dev_t *ext_hub_dev)
|
|||||||
|
|
||||||
// Check transfer status
|
// Check transfer status
|
||||||
if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Control request bad transfer status %d",
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Control request bad transfer status %d",
|
||||||
|
ext_hub_dev->constant.dev_addr,
|
||||||
ctrl_xfer->status);
|
ctrl_xfer->status);
|
||||||
return stage_pass;
|
return stage_pass;
|
||||||
}
|
}
|
||||||
@ -960,11 +1015,11 @@ static bool device_control_response_handling(ext_hub_dev_t *ext_hub_dev)
|
|||||||
case EXT_HUB_STAGE_CHECK_HUB_STATUS:
|
case EXT_HUB_STAGE_CHECK_HUB_STATUS:
|
||||||
stage_pass = handle_hub_status(ext_hub_dev);
|
stage_pass = handle_hub_status(ext_hub_dev);
|
||||||
break;
|
break;
|
||||||
case EXT_HUB_STAGE_PORT_FEATURE:
|
case EXT_HUB_STAGE_CHECK_PORT_FEATURE:
|
||||||
handle_port_feature(ext_hub_dev);
|
handle_port_feature(ext_hub_dev);
|
||||||
stage_pass = true;
|
stage_pass = true;
|
||||||
break;
|
break;
|
||||||
case EXT_HUB_STAGE_PORT_STATUS_REQUEST:
|
case EXT_HUB_STAGE_CHECK_PORT_STATUS:
|
||||||
handle_port_status(ext_hub_dev);
|
handle_port_status(ext_hub_dev);
|
||||||
stage_pass = true;
|
stage_pass = true;
|
||||||
break;
|
break;
|
||||||
@ -986,8 +1041,6 @@ static bool stage_need_process(ext_hub_stage_t stage)
|
|||||||
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
||||||
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
||||||
case EXT_HUB_STAGE_GET_HUB_STATUS:
|
case EXT_HUB_STAGE_GET_HUB_STATUS:
|
||||||
// Error stage
|
|
||||||
case EXT_HUB_STAGE_FAILURE:
|
|
||||||
need_process_cb = true;
|
need_process_cb = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -1002,10 +1055,12 @@ static bool stage_need_process(ext_hub_stage_t stage)
|
|||||||
// false - terminal stage
|
// false - terminal stage
|
||||||
static bool device_set_next_stage(ext_hub_dev_t *ext_hub_dev, bool last_stage_pass)
|
static bool device_set_next_stage(ext_hub_dev_t *ext_hub_dev, bool last_stage_pass)
|
||||||
{
|
{
|
||||||
|
bool call_proc_req_cb = false;
|
||||||
ext_hub_stage_t last_stage = ext_hub_dev->single_thread.stage;
|
ext_hub_stage_t last_stage = ext_hub_dev->single_thread.stage;
|
||||||
ext_hub_stage_t next_stage;
|
ext_hub_stage_t next_stage;
|
||||||
|
|
||||||
if (last_stage_pass) {
|
if (last_stage_pass) {
|
||||||
|
// Device doesn't have an error
|
||||||
ESP_LOGD(EXT_HUB_TAG, "Stage %s OK", ext_hub_stage_strings[last_stage]);
|
ESP_LOGD(EXT_HUB_TAG, "Stage %s OK", ext_hub_stage_strings[last_stage]);
|
||||||
if (last_stage == EXT_HUB_STAGE_GET_DEVICE_STATUS ||
|
if (last_stage == EXT_HUB_STAGE_GET_DEVICE_STATUS ||
|
||||||
last_stage == EXT_HUB_STAGE_GET_HUB_DESCRIPTOR ||
|
last_stage == EXT_HUB_STAGE_GET_HUB_DESCRIPTOR ||
|
||||||
@ -1016,24 +1071,18 @@ static bool device_set_next_stage(ext_hub_dev_t *ext_hub_dev, bool last_stage_pa
|
|||||||
// Terminal stages, move to IDLE
|
// Terminal stages, move to IDLE
|
||||||
next_stage = EXT_HUB_STAGE_IDLE;
|
next_stage = EXT_HUB_STAGE_IDLE;
|
||||||
}
|
}
|
||||||
|
ext_hub_dev->single_thread.stage = next_stage;
|
||||||
|
call_proc_req_cb = stage_need_process(next_stage);
|
||||||
} else {
|
} else {
|
||||||
|
// Device has an error
|
||||||
ESP_LOGE(EXT_HUB_TAG, "Stage %s FAILED", ext_hub_stage_strings[last_stage]);
|
ESP_LOGE(EXT_HUB_TAG, "Stage %s FAILED", ext_hub_stage_strings[last_stage]);
|
||||||
// These stages cannot fail
|
// Set error and wait device to be removed
|
||||||
assert(last_stage != EXT_HUB_STAGE_PORT_FEATURE ||
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
last_stage != EXT_HUB_STAGE_PORT_STATUS_REQUEST);
|
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_ERROR);
|
||||||
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
next_stage = EXT_HUB_STAGE_FAILURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ext_hub_dev->single_thread.stage = next_stage;
|
return call_proc_req_cb;
|
||||||
return stage_need_process(next_stage);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_error(ext_hub_dev_t *ext_hub_dev)
|
|
||||||
{
|
|
||||||
ext_hub_dev->single_thread.state = EXT_HUB_STATE_FAILED;
|
|
||||||
// TODO: IDF-10057 Hub handling error
|
|
||||||
ESP_LOGW(EXT_HUB_TAG, "%s has not been implemented yet", __FUNCTION__);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_device(ext_hub_dev_t *ext_hub_dev)
|
static void handle_device(ext_hub_dev_t *ext_hub_dev)
|
||||||
@ -1042,9 +1091,6 @@ static void handle_device(ext_hub_dev_t *ext_hub_dev)
|
|||||||
bool stage_pass = false;
|
bool stage_pass = false;
|
||||||
// FSM for external Hub
|
// FSM for external Hub
|
||||||
switch (ext_hub_dev->single_thread.stage) {
|
switch (ext_hub_dev->single_thread.stage) {
|
||||||
case EXT_HUB_STAGE_IDLE:
|
|
||||||
stage_pass = true;
|
|
||||||
break;
|
|
||||||
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
case EXT_HUB_STAGE_GET_DEVICE_STATUS:
|
||||||
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
case EXT_HUB_STAGE_GET_HUB_DESCRIPTOR:
|
||||||
case EXT_HUB_STAGE_GET_HUB_STATUS:
|
case EXT_HUB_STAGE_GET_HUB_STATUS:
|
||||||
@ -1053,16 +1099,12 @@ static void handle_device(ext_hub_dev_t *ext_hub_dev)
|
|||||||
case EXT_HUB_STAGE_CHECK_DEVICE_STATUS:
|
case EXT_HUB_STAGE_CHECK_DEVICE_STATUS:
|
||||||
case EXT_HUB_STAGE_CHECK_HUB_DESCRIPTOR:
|
case EXT_HUB_STAGE_CHECK_HUB_DESCRIPTOR:
|
||||||
case EXT_HUB_STAGE_CHECK_HUB_STATUS:
|
case EXT_HUB_STAGE_CHECK_HUB_STATUS:
|
||||||
case EXT_HUB_STAGE_PORT_FEATURE:
|
case EXT_HUB_STAGE_CHECK_PORT_FEATURE:
|
||||||
case EXT_HUB_STAGE_PORT_STATUS_REQUEST:
|
case EXT_HUB_STAGE_CHECK_PORT_STATUS:
|
||||||
stage_pass = device_control_response_handling(ext_hub_dev);
|
stage_pass = device_control_response_handling(ext_hub_dev);
|
||||||
break;
|
break;
|
||||||
case EXT_HUB_STAGE_FAILURE:
|
|
||||||
handle_error(ext_hub_dev);
|
|
||||||
stage_pass = true;
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// Should never occur
|
// No one must handle the device when EXT_HUB_STAGE_IDLE, so it should never occur
|
||||||
abort();
|
abort();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1101,21 +1143,6 @@ static void handle_ep1_clear(ext_hub_dev_t *ext_hub_dev)
|
|||||||
usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_CLEAR);
|
usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_CLEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_gone(ext_hub_dev_t *ext_hub_dev)
|
|
||||||
{
|
|
||||||
bool call_proc_req_cb = false;
|
|
||||||
|
|
||||||
// Set the flags
|
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
|
||||||
ext_hub_dev->dynamic.flags.waiting_free = 1;
|
|
||||||
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_FREE);
|
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
|
||||||
|
|
||||||
if (call_proc_req_cb) {
|
|
||||||
p_ext_hub_driver->constant.proc_req_cb(false, p_ext_hub_driver->constant.proc_req_cb_arg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ------------------------------ Driver ---------------------------------------
|
// ------------------------------ Driver ---------------------------------------
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
@ -1279,7 +1306,7 @@ next_iface:
|
|||||||
esp_err_t ext_hub_new_dev(uint8_t dev_addr)
|
esp_err_t ext_hub_new_dev(uint8_t dev_addr)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
ext_hub_dev_t *hub_dev = NULL;
|
ext_hub_dev_t *hub_dev = NULL;
|
||||||
@ -1317,7 +1344,7 @@ esp_err_t ext_hub_new_dev(uint8_t dev_addr)
|
|||||||
// Create External Hub device
|
// Create External Hub device
|
||||||
ret = device_alloc(&hub_config, &hub_dev);
|
ret = device_alloc(&hub_config, &hub_dev);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "External HUB device alloc error %s", esp_err_to_name(ret));
|
ESP_LOGE(EXT_HUB_TAG, "[%d] Unable to add device, error %s", dev_addr, esp_err_to_name(ret));
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1341,17 +1368,16 @@ exit:
|
|||||||
esp_err_t ext_hub_dev_gone(uint8_t dev_addr)
|
esp_err_t ext_hub_dev_gone(uint8_t dev_addr)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
|
|
||||||
ext_hub_dev_t *ext_hub_dev = NULL;
|
ext_hub_dev_t *ext_hub_dev = NULL;
|
||||||
bool in_pending = false;
|
bool call_proc_req_cb = false;
|
||||||
|
|
||||||
EXT_HUB_CHECK(dev_addr != 0, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(dev_addr != 0, ESP_ERR_INVALID_ARG);
|
||||||
// Find device with dev_addr in the devices TAILQ
|
// Find device with dev_addr in the devices TAILQ
|
||||||
// TODO: IDF-10058
|
// TODO: IDF-10058 Hubs support interface selection (HS)
|
||||||
// Release all devices by dev_addr
|
// All objects which are linked to dev_addr should be freed, but before IDF-10058, one address - one device
|
||||||
ret = get_dev_by_addr(dev_addr, &ext_hub_dev);
|
ret = get_dev_by_addr(dev_addr, &ext_hub_dev);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGD(EXT_HUB_TAG, "No device with address %d was found", dev_addr);
|
ESP_LOGD(EXT_HUB_TAG, "No device with address %d was found", dev_addr);
|
||||||
@ -1363,23 +1389,30 @@ esp_err_t ext_hub_dev_gone(uint8_t dev_addr)
|
|||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
if (ext_hub_dev->dynamic.flags.waiting_release ||
|
if (ext_hub_dev->dynamic.flags.waiting_release ||
|
||||||
ext_hub_dev->dynamic.flags.waiting_children) {
|
ext_hub_dev->dynamic.flags.waiting_children) {
|
||||||
// External Hub was already released or waiting children to be freed
|
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
// External Hub was already released or waiting children to be freed
|
||||||
ret = ESP_ERR_INVALID_STATE;
|
ret = ESP_ERR_INVALID_STATE;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (ext_hub_dev->dynamic.flags.in_pending_list) {
|
|
||||||
in_pending = true;
|
if ((ext_hub_dev->single_thread.maxchild == 0) && !ext_hub_dev->dynamic.flags.in_pending_list) {
|
||||||
// TODO: IDF-10490
|
// Not in pending list and doesn't have any children
|
||||||
// test case:
|
ext_hub_dev->dynamic.flags.waiting_free = 1;
|
||||||
// - detach the external Hub device right before the Hub Descriptor request.
|
ext_hub_dev->dynamic.flags.waiting_release = 1;
|
||||||
_device_set_actions(ext_hub_dev, DEV_ACTION_RELEASE);
|
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_RELEASE |
|
||||||
|
DEV_ACTION_FREE);
|
||||||
|
} else {
|
||||||
|
// If external Hub was configured and enabled, its EP In is active
|
||||||
|
// After detachment the Hub, the driver handles the EP In interrupt and added the device to the list
|
||||||
|
// Usually, when device was detached, device object is already in pending list
|
||||||
|
// with DEV_ACTION_EP1_DEQUEUE action. Then just add the RELEASE action
|
||||||
|
ext_hub_dev->dynamic.flags.waiting_release = 1;
|
||||||
|
call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_RELEASE);
|
||||||
}
|
}
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
// Device not in pending, can be released immediately
|
if (call_proc_req_cb) {
|
||||||
if (!in_pending) {
|
p_ext_hub_driver->constant.proc_req_cb(false, p_ext_hub_driver->constant.proc_req_cb_arg);
|
||||||
device_release(ext_hub_dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ESP_OK;
|
ret = ESP_OK;
|
||||||
@ -1433,7 +1466,7 @@ esp_err_t ext_hub_status_handle_complete(ext_hub_handle_t ext_hub_hdl)
|
|||||||
esp_err_t ext_hub_process(void)
|
esp_err_t ext_hub_process(void)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
// Keep processing until all device's with pending events have been handled
|
// Keep processing until all device's with pending events have been handled
|
||||||
while (!TAILQ_EMPTY(&p_ext_hub_driver->dynamic.ext_hubs_pending_tailq)) {
|
while (!TAILQ_EMPTY(&p_ext_hub_driver->dynamic.ext_hubs_pending_tailq)) {
|
||||||
// Move the device back into the idle device list,
|
// Move the device back into the idle device list,
|
||||||
@ -1467,9 +1500,6 @@ esp_err_t ext_hub_process(void)
|
|||||||
if (action_flags & DEV_ACTION_ERROR) {
|
if (action_flags & DEV_ACTION_ERROR) {
|
||||||
handle_error(ext_hub_dev);
|
handle_error(ext_hub_dev);
|
||||||
}
|
}
|
||||||
if (action_flags & DEV_ACTION_GONE) {
|
|
||||||
handle_gone(ext_hub_dev);
|
|
||||||
}
|
|
||||||
if (action_flags & DEV_ACTION_RELEASE) {
|
if (action_flags & DEV_ACTION_RELEASE) {
|
||||||
device_release(ext_hub_dev);
|
device_release(ext_hub_dev);
|
||||||
}
|
}
|
||||||
@ -1540,7 +1570,7 @@ esp_err_t ext_hub_get_status(ext_hub_handle_t ext_hub_hdl)
|
|||||||
esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
||||||
@ -1552,7 +1582,8 @@ esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|||||||
|
|
||||||
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
||||||
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
||||||
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED ||
|
||||||
|
ext_hub_dev->single_thread.state == EXT_HUB_STATE_RELEASED, ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
if (ext_hub_dev->dynamic.flags.waiting_release) {
|
if (ext_hub_dev->dynamic.flags.waiting_release) {
|
||||||
@ -1610,7 +1641,7 @@ exit:
|
|||||||
esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
@ -1625,7 +1656,7 @@ esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|||||||
esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
@ -1640,7 +1671,7 @@ esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|||||||
esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
@ -1648,6 +1679,8 @@ esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|||||||
uint8_t port_idx = port_num - 1;
|
uint8_t port_idx = port_num - 1;
|
||||||
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
||||||
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
||||||
|
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED ||
|
||||||
|
ext_hub_dev->single_thread.state == EXT_HUB_STATE_SUSPENDED, ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
return p_ext_hub_driver->constant.port_driver->disable(ext_hub_dev->constant.ports[port_idx]);
|
return p_ext_hub_driver->constant.port_driver->disable(ext_hub_dev->constant.ports[port_idx]);
|
||||||
}
|
}
|
||||||
@ -1655,7 +1688,7 @@ esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|||||||
esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, usb_speed_t *speed)
|
esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, usb_speed_t *speed)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
@ -1663,96 +1696,82 @@ esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num,
|
|||||||
uint8_t port_idx = port_num - 1;
|
uint8_t port_idx = port_num - 1;
|
||||||
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
||||||
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
EXT_HUB_CHECK(p_ext_hub_driver->constant.port_driver != NULL, ESP_ERR_NOT_SUPPORTED);
|
||||||
|
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED ||
|
||||||
|
ext_hub_dev->single_thread.state == EXT_HUB_STATE_SUSPENDED, ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
return p_ext_hub_driver->constant.port_driver->get_speed(ext_hub_dev->constant.ports[port_idx], speed);
|
return p_ext_hub_driver->constant.port_driver->get_speed(ext_hub_dev->constant.ports[port_idx], speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
// --------------------------- USB Chapter 11 ----------------------------------
|
// ---------------------------------- USB Chapter 11 -----------------------------------------------
|
||||||
// -----------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
static esp_err_t ext_hub_control_request(ext_hub_dev_t *ext_hub_dev, uint8_t port1, uint8_t request, uint8_t feature)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
|
||||||
|
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
|
||||||
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
|
||||||
usb_transfer_t *transfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
usb_transfer_t *transfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
||||||
|
switch (request) {
|
||||||
|
case USB_B_REQUEST_HUB_GET_PORT_STATUS:
|
||||||
|
USB_SETUP_PACKET_INIT_GET_PORT_STATUS((usb_setup_packet_t *)transfer->data_buffer, port1);
|
||||||
|
transfer->num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_port_status_t);
|
||||||
|
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_CHECK_PORT_STATUS;
|
||||||
|
break;
|
||||||
|
case USB_B_REQUEST_HUB_SET_PORT_FEATURE:
|
||||||
|
USB_SETUP_PACKET_INIT_SET_PORT_FEATURE((usb_setup_packet_t *)transfer->data_buffer, port1, feature);
|
||||||
|
transfer->num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_CHECK_PORT_FEATURE;
|
||||||
|
break;
|
||||||
|
case USB_B_REQUEST_HUB_CLEAR_FEATURE:
|
||||||
|
USB_SETUP_PACKET_INIT_CLEAR_PORT_FEATURE((usb_setup_packet_t *)transfer->data_buffer, port1, feature);
|
||||||
|
transfer->num_bytes = sizeof(usb_setup_packet_t);
|
||||||
|
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_CHECK_PORT_FEATURE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ESP_LOGE(EXT_HUB_TAG, "Request %X not supported", request);
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
EXT_HUB_CHECK(port_num != 0 && port_num <= ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
|
||||||
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
|
||||||
|
|
||||||
USB_SETUP_PACKET_INIT_SET_PORT_FEATURE((usb_setup_packet_t *)transfer->data_buffer, port_num, feature);
|
|
||||||
transfer->num_bytes = sizeof(usb_setup_packet_t);
|
|
||||||
|
|
||||||
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_PORT_FEATURE;
|
|
||||||
ret = usbh_dev_submit_ctrl_urb(ext_hub_dev->constant.dev_hdl, ext_hub_dev->constant.ctrl_urb);
|
ret = usbh_dev_submit_ctrl_urb(ext_hub_dev->constant.dev_hdl, ext_hub_dev->constant.ctrl_urb);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Set port feature %#x, failed to submit ctrl urb: %s",
|
ESP_LOGE(EXT_HUB_TAG, "Request %X, port %d, feature %d: failed to submit ctrl urb: %s",
|
||||||
ext_hub_dev->constant.dev_addr,
|
request, port1, feature,
|
||||||
port_num,
|
|
||||||
feature,
|
|
||||||
esp_err_to_name(ret));
|
esp_err_to_name(ret));
|
||||||
|
device_error(ext_hub_dev);
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature)
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
// ---------------------------------- Parent Request -----------------------------------------------
|
||||||
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
esp_err_t ext_hub_request(ext_port_hdl_t port_hdl, ext_port_parent_request_data_t *data, void *user_arg)
|
||||||
{
|
{
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
EXT_HUB_ENTER_CRITICAL();
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
EXT_HUB_EXIT_CRITICAL();
|
||||||
|
EXT_HUB_CHECK(data != NULL && port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
esp_err_t ret;
|
uint8_t port1;
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
ext_hub_handle_t ext_hub_hdl = (ext_hub_handle_t)ext_port_get_context(port_hdl);
|
||||||
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
||||||
usb_transfer_t *transfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
|
||||||
|
|
||||||
EXT_HUB_CHECK(port_num != 0 && port_num <= ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
ESP_ERROR_CHECK(ext_port_get_port_num(port_hdl, &port1));
|
||||||
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
EXT_HUB_CHECK(port1 != 0 && port1 <= ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
||||||
|
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED ||
|
||||||
|
ext_hub_dev->single_thread.state == EXT_HUB_STATE_SUSPENDED, ESP_ERR_INVALID_STATE);
|
||||||
|
|
||||||
USB_SETUP_PACKET_INIT_CLEAR_PORT_FEATURE((usb_setup_packet_t *)transfer->data_buffer, port_num, feature);
|
switch (data->type) {
|
||||||
transfer->num_bytes = sizeof(usb_setup_packet_t);
|
case EXT_PORT_PARENT_REQ_CONTROL:
|
||||||
|
return ext_hub_control_request(ext_hub_dev, port1, data->control.req, data->control.feature);
|
||||||
|
|
||||||
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_PORT_FEATURE;
|
case EXT_PORT_PARENT_REQ_PROC_COMPLETED:
|
||||||
ret = usbh_dev_submit_ctrl_urb(ext_hub_dev->constant.dev_hdl, ext_hub_dev->constant.ctrl_urb);
|
return ext_hub_status_handle_complete(ext_hub_hdl);
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Clear port feature %#x, failed to submit ctrl urb: %s",
|
default:
|
||||||
ext_hub_dev->constant.dev_addr,
|
break;
|
||||||
port_num,
|
|
||||||
feature,
|
|
||||||
esp_err_to_name(ret));
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
ESP_LOGE(EXT_HUB_TAG, "Request type %d not supported", data->type);
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num)
|
|
||||||
{
|
|
||||||
EXT_HUB_ENTER_CRITICAL();
|
|
||||||
EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
|
||||||
EXT_HUB_EXIT_CRITICAL();
|
|
||||||
|
|
||||||
esp_err_t ret;
|
|
||||||
EXT_HUB_CHECK(ext_hub_hdl != NULL, ESP_ERR_INVALID_ARG);
|
|
||||||
ext_hub_dev_t *ext_hub_dev = (ext_hub_dev_t *)ext_hub_hdl;
|
|
||||||
usb_transfer_t *transfer = &ext_hub_dev->constant.ctrl_urb->transfer;
|
|
||||||
|
|
||||||
EXT_HUB_CHECK(port_num != 0 && port_num <= ext_hub_dev->constant.hub_desc->bNbrPorts, ESP_ERR_INVALID_SIZE);
|
|
||||||
EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE);
|
|
||||||
|
|
||||||
USB_SETUP_PACKET_INIT_GET_PORT_STATUS((usb_setup_packet_t *)transfer->data_buffer, port_num);
|
|
||||||
transfer->num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_port_status_t);
|
|
||||||
|
|
||||||
ext_hub_dev->single_thread.stage = EXT_HUB_STAGE_PORT_STATUS_REQUEST;
|
|
||||||
ret = usbh_dev_submit_ctrl_urb(ext_hub_dev->constant.dev_hdl, ext_hub_dev->constant.ctrl_urb);
|
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Get port status, failed to submit ctrl urb: %s",
|
|
||||||
ext_hub_dev->constant.dev_addr,
|
|
||||||
port_num,
|
|
||||||
esp_err_to_name(ret));
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -78,7 +78,7 @@ struct ext_port_s {
|
|||||||
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
uint8_t parent_dev_addr; /**< Ports' parent device bus address */
|
uint8_t parent_dev_addr; /**< Ports' parent device bus address */
|
||||||
// Port related constant members
|
// Port related constant members
|
||||||
ext_hub_handle_t ext_hub_hdl; /**< Ports' parent External Hub handle */
|
void* context; /**< Ports' parent External Hub handle */
|
||||||
uint8_t port_num; /**< Ports' parent External Hub Port number */
|
uint8_t port_num; /**< Ports' parent External Hub Port number */
|
||||||
int power_on_delay_ms; /**< Ports' Power on time to Power Good, ms */
|
int power_on_delay_ms; /**< Ports' Power on time to Power Good, ms */
|
||||||
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
||||||
@ -102,6 +102,8 @@ typedef struct {
|
|||||||
void *proc_req_cb_arg; /**< External Port process callback argument */
|
void *proc_req_cb_arg; /**< External Port process callback argument */
|
||||||
ext_port_event_cb_t event_cb; /**< External Port event callback */
|
ext_port_event_cb_t event_cb; /**< External Port event callback */
|
||||||
void *event_cb_arg; /**< External Port event callback argument */
|
void *event_cb_arg; /**< External Port event callback argument */
|
||||||
|
ext_port_parent_request_cb_t hub_request_cb; /**< External Port Hub request callback */
|
||||||
|
void *hub_request_cb_arg; /**< External Port Hub request callback argument */
|
||||||
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
} constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */
|
||||||
} ext_port_driver_t;
|
} ext_port_driver_t;
|
||||||
|
|
||||||
@ -225,17 +227,27 @@ static inline bool port_has_finished_reset(ext_port_t *ext_port)
|
|||||||
* - ESP_ERR_INVALID_ARG: The parent hub handle couldn't be NULL
|
* - ESP_ERR_INVALID_ARG: The parent hub handle couldn't be NULL
|
||||||
* - ESP_ERR_INVALID_SIZE: The port number should be in a range: [1, .. , bNbrPort]
|
* - ESP_ERR_INVALID_SIZE: The port number should be in a range: [1, .. , bNbrPort]
|
||||||
* - ESP_ERR_INVALID_STATE: The parent hub device wasn't configured
|
* - ESP_ERR_INVALID_STATE: The parent hub device wasn't configured
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED: The request type is not supported by the External Hub Driver
|
||||||
* - ESP_OK: Status has been requested
|
* - ESP_OK: Status has been requested
|
||||||
*/
|
*/
|
||||||
static esp_err_t port_request_status(ext_port_t* ext_port)
|
static esp_err_t port_request_status(ext_port_t* ext_port)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ext_hub_get_port_status(ext_port->constant.ext_hub_hdl, ext_port->constant.port_num);
|
ext_port_parent_request_data_t data = {
|
||||||
|
.type = EXT_PORT_PARENT_REQ_CONTROL,
|
||||||
|
.control = {
|
||||||
|
.req = USB_B_REQUEST_HUB_GET_PORT_STATUS,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t ret = p_ext_port_driver->constant.hub_request_cb((ext_port_hdl_t)ext_port,
|
||||||
|
&data,
|
||||||
|
p_ext_port_driver->constant.hub_request_cb_arg);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
// Port is requesting status, lock the status
|
// Port is requesting status, lock the status
|
||||||
ext_port->flags.status_lock = 1;
|
ext_port->flags.status_lock = 1;
|
||||||
return ret;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -254,9 +266,17 @@ static esp_err_t port_request_status(ext_port_t* ext_port)
|
|||||||
*/
|
*/
|
||||||
static esp_err_t port_set_feature(ext_port_t *ext_port, const usb_hub_port_feature_t feature)
|
static esp_err_t port_set_feature(ext_port_t *ext_port, const usb_hub_port_feature_t feature)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ext_hub_set_port_feature(ext_port->constant.ext_hub_hdl,
|
ext_port_parent_request_data_t data = {
|
||||||
ext_port->constant.port_num,
|
.type = EXT_PORT_PARENT_REQ_CONTROL,
|
||||||
feature);
|
.control = {
|
||||||
|
.req = USB_B_REQUEST_HUB_SET_PORT_FEATURE,
|
||||||
|
.feature = feature,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t ret = p_ext_port_driver->constant.hub_request_cb((ext_port_hdl_t)ext_port,
|
||||||
|
&data,
|
||||||
|
p_ext_port_driver->constant.hub_request_cb_arg);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -293,9 +313,17 @@ static esp_err_t port_set_feature(ext_port_t *ext_port, const usb_hub_port_featu
|
|||||||
*/
|
*/
|
||||||
static esp_err_t port_clear_feature(ext_port_t *ext_port, const usb_hub_port_feature_t feature)
|
static esp_err_t port_clear_feature(ext_port_t *ext_port, const usb_hub_port_feature_t feature)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ext_hub_clear_port_feature(ext_port->constant.ext_hub_hdl,
|
ext_port_parent_request_data_t data = {
|
||||||
ext_port->constant.port_num,
|
.type = EXT_PORT_PARENT_REQ_CONTROL,
|
||||||
feature);
|
.control = {
|
||||||
|
.req = USB_B_REQUEST_HUB_CLEAR_FEATURE,
|
||||||
|
.feature = feature,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
esp_err_t ret = p_ext_port_driver->constant.hub_request_cb((ext_port_hdl_t)ext_port,
|
||||||
|
&data,
|
||||||
|
p_ext_port_driver->constant.hub_request_cb_arg);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -388,7 +416,6 @@ static void port_event(ext_port_t *ext_port, ext_port_event_t event)
|
|||||||
};
|
};
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case EXT_PORT_CONNECTED:
|
case EXT_PORT_CONNECTED:
|
||||||
event_data.connected.ext_hub_hdl = ext_port->constant.ext_hub_hdl;
|
|
||||||
event_data.connected.parent_dev_hdl = ext_port->constant.parent_dev_hdl;
|
event_data.connected.parent_dev_hdl = ext_port->constant.parent_dev_hdl;
|
||||||
event_data.connected.parent_port_num = ext_port->constant.port_num;
|
event_data.connected.parent_port_num = ext_port->constant.port_num;
|
||||||
break;
|
break;
|
||||||
@ -406,7 +433,7 @@ static void port_event(ext_port_t *ext_port, ext_port_event_t event)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
p_ext_port_driver->constant.event_cb(&event_data, p_ext_port_driver->constant.event_cb_arg);
|
p_ext_port_driver->constant.event_cb((ext_port_hdl_t)ext_port, &event_data, p_ext_port_driver->constant.event_cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -431,7 +458,7 @@ static ext_port_t *get_port_from_pending_list(void)
|
|||||||
* - Port state: USB_PORT_STATE_NOT_CONFIGURED
|
* - Port state: USB_PORT_STATE_NOT_CONFIGURED
|
||||||
* - Port device status: PORT_DEV_NOT_PRESENT
|
* - Port device status: PORT_DEV_NOT_PRESENT
|
||||||
*
|
*
|
||||||
* @param[in] ext_hub_hdl Ports' parent hub handle
|
* @param[in] context Ports' parent hub handle
|
||||||
* @param[in] parent_dev_hdl Ports' parent device handle
|
* @param[in] parent_dev_hdl Ports' parent device handle
|
||||||
* @param[in] parent_port_num Ports' parent port number
|
* @param[in] parent_port_num Ports' parent port number
|
||||||
* @param[in] port_delay_ms Ports' Power on time to Power Good, ms
|
* @param[in] port_delay_ms Ports' Power on time to Power Good, ms
|
||||||
@ -442,10 +469,10 @@ static ext_port_t *get_port_from_pending_list(void)
|
|||||||
* - ESP_ERR_NOT_FINISHED: Unable to allocate the port object: parent device is not available
|
* - ESP_ERR_NOT_FINISHED: Unable to allocate the port object: parent device is not available
|
||||||
* - ESP_OK: Port object created successfully
|
* - ESP_OK: Port object created successfully
|
||||||
*/
|
*/
|
||||||
static esp_err_t port_alloc(ext_hub_handle_t ext_hub_hdl, usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, uint16_t port_delay_ms, ext_port_t **port_obj)
|
static esp_err_t port_alloc(void* context, usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, uint16_t port_delay_ms, ext_port_t **port_obj)
|
||||||
{
|
{
|
||||||
uint8_t parent_dev_addr = 0;
|
uint8_t parent_dev_addr = 0;
|
||||||
EXT_PORT_CHECK(ext_hub_hdl != NULL && parent_dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_PORT_CHECK(context != NULL && parent_dev_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
// This is the one exception from the requirement to use only the Ext Hub Driver API.
|
// This is the one exception from the requirement to use only the Ext Hub Driver API.
|
||||||
// TODO: IDF-10023 Move responsibility of parent-child tree building to Hub Driver instead of USBH
|
// TODO: IDF-10023 Move responsibility of parent-child tree building to Hub Driver instead of USBH
|
||||||
EXT_PORT_CHECK(usbh_dev_get_addr(parent_dev_hdl, &parent_dev_addr) == ESP_OK, ESP_ERR_NOT_FINISHED);
|
EXT_PORT_CHECK(usbh_dev_get_addr(parent_dev_hdl, &parent_dev_addr) == ESP_OK, ESP_ERR_NOT_FINISHED);
|
||||||
@ -457,7 +484,7 @@ static esp_err_t port_alloc(ext_hub_handle_t ext_hub_hdl, usb_device_handle_t pa
|
|||||||
|
|
||||||
ext_port->constant.parent_dev_hdl = parent_dev_hdl;
|
ext_port->constant.parent_dev_hdl = parent_dev_hdl;
|
||||||
ext_port->constant.parent_dev_addr = parent_dev_addr;
|
ext_port->constant.parent_dev_addr = parent_dev_addr;
|
||||||
ext_port->constant.ext_hub_hdl = ext_hub_hdl;
|
ext_port->constant.context = context;
|
||||||
ext_port->constant.port_num = parent_port_num;
|
ext_port->constant.port_num = parent_port_num;
|
||||||
#if (EXT_PORT_POWER_ON_CUSTOM_DELAY)
|
#if (EXT_PORT_POWER_ON_CUSTOM_DELAY)
|
||||||
ext_port->constant.power_on_delay_ms = EXT_PORT_POWER_ON_CUSTOM_DELAY_MS;
|
ext_port->constant.power_on_delay_ms = EXT_PORT_POWER_ON_CUSTOM_DELAY_MS;
|
||||||
@ -523,7 +550,7 @@ static esp_err_t handle_complete(ext_port_t *ext_port)
|
|||||||
// When multiply Hubs are attached, we can have ports in pending list, but for another parent
|
// When multiply Hubs are attached, we can have ports in pending list, but for another parent
|
||||||
ext_port_t *port = NULL;
|
ext_port_t *port = NULL;
|
||||||
TAILQ_FOREACH(port, &p_ext_port_driver->single_thread.pending_tailq, tailq_entry) {
|
TAILQ_FOREACH(port, &p_ext_port_driver->single_thread.pending_tailq, tailq_entry) {
|
||||||
if (port->constant.ext_hub_hdl == ext_port->constant.ext_hub_hdl) {
|
if (port->constant.context == ext_port->constant.context) {
|
||||||
// Port with same parent has been found
|
// Port with same parent has been found
|
||||||
all_ports_were_handled = false;
|
all_ports_were_handled = false;
|
||||||
break;
|
break;
|
||||||
@ -537,7 +564,13 @@ static esp_err_t handle_complete(ext_port_t *ext_port)
|
|||||||
|
|
||||||
if (all_ports_were_handled) {
|
if (all_ports_were_handled) {
|
||||||
// Notify parent to enable Interrupt EP
|
// Notify parent to enable Interrupt EP
|
||||||
ext_hub_status_handle_complete(ext_port->constant.ext_hub_hdl);
|
ext_port_parent_request_data_t data = {
|
||||||
|
.type = EXT_PORT_PARENT_REQ_PROC_COMPLETED,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(p_ext_port_driver->constant.hub_request_cb((ext_port_hdl_t)ext_port,
|
||||||
|
&data,
|
||||||
|
p_ext_port_driver->constant.hub_request_cb_arg));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_pending_ports) {
|
if (has_pending_ports) {
|
||||||
@ -684,7 +717,7 @@ static void handle_port_state(ext_port_t *ext_port)
|
|||||||
switch (curr_state) {
|
switch (curr_state) {
|
||||||
case USB_PORT_STATE_NOT_CONFIGURED:
|
case USB_PORT_STATE_NOT_CONFIGURED:
|
||||||
new_state = USB_PORT_STATE_POWERED_OFF;
|
new_state = USB_PORT_STATE_POWERED_OFF;
|
||||||
port_request_status(ext_port);
|
ESP_ERROR_CHECK(port_request_status(ext_port));
|
||||||
need_handling = true;
|
need_handling = true;
|
||||||
break;
|
break;
|
||||||
case USB_PORT_STATE_POWERED_OFF:
|
case USB_PORT_STATE_POWERED_OFF:
|
||||||
@ -943,7 +976,7 @@ static esp_err_t port_new(void *port_cfg, void **port_hdl)
|
|||||||
|
|
||||||
ext_port_t *port = NULL;
|
ext_port_t *port = NULL;
|
||||||
ext_port_config_t *config = (ext_port_config_t *)port_cfg;
|
ext_port_config_t *config = (ext_port_config_t *)port_cfg;
|
||||||
esp_err_t ret = port_alloc(config->ext_hub_hdl,
|
esp_err_t ret = port_alloc(config->context,
|
||||||
config->parent_dev_hdl,
|
config->parent_dev_hdl,
|
||||||
config->parent_port_num,
|
config->parent_port_num,
|
||||||
config->port_power_delay_ms,
|
config->port_power_delay_ms,
|
||||||
@ -980,6 +1013,7 @@ static esp_err_t port_recycle(void *port_hdl)
|
|||||||
EXT_PORT_CHECK(port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
EXT_PORT_CHECK(port_hdl != NULL, ESP_ERR_INVALID_ARG);
|
||||||
ext_port_t *ext_port = (ext_port_t *) port_hdl;
|
ext_port_t *ext_port = (ext_port_t *) port_hdl;
|
||||||
|
|
||||||
|
EXT_PORT_CHECK(ext_port->flags.is_gone == 0, ESP_ERR_INVALID_STATE);
|
||||||
ESP_LOGD(EXT_PORT_TAG, "Port %d request recycle, state=%d", ext_port->constant.port_num, ext_port->state);
|
ESP_LOGD(EXT_PORT_TAG, "Port %d request recycle, state=%d", ext_port->constant.port_num, ext_port->state);
|
||||||
|
|
||||||
port_set_actions(ext_port, PORT_ACTION_RECYCLE);
|
port_set_actions(ext_port, PORT_ACTION_RECYCLE);
|
||||||
@ -1285,7 +1319,7 @@ static esp_err_t port_req_process(void* port_hdl)
|
|||||||
/**
|
/**
|
||||||
* @brief External Port Driver API
|
* @brief External Port Driver API
|
||||||
*/
|
*/
|
||||||
const ext_hub_port_driver_t ext_port_driver = {
|
const ext_port_driver_api_t ext_port_driver = {
|
||||||
.new = port_new,
|
.new = port_new,
|
||||||
.reset = port_reset,
|
.reset = port_reset,
|
||||||
.recycle = port_recycle,
|
.recycle = port_recycle,
|
||||||
@ -1302,6 +1336,10 @@ const ext_hub_port_driver_t ext_port_driver = {
|
|||||||
esp_err_t ext_port_install(const ext_port_driver_config_t *config)
|
esp_err_t ext_port_install(const ext_port_driver_config_t *config)
|
||||||
{
|
{
|
||||||
EXT_PORT_CHECK(p_ext_port_driver == NULL, ESP_ERR_NOT_ALLOWED);
|
EXT_PORT_CHECK(p_ext_port_driver == NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
|
EXT_PORT_CHECK(config != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
EXT_PORT_CHECK(config->proc_req_cb != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
EXT_PORT_CHECK(config->event_cb != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
EXT_PORT_CHECK(config->hub_request_cb != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
|
||||||
ext_port_driver_t *ext_port_drv = heap_caps_calloc(1, sizeof(ext_port_driver_t), MALLOC_CAP_DEFAULT);
|
ext_port_driver_t *ext_port_drv = heap_caps_calloc(1, sizeof(ext_port_driver_t), MALLOC_CAP_DEFAULT);
|
||||||
EXT_PORT_CHECK(ext_port_drv != NULL, ESP_ERR_NO_MEM);
|
EXT_PORT_CHECK(ext_port_drv != NULL, ESP_ERR_NO_MEM);
|
||||||
@ -1311,6 +1349,8 @@ esp_err_t ext_port_install(const ext_port_driver_config_t *config)
|
|||||||
ext_port_drv->constant.proc_req_cb_arg = config->proc_req_cb_arg;
|
ext_port_drv->constant.proc_req_cb_arg = config->proc_req_cb_arg;
|
||||||
ext_port_drv->constant.event_cb = config->event_cb;
|
ext_port_drv->constant.event_cb = config->event_cb;
|
||||||
ext_port_drv->constant.event_cb_arg = config->event_cb_arg;
|
ext_port_drv->constant.event_cb_arg = config->event_cb_arg;
|
||||||
|
ext_port_drv->constant.hub_request_cb = config->hub_request_cb;
|
||||||
|
ext_port_drv->constant.hub_request_cb_arg = config->hub_request_cb_arg;
|
||||||
TAILQ_INIT(&ext_port_drv->single_thread.pending_tailq);
|
TAILQ_INIT(&ext_port_drv->single_thread.pending_tailq);
|
||||||
|
|
||||||
p_ext_port_driver = ext_port_drv;
|
p_ext_port_driver = ext_port_drv;
|
||||||
@ -1397,8 +1437,24 @@ esp_err_t ext_port_process(void)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ext_hub_port_driver_t *ext_port_get_driver(void)
|
const ext_port_driver_api_t *ext_port_get_driver(void)
|
||||||
{
|
{
|
||||||
EXT_PORT_CHECK(p_ext_port_driver != NULL, NULL);
|
EXT_PORT_CHECK(p_ext_port_driver != NULL, NULL);
|
||||||
return &ext_port_driver;
|
return &ext_port_driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* ext_port_get_context(ext_port_hdl_t port_hdl)
|
||||||
|
{
|
||||||
|
EXT_PORT_CHECK(p_ext_port_driver != NULL, NULL);
|
||||||
|
ext_port_t *ext_port = (ext_port_t *)port_hdl;
|
||||||
|
return ext_port->constant.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t ext_port_get_port_num(ext_port_hdl_t port_hdl, uint8_t *port1)
|
||||||
|
{
|
||||||
|
EXT_PORT_CHECK(p_ext_port_driver != NULL, ESP_ERR_NOT_ALLOWED);
|
||||||
|
EXT_PORT_CHECK(port_hdl != NULL && port1 != NULL, ESP_ERR_INVALID_ARG);
|
||||||
|
ext_port_t *ext_port = (ext_port_t *)port_hdl;
|
||||||
|
*port1 = ext_port->constant.port_num;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
@ -392,7 +392,7 @@ static inline bool _buffer_check_done(pipe_t *pipe)
|
|||||||
}
|
}
|
||||||
#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
|
#if (CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3)
|
||||||
// The HW can't handle two transactions with preamble in one frame.
|
// The HW can't handle two transactions with preamble in one frame.
|
||||||
// TODO: IDF-15060
|
// TODO: IDF-12986
|
||||||
if (pipe->ep_char.ls_via_fs_hub) {
|
if (pipe->ep_char.ls_via_fs_hub) {
|
||||||
esp_rom_delay_us(1000);
|
esp_rom_delay_us(1000);
|
||||||
}
|
}
|
||||||
@ -1630,7 +1630,7 @@ static void pipe_set_ep_char(const hcd_pipe_config_t *pipe_config, usb_transfer_
|
|||||||
ep_char->dev_addr = pipe_config->dev_addr;
|
ep_char->dev_addr = pipe_config->dev_addr;
|
||||||
ep_char->ls_via_fs_hub = 0;
|
ep_char->ls_via_fs_hub = 0;
|
||||||
if (pipe_idx > 0) {
|
if (pipe_idx > 0) {
|
||||||
// TODO: remove warning after IDF-15060
|
// TODO: remove warning after IDF-12986
|
||||||
if (port_speed == USB_SPEED_FULL && pipe_config->dev_speed == USB_SPEED_LOW) {
|
if (port_speed == USB_SPEED_FULL && pipe_config->dev_speed == USB_SPEED_LOW) {
|
||||||
ESP_LOGW(HCD_DWC_TAG, "Low-speed, extra delay will be applied in ISR");
|
ESP_LOGW(HCD_DWC_TAG, "Low-speed, extra delay will be applied in ISR");
|
||||||
ep_char->ls_via_fs_hub = 1;
|
ep_char->ls_via_fs_hub = 1;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -19,7 +19,7 @@
|
|||||||
#include "usb/usb_helpers.h"
|
#include "usb/usb_helpers.h"
|
||||||
|
|
||||||
#if ENABLE_USB_HUBS
|
#if ENABLE_USB_HUBS
|
||||||
#include "ext_port.h"
|
#include "ext_hub.h"
|
||||||
#endif // ENABLE_USB_HUBS
|
#endif // ENABLE_USB_HUBS
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -28,7 +28,6 @@ implement the bare minimum to control the root HCD port.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define HUB_ROOT_PORT_NUM 1 // HCD only supports one port
|
#define HUB_ROOT_PORT_NUM 1 // HCD only supports one port
|
||||||
#define HUB_ROOT_DEV_UID 1 // Unique device ID
|
|
||||||
|
|
||||||
#ifdef CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
|
#ifdef CONFIG_USB_HOST_HW_BUFFER_BIAS_IN
|
||||||
#define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_RX
|
#define HUB_ROOT_HCD_PORT_FIFO_BIAS HCD_PORT_FIFO_BIAS_RX
|
||||||
@ -93,7 +92,6 @@ typedef struct {
|
|||||||
|
|
||||||
struct {
|
struct {
|
||||||
TAILQ_HEAD(tailhead_devs, dev_tree_node_s) dev_nodes_tailq; /**< Tailq of attached devices */
|
TAILQ_HEAD(tailhead_devs, dev_tree_node_s) dev_nodes_tailq; /**< Tailq of attached devices */
|
||||||
unsigned int next_uid; /**< Unique ID for next upcoming device */
|
|
||||||
} single_thread; /**< Single thread members don't require a critical section so long as they are never accessed from multiple threads */
|
} single_thread; /**< Single thread members don't require a critical section so long as they are never accessed from multiple threads */
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
@ -153,53 +151,52 @@ static bool root_port_callback(hcd_port_handle_t port_hdl, hcd_port_event_t port
|
|||||||
*
|
*
|
||||||
* @return esp_err_t
|
* @return esp_err_t
|
||||||
*/
|
*/
|
||||||
static esp_err_t new_dev_tree_node(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, usb_speed_t speed)
|
static esp_err_t dev_tree_node_new(usb_device_handle_t parent_dev_hdl, uint8_t parent_port_num, usb_speed_t speed)
|
||||||
{
|
{
|
||||||
esp_err_t ret;
|
esp_err_t ret;
|
||||||
unsigned int node_uid = p_hub_driver_obj->single_thread.next_uid;
|
// Allocate memory for a new device tree node
|
||||||
|
|
||||||
dev_tree_node_t *dev_tree_node = heap_caps_calloc(1, sizeof(dev_tree_node_t), MALLOC_CAP_DEFAULT);
|
dev_tree_node_t *dev_tree_node = heap_caps_calloc(1, sizeof(dev_tree_node_t), MALLOC_CAP_DEFAULT);
|
||||||
if (dev_tree_node == NULL) {
|
if (dev_tree_node == NULL) {
|
||||||
return ESP_ERR_NO_MEM;
|
return ESP_ERR_NO_MEM;
|
||||||
}
|
}
|
||||||
|
// Assign initial UID based on the current number of registered devices
|
||||||
|
int device_num = 0;
|
||||||
|
ESP_ERROR_CHECK(usbh_devs_num(&device_num));
|
||||||
|
dev_tree_node->uid = device_num + 1;
|
||||||
|
// Ensure the UID is unique
|
||||||
|
while (usbh_devs_is_uid_in_use(dev_tree_node->uid)) {
|
||||||
|
dev_tree_node->uid++;
|
||||||
|
assert(dev_tree_node->uid != 0); // No overflow possible
|
||||||
|
}
|
||||||
|
|
||||||
// Allocate a new USBH device
|
dev_tree_node->parent_dev_hdl = parent_dev_hdl;
|
||||||
|
dev_tree_node->parent_port_num = parent_port_num;
|
||||||
|
|
||||||
|
// Initialize and register a new USBH Device with the assigned UID
|
||||||
usbh_dev_params_t params = {
|
usbh_dev_params_t params = {
|
||||||
.uid = node_uid,
|
.uid = dev_tree_node->uid,
|
||||||
.speed = speed,
|
.speed = speed,
|
||||||
.root_port_hdl = p_hub_driver_obj->constant.root_port_hdl, // Always the same for all devices
|
.root_port_hdl = p_hub_driver_obj->constant.root_port_hdl, // Always the same for all devices
|
||||||
// TODO: IDF-10023 Move responsibility of parent-child tree building to Hub Driver instead of USBH
|
// TODO: IDF-10023 Move parent-child tree management responsibility to Hub Driver
|
||||||
.parent_dev_hdl = parent_dev_hdl,
|
.parent_dev_hdl = parent_dev_hdl,
|
||||||
.parent_port_num = parent_port_num,
|
.parent_port_num = parent_port_num,
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = usbh_devs_add(¶ms);
|
ret = usbh_devs_add(¶ms);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
// USBH devs add could failed due to lack of free hcd channels
|
// Device registration may fail if there are no available HCD channels.
|
||||||
// TODO: IDF-10044 Hub should recover after running out of hcd channels
|
// TODO: IDF-10044 Implement hub recovery mechanism for running out of HCD channels.
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_tree_node->uid = node_uid;
|
|
||||||
dev_tree_node->parent_dev_hdl = parent_dev_hdl;
|
|
||||||
dev_tree_node->parent_port_num = parent_port_num;
|
|
||||||
TAILQ_INSERT_TAIL(&p_hub_driver_obj->single_thread.dev_nodes_tailq, dev_tree_node, tailq_entry);
|
TAILQ_INSERT_TAIL(&p_hub_driver_obj->single_thread.dev_nodes_tailq, dev_tree_node, tailq_entry);
|
||||||
|
|
||||||
p_hub_driver_obj->single_thread.next_uid++;
|
ESP_LOGD(HUB_DRIVER_TAG, "Device tree node (uid=%d): new", dev_tree_node->uid);
|
||||||
if (p_hub_driver_obj->single_thread.next_uid == 0) {
|
|
||||||
ESP_LOGW(HUB_DRIVER_TAG, "Counter overflowed, possibility of uid collisions");
|
|
||||||
p_hub_driver_obj->single_thread.next_uid = HUB_ROOT_DEV_UID;
|
|
||||||
}
|
|
||||||
// Verify presence of a device with newly prepared uid in USBH
|
|
||||||
// TODO: IDF-10022 Provide a mechanism to request presence status of a device with uid in USBH device object list
|
|
||||||
// Return if device uid is not in USBH device object list, repeat until uid will be founded
|
|
||||||
|
|
||||||
ESP_LOGD(HUB_DRIVER_TAG, "Device tree node (uid=%d): new", node_uid);
|
|
||||||
|
|
||||||
hub_event_data_t event_data = {
|
hub_event_data_t event_data = {
|
||||||
.event = HUB_EVENT_CONNECTED,
|
.event = HUB_EVENT_CONNECTED,
|
||||||
.connected = {
|
.connected = {
|
||||||
.uid = node_uid,
|
.uid = dev_tree_node->uid,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
p_hub_driver_obj->constant.event_cb(&event_data, p_hub_driver_obj->constant.event_cb_arg);
|
p_hub_driver_obj->constant.event_cb(&event_data, p_hub_driver_obj->constant.event_cb_arg);
|
||||||
@ -328,14 +325,16 @@ static void ext_port_callback(void *user_arg)
|
|||||||
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
p_hub_driver_obj->constant.proc_req_cb(USB_PROC_REQ_SOURCE_HUB, false, p_hub_driver_obj->constant.proc_req_cb_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ext_port_event_callback(ext_port_event_data_t *event_data, void *arg)
|
static void ext_port_event_callback(ext_port_hdl_t port_hdl, ext_port_event_data_t *event_data, void *arg)
|
||||||
{
|
{
|
||||||
|
ext_hub_handle_t ext_hub_hdl = (ext_hub_handle_t) ext_port_get_context(port_hdl);
|
||||||
|
|
||||||
switch (event_data->event) {
|
switch (event_data->event) {
|
||||||
case EXT_PORT_CONNECTED:
|
case EXT_PORT_CONNECTED:
|
||||||
// First reset is done by ext_port logic
|
// First reset is done by ext_port logic
|
||||||
usb_speed_t port_speed;
|
usb_speed_t port_speed;
|
||||||
|
|
||||||
if (ext_hub_port_get_speed(event_data->connected.ext_hub_hdl,
|
if (ext_hub_port_get_speed(ext_hub_hdl,
|
||||||
event_data->connected.parent_port_num,
|
event_data->connected.parent_port_num,
|
||||||
&port_speed) != ESP_OK) {
|
&port_speed) != ESP_OK) {
|
||||||
goto new_ds_dev_err;
|
goto new_ds_dev_err;
|
||||||
@ -354,23 +353,13 @@ static void ext_port_event_callback(ext_port_event_data_t *event_data, void *arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (!CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS)
|
if (dev_tree_node_new(event_data->connected.parent_dev_hdl, event_data->connected.parent_port_num, port_speed) != ESP_OK) {
|
||||||
if (port_speed == USB_SPEED_LOW) {
|
|
||||||
ESP_LOGE(HUB_DRIVER_TAG, "Connected %s-speed device, not supported",
|
|
||||||
(char *[]) {
|
|
||||||
"Low", "Full", "High"
|
|
||||||
}[port_speed]);
|
|
||||||
goto new_ds_dev_err;
|
|
||||||
}
|
|
||||||
#endif // CONFIG_USB_HOST_EXT_PORT_SUPPORT_LS
|
|
||||||
|
|
||||||
if (new_dev_tree_node(event_data->connected.parent_dev_hdl, event_data->connected.parent_port_num, port_speed) != ESP_OK) {
|
|
||||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add new downstream device");
|
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add new downstream device");
|
||||||
goto new_ds_dev_err;
|
goto new_ds_dev_err;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
new_ds_dev_err:
|
new_ds_dev_err:
|
||||||
ext_hub_port_disable(event_data->connected.ext_hub_hdl, event_data->connected.parent_port_num);
|
ext_hub_port_disable(ext_hub_hdl, event_data->connected.parent_port_num);
|
||||||
break;
|
break;
|
||||||
case EXT_PORT_RESET_COMPLETED:
|
case EXT_PORT_RESET_COMPLETED:
|
||||||
ESP_ERROR_CHECK(dev_tree_node_reset_completed(event_data->reset_completed.parent_dev_hdl, event_data->reset_completed.parent_port_num));
|
ESP_ERROR_CHECK(dev_tree_node_reset_completed(event_data->reset_completed.parent_dev_hdl, event_data->reset_completed.parent_port_num));
|
||||||
@ -406,7 +395,7 @@ static void root_port_handle_events(hcd_port_handle_t root_port_hdl)
|
|||||||
goto new_dev_err;
|
goto new_dev_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_dev_tree_node(NULL, 0, speed) != ESP_OK) {
|
if (dev_tree_node_new(NULL, 0, speed) != ESP_OK) {
|
||||||
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add new device");
|
ESP_LOGE(HUB_DRIVER_TAG, "Failed to add new device");
|
||||||
goto new_dev_err;
|
goto new_dev_err;
|
||||||
}
|
}
|
||||||
@ -548,6 +537,7 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
|
|||||||
ext_port_driver_config_t ext_port_config = {
|
ext_port_driver_config_t ext_port_config = {
|
||||||
.proc_req_cb = ext_port_callback,
|
.proc_req_cb = ext_port_callback,
|
||||||
.event_cb = ext_port_event_callback,
|
.event_cb = ext_port_event_callback,
|
||||||
|
.hub_request_cb = ext_hub_request,
|
||||||
};
|
};
|
||||||
ret = ext_port_install(&ext_port_config);
|
ret = ext_port_install(&ext_port_config);
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
@ -589,7 +579,6 @@ esp_err_t hub_install(hub_config_t *hub_config, void **client_ret)
|
|||||||
hub_driver_obj->constant.proc_req_cb_arg = hub_config->proc_req_cb_arg;
|
hub_driver_obj->constant.proc_req_cb_arg = hub_config->proc_req_cb_arg;
|
||||||
hub_driver_obj->constant.event_cb = hub_config->event_cb;
|
hub_driver_obj->constant.event_cb = hub_config->event_cb;
|
||||||
hub_driver_obj->constant.event_cb_arg = hub_config->event_cb_arg;
|
hub_driver_obj->constant.event_cb_arg = hub_config->event_cb_arg;
|
||||||
hub_driver_obj->single_thread.next_uid = HUB_ROOT_DEV_UID;
|
|
||||||
TAILQ_INIT(&hub_driver_obj->single_thread.dev_nodes_tailq);
|
TAILQ_INIT(&hub_driver_obj->single_thread.dev_nodes_tailq);
|
||||||
// Driver is not installed, we can modify dynamic section outside of the critical section
|
// Driver is not installed, we can modify dynamic section outside of the critical section
|
||||||
hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_NOT_POWERED;
|
hub_driver_obj->dynamic.root_port_state = ROOT_PORT_STATE_NOT_POWERED;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -13,10 +13,7 @@
|
|||||||
#include "usb/usb_types_stack.h"
|
#include "usb/usb_types_stack.h"
|
||||||
#include "usb/usb_types_ch9.h"
|
#include "usb/usb_types_ch9.h"
|
||||||
#include "usb/usb_types_ch11.h"
|
#include "usb/usb_types_ch11.h"
|
||||||
|
#include "ext_port.h"
|
||||||
#if CONFIG_USB_HOST_HUB_MULTI_LEVEL
|
|
||||||
#define ENABLE_MULTIPLE_HUBS 1
|
|
||||||
#endif // CONFIG_USB_HOST_HUB_MULTI_LEVEL
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -34,32 +31,13 @@ typedef struct ext_hub_s *ext_hub_handle_t;
|
|||||||
*/
|
*/
|
||||||
typedef bool (*ext_hub_cb_t)(bool in_isr, void *user_arg);
|
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 (*del)(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
|
* @brief External Hub Driver configuration
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ext_hub_cb_t proc_req_cb; /**< External Hub process callback */
|
ext_hub_cb_t proc_req_cb; /**< External Hub process callback */
|
||||||
void *proc_req_cb_arg; /**< External Hub process callback argument */
|
void *proc_req_cb_arg; /**< External Hub process callback argument */
|
||||||
const ext_hub_port_driver_t* port_driver; /**< External Port Driver */
|
const ext_port_driver_api_t* port_driver; /**< External Port Driver */
|
||||||
} ext_hub_config_t;
|
} ext_hub_config_t;
|
||||||
|
|
||||||
// ------------------------------ Driver ---------------------------------------
|
// ------------------------------ Driver ---------------------------------------
|
||||||
@ -127,7 +105,7 @@ esp_err_t ext_hub_get_handle(usb_device_handle_t dev_hdl, ext_hub_handle_t *ext_
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: New device added successfully
|
* - ESP_OK: New device added successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed, or not in correct state to add a new device
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_new_dev(uint8_t dev_addr);
|
esp_err_t ext_hub_new_dev(uint8_t dev_addr);
|
||||||
|
|
||||||
@ -141,7 +119,7 @@ esp_err_t ext_hub_new_dev(uint8_t dev_addr);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Device freed successfully
|
* - ESP_OK: Device freed successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_NOT_FOUND: Device address not found
|
* - ESP_ERR_NOT_FOUND: Device address not found
|
||||||
*/
|
*/
|
||||||
@ -182,7 +160,7 @@ esp_err_t ext_hub_status_handle_complete(ext_hub_handle_t ext_hub_hdl);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: All events handled
|
* - ESP_OK: All events handled
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_process(void);
|
esp_err_t ext_hub_process(void);
|
||||||
|
|
||||||
@ -199,10 +177,11 @@ esp_err_t ext_hub_process(void);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: The port can be recycled
|
* - ESP_OK: The port can be recycled
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
||||||
|
* - ESP_ERR_INVALID_STATE: External Hub wasn't configured or suspended
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
||||||
|
|
||||||
@ -214,7 +193,7 @@ esp_err_t ext_hub_port_recycle(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: The port can be reset
|
* - ESP_OK: The port can be reset
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
||||||
@ -229,7 +208,7 @@ esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Device on the External hub's port has been enumerated
|
* - ESP_OK: Device on the External hub's port has been enumerated
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
||||||
@ -244,10 +223,11 @@ esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Device on the External hub's port can be disabled
|
* - ESP_OK: Device on the External hub's port can be disabled
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
||||||
|
* - ESP_ERR_INVALID_STATE: External Hub wasn't configured or suspended
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
||||||
|
|
||||||
@ -260,61 +240,34 @@ esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: The device's speed obtained successfully
|
* - ESP_OK: The device's speed obtained successfully
|
||||||
* - ESP_ERR_INVALID_STATE: External Hub driver is not installed
|
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
||||||
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
* - ESP_ERR_NOT_SUPPORTED: External Port Driver has not been installed
|
||||||
|
* - ESP_ERR_INVALID_STATE: External Hub wasn't configured or suspended
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, usb_speed_t *speed);
|
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 ----------------------------------
|
// --------------------------- USB Chapter 11 ----------------------------------
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set Port Feature request
|
* @brief USB Hub Request
|
||||||
*
|
*
|
||||||
* @param[in] ext_hub_hdl External Hub device handle
|
* @note This function designed to be called from the External Port driver
|
||||||
* @param[in] port_num Port number
|
*
|
||||||
* @param[in] feature Port Feature to set
|
* @param[in] port_hdl External Port handle
|
||||||
|
* @param[in] data Request data
|
||||||
|
* @param[in] user_arg User argument
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Port's feature set successfully
|
* - ESP_ERR_NOT_ALLOWED if External Hub Driver is not installed
|
||||||
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
* - ESP_ERR_INVALID_ARG if invalid argument
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
* - ESP_ERR_INVALID_SIZE if port number is out of the hub's range
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
* - ESP_ERR_INVALID_STATE if the hub device wasn't configured
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if the request is not supported
|
||||||
|
* - ESP_OK if control transfer was successfully submitted
|
||||||
*/
|
*/
|
||||||
esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, uint8_t feature);
|
esp_err_t ext_hub_request(ext_port_hdl_t port_hdl, ext_port_parent_request_data_t *data, void *user_arg);
|
||||||
|
|
||||||
/**
|
|
||||||
* @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_OK: Port's feature cleared successfully
|
|
||||||
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
|
||||||
*/
|
|
||||||
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_OK: Port's status obtained successfully
|
|
||||||
* - ESP_ERR_NOT_ALLOWED: External Hub driver is not installed
|
|
||||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
|
||||||
* - ESP_ERR_INVALID_SIZE: External Hub port number out of the hub's range
|
|
||||||
*/
|
|
||||||
esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -7,15 +7,38 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "ext_hub.h"
|
|
||||||
#include "usb/usb_types_stack.h"
|
#include "usb/usb_types_stack.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if CONFIG_USB_HOST_HUB_MULTI_LEVEL
|
||||||
|
#define ENABLE_MULTIPLE_HUBS 1
|
||||||
|
#endif // CONFIG_USB_HOST_HUB_MULTI_LEVEL
|
||||||
|
|
||||||
typedef struct ext_port_s *ext_port_hdl_t;
|
typedef struct ext_port_s *ext_port_hdl_t;
|
||||||
|
|
||||||
|
// ------------------------ 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 (*del)(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_port_driver_api_t;
|
||||||
|
|
||||||
// ------------------------------ Events ---------------------------------------
|
// ------------------------------ Events ---------------------------------------
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EXT_PORT_CONNECTED = 0x01, /**< Port has a device connection event */
|
EXT_PORT_CONNECTED = 0x01, /**< Port has a device connection event */
|
||||||
@ -30,7 +53,6 @@ typedef struct {
|
|||||||
ext_port_event_t event;
|
ext_port_event_t event;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
ext_hub_handle_t ext_hub_hdl; /**< Ports' parent external Hub handle */
|
|
||||||
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
uint8_t parent_port_num; /**< Ports' parent port number */
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
} connected; /**< EXT_PORT_CONNECTED event specific data */
|
} connected; /**< EXT_PORT_CONNECTED event specific data */
|
||||||
@ -47,6 +69,24 @@ typedef struct {
|
|||||||
};
|
};
|
||||||
} ext_port_event_data_t;
|
} ext_port_event_data_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
EXT_PORT_PARENT_REQ_CONTROL = 0x01, /** Port requires action from the parent Hub */
|
||||||
|
EXT_PORT_PARENT_REQ_PROC_COMPLETED /** All ports were handled */
|
||||||
|
} ext_port_parent_request_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Specific data for External Port parent Hub class specific request
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
ext_port_parent_request_type_t type;
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
usb_hub_class_request_t req;
|
||||||
|
uint8_t feature;
|
||||||
|
} control;
|
||||||
|
};
|
||||||
|
} ext_port_parent_request_data_t;
|
||||||
|
|
||||||
// ------------------------------ Callbacks ------------------------------------
|
// ------------------------------ Callbacks ------------------------------------
|
||||||
/**
|
/**
|
||||||
* @brief Callback used to indicate that the External Port Driver requires process callback
|
* @brief Callback used to indicate that the External Port Driver requires process callback
|
||||||
@ -60,7 +100,12 @@ typedef void (*ext_port_cb_t)(void *user_arg);
|
|||||||
*
|
*
|
||||||
* @note For the Hub Driver only
|
* @note For the Hub Driver only
|
||||||
*/
|
*/
|
||||||
typedef void (*ext_port_event_cb_t)(ext_port_event_data_t *event_data, void *arg);
|
typedef void (*ext_port_event_cb_t)(ext_port_hdl_t port_hdl, ext_port_event_data_t *event_data, void *arg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Callback used to indicate that the External Port driver requires a Hub class specific request
|
||||||
|
*/
|
||||||
|
typedef esp_err_t (*ext_port_parent_request_cb_t)(ext_port_hdl_t port_hdl, ext_port_parent_request_data_t *data, void *user_arg);
|
||||||
|
|
||||||
// ----------------- External Port Driver configuration ------------------------
|
// ----------------- External Port Driver configuration ------------------------
|
||||||
|
|
||||||
@ -72,6 +117,8 @@ typedef struct {
|
|||||||
void *proc_req_cb_arg; /**< External Port process callback argument */
|
void *proc_req_cb_arg; /**< External Port process callback argument */
|
||||||
ext_port_event_cb_t event_cb; /**< External Port event callback */
|
ext_port_event_cb_t event_cb; /**< External Port event callback */
|
||||||
void *event_cb_arg; /**< External Port event callback argument */
|
void *event_cb_arg; /**< External Port event callback argument */
|
||||||
|
ext_port_parent_request_cb_t hub_request_cb;/**< External Port Hub request callback */
|
||||||
|
void *hub_request_cb_arg; /**< External Port Hub request callback argument */
|
||||||
} ext_port_driver_config_t;
|
} ext_port_driver_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -80,7 +127,7 @@ typedef struct {
|
|||||||
* Structure is used to create new port
|
* Structure is used to create new port
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ext_hub_handle_t ext_hub_hdl; /**< Ports' parent external Hub handle */
|
void* context; /**< Ports' parent external Hub handle */
|
||||||
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
usb_device_handle_t parent_dev_hdl; /**< Ports' parent device handle */
|
||||||
uint8_t parent_port_num; /**< Ports' parent port number */
|
uint8_t parent_port_num; /**< Ports' parent port number */
|
||||||
uint16_t port_power_delay_ms; /**< Ports' Power on time to Power Good, ms */
|
uint16_t port_power_delay_ms; /**< Ports' Power on time to Power Good, ms */
|
||||||
@ -136,7 +183,28 @@ esp_err_t ext_port_process(void);
|
|||||||
* - NULL: The Driver has not been installed
|
* - NULL: The Driver has not been installed
|
||||||
* - not NULL: Pointer to the External Port Driver API
|
* - not NULL: Pointer to the External Port Driver API
|
||||||
*/
|
*/
|
||||||
const ext_hub_port_driver_t *ext_port_get_driver(void);
|
const ext_port_driver_api_t *ext_port_get_driver(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns External Port Driver's context
|
||||||
|
*
|
||||||
|
* @param[in] port_hdl Port object handle
|
||||||
|
* @return
|
||||||
|
* - Pointer to the External Port Driver context
|
||||||
|
*/
|
||||||
|
void* ext_port_get_context(ext_port_hdl_t port_hdl);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Returns External Port's port number
|
||||||
|
*
|
||||||
|
* @param[in] port_hdl Port object handle
|
||||||
|
* @param[out] port1 Port number, starting from 1
|
||||||
|
* @return
|
||||||
|
* - ESP_ERR_NOT_ALLOWED if the External Port Driver has not been installed
|
||||||
|
* - ESP_ERR_INVALID_ARG if the Port handle is NULL
|
||||||
|
* - ESP_OK if the port number was successfully returned
|
||||||
|
*/
|
||||||
|
esp_err_t ext_port_get_port_num(ext_port_hdl_t port_hdl, uint8_t *port1);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -194,6 +194,18 @@ esp_err_t usbh_uninstall(void);
|
|||||||
esp_err_t usbh_process(void);
|
esp_err_t usbh_process(void);
|
||||||
|
|
||||||
// ---------------------- Device Pool Functions --------------------------------
|
// ---------------------- Device Pool Functions --------------------------------
|
||||||
|
/**
|
||||||
|
* @brief Determines whether a UID is currently assigned in the USBH device list
|
||||||
|
*
|
||||||
|
* @note This function may block execution while checking the device list.
|
||||||
|
*
|
||||||
|
* @param[in] uid Unique ID to check
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* - true if UID is already in use.
|
||||||
|
* - false if UID is available for assignment.
|
||||||
|
*/
|
||||||
|
bool usbh_devs_is_uid_in_use(uint32_t uid);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get the current number of devices
|
* @brief Get the current number of devices
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
@ -781,6 +781,14 @@ esp_err_t usbh_process(void)
|
|||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// ------------------------- Device Pool Functions -----------------------------
|
// ------------------------- Device Pool Functions -----------------------------
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
bool usbh_devs_is_uid_in_use(uint32_t uid)
|
||||||
|
{
|
||||||
|
bool uid_in_use;
|
||||||
|
USBH_ENTER_CRITICAL();
|
||||||
|
uid_in_use = (_find_dev_from_uid(uid) != NULL); // Check if UID exists
|
||||||
|
USBH_EXIT_CRITICAL();
|
||||||
|
return uid_in_use;
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t usbh_devs_num(int *num_devs_ret)
|
esp_err_t usbh_devs_num(int *num_devs_ret)
|
||||||
{
|
{
|
||||||
|
@ -47,7 +47,6 @@ Currently, the Host Library and the underlying Host Stack has the following limi
|
|||||||
- Only supports using one configuration. Changing to other configurations after enumeration is not supported yet.
|
- Only supports using one configuration. Changing to other configurations after enumeration is not supported yet.
|
||||||
- Transfer timeouts are not supported yet.
|
- Transfer timeouts are not supported yet.
|
||||||
:esp32p4: - {IDF_TARGET_NAME} contains two USB-OTG peripherals USB 2.0 OTG High-Speed and USB 2.0 OTG Full-Speed. Only the High-Speed instance is supported now.
|
:esp32p4: - {IDF_TARGET_NAME} contains two USB-OTG peripherals USB 2.0 OTG High-Speed and USB 2.0 OTG Full-Speed. Only the High-Speed instance is supported now.
|
||||||
- The External Hub Driver: Supports only devices with the same speed as upstream port speed (e.g., Low-speed device won't work through Full-speed external Hub).
|
|
||||||
- The External Hub Driver: Remote Wakeup feature is not supported (External Hubs are active, even if there are no devices inserted).
|
- The External Hub Driver: Remote Wakeup feature is not supported (External Hubs are active, even if there are no devices inserted).
|
||||||
- The External Hub Driver: Doesn't handle error cases (overcurrent handling, errors during initialization etc. are not implemented yet).
|
- The External Hub Driver: Doesn't handle error cases (overcurrent handling, errors during initialization etc. are not implemented yet).
|
||||||
- The External Hub Driver: No Interface selection. The Driver uses the first available Interface with Hub Class code (09h).
|
- The External Hub Driver: No Interface selection. The Driver uses the first available Interface with Hub Class code (09h).
|
||||||
|
@ -47,7 +47,6 @@ USB 主机库(以下简称主机库)是 USB 主机栈的最底层,提供
|
|||||||
- 仅支持使用发现的首个配置,尚不支持变更为其他配置。
|
- 仅支持使用发现的首个配置,尚不支持变更为其他配置。
|
||||||
- 尚不支持传输超时。
|
- 尚不支持传输超时。
|
||||||
:esp32p4: - {IDF_TARGET_NAME} 包含两个 USB-OTG 外设:USB 2.0 OTG 高速和 USB 2.0 OTG 全速。目前仅支持高速实例。
|
:esp32p4: - {IDF_TARGET_NAME} 包含两个 USB-OTG 外设:USB 2.0 OTG 高速和 USB 2.0 OTG 全速。目前仅支持高速实例。
|
||||||
- 外部 Hub 驱动:仅支持与上游端口速率相同的设备。(例如,低速设备无法通过全速外部 Hub 工作。)
|
|
||||||
- 外部 Hub 驱动:不支持远程唤醒功能(即使没有设备插入,外部 Hub 也处于工作状态)。
|
- 外部 Hub 驱动:不支持远程唤醒功能(即使没有设备插入,外部 Hub 也处于工作状态)。
|
||||||
- 外部 Hub 驱动:不处理错误用例(尚未实现过流处理、初始化错误等功能)。
|
- 外部 Hub 驱动:不处理错误用例(尚未实现过流处理、初始化错误等功能)。
|
||||||
- 外部 Hub 驱动:不支持接口选择。驱动程序使用具有 Hub 类代码 (09h) 的第一个可用接口。
|
- 外部 Hub 驱动:不支持接口选择。驱动程序使用具有 Hub 类代码 (09h) 的第一个可用接口。
|
||||||
|
Reference in New Issue
Block a user