-
Notifications
You must be signed in to change notification settings - Fork 9.1k
usb: host: class: support for usb host hub class #99735
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
5849bba
1eb61c3
dd654b3
37d4fb2
6516d50
5a30c41
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96,6 +96,14 @@ struct usb_device { | |
| struct usb_host_ep ep_out[16]; | ||
| /** Pointers to device IN endpoints */ | ||
| struct usb_host_ep ep_in[16]; | ||
| /** Pointer to the hub to which this device is connected */ | ||
| struct usb_device *hub; | ||
| /** Device's hub Think Time */ | ||
| uint16_t tt; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe, it makes sense to introduce the whole hub-related part of the struct? Or to introduce them as "getters", because they are part of the Hub Descriptor that we need to request in the potential class driver? Because, there are several other important hub characteristics, that we might be able to use in the driver. Such as:
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you for the comment here, Actually previously the total |
||
| /** Device's hub port */ | ||
| uint8_t hub_port; | ||
| /** Device's level (root device = 0) */ | ||
| uint8_t level; | ||
|
jfischer-no marked this conversation as resolved.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just out of curiosity, what is the purpose of having
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The level field is essentially a topology depth indicator that helps the USB stack: |
||
| }; | ||
|
|
||
| /** | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This file seems like a candidate to be
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, agree, updated. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,44 +1,229 @@ | ||
| /* | ||
| * Copyright (c) 2022 Emerson Electric Co. | ||
| * | ||
| * SPDX-FileCopyrightText: Copyright 2026 NXP | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
|
|
||
| /** | ||
| * @file | ||
| * @brief USB Hub Class device API header | ||
| * @brief USB Hub Class public header | ||
| */ | ||
|
|
||
| #ifndef ZEPHYR_INCLUDE_USB_CLASS_USB_HUB_H_ | ||
| #define ZEPHYR_INCLUDE_USB_CLASS_USB_HUB_H_ | ||
|
|
||
| /** USB Hub Class Feature Selectors defined in spec. Table 11-17 */ | ||
| /** | ||
| * @name USB Hub Class Codes | ||
| * @{ | ||
| */ | ||
| /** Hub class code. */ | ||
| #define USB_HUB_CLASS_CODE 0x09 | ||
| /** Hub subclass code. */ | ||
| #define USB_HUB_SUBCLASS_CODE 0x00 | ||
| /** Hub protocol code. */ | ||
| #define USB_HUB_PROTOCOL_CODE 0x00 | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Descriptor Types | ||
| * @{ | ||
| */ | ||
| /** Hub descriptor type. */ | ||
| #define USB_HUB_DESCRIPTOR_TYPE 0x29 | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Class Request Codes. | ||
| * | ||
| * See Table 11-16 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Get Status request. */ | ||
| #define USB_HCREQ_GET_STATUS 0x00 | ||
| /** Clear Feature request. */ | ||
| #define USB_HCREQ_CLEAR_FEATURE 0x01 | ||
| /** Set Feature request. */ | ||
| #define USB_HCREQ_SET_FEATURE 0x03 | ||
| /** Get Descriptor request. */ | ||
| #define USB_HCREQ_GET_DESCRIPTOR 0x06 | ||
| /** Set Descriptor request. */ | ||
| #define USB_HCREQ_SET_DESCRIPTOR 0x07 | ||
| /** Clear TT Buffer request. */ | ||
| #define USB_HCREQ_CLEAR_TT_BUFFER 0x08 | ||
| /** Reset TT request. */ | ||
| #define USB_HCREQ_RESET_TT 0x09 | ||
| /** Get TT State request. */ | ||
| #define USB_HCREQ_GET_TT_STATE 0x0A | ||
| /** Stop TT request. */ | ||
| #define USB_HCREQ_STOP_TT 0x0B | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Class Feature Selectors. | ||
| * | ||
| * See Table 11-17 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Hub local power change feature selector. */ | ||
| #define USB_HCFS_C_HUB_LOCAL_POWER 0x00 | ||
| /** Hub over-current change feature selector. */ | ||
| #define USB_HCFS_C_HUB_OVER_CURRENT 0x01 | ||
| /** Port connection feature selector. */ | ||
| #define USB_HCFS_PORT_CONNECTION 0x00 | ||
| /** Port enable feature selector. */ | ||
| #define USB_HCFS_PORT_ENABLE 0x01 | ||
| /** Port suspend feature selector. */ | ||
| #define USB_HCFS_PORT_SUSPEND 0x02 | ||
| /** Port over-current feature selector. */ | ||
| #define USB_HCFS_PORT_OVER_CURRENT 0x03 | ||
| /** Port reset feature selector. */ | ||
| #define USB_HCFS_PORT_RESET 0x04 | ||
| /** Port power feature selector. */ | ||
| #define USB_HCFS_PORT_POWER 0x08 | ||
| /** Port low speed feature selector. */ | ||
| #define USB_HCFS_PORT_LOW_SPEED 0x09 | ||
| /** Port connection change feature selector. */ | ||
| #define USB_HCFS_C_PORT_CONNECTION 0x10 | ||
| /** Port enable change feature selector. */ | ||
| #define USB_HCFS_C_PORT_ENABLE 0x11 | ||
| /** Port suspend change feature selector. */ | ||
| #define USB_HCFS_C_PORT_SUSPEND 0x12 | ||
| /** Port over-current change feature selector. */ | ||
| #define USB_HCFS_C_PORT_OVER_CURRENT 0x13 | ||
| /** Port reset change feature selector. */ | ||
| #define USB_HCFS_C_PORT_RESET 0x14 | ||
| /** Port test feature selector. */ | ||
| #define USB_HCFS_PORT_TEST 0x15 | ||
| /** Port indicator feature selector. */ | ||
| #define USB_HCFS_PORT_INDICATOR 0x16 | ||
| /** @} */ | ||
|
|
||
| /** USB Hub Class Request Codes defined in spec. Table 11-16 */ | ||
| #define USB_HCREQ_GET_STATUS 0x00 | ||
| #define USB_HCREQ_CLEAR_FEATURE 0x01 | ||
| #define USB_HCREQ_SET_FEATURE 0x03 | ||
| #define USB_HCREQ_GET_DESCRIPTOR 0x06 | ||
| #define USB_HCREQ_SET_DESCRIPTOR 0x07 | ||
| #define USB_HCREQ_CLEAR_TT_BUFFER 0x08 | ||
| #define USB_HCREQ_RESET_TT 0x09 | ||
| #define USB_HCREQ_GET_TT_STATE 0x0A | ||
| #define USB_HCREQ_STOP_TT 0x0B | ||
| /** | ||
| * @name USB Hub Status Field, wHubStatus. | ||
| * | ||
| * See Table 11-19 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Hub local power status bit. */ | ||
| #define USB_HUB_STATUS_LOCAL_POWER BIT(0) | ||
| /** Hub over-current status bit. */ | ||
| #define USB_HUB_STATUS_OVER_CURRENT BIT(1) | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Change Field, wHubChange. | ||
| * | ||
| * See Table 11-20 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Hub local power change bit. */ | ||
| #define USB_HUB_CHANGE_LOCAL_POWER BIT(0) | ||
| /** Hub over-current change bit. */ | ||
| #define USB_HUB_CHANGE_OVER_CURRENT BIT(1) | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Port Status Field, wPortStatus. | ||
| * | ||
| * See Table 11-21 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Port connection status bit. */ | ||
| #define USB_HUB_PORT_STATUS_CONNECTION BIT(0) | ||
| /** Port enable status bit. */ | ||
| #define USB_HUB_PORT_STATUS_ENABLE BIT(1) | ||
| /** Port suspend status bit. */ | ||
| #define USB_HUB_PORT_STATUS_SUSPEND BIT(2) | ||
| /** Port over-current status bit. */ | ||
| #define USB_HUB_PORT_STATUS_OVER_CURRENT BIT(3) | ||
| /** Port reset status bit. */ | ||
| #define USB_HUB_PORT_STATUS_RESET BIT(4) | ||
| /** Port power status bit. */ | ||
| #define USB_HUB_PORT_STATUS_POWER BIT(8) | ||
| /** Port low speed device attached bit. */ | ||
| #define USB_HUB_PORT_STATUS_LOW_SPEED BIT(9) | ||
| /** Port high speed device attached bit. */ | ||
| #define USB_HUB_PORT_STATUS_HIGH_SPEED BIT(10) | ||
| /** Port test mode bit. */ | ||
| #define USB_HUB_PORT_STATUS_TEST BIT(11) | ||
| /** Port indicator control bit. */ | ||
| #define USB_HUB_PORT_STATUS_INDICATOR BIT(12) | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * @name USB Hub Port Change Field, wPortChange. | ||
| * | ||
| * See Table 11-22 of the specification. | ||
| * @{ | ||
| */ | ||
| /** Port connection change bit. */ | ||
| #define USB_HUB_PORT_CHANGE_CONNECTION BIT(0) | ||
| /** Port enable change bit. */ | ||
| #define USB_HUB_PORT_CHANGE_ENABLE BIT(1) | ||
| /** Port suspend change bit. */ | ||
| #define USB_HUB_PORT_CHANGE_SUSPEND BIT(2) | ||
| /** Port over-current change bit. */ | ||
| #define USB_HUB_PORT_CHANGE_OVER_CURRENT BIT(3) | ||
| /** Port reset change bit. */ | ||
| #define USB_HUB_PORT_CHANGE_RESET BIT(4) | ||
| /** @} */ | ||
|
|
||
| /** | ||
| * Maximum hub descriptor size. | ||
| * 7 bytes (fixed) + max 32 bytes (DeviceRemovable) + max 32 bytes (PortPwrCtrlMask) | ||
| */ | ||
| #define USB_HUB_DESC_BUF_SIZE 71 | ||
|
|
||
| /** | ||
| * @brief Get Think Time value from wHubCharacteristics field. | ||
| * | ||
| * @param wHubCharacteristics Hub descriptor field wHubCharacteristics. | ||
| * | ||
| * @return Think time in full-speed bit times (8, 16, 24, or 32). | ||
| */ | ||
| #define USB_HUB_GET_THINK_TIME(wHubCharacteristics) \ | ||
| (((((wHubCharacteristics) & 0x0060U) >> 5) + 1) << 3) | ||
|
|
||
| /** | ||
| * USB Hub Descriptor. See Table 11-13 of the specification. | ||
| */ | ||
| struct usb_hub_descriptor { | ||
| /** Descriptor length. */ | ||
| uint8_t bDescLength; | ||
| /** Descriptor type. */ | ||
| uint8_t bDescriptorType; | ||
| /** Number of downstream facing ports. */ | ||
| uint8_t bNbrPorts; | ||
| /** Hub characteristics bitmap. */ | ||
| uint16_t wHubCharacteristics; | ||
| /** Time from power-on to power-good (in 2ms intervals). */ | ||
| uint8_t bPwrOn2PwrGood; | ||
| /** Maximum current requirements of the Hub Controller (in mA). */ | ||
| uint8_t bHubContrCurrent; | ||
| /** Variable length fields follow: | ||
| * uint8_t DeviceRemovable[]; | ||
| * uint8_t PortPwrCtrlMask[]; | ||
| */ | ||
| } __packed; | ||
|
|
||
| /** | ||
| * USB Hub Status. See Table 11-19 and 11-20 of the specification. | ||
| */ | ||
| struct usb_hub_status { | ||
| /** Hub status bitmap. */ | ||
| uint16_t wHubStatus; | ||
| /** Hub status change bitmap. */ | ||
| uint16_t wHubChange; | ||
| } __packed; | ||
|
|
||
| /** | ||
| * USB Hub Port Status. See Table 11-21 and 11-22 of the specification. | ||
| */ | ||
| struct usb_hub_port_status { | ||
| /** Port status bitmap. */ | ||
| uint16_t wPortStatus; | ||
| /** Port status change bitmap. */ | ||
| uint16_t wPortChange; | ||
| } __packed; | ||
|
|
||
| #endif /* ZEPHYR_INCLUDE_USB_CLASS_USB_HUB_H_ */ |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| # SPDX-FileCopyrightText: Copyright Nordic Semiconductor ASA | ||
| # SPDX-FileCopyrightText: Copyright 2025 - 2026 NXP | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| rsource "Kconfig.uvc" | ||
| rsource "Kconfig.hub" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,71 @@ | ||
| # SPDX-FileCopyrightText: Copyright 2025 - 2026 NXP | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|
|
||
| config USBH_HUB_CLASS | ||
| bool "USB Host Hub Class Driver" | ||
| help | ||
| Enable USB Host Hub Class driver support. | ||
| This allows connecting USB hubs and managing downstream devices. | ||
|
|
||
| if USBH_HUB_CLASS | ||
|
|
||
| config USBH_HUB_INSTANCES_COUNT | ||
| int "Maximum Hub count" | ||
| default 7 | ||
| range 1 16 | ||
| help | ||
| Maximum number of hub devices supported in the entire USB topology. | ||
|
|
||
| config USBH_HUB_MAX_LEVELS | ||
| int "Maximum Hub chain depth" | ||
| default 5 | ||
| range 1 5 | ||
| help | ||
| Maximum number of non-root hubs allowed in a single communication path | ||
| between the host and any device. USB specification allows up to five | ||
| non-root hubs per path, which means the deepest path can include 5 hubs. | ||
|
|
||
| config USBH_HUB_PORT_RESET_TIMES | ||
| int "Hub port reset retry count" | ||
| default 2 | ||
| range 1 10 | ||
| help | ||
| Maximum number of port reset retries before giving up. | ||
|
|
||
| config USBH_HUB_CONFIG_WAIT_MS | ||
| int "Hub configuration wait interval (ms)" | ||
| default 100 | ||
| range 50 1000 | ||
| help | ||
| Polling interval when waiting for hub to be configured. | ||
| Typical hub configuration completes within 100ms. | ||
|
|
||
| config USBH_HUB_ENUM_RETRY_DELAY_MS | ||
| int "Enumeration retry delay (ms)" | ||
| default 500 | ||
| range 100 3000 | ||
| help | ||
| Delay before retrying device enumeration after failure. | ||
|
|
||
| config USBH_HUB_CHILD_PROBE_DELAY_MS | ||
| int "Child hub probe delay (ms)" | ||
| default 50 | ||
| range 10 500 | ||
| help | ||
| Delay to wait for child hub initialization. | ||
|
|
||
| config USBH_HUB_ERROR_RECOVERY_DELAY_MS | ||
| int "Error recovery delay (ms)" | ||
| default 1000 | ||
| range 500 5000 | ||
| help | ||
| Delay before retrying after serious errors. | ||
| Longer delays help avoid flooding the bus. | ||
|
|
||
| module = USBH_HUB | ||
| module-str = usbh hub | ||
| default-count = 1 | ||
| source "subsys/logging/Kconfig.template.log_config" | ||
| source "subsys/usb/common/Kconfig.template.instances_count" | ||
|
|
||
| endif # USBH_HUB_CLASS |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From the architectural point of view, any hub device is the same usb device as anything else. So that means, that after connection, we already have it in the
sys_dlist_t udevs.What gives us the option to keep the pointer to the "parent", which can be the pointer to the another
udevin the list in case the device is a downstream device.For the device, connected to the root port, it is possible to keep this pointer NULL.
From the uhc logic, there should be no difference, when a new device is connected (to directly to the root port or via external hub port) and it might be handled with the same
dev_connected_handler().This flag (
parent == NULLorparent == *another udev) might be used to build the device tree.Maybe, it makes sense to think about the new abstraction object:
device node, which represents the combination: parent + port number? Thus, the root node will be [NULL, 0], and any other node will be [*udev, port_num].PS. As a bonus, if the hardware has two usb controllers, we can distinguish them by port number, when parent is NULL.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@roma-jam
Thank you for this elegant architectural proposal! I agree that the hub pointer naturally represents the device tree structure, and your device_node abstraction of [parent, port] is conceptually clean. However, I'd like to respectfully point out that while hub devices are indeed USB devices from a data structure perspective, they differ fundamentally in their operational role: hub devices actively manage port events through continuous interrupt endpoint monitoring and complex state machines, whereas regular devices are passive. The current architecture already achieves the unification you're suggesting—both root port and hub port connections ultimately call the same usbh_device_connect() function with a properly initialized usb_device that has its hub and hub_port fields set correctly. The difference is in how the connection is detected (hardware interrupt for root ports vs. software polling for hub ports), not in how the device tree is represented. Regarding your PS about multiple controllers: the current implementation already supports this through separate usbh_context instances per controller, with events carrying the controller information via the event.dev field, so distinguishing controllers by port number when parent == NULL wouldn't be necessary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The one thing I still wonder about - what can be used during enqueuing to tell the driver, that this particular device is connected via hub?
Some drivers required this knowledge to configure the channel correctly.
To do this, and to handle this correctly, we need to be able to get the following params:
This is important to support combinations as High speed hubs + Low speed devices, connected to Hubs port.
Is there any way to get these params?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good question! The current implementation already provides access to both parameters you mentioned through the struct usb_device pointer:
Parent port speed (Hub's speed): Available via udev->hub->speed
Device speed: Available via udev->speed
The struct usb_device contains the complete topology information including the parent hub pointer. For example, in the MCUX driver, we use USB_HostHelperGetPeripheralInformation() to query these parameters:
These helper functions traverse the udev->hub chain to find the high-speed hub and extract the necessary TT-related information for handling HS Hub + LS/FS device combinations. The driver can access all the topology information it needs through the device pointer to properly configure channels for different speed combinations.