Skip to main content

VxWorks USB Stack Architecture: How usbdCoreLib Works

·1680 words·8 mins
VxWorks USB UsbdCoreLib USB Driver HCD Embedded Systems RTOS Device Drivers
Table of Contents

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_COMPLETE
  • CALLBACK_FNC_NOTIFY_ATTACH
  • CALLBACK_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:

  1. Starting the Operating System Services (OSS) library.
  2. Initializing the usbHandle handle-management system.
  3. Creating structural synchronization objects such as structMutex.
  4. Registering the internal USBD client.
  5. 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:

  • validateClient
  • validateNode
  • validatePipe
  • validateUrb

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:

  1. Allocates a USBD_NODE.
  2. Creates its default control pipe.
  3. Retrieves device descriptors.
  4. Assigns a USB bus address.
  5. Initializes device-specific structures.
  6. 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:

  • notifyIfMatch
  • notifyClients
  • scanClassTypes
  • fncDynaAttachReg
  • fncDynaAttachUnreg

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:

  • fncFeatureClear
  • fncFeatureSet
  • fncConfigGet
  • fncConfigSet
  • fncDescriptorGet
  • fncDescriptorSet
  • fncInterfaceGet
  • fncInterfaceSet
  • fncStatusGet
  • fncAddressGet
  • fncAddressSet
  • fncVendorSpecific

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:

  • fncSofMasterTake
  • fncSofMasterRelease

SOF timing parameters can be queried and configured using:

  • fncSofIntervalGet
  • fncSofIntervalSet

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.

Related

VxWorks USB Driver Development: USBD Architecture and usbdLib API Guide
·3405 words·16 mins
VxWorks USB USB Driver USBD UsbdLib HCD Embedded Systems Device Drivers
VxWorks USB Driver Development: USB Architecture and Protocol Basics
·2493 words·12 mins
VxWorks USB Device Drivers Embedded Systems RTOS USB 2.0 HCD USBD
PCI-Based CAN Card Design and VxWorks Driver Development
·683 words·4 mins
VxWorks CAN Bus PCI Device Drivers Embedded Systems RTOS Hardware Design C Programming