diff --git a/components/usb/ext_hub.c b/components/usb/ext_hub.c index fe5692ba79..4bda2ca240 100644 --- a/components/usb/ext_hub.c +++ b/components/usb/ext_hub.c @@ -10,13 +10,14 @@ #include "esp_heap_caps.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "hal/usb_dwc_hal.h" // for OTG_HSPHY_INTERFACE +#include "freertos/semphr.h" #include "usb_private.h" #include "ext_hub.h" #include "usb/usb_helpers.h" typedef struct ext_port_s *ext_port_hdl_t; /* This will be implemented during ext_port driver implementation */ +#define EXT_HUB_MAX_STATUS_BYTES_SIZE (sizeof(uint32_t)) #define EXT_HUB_STATUS_CHANGE_FLAG (1 << 0) #define EXT_HUB_STATUS_PORT1_CHANGE_FLAG (1 << 1) #define EXT_HUB_CTRL_TRANSFER_MAX_DATA_LEN CONFIG_USB_HOST_CONTROL_TRANSFER_MAX_SIZE @@ -30,7 +31,6 @@ typedef enum { 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_SUSPENDED, /**< Device suspended */ - EXT_HUB_STATE_RELEASED, /**< Device released by USB Host driver (device could still be present on bus) */ EXT_HUB_STATE_FAILED /**< Device has internal error */ } ext_hub_state_t; @@ -107,17 +107,23 @@ struct ext_hub_s { union { struct { uint32_t in_pending_list: 1; /**< Device is in pending list */ + uint32_t waiting_release: 1; /**< Device waiting to be released */ + uint32_t waiting_children: 1; /**< Device is waiting children to be freed */ uint32_t waiting_free: 1; /**< Device waiting to be freed */ uint32_t is_gone: 1; /**< Device is gone */ - uint32_t reserved29: 29; /**< Reserved */ + uint32_t reserved27: 27; /**< Reserved */ }; uint32_t val; /**< Device's flags value */ } flags; uint32_t action_flags; /**< Device's action flags */ - ext_hub_state_t state; /**< Device's state */ - ext_hub_stage_t stage; /**< Device's stage */ } dynamic; /**< Dynamic members require a critical section */ + struct { + ext_hub_stage_t stage; /**< Device's stage */ + uint8_t maxchild; /**< Amount of allocated ports. Could be 0 for some Hubs. Is increased when new port is added and decreased when port has been freed. */ + ext_hub_state_t state; /**< Device's state */ + } single_thread; /**< Single thread members don't require a critical section, as long as they are never accessed from multiple threads */ + struct { // For optimisation & debug only uint8_t iface_num; /**< Device's bInterfaceNum */ @@ -129,7 +135,6 @@ struct ext_hub_s { usbh_ep_handle_t ep_in_hdl; /**< Device's Interrupt EP handle */ usb_hub_descriptor_t *hub_desc; /**< Device's Hub descriptor pointer. Could be NULL when not requested */ - uint8_t maxchild; /**< Number of ports. Could be 0 for some Hubs. */ ext_port_hdl_t *ports; /**< Flexible array of Ports. Could be NULL, when maxchild is 0 */ } constant; /**< Constant members. Do not change after installation thus do not require a critical section or mutex */ }; @@ -177,7 +182,6 @@ const char *EXT_HUB_TAG = "EXT_HUB"; // ----------------------- Forward declaration --------------------------------- // ----------------------------------------------------------------------------- static bool _device_set_actions(ext_hub_dev_t *ext_hub_dev, uint32_t action_flags); -static void device_disable(ext_hub_dev_t *ext_hub_dev); static void device_error(ext_hub_dev_t *ext_hub_dev); static void device_status_change_handle(ext_hub_dev_t *ext_hub_dev, const uint8_t* data, const int length); @@ -271,7 +275,6 @@ static void interrupt_transfer_complete_cb(usb_transfer_t *intr_xfer) break; case USB_TRANSFER_STATUS_CANCELED: // Cancellation due to USB Host uninstall routine - device_disable(ext_hub_dev); break; default: // Any other error @@ -313,11 +316,10 @@ static bool _device_set_actions(ext_hub_dev_t *ext_hub_dev, uint32_t action_flag static esp_err_t device_enable_int_ep(ext_hub_dev_t *ext_hub_dev) { - esp_err_t ret = ESP_OK; - ret = usbh_ep_enqueue_urb(ext_hub_dev->constant.ep_in_hdl, ext_hub_dev->constant.in_urb); + 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); if (ret != ESP_OK) { - ESP_LOGE(EXT_HUB_TAG, "Failed to submit in urb (%#x)", ret); - return ret; + ESP_LOGE(EXT_HUB_TAG, "Failed to submit in urb: %s", esp_err_to_name(ret)); } return ret; } @@ -331,58 +333,51 @@ static void device_has_changed(ext_hub_dev_t *ext_hub_dev) device_enable_int_ep(ext_hub_dev); } +// // Figure 11-22. Hub and Port Status Change Bitmap +// Bit | N | ... | 3 | 2 | 1 | 0 | +// Value |1/0| ... |1/0|1/0|1/0|1/0| +// | | | | | +// | | | | +------ Hub change detected +// | | | +---------- Port 1 change detected +// | | +-------------- Port 2 change detected +// | +------------------ Port 3 change detected +// | ... +// +---------------------------- Port N change detected +// static void device_status_change_handle(ext_hub_dev_t *ext_hub_dev, const uint8_t* data, const int length) { - uint8_t port_idx = 0; - uint8_t max_port_num = (sizeof(uint8_t) * 8) - 1; // Maximal Port number in one uint8_t byte - // Hub status change - if (data[0] & EXT_HUB_STATUS_CHANGE_FLAG) { - device_has_changed(ext_hub_dev); + uint32_t device_status = 0; + // Driver does not support Hubs with EP IN wMaxPacketSize > 4 + assert(length <= EXT_HUB_MAX_STATUS_BYTES_SIZE); + + for (uint32_t i = 0; i < length; i++) { + device_status |= (uint32_t)(data[i] << i); } - // Ports status change - for (uint8_t i = 0; i < length; i++) { - for (uint8_t j = 0; j < max_port_num; j++) { - if (data[i] & (EXT_HUB_STATUS_PORT1_CHANGE_FLAG << j)) { - // Notify Hub driver - port_idx = (j + (i * max_port_num)); - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->get_status(ext_hub_dev->constant.ports[port_idx]); - } + + if (device_status) { + // If device has a status change, we will re-trigger back the transfer + // after all handling will be done + if (device_status & EXT_HUB_STATUS_CHANGE_FLAG) { + // Check device status + device_has_changed(ext_hub_dev); + } + // HINTs: + // - every byte of Data IN has 8 bits of possible port statuses bits: (length * 8) + // - very first bit of status is used for Hub status: (length * 8) - 1 + for (uint8_t i = 0; i < (length * 8) - 1; i++) { + // Check ports statuses + if (device_status & (EXT_HUB_STATUS_PORT1_CHANGE_FLAG << i)) { + 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 + // Request Port status to handle changes + p_ext_hub_driver->constant.port_driver->get_status(ext_hub_dev->constant.ports[i]); } } - } -} - -static void device_disable(ext_hub_dev_t *ext_hub_dev) -{ - bool call_proc_req_cb = false; - - ESP_LOGD(EXT_HUB_TAG, "[%d] Device disable", ext_hub_dev->constant.dev_addr); - - if (ext_hub_dev->dynamic.state == EXT_HUB_STATE_RELEASED || ext_hub_dev->dynamic.flags.is_gone) { - ESP_LOGD(EXT_HUB_TAG, "Device in release state or already gone"); - return; - } - - // Mark all Ports are disable and then gone - for (uint8_t i = 0; i < ext_hub_dev->constant.maxchild; i++) { - if (p_ext_hub_driver->constant.port_driver) { - // TODO: IDF-10054 Hubs should disable their ports power - // Meanwhile, mark the port as gone - p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]); - } - } - - // Close the device - ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl)); - - EXT_HUB_ENTER_CRITICAL(); - 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); + } else { + // Device status is 0. Could happen when HS Hub with HS device is connected to HS Hub, connected to FS root port + // Re-trigger transfer right now + device_enable_int_ep(ext_hub_dev); } } @@ -399,31 +394,107 @@ static void device_error(ext_hub_dev_t *ext_hub_dev) } } -static void device_release(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) { + assert(p_ext_hub_driver->constant.port_driver); + esp_err_t ret = p_ext_hub_driver->constant.port_driver->new (NULL, (void**) &ext_hub_dev->constant.ports[port_idx]); + if (ret != ESP_OK) { + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Port allocation error: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret)); + goto fail; + } + + ext_hub_dev->single_thread.maxchild++; + +fail: + return ret; +} + +static esp_err_t device_port_free(ext_hub_dev_t *ext_hub_dev, uint8_t port_idx) +{ + EXT_HUB_CHECK(ext_hub_dev->constant.ports[port_idx] != NULL, ESP_ERR_INVALID_STATE); + bool call_proc_req_cb = false; + bool all_ports_freed = false; - ESP_LOGD(EXT_HUB_TAG, "[%d] Device release", ext_hub_dev->constant.dev_addr); + assert(ext_hub_dev->single_thread.maxchild != 0); + assert(p_ext_hub_driver->constant.port_driver); + esp_err_t ret = p_ext_hub_driver->constant.port_driver->free(ext_hub_dev->constant.ports[port_idx]); - // Mark all Ports are disable and then gone - for (uint8_t i = 0; i < ext_hub_dev->constant.maxchild; i++) { - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]); + if (ret != ESP_OK) { + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to free port: %s", ext_hub_dev->constant.dev_addr, port_idx + 1, esp_err_to_name(ret)); + goto exit; + } + + ext_hub_dev->constant.ports[port_idx] = NULL; + ext_hub_dev->single_thread.maxchild--; + + EXT_HUB_ENTER_CRITICAL(); + if (ext_hub_dev->dynamic.flags.is_gone) { + all_ports_freed = (ext_hub_dev->single_thread.maxchild == 0); + + if (all_ports_freed) { + ext_hub_dev->dynamic.flags.waiting_children = 0; + ext_hub_dev->dynamic.flags.waiting_free = 1; + call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_FREE); + } else { + ext_hub_dev->dynamic.flags.waiting_children = 1; } } + EXT_HUB_EXIT_CRITICAL(); + + // Close the device if all children were freed + if (all_ports_freed) { + ESP_LOGD(EXT_HUB_TAG, "[%d] Release USBH device object", ext_hub_dev->constant.dev_addr); + ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl)); + } + + if (call_proc_req_cb) { + p_ext_hub_driver->constant.proc_req_cb(false, p_ext_hub_driver->constant.proc_req_cb_arg); + } + + ret = ESP_OK; +exit: + return ret; +} + +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); // Release IN EP ESP_ERROR_CHECK(usbh_ep_command(ext_hub_dev->constant.ep_in_hdl, USBH_EP_CMD_HALT)); - // Close the device - ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl)); - EXT_HUB_ENTER_CRITICAL(); - call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_FREE); + ext_hub_dev->dynamic.flags.is_gone = 1; + ext_hub_dev->dynamic.flags.waiting_release = 0; 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); + if (ext_hub_dev->single_thread.state >= EXT_HUB_STATE_CONFIGURED) { + // Hub device was configured and has a descriptor + assert(ext_hub_dev->constant.hub_desc != NULL); + assert(p_ext_hub_driver->constant.port_driver); + // Mark all ports as gone + for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) { + if (ext_hub_dev->constant.ports[i]) { + 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 + ret = device_port_free(ext_hub_dev, i); + if (ret != ESP_OK) { + // Hub runs into an error state + // TODO: IDF-10057 Hub handling error + } + } 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); + } 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)); + } + } + } } } @@ -474,6 +545,12 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d goto ctrl_urb_fail; } + 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); + ret = ESP_ERR_NOT_SUPPORTED; + goto in_urb_fail; + } + in_urb = urb_alloc(config->ep_in_desc->wMaxPacketSize, 0); // Allocate Interrupt transfer URB if (in_urb == NULL) { @@ -494,7 +571,7 @@ 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); if (ret != ESP_OK) { - ESP_LOGE(EXT_HUB_TAG, "Endpoint allocation failure (%#x)", ret); + ESP_LOGE(EXT_HUB_TAG, "Endpoint allocation failure: %s", esp_err_to_name(ret)); goto ep_fail; } // Configure Control transfer URB @@ -508,19 +585,21 @@ static esp_err_t device_alloc(device_config_t *config, ext_hub_dev_t **ext_hub_d in_urb->transfer.context = (void *) hub_dev; in_urb->transfer.num_bytes = config->ep_in_desc->wMaxPacketSize; - // Save constant parameters + hub_dev->dynamic.flags.val = 0; + hub_dev->dynamic.action_flags = 0; + // Mutex protected + hub_dev->single_thread.state = EXT_HUB_STATE_ATTACHED; + // Constant members hub_dev->constant.ep_in_hdl = ep_hdl; hub_dev->constant.ctrl_urb = ctrl_urb; hub_dev->constant.in_urb = in_urb; hub_dev->constant.dev_hdl = config->dev_hdl; hub_dev->constant.dev_addr = config->dev_addr; hub_dev->constant.iface_num = config->iface_desc->bInterfaceNumber; + // Single thread members + hub_dev->single_thread.stage = EXT_HUB_STAGE_IDLE; // We will update number of ports during Hub Descriptor handling stage - hub_dev->constant.maxchild = 0; - - hub_dev->dynamic.flags.val = 0; - hub_dev->dynamic.state = EXT_HUB_STATE_ATTACHED; - hub_dev->dynamic.stage = EXT_HUB_STAGE_IDLE; + hub_dev->single_thread.maxchild = 0; EXT_HUB_ENTER_CRITICAL(); TAILQ_INSERT_TAIL(&p_ext_hub_driver->dynamic.ext_hubs_tailq, hub_dev, dynamic.tailq_entry); @@ -543,6 +622,7 @@ fail: static esp_err_t device_configure(ext_hub_dev_t *ext_hub_dev) { + esp_err_t ret; EXT_HUB_CHECK(ext_hub_dev->constant.hub_desc != NULL, ESP_ERR_INVALID_STATE); usb_hub_descriptor_t *hub_desc = ext_hub_dev->constant.hub_desc; @@ -596,43 +676,51 @@ static esp_err_t device_configure(ext_hub_dev_t *ext_hub_dev) // Create External Port flexible array 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) { - ESP_LOGE(EXT_HUB_TAG, "Ports allocation err"); - return ESP_ERR_NO_MEM; + ESP_LOGE(EXT_HUB_TAG, "Ports list allocation error"); + ret = ESP_ERR_NO_MEM; + goto fail; } - // Update device port amount - ext_hub_dev->constant.maxchild = ext_hub_dev->constant.hub_desc->bNbrPorts; - // Create port and add it to pending list - for (uint8_t i = 0; i < ext_hub_dev->constant.maxchild; i++) { - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->new (NULL, (void**) &ext_hub_dev->constant.ports[i]); + for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) { + ext_hub_dev->constant.ports[i] = NULL; + ret = device_port_new(ext_hub_dev, i); + if (ret != ESP_OK) { + goto fail; } } - EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.state = EXT_HUB_STATE_CONFIGURED; - EXT_HUB_EXIT_CRITICAL(); - + ext_hub_dev->single_thread.state = EXT_HUB_STATE_CONFIGURED; return ESP_OK; + +fail: + // Free allocated resources + for (uint8_t i = 0; i < ext_hub_dev->constant.hub_desc->bNbrPorts; i++) { + if (ext_hub_dev->constant.ports[i]) { + device_port_free(ext_hub_dev, i); + } + } + + if (ext_hub_dev->constant.ports) { + assert(ext_hub_dev->single_thread.maxchild == 0); + heap_caps_free(ext_hub_dev->constant.ports); + } + + return ret; } static void device_free(ext_hub_dev_t *ext_hub_dev) { + assert(ext_hub_dev->single_thread.maxchild == 0); // All ports should be freed by now + ESP_LOGD(EXT_HUB_TAG, "[%d] Freeing device", ext_hub_dev->constant.dev_addr); EXT_HUB_ENTER_CRITICAL(); + assert(ext_hub_dev->dynamic.flags.waiting_free == 1); // Hub should await being freed ext_hub_dev->dynamic.flags.waiting_free = 0; TAILQ_REMOVE(&p_ext_hub_driver->dynamic.ext_hubs_tailq, ext_hub_dev, dynamic.tailq_entry); EXT_HUB_EXIT_CRITICAL(); - // Free ports - for (uint8_t i = 0; i < ext_hub_dev->constant.maxchild; i++) { - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->free(ext_hub_dev->constant.ports[i]); - } - } - if (ext_hub_dev->constant.hub_desc) { heap_caps_free(ext_hub_dev->constant.hub_desc); } @@ -723,7 +811,7 @@ static bool handle_hub_descriptor(ext_hub_dev_t *ext_hub_dev) 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_LOGE(EXT_HUB_TAG, "Bad transfer status %d: stage=%d", ctrl_xfer->status, ext_hub_dev->dynamic.stage); + ESP_LOGE(EXT_HUB_TAG, "Bad transfer status %d: stage=%d", ctrl_xfer->status, ext_hub_dev->single_thread.stage); return false; } @@ -784,12 +872,37 @@ static bool handle_hub_status(ext_hub_dev_t *ext_hub_dev) return true; } +static void handle_port_feature(ext_hub_dev_t *ext_hub_dev) +{ + usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer; + + uint8_t port_num = USB_SETUP_PACKET_GET_PORT((usb_setup_packet_t *)ctrl_xfer->data_buffer); + uint8_t port_idx = port_num - 1; + + assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts); + assert(p_ext_hub_driver->constant.port_driver); + 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) +{ + usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer; + + uint8_t port_num = USB_SETUP_PACKET_GET_PORT((usb_setup_packet_t *)ctrl_xfer->data_buffer); + uint8_t port_idx = port_num - 1; + const usb_port_status_t *new_status = (const usb_port_status_t *)(ctrl_xfer->data_buffer + sizeof(usb_setup_packet_t)); + + assert(port_idx < ext_hub_dev->constant.hub_desc->bNbrPorts); + 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); +} + static bool device_control_request(ext_hub_dev_t *ext_hub_dev) { esp_err_t ret; usb_transfer_t *transfer = &ext_hub_dev->constant.ctrl_urb->transfer; - switch (ext_hub_dev->dynamic.stage) { + switch (ext_hub_dev->single_thread.stage) { case EXT_HUB_STAGE_GET_DEVICE_STATUS: USB_SETUP_PACKET_INIT_GET_STATUS((usb_setup_packet_t *)transfer->data_buffer); transfer->num_bytes = sizeof(usb_setup_packet_t) + sizeof(usb_device_status_t); @@ -810,7 +923,7 @@ static bool device_control_request(ext_hub_dev_t *ext_hub_dev) 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, "Failed to submit ctrl urb, error %#x", ret); + ESP_LOGE(EXT_HUB_TAG, "Failed to submit ctrl urb: %s", esp_err_to_name(ret)); return false; } @@ -820,8 +933,19 @@ static bool device_control_request(ext_hub_dev_t *ext_hub_dev) static bool device_control_response_handling(ext_hub_dev_t *ext_hub_dev) { bool stage_pass = false; + usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer; - switch (ext_hub_dev->dynamic.stage) { + // Check transfer status + if (ctrl_xfer->status != USB_TRANSFER_STATUS_COMPLETED) { + ESP_LOGE(EXT_HUB_TAG, "Control request bad transfer status %d", + ctrl_xfer->status); + return stage_pass; + } + + // Transfer completed, verbose data in ESP_LOG_VERBOSE is set + ESP_LOG_BUFFER_HEXDUMP(EXT_HUB_TAG, ctrl_xfer->data_buffer, ctrl_xfer->actual_num_bytes, ESP_LOG_VERBOSE); + + switch (ext_hub_dev->single_thread.stage) { case EXT_HUB_STAGE_CHECK_DEVICE_STATUS: stage_pass = handle_device_status(ext_hub_dev); break; @@ -831,6 +955,14 @@ static bool device_control_response_handling(ext_hub_dev_t *ext_hub_dev) case EXT_HUB_STAGE_CHECK_HUB_STATUS: stage_pass = handle_hub_status(ext_hub_dev); break; + case EXT_HUB_STAGE_PORT_FEATURE: + handle_port_feature(ext_hub_dev); + stage_pass = true; + break; + case EXT_HUB_STAGE_PORT_STATUS_REQUEST: + handle_port_status(ext_hub_dev); + stage_pass = true; + break; default: // Should never occur abort(); @@ -865,8 +997,7 @@ static bool stage_need_process(ext_hub_stage_t stage) // false - terminal stage static bool device_set_next_stage(ext_hub_dev_t *ext_hub_dev, bool last_stage_pass) { - bool need_process_cb; - ext_hub_stage_t last_stage = ext_hub_dev->dynamic.stage; + ext_hub_stage_t last_stage = ext_hub_dev->single_thread.stage; ext_hub_stage_t next_stage; if (last_stage_pass) { @@ -889,37 +1020,15 @@ static bool device_set_next_stage(ext_hub_dev_t *ext_hub_dev, bool last_stage_pa next_stage = EXT_HUB_STAGE_FAILURE; } - EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = next_stage; - need_process_cb = stage_need_process(next_stage); - EXT_HUB_EXIT_CRITICAL(); - - return need_process_cb; + ext_hub_dev->single_thread.stage = next_stage; + return stage_need_process(next_stage); } -static void handle_port_feature(ext_hub_dev_t *ext_hub_dev) +static void handle_error(ext_hub_dev_t *ext_hub_dev) { - usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer; - uint8_t port_num = USB_SETUP_PACKET_GET_PORT((usb_setup_packet_t *)ctrl_xfer->data_buffer); - uint8_t port_idx = port_num - 1; - - assert(port_idx < ext_hub_dev->constant.maxchild); - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->get_status(ext_hub_dev->constant.ports[port_idx]); - } -} - -static void handle_port_status(ext_hub_dev_t *ext_hub_dev) -{ - usb_transfer_t *ctrl_xfer = &ext_hub_dev->constant.ctrl_urb->transfer; - uint8_t port_num = USB_SETUP_PACKET_GET_PORT((usb_setup_packet_t *)ctrl_xfer->data_buffer); - uint8_t port_idx = port_num - 1; - const usb_port_status_t *new_status = (const usb_port_status_t *)(ctrl_xfer->data_buffer + sizeof(usb_setup_packet_t)); - - assert(port_idx < ext_hub_dev->constant.maxchild); - if (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); - } + 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) @@ -927,7 +1036,7 @@ static void handle_device(ext_hub_dev_t *ext_hub_dev) bool call_proc_req_cb; bool stage_pass = false; // FSM for external Hub - switch (ext_hub_dev->dynamic.stage) { + switch (ext_hub_dev->single_thread.stage) { case EXT_HUB_STAGE_IDLE: break; case EXT_HUB_STAGE_GET_DEVICE_STATUS: @@ -935,20 +1044,16 @@ static void handle_device(ext_hub_dev_t *ext_hub_dev) case EXT_HUB_STAGE_GET_HUB_STATUS: stage_pass = device_control_request(ext_hub_dev); break; + case EXT_HUB_STAGE_CHECK_DEVICE_STATUS: case EXT_HUB_STAGE_CHECK_HUB_DESCRIPTOR: + case EXT_HUB_STAGE_CHECK_HUB_STATUS: + case EXT_HUB_STAGE_PORT_FEATURE: + case EXT_HUB_STAGE_PORT_STATUS_REQUEST: stage_pass = device_control_response_handling(ext_hub_dev); break; - case EXT_HUB_STAGE_PORT_FEATURE: - handle_port_feature(ext_hub_dev); - stage_pass = true; - break; - case EXT_HUB_STAGE_PORT_STATUS_REQUEST: - handle_port_status(ext_hub_dev); - stage_pass = true; - break; case EXT_HUB_STAGE_FAILURE: - ESP_LOGW(EXT_HUB_TAG, "External Hub device failure handling has not been implemented yet"); - // device_error(ext_hub_dev); + handle_error(ext_hub_dev); + stage_pass = true; break; default: // Should never occur @@ -973,6 +1078,7 @@ static void handle_ep1_dequeue(ext_hub_dev_t *ext_hub_dev) { // Dequeue all URBs and run their transfer callback ESP_LOGD(EXT_HUB_TAG, "[%d] Interrupt dequeue", ext_hub_dev->constant.dev_addr); + urb_t *urb; usbh_ep_dequeue_urb(ext_hub_dev->constant.ep_in_hdl, &urb); while (urb != NULL) { @@ -989,13 +1095,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); } -static void handle_error(ext_hub_dev_t *ext_hub_dev) -{ - // TODO: IDF-10057 Hub handling error - ESP_LOGW(EXT_HUB_TAG, "%s has not been implemented yet", __FUNCTION__); - device_disable(ext_hub_dev); -} - static void handle_gone(ext_hub_dev_t *ext_hub_dev) { bool call_proc_req_cb = false; @@ -1019,8 +1118,11 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config) { esp_err_t ret; ext_hub_driver_t *ext_hub_drv = heap_caps_calloc(1, sizeof(ext_hub_driver_t), MALLOC_CAP_DEFAULT); - EXT_HUB_CHECK(ext_hub_drv != NULL, ESP_ERR_NO_MEM); - + SemaphoreHandle_t mux_lock = xSemaphoreCreateMutex(); + if (ext_hub_drv == NULL || mux_lock == NULL) { + ret = ESP_ERR_NO_MEM; + goto err; + } // Save callbacks ext_hub_drv->constant.proc_req_cb = config->proc_req_cb; ext_hub_drv->constant.proc_req_cb_arg = config->proc_req_cb_arg; @@ -1028,7 +1130,9 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config) ext_hub_drv->constant.port_driver = config->port_driver; if (ext_hub_drv->constant.port_driver == NULL) { - ESP_LOGW(EXT_HUB_TAG, "Port Driver has not been installed"); + ESP_LOGE(EXT_HUB_TAG, "Port Driver has not been implemented yet"); + ret = ESP_ERR_NOT_FINISHED; + goto err; } TAILQ_INIT(&ext_hub_drv->dynamic.ext_hubs_tailq); @@ -1038,7 +1142,7 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config) if (p_ext_hub_driver != NULL) { EXT_HUB_EXIT_CRITICAL(); ret = ESP_ERR_INVALID_STATE; - goto fail; + goto err; } p_ext_hub_driver = ext_hub_drv; EXT_HUB_EXIT_CRITICAL(); @@ -1046,7 +1150,10 @@ esp_err_t ext_hub_install(const ext_hub_config_t *config) ESP_LOGD(EXT_HUB_TAG, "Driver installed"); return ESP_OK; -fail: +err: + if (mux_lock != NULL) { + vSemaphoreDelete(mux_lock); + } heap_caps_free(ext_hub_drv); return ret; } @@ -1061,6 +1168,7 @@ esp_err_t ext_hub_uninstall(void) p_ext_hub_driver = NULL; EXT_HUB_EXIT_CRITICAL(); + // Free resources heap_caps_free(ext_hub_drv); ESP_LOGD(EXT_HUB_TAG, "Driver uninstalled"); return ESP_OK; @@ -1108,33 +1216,28 @@ static esp_err_t find_first_intf_desc(const usb_config_desc_t *config_desc, devi // Parse all interfaces if (USB_CLASS_HUB == next_intf_desc->bInterfaceClass) { // We have found the first interface descriptor with matching bInterfaceNumber -#if (OTG_HSPHY_INTERFACE != 0) - // TODO: IDF-10059 Hubs support multiple TT (HS) - if (next_intf_desc->bInterfaceProtocol != USB_B_DEV_PROTOCOL_HUB_HS_NO_TT) { - ESP_LOGD(EXT_HUB_TAG, "Transaction Translator:"); - ESP_LOGW(EXT_HUB_TAG, "Transaction Translator has not been implemented yet"); - } - switch (next_intf_desc->bInterfaceProtocol) { - case USB_B_DEV_PROTOCOL_HUB_HS_NO_TT: - ESP_LOGD(EXT_HUB_TAG, "\tNo TT"); - break; - case USB_B_DEV_PROTOCOL_HUB_HS_SINGLE_TT: - ESP_LOGD(EXT_HUB_TAG, "\tSingle TT"); - break; - case USB_B_DEV_PROTOCOL_HUB_HS_MULTI_TT: - ESP_LOGD(EXT_HUB_TAG, "\tMulti TT"); - break; - default: - ESP_LOGE(EXT_HUB_TAG, "\tInterface Protocol (%#x) not supported", next_intf_desc->bInterfaceProtocol); - goto next_iface; - } -#else if (next_intf_desc->bInterfaceProtocol != USB_B_DEV_PROTOCOL_HUB_FS) { - ESP_LOGE(EXT_HUB_TAG, "\tProtocol (%#x) not supported", next_intf_desc->bInterfaceProtocol); - goto next_iface; + // TODO: IDF-10059 Hubs support multiple TT (HS) + if (next_intf_desc->bInterfaceProtocol != USB_B_DEV_PROTOCOL_HUB_HS_NO_TT) { + ESP_LOGW(EXT_HUB_TAG, "Transaction Translator has not been implemented yet"); + } + + switch (next_intf_desc->bInterfaceProtocol) { + case USB_B_DEV_PROTOCOL_HUB_HS_NO_TT: + ESP_LOGD(EXT_HUB_TAG, "\tNo TT"); + break; + case USB_B_DEV_PROTOCOL_HUB_HS_SINGLE_TT: + ESP_LOGD(EXT_HUB_TAG, "\tSingle TT"); + break; + case USB_B_DEV_PROTOCOL_HUB_HS_MULTI_TT: + ESP_LOGD(EXT_HUB_TAG, "\tMulti TT"); + break; + default: + ESP_LOGE(EXT_HUB_TAG, "\tInterface Protocol (%#x) not supported", next_intf_desc->bInterfaceProtocol); + goto next_iface; + } } -#endif // OTG_HSPHY_INTERFACE != 0 // Hub Interface always should have only one Interrupt endpoint if (next_intf_desc->bNumEndpoints != 1) { @@ -1213,8 +1316,9 @@ esp_err_t ext_hub_new_dev(uint8_t dev_addr) goto exit; } + hub_dev->single_thread.stage = EXT_HUB_STAGE_GET_HUB_DESCRIPTOR; + EXT_HUB_ENTER_CRITICAL(); - hub_dev->dynamic.stage = EXT_HUB_STAGE_GET_HUB_DESCRIPTOR; call_proc_req_cb = _device_set_actions(hub_dev, DEV_ACTION_REQ); EXT_HUB_EXIT_CRITICAL(); @@ -1237,7 +1341,7 @@ esp_err_t ext_hub_dev_gone(uint8_t dev_addr) esp_err_t ret; ext_hub_dev_t *ext_hub_dev = NULL; - bool call_proc_req_cb = false; + bool in_pending = false; EXT_HUB_CHECK(dev_addr != 0, ESP_ERR_INVALID_ARG); // Find device with dev_addr in the devices TAILQ @@ -1246,49 +1350,61 @@ esp_err_t ext_hub_dev_gone(uint8_t dev_addr) ret = get_dev_by_addr(dev_addr, &ext_hub_dev); if (ret != ESP_OK) { ESP_LOGD(EXT_HUB_TAG, "No device with address %d was found", dev_addr); - return ret; + goto exit; } - ESP_LOGE(EXT_HUB_TAG, "[%d] Device gone", ext_hub_dev->constant.dev_addr); - - for (uint8_t i = 0; i < ext_hub_dev->constant.maxchild; i++) { - if (p_ext_hub_driver->constant.port_driver) { - p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[i]); - } - } - - // Close the device - ESP_ERROR_CHECK(usbh_dev_close(ext_hub_dev->constant.dev_hdl)); + ESP_LOGD(EXT_HUB_TAG, "[%d] Device gone", ext_hub_dev->constant.dev_addr); EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.flags.is_gone = 1; - call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_GONE); + if (ext_hub_dev->dynamic.flags.waiting_release || + ext_hub_dev->dynamic.flags.waiting_children) { + // External Hub was already released or waiting children to be freed + EXT_HUB_EXIT_CRITICAL(); + ret = ESP_ERR_INVALID_STATE; + goto exit; + } + if (ext_hub_dev->dynamic.flags.in_pending_list) { + in_pending = true; + // TODO: IDF-10490 + // test case: + // - detach the external Hub device right before the Hub Descriptor request. + _device_set_actions(ext_hub_dev, DEV_ACTION_RELEASE); + } 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); + // Device not in pending, can be released immediately + if (!in_pending) { + device_release(ext_hub_dev); } + + ret = ESP_OK; +exit: return ret; } esp_err_t ext_hub_all_free(void) { - ext_hub_dev_t *hub = NULL; bool call_proc_req_cb = false; + bool wait_for_free = false; EXT_HUB_ENTER_CRITICAL(); - EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE); - TAILQ_FOREACH(hub, &p_ext_hub_driver->dynamic.ext_hubs_tailq, dynamic.tailq_entry) { - hub->dynamic.flags.waiting_free = 1; - _device_set_actions(hub, DEV_ACTION_RELEASE); - hub->dynamic.state = EXT_HUB_STATE_RELEASED; - call_proc_req_cb = true; - } - TAILQ_FOREACH(hub, &p_ext_hub_driver->dynamic.ext_hubs_pending_tailq, dynamic.tailq_entry) { - hub->dynamic.flags.waiting_free = 1; - hub->dynamic.state = EXT_HUB_STATE_RELEASED; - _device_set_actions(hub, DEV_ACTION_RELEASE); - call_proc_req_cb = true; + for (int i = 0; i < 2; i++) { + ext_hub_dev_t *hub_curr; + ext_hub_dev_t *hub_next; + // Go through pending list first + if (i == 0) { + hub_curr = TAILQ_FIRST(&p_ext_hub_driver->dynamic.ext_hubs_pending_tailq); + } else { + hub_curr = TAILQ_FIRST(&p_ext_hub_driver->dynamic.ext_hubs_tailq); + } + while (hub_curr != NULL) { + hub_next = TAILQ_NEXT(hub_curr, dynamic.tailq_entry); + hub_curr->dynamic.flags.waiting_release = 1; + call_proc_req_cb = _device_set_actions(hub_curr, DEV_ACTION_RELEASE); + // At least one hub should be released + wait_for_free = true; + hub_curr = hub_next; + } } EXT_HUB_EXIT_CRITICAL(); @@ -1296,7 +1412,7 @@ esp_err_t ext_hub_all_free(void) p_ext_hub_driver->constant.proc_req_cb(false, p_ext_hub_driver->constant.proc_req_cb_arg); } - return ESP_OK; + return (wait_for_free) ? ESP_ERR_NOT_FINISHED : ESP_OK; } esp_err_t ext_hub_status_handle_complete(ext_hub_handle_t ext_hub_hdl) @@ -1304,9 +1420,7 @@ esp_err_t ext_hub_status_handle_complete(ext_hub_handle_t ext_hub_hdl) 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_CHECK(ext_hub_dev->dynamic.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE); - - ESP_LOGD(EXT_HUB_TAG, "[%d] Status handle complete, wait status change ...", ext_hub_hdl->constant.dev_addr); + EXT_HUB_CHECK(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE); return device_enable_int_ep(ext_hub_dev); } @@ -1380,8 +1494,9 @@ esp_err_t ext_hub_get_hub_status(ext_hub_handle_t ext_hub_hdl) 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->single_thread.stage = EXT_HUB_STAGE_GET_HUB_STATUS; + EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = EXT_HUB_STAGE_GET_HUB_STATUS; bool call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_REQ); EXT_HUB_EXIT_CRITICAL(); @@ -1401,8 +1516,8 @@ esp_err_t ext_hub_get_status(ext_hub_handle_t ext_hub_hdl) 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->single_thread.stage = EXT_HUB_STAGE_GET_DEVICE_STATUS; EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = EXT_HUB_STAGE_GET_DEVICE_STATUS; bool call_proc_req_cb = _device_set_actions(ext_hub_dev, DEV_ACTION_REQ); EXT_HUB_EXIT_CRITICAL(); @@ -1422,17 +1537,68 @@ esp_err_t ext_hub_port_recycle(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_INVALID_STATE); 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; + + esp_err_t ret; uint8_t port_idx = port_num - 1; - EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.maxchild, ESP_ERR_INVALID_SIZE); - if (p_ext_hub_driver->constant.port_driver) { - ret = p_ext_hub_driver->constant.port_driver->recycle(ext_hub_dev->constant.ports[port_idx]); - } else { - ret = ESP_ERR_NOT_SUPPORTED; + bool free_port = false; + bool release_port = false; + + 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(ext_hub_dev->single_thread.state == EXT_HUB_STATE_CONFIGURED, ESP_ERR_INVALID_STATE); + + EXT_HUB_ENTER_CRITICAL(); + if (ext_hub_dev->dynamic.flags.waiting_release) { + release_port = true; + } else if (ext_hub_dev->dynamic.flags.waiting_children) { + assert(ext_hub_dev->dynamic.flags.waiting_release == 0); // Device should be already released + assert(ext_hub_dev->dynamic.flags.is_gone == 1); // Device should be gone by now + free_port = true; } + EXT_HUB_EXIT_CRITICAL(); + + if (!release_port && !free_port) { + // Parent still present, recycle the port + ret = p_ext_hub_driver->constant.port_driver->recycle(ext_hub_dev->constant.ports[port_idx]); + if (ret != ESP_OK) { + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to recycle the port: %s", ext_hub_dev->constant.dev_addr, port_num, esp_err_to_name(ret)); + goto exit; + } + } else { + if (release_port) { + // Notify the port that parent is not available anymore and port should be recycled then freed + ret = p_ext_hub_driver->constant.port_driver->gone(ext_hub_dev->constant.ports[port_idx]); + if (ret == ESP_OK) { + ESP_LOGD(EXT_HUB_TAG, "[%d:%d] Port doesn't have a device and can be freed right now", + ext_hub_dev->constant.dev_addr, + port_num); + assert(free_port == false); + free_port = true; + } 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, + port_num); + // Logically, recycling logic are finished for the Hub Driver, return ESP_OK to free the node + assert(free_port == false); + } else { + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Unable to mark port as gone: %s", + ext_hub_dev->constant.dev_addr, port_num, esp_err_to_name(ret)); + } + } + + if (free_port) { + ret = device_port_free(ext_hub_dev, port_idx); + if (ret != ESP_OK) { + goto exit; + } + } + } + + ret = ESP_OK; +exit: return ret; } @@ -1442,17 +1608,13 @@ esp_err_t ext_hub_port_reset(ext_hub_handle_t ext_hub_hdl, uint8_t port_num) EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE); 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; uint8_t port_idx = port_num - 1; - EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.maxchild, ESP_ERR_INVALID_SIZE); - if (p_ext_hub_driver->constant.port_driver) { - ret = p_ext_hub_driver->constant.port_driver->reset(ext_hub_dev->constant.ports[port_idx]); - } else { - ret = ESP_ERR_NOT_SUPPORTED; - } - return ret; + 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); + + return p_ext_hub_driver->constant.port_driver->reset(ext_hub_dev->constant.ports[port_idx]); } esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num) @@ -1461,17 +1623,13 @@ esp_err_t ext_hub_port_active(ext_hub_handle_t ext_hub_hdl, uint8_t port_num) EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE); 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; uint8_t port_idx = port_num - 1; - EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.maxchild, ESP_ERR_INVALID_SIZE); - if (p_ext_hub_driver->constant.port_driver) { - ret = p_ext_hub_driver->constant.port_driver->active(ext_hub_dev->constant.ports[port_idx]); - } else { - ret = ESP_ERR_NOT_SUPPORTED; - } - return ret; + 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); + + return p_ext_hub_driver->constant.port_driver->active(ext_hub_dev->constant.ports[port_idx]); } esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num) @@ -1480,17 +1638,13 @@ esp_err_t ext_hub_port_disable(ext_hub_handle_t ext_hub_hdl, uint8_t port_num) EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE); 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; uint8_t port_idx = port_num - 1; - EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.maxchild, ESP_ERR_INVALID_SIZE); - if (p_ext_hub_driver->constant.port_driver) { - ret = p_ext_hub_driver->constant.port_driver->disable(ext_hub_dev->constant.ports[port_idx]); - } else { - ret = ESP_ERR_NOT_SUPPORTED; - } - return ret; + 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); + + return p_ext_hub_driver->constant.port_driver->disable(ext_hub_dev->constant.ports[port_idx]); } esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, usb_speed_t *speed) @@ -1499,17 +1653,13 @@ esp_err_t ext_hub_port_get_speed(ext_hub_handle_t ext_hub_hdl, uint8_t port_num, EXT_HUB_CHECK_FROM_CRIT(p_ext_hub_driver != NULL, ESP_ERR_INVALID_STATE); 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; uint8_t port_idx = port_num - 1; - EXT_HUB_CHECK(port_idx < ext_hub_dev->constant.maxchild, ESP_ERR_INVALID_SIZE); - if (p_ext_hub_driver->constant.port_driver) { - ret = p_ext_hub_driver->constant.port_driver->get_speed(ext_hub_dev->constant.ports[port_idx], speed); - } else { - ret = ESP_ERR_NOT_SUPPORTED; - } - return ret; + 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); + + return p_ext_hub_driver->constant.port_driver->get_speed(ext_hub_dev->constant.ports[port_idx], speed); } // ----------------------------------------------------------------------------- @@ -1527,18 +1677,20 @@ esp_err_t ext_hub_set_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_nu 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.maxchild, ESP_ERR_INVALID_SIZE); + 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_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = EXT_HUB_STAGE_PORT_FEATURE; - EXT_HUB_EXIT_CRITICAL(); - + 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); if (ret != ESP_OK) { - ESP_LOGE(EXT_HUB_TAG, "Failed to submit ctrl urb, error %#x", ret); + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Set port feature %#x, failed to submit ctrl urb: %s", + ext_hub_dev->constant.dev_addr, + port_num, + feature, + esp_err_to_name(ret)); } return ret; } @@ -1554,18 +1706,20 @@ esp_err_t ext_hub_clear_port_feature(ext_hub_handle_t ext_hub_hdl, uint8_t port_ 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.maxchild, ESP_ERR_INVALID_SIZE); + 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_CLEAR_PORT_FEATURE((usb_setup_packet_t *)transfer->data_buffer, port_num, feature); transfer->num_bytes = sizeof(usb_setup_packet_t); - EXT_HUB_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = EXT_HUB_STAGE_PORT_FEATURE; - EXT_HUB_EXIT_CRITICAL(); - + 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); if (ret != ESP_OK) { - ESP_LOGE(EXT_HUB_TAG, "Failed to submit ctrl urb, error %#x", ret); + ESP_LOGE(EXT_HUB_TAG, "[%d:%d] Clear port feature %#x, failed to submit ctrl urb: %s", + ext_hub_dev->constant.dev_addr, + port_num, + feature, + esp_err_to_name(ret)); } return ret; } @@ -1581,18 +1735,19 @@ esp_err_t ext_hub_get_port_status(ext_hub_handle_t ext_hub_hdl, uint8_t port_num 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.maxchild, ESP_ERR_INVALID_SIZE); + 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_ENTER_CRITICAL(); - ext_hub_dev->dynamic.stage = EXT_HUB_STAGE_PORT_STATUS_REQUEST; - EXT_HUB_EXIT_CRITICAL(); - + 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, "Failed to submit ctrl urb, error %#x", ret); + 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; } diff --git a/components/usb/private_include/ext_hub.h b/components/usb/private_include/ext_hub.h index 9fde880fe7..1e1db24be6 100644 --- a/components/usb/private_include/ext_hub.h +++ b/components/usb/private_include/ext_hub.h @@ -50,6 +50,7 @@ typedef struct { 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; /**