From 513b74b8456d0fe9d78c180bc921fc63e1b82530 Mon Sep 17 00:00:00 2001 From: songyanguang <345810377@qq.com> Date: Mon, 24 Jun 2024 15:31:57 +0800 Subject: [PATCH] Modify usbh_hub.c according to freertos source code. --- .../usb/components/class/hub/usbh_hub.c | 743 +++++++++++++++++- 1 file changed, 741 insertions(+), 2 deletions(-) diff --git a/Ubiquitous/XiZi_AIoT/services/drivers/usb/components/class/hub/usbh_hub.c b/Ubiquitous/XiZi_AIoT/services/drivers/usb/components/class/hub/usbh_hub.c index cbf69aee1..98e59adc6 100644 --- a/Ubiquitous/XiZi_AIoT/services/drivers/usb/components/class/hub/usbh_hub.c +++ b/Ubiquitous/XiZi_AIoT/services/drivers/usb/components/class/hub/usbh_hub.c @@ -3,6 +3,745 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "usbh_hub.h" -#include "usb_def.h" +/************************************************* +File name: usbh_hub.c +Description: adopt cherry USB to XiZi AIOT. +Others: CherryUSB third-party/cherryusb/class/hub/usbh_hub.c for references + https://gitee.com/phytium_embedded/phytium-free-rtos-sdk/blob/master/third-party/cherryusb/class/hub/usbh_hub.c +*************************************************/ +#include "usbh_hub.h" + +#define DEV_FORMAT "/usb%d/hub%d" + +#define HUB_DEBOUNCE_TIMEOUT 1500 +#define HUB_DEBOUNCE_STEP 25 +#define HUB_DEBOUNCE_STABLE 100 +#define DELAY_TIME_AFTER_RESET 200 + +#define EXTHUB_FIRST_INDEX 2 + +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +static uint32_t g_devinuse = 0; +#endif + +extern int usbh_hport_activate_ep0(struct usbh_hubport *hport); +extern int usbh_hport_deactivate_ep0(struct usbh_hubport *hport); +extern int usbh_enumerate(struct usbh_hubport *hport); +static void usbh_hub_thread_wakeup(struct usbh_hub *hub); + +static const char *speed_table[] = { "error-speed", "low-speed", "full-speed", "high-speed", "wireless-speed", "super-speed", "superplus-speed" }; + +struct usbh_hubport *usbh_get_roothub_port (struct usbh_bus *bus, unsigned int address) +{ + return &(bus->roothub.child[ address - 1 ]); +} + +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +static int usbh_hub_devno_alloc(void) +{ + int devno; + + for (devno = EXTHUB_FIRST_INDEX; devno < 32; devno++) { + uint32_t bitno = 1 << devno; + if ((g_devinuse & bitno) == 0) { + g_devinuse |= bitno; + return devno; + } + } + + return -EMFILE; +} + +static void usbh_hub_devno_free(uint8_t devno) +{ + if (devno >= EXTHUB_FIRST_INDEX && devno < 32) { + g_devinuse &= ~(1 << devno); + } +} +#endif + +static void usbh_hub_register(struct usbh_hub *hub) +{ + struct usbh_bus *usb = hub->usb; + usb_slist_add_tail(&usb->hub_class_head, &hub->list); +} + +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +static void usbh_hub_unregister(struct usbh_hub *hub) +{ + struct usbh_bus *usb = hub->usb; + usb_slist_remove(&usb->hub_class_head, &hub->list); +} + +static int _usbh_hub_get_hub_descriptor(struct usbh_hub *hub, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + int ret; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = USB_REQUEST_GET_DESCRIPTOR; + + /* TODO: hub descriptor has some difference between USB 2.0 and USB 3.x, + and we havn't handle the difference here */ + if ((hub->parent->speed == USB_SPEED_SUPER) || + (hub->parent->speed == USB_SPEED_SUPER_PLUS)) { + setup->wValue = HUB_DESCRIPTOR_TYPE_HUB3 << 8; + } else { + setup->wValue = HUB_DESCRIPTOR_TYPE_HUB << 8; + } + + setup->wIndex = 0; + setup->wLength = USB_SIZEOF_HUB_DESC; + + ret = usbh_control_transfer(hub->parent->ep0, setup, hub->g_hub_buf); + if (ret < 0) { + return ret; + } + memcpy(buffer, hub->g_hub_buf, USB_SIZEOF_HUB_DESC); + return ret; +} + +static int _usbh_hub_get_status(struct usbh_hub *hub, uint8_t *buffer) +{ + struct usb_setup_packet *setup; + int ret; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = HUB_REQUEST_GET_STATUS; + setup->wValue = 0; + setup->wIndex = 0; + setup->wLength = 2; + + ret = usbh_control_transfer(hub->parent->ep0, setup, hub->g_hub_buf); + if (ret < 0) { + return ret; + } + memcpy(buffer, hub->g_hub_buf, 2); + return ret; +} +#endif + +static int _usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) +{ + struct usb_setup_packet *setup; + int ret; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_GET_STATUS; + setup->wValue = 0; + setup->wIndex = port; + setup->wLength = 4; + + ret = usbh_control_transfer(hub->parent->ep0, setup, hub->g_hub_buf); + if (ret < 0) { + return ret; + } + memcpy(port_status, hub->g_hub_buf, 4); + return ret; +} + +static int _usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet *setup; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_SET_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + + return usbh_control_transfer(hub->parent->ep0, setup, NULL); +} + +static int _usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet *setup; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_CLEAR_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + + return usbh_control_transfer(hub->parent->ep0, setup, NULL); +} + +static int _usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth) +{ + struct usb_setup_packet *setup; + + setup = hub->parent->setup; + + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH; + setup->wValue = depth; + setup->wIndex = 0; + setup->wLength = 0; + + return usbh_control_transfer(hub->parent->ep0, setup, NULL); +} + +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +static int parse_hub_descriptor(struct usb_hub_descriptor *desc, uint16_t length) +{ + if (desc->bLength != USB_SIZEOF_HUB_DESC) { + USB_LOG_ERR("invalid device bLength 0x%02x\r\n", desc->bLength); + return -1; + } else if (desc->bDescriptorType != HUB_DESCRIPTOR_TYPE_HUB) { + USB_LOG_ERR("unexpected descriptor 0x%02x\r\n", desc->bDescriptorType); + return -2; + } else { + USB_LOG_RAW("Hub Descriptor:\r\n"); + USB_LOG_RAW("bLength: 0x%02x \r\n", desc->bLength); + USB_LOG_RAW("bDescriptorType: 0x%02x \r\n", desc->bDescriptorType); + USB_LOG_RAW("bNbrPorts: 0x%02x \r\n", desc->bNbrPorts); + USB_LOG_RAW("wHubCharacteristics: 0x%04x \r\n", desc->wHubCharacteristics); + USB_LOG_RAW("bPwrOn2PwrGood: 0x%02x \r\n", desc->bPwrOn2PwrGood); + USB_LOG_RAW("bHubContrCurrent: 0x%02x \r\n", desc->bHubContrCurrent); + USB_LOG_RAW("DeviceRemovable: 0x%02x \r\n", desc->DeviceRemovable); + USB_LOG_RAW("PortPwrCtrlMask: 0x%02x \r\n", desc->PortPwrCtrlMask); + } + return 0; +} +#endif + +static int usbh_hub_get_portstatus(struct usbh_hub *hub, uint8_t port, struct hub_port_status *port_status) +{ + struct usb_setup_packet roothub_setup; + struct usb_setup_packet *setup; + + if (hub->is_roothub) { + setup = &roothub_setup; + setup->bmRequestType = USB_REQUEST_DIR_IN | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_GET_STATUS; + setup->wValue = 0; + setup->wIndex = port; + setup->wLength = 4; + return usbh_roothub_control(hub->usb, &roothub_setup, (uint8_t *)port_status); + } else { + return _usbh_hub_get_portstatus(hub, port, port_status); + } +} + +static int usbh_hub_set_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet roothub_setup; + struct usb_setup_packet *setup; + + if (hub->is_roothub) { + setup = &roothub_setup; + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_SET_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + return usbh_roothub_control(hub->usb, setup, NULL); + } else { + return _usbh_hub_set_feature(hub, port, feature); + } +} + +static int usbh_hub_clear_feature(struct usbh_hub *hub, uint8_t port, uint8_t feature) +{ + struct usb_setup_packet roothub_setup; + struct usb_setup_packet *setup; + + if (hub->is_roothub) { + setup = &roothub_setup; + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_OTHER; + setup->bRequest = HUB_REQUEST_CLEAR_FEATURE; + setup->wValue = feature; + setup->wIndex = port; + setup->wLength = 0; + return usbh_roothub_control(hub->usb, setup, NULL); + } else { + return _usbh_hub_clear_feature(hub, port, feature); + } +} + +static int usbh_hub_set_depth(struct usbh_hub *hub, uint16_t depth) +{ + struct usb_setup_packet roothub_setup; + struct usb_setup_packet *setup; + + if (hub->is_roothub) { + setup = &roothub_setup; + setup->bmRequestType = USB_REQUEST_DIR_OUT | USB_REQUEST_CLASS | USB_REQUEST_RECIPIENT_DEVICE; + setup->bRequest = HUB_REQUEST_SET_HUB_DEPTH; + setup->wValue = depth; + setup->wIndex = 0; + setup->wLength = 0; + return usbh_roothub_control(hub->usb, setup, NULL); + } else { + return _usbh_hub_set_depth(hub, depth); + } +} + +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +static void hub_int_complete_callback(void *arg, int nbytes) +{ + struct usbh_hub *hub = (struct usbh_hub *)arg; + + if (nbytes > 0) { + usbh_hub_thread_wakeup(hub); + } +} + +static int usbh_hub_connect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usb_endpoint_descriptor *ep_desc; + struct hub_port_status port_status; + struct usbh_bus *usb = usbh_get_bus_of_port(hport); + int ret; + int index; + + index = usbh_hub_devno_alloc(); + if (index > (CONFIG_USBHOST_MAX_EXTHUBS + EXTHUB_FIRST_INDEX - 1)) { + USB_LOG_ERR("No memory to alloc hub class\r\n"); + usbh_hub_devno_free(index); + return -ENOMEM; + } + + struct usbh_hub *hub = &(usb->exthub[index - EXTHUB_FIRST_INDEX]); + + memset(hub, 0, sizeof(struct usbh_hub)); + hub->hub_addr = hport->dev_addr; + hub->parent = hport; + hub->index = index; + + hport->config.intf[intf].priv = hub; + + ret = _usbh_hub_get_hub_descriptor(hub, (uint8_t *)&hub->hub_desc); + if (ret < 0) { + return ret; + } + + parse_hub_descriptor(&hub->hub_desc, USB_SIZEOF_HUB_DESC); + + for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + hub->child[port].port = port + 1; + hub->child[port].parent = hub; + } + + ep_desc = &hport->config.intf[intf].altsetting[0].ep[0].ep_desc; + if (ep_desc->bEndpointAddress & 0x80) { + usbh_hport_activate_epx(&hub->intin, hport, ep_desc); + } else { + return -1; + } + + if (hport->speed == USB_SPEED_SUPER) { + uint16_t depth = 0; + struct usbh_hubport *parent = hport->parent->parent; + while (parent) { + depth++; + parent = parent->parent->parent; + } + + ret = usbh_hub_set_depth(hub, depth); + if (ret < 0) { + return ret; + } + } + + for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_POWER); + if (ret < 0) { + return ret; + } + } + + for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); + USB_LOG_INFO("port %u, status:0x%02x, change:0x%02x\r\n", port + 1, port_status.wPortStatus, port_status.wPortChange); + if (ret < 0) { + return ret; + } + } + + hub->ports = hub->hub_desc.bNbrPorts; + hub->connected = true; + hub->usb = usb; + snprintf(hport->config.intf[intf].devname, CONFIG_USBHOST_DEV_NAMELEN, DEV_FORMAT, usb->id, hub->index); + usbh_hub_register(hub); + USB_LOG_RAW("Register HUB Class:%s for USB-%d\r\n", hport->config.intf[intf].devname, usb->id); + + hub->int_buffer = hub->g_hub_intbuf[hub->index - 1]; + usbh_int_urb_fill(&hub->intin_urb, hub->intin, hub->int_buffer, 1, 0, hub_int_complete_callback, hub); + usbh_submit_urb(&hub->intin_urb); + return 0; +} + +static int usbh_hub_disconnect(struct usbh_hubport *hport, uint8_t intf) +{ + struct usbh_hubport *child; + int ret = 0; + + struct usbh_hub *hub = (struct usbh_hub *)hport->config.intf[intf].priv; + + if (hub) { + usbh_hub_devno_free(hub->index); + + if (hub->intin) { + usbh_pipe_free(hub->intin); + } + + for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + child = &hub->child[port]; + usbh_hport_deactivate_ep0(child); + for (uint8_t i = 0; i < child->config.config_desc.bNumInterfaces; i++) { + if (child->config.intf[i].class_driver && child->config.intf[i].class_driver->disconnect) { + CLASS_DISCONNECT(child, i); + } + } + + child->config.config_desc.bNumInterfaces = 0; + child->parent = NULL; + } + + usbh_hub_unregister(hub); + memset(hub, 0, sizeof(struct usbh_hub)); + + if (hport->config.intf[intf].devname[0] != '\0') + USB_LOG_RAW("Unregister HUB Class:%s\r\n", hport->config.intf[intf].devname); + } + return ret; +} +#endif + +static void usbh_hubport_release(struct usbh_hubport *child) +{ + if (child->connected) { + child->connected = false; + usbh_hport_deactivate_ep0(child); + for (uint8_t i = 0; i < child->config.config_desc.bNumInterfaces; i++) { + if (child->config.intf[i].class_driver && child->config.intf[i].class_driver->disconnect) { + CLASS_DISCONNECT(child, i); + } + } + child->config.config_desc.bNumInterfaces = 0; + } +} + +static void usbh_hub_dump_status(struct usbh_hub *hub, uint8_t port, uint16_t portstatus, uint16_t portchange) +{ + USB_LOG_DBG("===================== \r\n"); + USB_LOG_DBG("hub-%d port-%d status 0x%x\r\n", hub->hub_addr, port, portstatus); + USB_LOG_DBG(" connection: %d\r\n", !!(portstatus & HUB_PORT_STATUS_CONNECTION)); + USB_LOG_DBG(" enable: %d\r\n", !!(portstatus & HUB_PORT_STATUS_ENABLE)); + USB_LOG_DBG(" suspend: %d\r\n", !!(portstatus & HUB_PORT_STATUS_SUSPEND)); + USB_LOG_DBG(" overcurrent: %d\r\n", !!(portstatus & HUB_PORT_STATUS_OVERCURRENT)); + USB_LOG_DBG(" reseting: %d\r\n", !!(portstatus & HUB_PORT_STATUS_RESET)); + USB_LOG_DBG(" power: %d\r\n", !!(portstatus & HUB_PORT_STATUS_POWER)); + if (portstatus & HUB_PORT_STATUS_LOW_SPEED) { + USB_LOG_DBG(" low-speed\r\n"); + } else if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) { + USB_LOG_DBG(" high-speed\r\n"); + } else { + USB_LOG_DBG(" full-speed\r\n"); + } + USB_LOG_DBG(" test: %d\r\n", !!(portstatus & HUB_PORT_STATUS_TEST)); + USB_LOG_DBG(" indicator: %d\r\n", !!(portstatus & HUB_PORT_STATUS_INDICATOR)); + USB_LOG_DBG("hub-%d port-%d change 0x%x\r\n", hub->hub_addr, port, portchange); + USB_LOG_DBG(" connection change: %d\r\n", !!(portchange & HUB_PORT_STATUS_C_CONNECTION)); + USB_LOG_DBG(" enable change: %d\r\n", !!(portchange & HUB_PORT_STATUS_C_ENABLE)); + USB_LOG_DBG(" suspend change: %d\r\n", !!(portchange & HUB_PORT_STATUS_C_SUSPEND)); + USB_LOG_DBG(" overcurrent change: %d\r\n", !!(portchange & HUB_PORT_STATUS_C_OVERCURRENT)); + USB_LOG_DBG(" reset change: %d\r\n", !!(portchange & HUB_PORT_STATUS_C_RESET)); + USB_LOG_DBG("=====================\r\n"); +} + +static void usbh_hub_events(struct usbh_hub *hub) +{ + struct usbh_hubport *child; + struct hub_port_status port_status; + uint8_t portchange_index; + uint16_t portstatus; + uint16_t portchange; + uint16_t mask; + uint16_t feat; + uint8_t speed; + int ret; + + if (!hub->connected) { + return; + } + + for (uint8_t port = 0; port < hub->hub_desc.bNbrPorts; port++) { + portchange_index = hub->int_buffer[0]; + + USB_LOG_DBG("Port change:0x%02x, Port:%u\r\n", portchange_index, port + 1); + + if (!(portchange_index & (1 << (port + 1)))) { + continue; + } + portchange_index &= ~(1 << (port + 1)); + USB_LOG_DBG("Port %d change\r\n", port + 1); + + /* Read hub port status */ + ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret); + continue; + } + + portstatus = port_status.wPortStatus; + portchange = port_status.wPortChange; + + usbh_hub_dump_status(hub, port + 1, portstatus, portchange); + + /* First, clear all change bits */ + mask = 1; + feat = HUB_PORT_FEATURE_C_CONNECTION; + while (portchange) { + if (portchange & mask) { + ret = usbh_hub_clear_feature(hub, port + 1, feat); + if (ret < 0) { + USB_LOG_ERR("Failed to clear port %u, change mask:%04x, errorcode:%d\r\n", port + 1, mask, ret); + continue; + } + portchange &= (~mask); + } + mask <<= 1; + feat++; + } + + portchange = port_status.wPortChange; + + /* Second, if port changes, debounces first */ + if (portchange & HUB_PORT_STATUS_C_CONNECTION) { + uint16_t connection = 0; + uint16_t debouncestable = 0; + for (uint32_t debouncetime = 0; debouncetime < HUB_DEBOUNCE_TIMEOUT; debouncetime += HUB_DEBOUNCE_STEP) { + /* Read hub port status */ + ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret); + continue; + } + + portstatus = port_status.wPortStatus; + portchange = port_status.wPortChange; + + USB_LOG_DBG("Port %u, status:0x%02x, change:0x%02x\r\n", port + 1, portstatus, portchange); + + if (!(portchange & HUB_PORT_STATUS_C_CONNECTION) && + ((portstatus & HUB_PORT_STATUS_CONNECTION) == connection)) { + debouncestable += HUB_DEBOUNCE_STEP; + if (debouncestable >= HUB_DEBOUNCE_STABLE) { + break; + } + } else { + debouncestable = 0; + connection = portstatus & HUB_PORT_STATUS_CONNECTION; + } + + if (portchange & HUB_PORT_STATUS_C_CONNECTION) { + usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_CONNECTION); + } + + usb_osal_msleep(HUB_DEBOUNCE_STEP); + } + + /** check if debounce ok */ + if (debouncestable < HUB_DEBOUNCE_STABLE) { + USB_LOG_ERR("Failed to debounce port %u\r\n", port + 1); + break; + } + + /* Last, check connect status */ + if (portstatus & HUB_PORT_STATUS_CONNECTION) { + ret = usbh_hub_set_feature(hub, port + 1, HUB_PORT_FEATURE_RESET); + if (ret < 0) { + USB_LOG_ERR("Failed to reset port %u,errorcode:%d\r\n", port + 1, ret); + continue; + } + + usb_osal_msleep(DELAY_TIME_AFTER_RESET); + /* Read hub port status */ + ret = usbh_hub_get_portstatus(hub, port + 1, &port_status); + if (ret < 0) { + USB_LOG_ERR("Failed to read port %u status, errorcode: %d\r\n", port + 1, ret); + continue; + } + + portstatus = port_status.wPortStatus; + portchange = port_status.wPortChange; + USB_LOG_DBG("Reset: %d, Enable: %d \r\n", + !!(portstatus & HUB_PORT_STATUS_RESET), + !!(portstatus & HUB_PORT_STATUS_ENABLE)); + if (!(portstatus & HUB_PORT_STATUS_RESET) && (portstatus & HUB_PORT_STATUS_ENABLE)) { + if (portchange & HUB_PORT_STATUS_C_RESET) { + ret = usbh_hub_clear_feature(hub, port + 1, HUB_PORT_FEATURE_C_RESET); + if (ret < 0) { + USB_LOG_ERR("Failed to clear port %u reset change, errorcode: %d\r\n", port + 1, ret); + } + } + + if (portstatus & HUB_PORT_STATUS_HIGH_SPEED) { + speed = USB_SPEED_HIGH; + } else if (portstatus & HUB_PORT_STATUS_LOW_SPEED) { + speed = USB_SPEED_LOW; + } +#ifdef CONFIG_USBHOST_XHCI + /* USB3.0 speed cannot get from portstatus, checkout port speed instead */ + else { + extern uint8_t usbh_get_port_speed(struct usbh_hub *hub, const uint8_t port); + + uint8_t port_speed = usbh_get_port_speed(hub, port + 1); + if (port_speed >= USB_SPEED_SUPER) { + /* assert that when using USB 3.0 ports, attached device must also be USB 3.0 speed */ + speed = port_speed; + } else { + speed = USB_SPEED_FULL; + } + } +#else + else { + speed = USB_SPEED_FULL; + } +#endif + + child = &hub->child[port]; + /** release child sources first */ + usbh_hubport_release(child); + + memset(child, 0, sizeof(struct usbh_hubport)); + child->parent = hub; + child->connected = true; + child->port = port + 1; + child->speed = speed; + + USB_LOG_RAW("New %s device on Hub %u, Port %u connected\r\n", speed_table[speed], hub->index, port + 1); + + /* Configure EP0 with the default maximum packet size */ + usbh_hport_activate_ep0(child); + + if (usbh_enumerate(child) < 0) { + /** release child sources */ + usbh_hubport_release(child); + USB_LOG_ERR("Port %u enumerate fail\r\n", port + 1); + } + } else { + child = &hub->child[port]; + /** release child sources */ + usbh_hubport_release(child); + + /** some USB 3.0 ip may failed to enable USB 2.0 port for USB 3.0 device */ + USB_LOG_WRN("Failed to enable port %u\r\n", port + 1); + + continue; + } + } else { + child = &hub->child[port]; + /** release child sources */ + usbh_hubport_release(child); + USB_LOG_INFO("Device on Hub %u, Port %u disconnected\r\n", hub->index, port + 1); + } + } + } + + hub->int_buffer[0] = 0; + /* Start next hub int transfer */ + if (!hub->is_roothub && hub->connected) { + usbh_submit_urb(&hub->intin_urb); + } +} + +static void usbh_hub_thread(void *argument) +{ + struct usbh_hub *hub; + int ret = 0; + struct usbh_bus *usb = (struct usbh_bus *)argument; + + usb_hc_init(usb->id); + while (1) { + USB_LOG_DBG("wait event of mq@%p \r\n", usb->hub_mq); + ret = usb_osal_mq_recv(usb->hub_mq, (void *)&hub, 0xffffffff); + if (ret < 0) { + continue; + } + USB_LOG_DBG("handle event of hub@%p \r\n", hub); + usbh_hub_events(hub); + } +} + +static void usbh_roothub_register(struct usbh_bus *usb) +{ + struct usbh_hub* roothub = &(usb->roothub); + memset(roothub, 0, sizeof(struct usbh_hub)); + + roothub->connected = true; + roothub->index = 1; + roothub->is_roothub = true; + roothub->parent = NULL; + roothub->hub_addr = 1; + roothub->hub_desc.bNbrPorts = CONFIG_USBHOST_MAX_RHPORTS; + roothub->usb = usb; + usbh_hub_register(roothub); +} + +static void usbh_hub_thread_wakeup(struct usbh_hub *hub) +{ + USB_LOG_DBG("try to wakeup hub0x%x \r\n", hub); + struct usbh_bus *usb = hub->usb; + usb_osal_mq_send(usb->hub_mq, (uintptr_t)hub); +} + +void usbh_roothub_thread_wakeup(uint32_t usb_id, uint8_t port) +{ + struct usbh_bus* usb = usbh_get_bus_of_index(usb_id); + struct usbh_hub* roothub = &(usb->roothub); + + roothub->int_buffer = roothub->g_hub_intbuf[roothub->index - 1]; + roothub->int_buffer[0] |= (1 << port); + USB_LOG_DBG("port-%u wakeup roothub@%p \r\n", port, roothub); + usbh_hub_thread_wakeup(roothub); +} + +int usbh_hub_initialize(struct usbh_bus *usb) +{ + usbh_roothub_register(usb); + + usb->hub_mq = usb_osal_mq_create(7); + if (usb->hub_mq == NULL) { + return -1; + } + + char thread_name[12]; + snprintf(thread_name, 12, "usb%d_hub", usb->id); + usb->hub_thread = usb_osal_thread_create(thread_name, CONFIG_USBHOST_PSC_STACKSIZE, CONFIG_USBHOST_PSC_PRIO, usbh_hub_thread, usb); + if (usb->hub_thread == NULL) { + return -1; + } + + return 0; +} +#if CONFIG_USBHOST_MAX_EXTHUBS > 0 +const struct usbh_class_driver hub_class_driver = { + .driver_name = "hub", + .connect = usbh_hub_connect, + .disconnect = usbh_hub_disconnect +}; + +CLASS_INFO_DEFINE const struct usbh_class_info hub_class_info = { + .match_flags = USB_CLASS_MATCH_INTF_CLASS, + .class = USB_DEVICE_CLASS_HUB, + .subclass = 0, + .protocol = 0, + .vid = 0x00, + .pid = 0x00, + .class_driver = &hub_class_driver +}; +#endif