Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions drivers/usb/uhc/uhc_mcux_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,49 @@ usb_status_t USB_HostHelperGetPeripheralInformation(usb_device_handle deviceHand
break;

case kUSB_HostGetDeviceHubNumber:
if (udev->hub != NULL) {
*infoValue = udev->hub->addr;
} else {
*infoValue = 0;
}
break;

case kUSB_HostGetDevicePortNumber:
*infoValue = udev->hub_port;
break;

case kUSB_HostGetDeviceHSHubNumber:
for (; udev->hub != NULL; udev = udev->hub) {
if (udev->hub->speed == USB_SPEED_SPEED_HS) {
*infoValue = udev->hub->addr;
return kStatus_USB_Success;
}
}
*infoValue = 0;
break;

case kUSB_HostGetDeviceHSHubPort:
case kUSB_HostGetHubThinkTime:
for (; udev->hub != NULL; udev = udev->hub) {
if (udev->hub->speed == USB_SPEED_SPEED_HS) {
*infoValue = udev->hub->hub_port;
return kStatus_USB_Success;
}
}
*infoValue = 0;
break;

case kUSB_HostGetHubThinkTime: {
uint16_t total_think_time = 0;

for (; udev->hub != NULL; udev = udev->hub) {
total_think_time += udev->hub->tt;
}
*infoValue = total_think_time;
break;
}

case kUSB_HostGetDeviceLevel:
*infoValue = 1;
*infoValue = udev->level;
break;

case kUSB_HostGetDeviceSpeed:
Expand Down
8 changes: 8 additions & 0 deletions include/zephyr/drivers/usb/uhc.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Copy Markdown
Contributor

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 udev in 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 == NULL or parent == *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.

Copy link
Copy Markdown
Contributor Author

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 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:

  • parent port speed [speed of the hub]
  • device speed [speed of the port, when it is enabled]

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?

Copy link
Copy Markdown
Contributor Author

@AidenHu AidenHu May 8, 2026

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:

// Get device speed
USB_HostHelperGetPeripheralInformation(udev, kUSB_HostGetDeviceSpeed, &device_speed);

// Get HS hub address (if device is LS/FS connected to HS hub)
USB_HostHelperGetPeripheralInformation(udev, kUSB_HostGetDeviceHSHubNumber, &hs_hub_addr);

// Get HS hub port
USB_HostHelperGetPeripheralInformation(udev, kUSB_HostGetDeviceHSHubPort, &hs_hub_port);

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.

/** Device's hub Think Time */
uint16_t tt;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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:

  • number of ports (relevant, when we handle the port(s) connection)
  • time from power on to power good (relevant, when hub can power on each port or all ports altogether)
  • characteristics (tt includes here, as well as port logical switching and led support)
  • current consumption

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the comment here,

Actually previously the total uint16_t hub_characteristics; is added here, but @jfischer-no suggests keep think time here.

/** Device's hub port */
uint8_t hub_port;
/** Device's level (root device = 0) */
uint8_t level;
Comment thread
jfischer-no marked this conversation as resolved.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, what is the purpose of having level in explicit form?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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:
Enforce USB specification limits on hub chaining
Provide necessary information to the hardware controller driver
Maintain and validate the device tree structure

};

/**
Expand Down
211 changes: 198 additions & 13 deletions include/zephyr/usb/class/usb_hub.h
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file seems like a candidate to be usb_ch11.h, not usb/class/usb_hub.h?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The 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_ */
4 changes: 4 additions & 0 deletions subsys/usb/host/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ zephyr_library_sources(
usbh_class.c
)

if(CONFIG_USBH_SHELL OR CONFIG_USBH_HUB_CLASS)
zephyr_library_sources(usbh_ch11.c)
endif()

zephyr_library_sources_ifdef(
CONFIG_USBH_SHELL
usbh_shell.c
Expand Down
2 changes: 2 additions & 0 deletions subsys/usb/host/class/Kconfig
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"
71 changes: 71 additions & 0 deletions subsys/usb/host/class/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
Loading
Loading