VxWorks USB Stack Architecture: How usbdCoreLib Works
usbdCoreLib is the central implementation layer of the USB Driver (USBD) framework in VxWorks. It connects high-level USB client driversβsuch as mass-storage, HID, and other device-class driversβwith low-level Host Controller Drivers (HCDs).
The architecture abstracts USB hardware and protocol details into four primary objects:
USBD_NODEβ Represents a USB device or hub in the USB topology.USBD_PIPEβ Represents a logical communication channel to a device endpoint.USBD_CLIENTβ Represents a registered USB driver or software module.USB_IRPβ Represents an asynchronous I/O request submitted through a pipe.
Together, these abstractions allow client drivers to operate without directly managing host-controller hardware.
π§© Core USBD Abstractions #
The USB stack can be viewed as a layered architecture:
Client Driver β USBD Client β Pipe β IRP β HCD β USB Controller
Each layer has a distinct responsibility.
USBD_NODE
#
A USBD_NODE represents a USB device or hub attached to the bus. Nodes form a topology tree, allowing the framework to represent relationships between root hubs, external hubs, and downstream devices.
A node contains information required for enumeration, descriptors, configuration, control transfers, and child-device management.
USBD_PIPE
#
A USBD_PIPE represents communication with a particular endpoint on a USB device.
Pipes connect a registered client to an endpoint associated with a USBD_NODE. They also track transfer-related information such as endpoint characteristics and allocated bus bandwidth.
USBD_CLIENT
#
A USBD_CLIENT represents a driver registered with the USBD framework.
Clients declare which devices or interfaces they can handle and receive notifications when matching USB devices are attached or removed.
USB_IRP
#
A USB_IRP is the asynchronous transaction unit used to submit USB operations.
Client transfers are converted into IRPs and passed through pipes to the HCD. Completion callbacks then propagate the result back toward the client.
π§΅ Threading and Concurrency Model #
usbdCoreLib uses dedicated tasks to separate asynchronous USB activity.
clientThread
#
Each registered client receives its own clientThread.
The thread waits on a callback queue and processes events such as:
CALLBACK_FNC_IRP_COMPLETECALLBACK_FNC_NOTIFY_ATTACHCALLBACK_FNC_MNGMT_EVENT- Termination requests
This design prevents callbacks belonging to one USB client from blocking the execution of another client.
An internal client is also created during USBD initialization. It uses the default control pipe to communicate with newly discovered devices before they are configured.
busThread
#
Each attached host-controller bus receives a dedicated busThread.
This task monitors bus events, processes hub status changes, handles hot-plug activity, and maintains USB topology state.
The separation between client and bus threads keeps device-driver callbacks independent from bus-level monitoring.
π USBD Initialization and Lifecycle #
The main entry point for USBD operations is usbdCoreEntry, which routes URB-related requests into usbdCoreLib.
Initialization is performed by fncInitialize.
The initialization process includes:
- Starting the Operating System Services (OSS) library.
- Initializing the
usbHandlehandle-management system. - Creating structural synchronization objects such as
structMutex. - Registering the internal USBD client.
- Preparing the framework to accept HCD and client registrations.
Shutdown follows the reverse process.
fncShutdown and doShutdown unregister clients, detach HCD instances, release synchronization resources, and shut down the underlying libraries.
The fncVersionGet routine exposes the framework version through USBD_VERSION.
π Handle and Request Validation #
Because USB objects are dynamically created and destroyed, handle validation is an important part of the architecture.
The framework provides dedicated validation routines:
validateClientvalidateNodevalidatePipevalidateUrb
These functions use usbHandleValidate to verify object handles and also check deletion-pending states such as nodeDeletePending and pipeDeletePending.
validateUrb additionally verifies the URB structure and its associated client handle.
Once an IRP or URB completes, setUrbResult records the execution status and invokes the appropriate callback.
π₯ USB Client Registration and Management #
Client drivers enter the USBD framework through fncClientReg.
After registration, a client receives its own execution context and callback queue.
The primary management routines include:
fncClientRegβ Registers a client driver.fncClientUnregβ Removes a client.destroyClientβ Releases client resources and terminates its worker thread.fncMngmtCallbackSetβ Configures management-event callbacks.
When a client is destroyed, its pipes and notification structures are unlinked, its callback thread is terminated, and associated queue resources are released.
This lifecycle allows USB drivers to dynamically enter and leave the stack without requiring the entire USB subsystem to restart.
π³ Device Enumeration and USB Topology #
Device enumeration is centered around createNode.
When a new USB device appears, the framework:
- Allocates a
USBD_NODE. - Creates its default control pipe.
- Retrieves device descriptors.
- Assigns a USB bus address.
- Initializes device-specific structures.
- Discovers supported configurations and interfaces.
The resulting nodes form a topology tree representing the USB bus.
When a device is removed, destroyNode recursively tears down the corresponding device hierarchy.
destroyAllNodes performs the same process across an entire bus or node subtree.
USB Address Management #
USB bus addresses are managed through the adrsVec bitmap, which tracks the available device addresses.
assignAddress allocates an available address and programs the device through a default control transfer.
releaseAddress returns the address to the available pool when the device is removed.
π Device Classes and Driver Matching #
After enumeration, interrogateDeviceClass scans device configurations and interfaces to determine which classes are supported.
createNodeClass creates USBD_NODE_CLASS structures describing those interfaces.
This information is subsequently used to determine which registered clients should receive notifications about the device.
Dynamic driver matching is handled through routines such as:
notifyIfMatchnotifyClientsscanClassTypesfncDynaAttachRegfncDynaAttachUnreg
These mechanisms allow drivers to automatically respond to newly connected devices based on class, subclass, protocol, or other device properties.
π Hub Management and Hot-Plug Detection #
USB hubs require continuous monitoring because downstream devices can appear or disappear at runtime.
initHubNode initializes hub-specific structures, while initHubIrp creates the interrupt request used to monitor hub status.
When the hub reports a status change, hubIrpCallback and checkHubStatus process the event.
updateHubPort then evaluates the affected port.
For an insertion event, the framework can create a new node and begin enumeration.
For a removal event, the corresponding node tree is destroyed and resources are reclaimed.
This provides the foundation for USB hot-plug functionality in the VxWorks stack.
π₯οΈ Host Controller Driver Integration #
The HCD layer represents the hardware-specific portion of the USB stack.
fncHcdAttach attaches a Host Controller Driver to the USBD framework, while fncHcdDetach removes it.
destroyHcd releases the HCD’s associated resources.
During attachment, the framework creates USBD_BUS objects and initializes the associated busThread.
initHcdBus prepares an individual HCD-managed bus and establishes its root node.
The HCD can also report hardware-level management events through hcdMngmtCallback, which distributes relevant notifications to registered client threads.
This separation allows the higher-level USBD framework to remain largely independent of the underlying USB controller implementation.
π‘ Control Transfers and Endpoint Requests #
Control transfers are handled through the device’s default control pipe.
controlRequest constructs the required setup, data, and status stages and submits the resulting request.
Synchronization is provided through controlSem, ensuring appropriate serialization of control operations.
controlIrpCallback processes the completion of these requests.
The framework exposes standard USB control operations through routines including:
fncFeatureClearfncFeatureSetfncConfigGetfncConfigSetfncDescriptorGetfncDescriptorSetfncInterfaceGetfncInterfaceSetfncStatusGetfncAddressGetfncAddressSetfncVendorSpecific
resetDataToggle is also used to reset endpoint data toggles when required after configuration changes or clear-halt operations.
π Pipes, Transfers, and IRP Execution #
Client drivers generally operate through pipes rather than interacting with the HCD directly.
fncPipeCreate establishes a logical pipe for a particular endpoint, while fncPipeDestroy and destroyPipe release it.
Pipe creation also accounts for bus-bandwidth requirements, including the bandwidth represented in nanoseconds for scheduled transfer types.
When a client requests a transfer, fncTransfer wraps the request in an IRP and submits it through:
usbHcdIrpSubmit
When the HCD completes the operation, transferIrpCallback is invoked.
The callback can:
- Update transfer status.
- Adjust bandwidth accounting.
- Advance data-toggle state for Bulk and Interrupt pipes.
- Dispatch the client’s completion callback.
Transfer cancellation follows a similar path.
fncTransferAbort initiates cancellation, while doTransferAbort coordinates with the HCD and waits for cancellation completion through usbHcdIrpCancel.
β±οΈ Frame Timing and SOF Management #
Isochronous and other time-sensitive USB transfers require access to USB frame information.
fncSynchFrameGet retrieves synchronized frame information, while fncCurrentFrameGet reports the current frame number.
The framework also provides Start-of-Frame ownership mechanisms:
fncSofMasterTakefncSofMasterRelease
SOF timing parameters can be queried and configured using:
fncSofIntervalGetfncSofIntervalSet
These facilities allow higher-level USB components to coordinate with HCD-specific frame scheduling.
β‘ Bus Power and State Management #
USB bus state can be controlled through fncBusStateSet.
This includes operations associated with states such as:
- Suspend
- Resume
The ability to manage bus state at the USBD layer allows device and power-management behavior to be coordinated with the underlying host controller.
π Statistics and Topology Queries #
usbdCoreLib also exposes APIs for retrieving information about the current USB environment.
Important query routines include:
fncStatisticsGetβ Retrieves USB statistics.fncBusCountGetβ Returns the number of USB buses.fncRootIdGetβ Retrieves the root-node identifier.fncHubPortCountGetβ Reports hub port information.fncNodeIdGetβ Retrieves a node identifier.fncNodeInfoGetβ Returns node metadata.
These interfaces provide clients and management software with visibility into the current USB topology and runtime state.
ποΈ Putting the Architecture Together #
The overall usbdCoreLib architecture can be summarized as a chain of abstractions:
USB Client β USBD_PIPE β USB_IRP β HCD β USB Hardware
At the same time, the device hierarchy is represented through:
Root Hub β Hub USBD_NODE β Child USBD_NODE β Endpoint β USBD_PIPE
Two independent asynchronous mechanisms keep the system responsive:
clientThread β Client callbacks and IRP completion
busThread β Hub monitoring, hot-plug events, and topology changes
This separation allows VxWorks to handle device enumeration, driver attachment, asynchronous transfers, hot-plug events, and hardware-specific controller operations without exposing low-level USB controller details to client drivers.
π§ Why usbdCoreLib Matters
#
usbdCoreLib effectively serves as the orchestration layer of the VxWorks USB stack.
Its four fundamental abstractionsβNODE, PIPE, CLIENT, and IRPβseparate device topology, endpoint communication, driver ownership, and asynchronous transfers.
Above it, USB class drivers can focus on device functionality. Below it, HCDs handle controller-specific hardware operations.
That division of responsibility is what makes the stack extensible: new USB devices and client drivers can be introduced without redesigning the underlying controller layer, while different HCD implementations can support different USB host hardware beneath the same USBD programming model.