Skip to main content

VxWorks USB Keyboard Driver Development: usbKeyboardLib

·3104 words·15 mins
VxWorks USB USB Drivers Device Drivers SIO HID Embedded Systems RTOS
Table of Contents

VxWorks USB Keyboard Driver Development: usbKeyboardLib

The VxWorks USB keyboard implementation demonstrates how the USB application layer bridges the USB Driver (USBD) subsystem and the operating system’s standard SIO and file-system interfaces. The implementation is divided into two complementary libraries: usbKeyboardLib, which contains the USB keyboard driver core, and usrUsbKbdInit, which exposes the keyboard through the standard VxWorks device abstraction.

At the USB layer, the driver communicates with HID keyboard devices through interrupt pipes and USB IRPs (I/O Request Packets). At the OS-facing layer, keyboard data is converted into SIO-compatible input and optionally exposed through a device entry such as /usbKb/0.

This architecture also accounts for USB’s dynamic device model. Keyboard insertion and removal are propagated through multiple callback layers, while reference counting prevents a software channel from being destroyed while it is still being used by an upper-layer task.

๐Ÿ—๏ธ USB Keyboard Driver Architecture
#

The USB keyboard implementation consists of two primary components:

  • usbKeyboardLib: Implements the USB keyboard driver core and SIO interface.
  • usrUsbKbdInit: Creates the file-system abstraction used by applications to access keyboard devices.

The overall data path is:

USB Keyboard
     โ”‚
     โ–ผ
USBD / USB HCD
     โ”‚
     โ”‚ Interrupt Pipe + USB_IRP
     โ–ผ
usbKeyboardIrpCallback()
     โ”‚
     โ–ผ
interpKbdReport()
     โ”‚
     โ”œโ”€โ”€ RAW mode
     โ”‚
     โ””โ”€โ”€ ASCII mode
     โ”‚
     โ–ผ
SIO_CHAN / USB_KBD_SIO_CHAN
     โ”‚
     โ–ผ
usrUsbKbdInit
     โ”‚
     โ–ผ
VxWorks I/O System
     โ”‚
     โ–ผ
fopen / read / ioctl / close

Before usbKeyboardDevInit() is invoked, usbdInitialize() must have initialized the USBD subsystem, and at least one USB Host Controller Driver (HCD) must be attached using usbdHcdAttach().

SIO channel relationship
#

Each detected keyboard is represented by a USB_KBD_SIO_CHAN structure. This structure derives from SIO_CHAN and provides access to the generic SIO driver operations through its pDrvFuncs member.

The relationship can be summarized as:

USB_KBD_SIO_CHAN
      โ”‚
      โ””โ”€โ”€ SIO_CHAN
            โ”‚
            โ””โ”€โ”€ pDrvFuncs
                  โ”‚
                  โ””โ”€โ”€ sio_drv_funcs

This allows the USB-specific implementation to integrate with existing VxWorks SIO consumers without requiring those consumers to understand USB-specific details.

๐Ÿ”Œ USB Keyboard Initialization and Registration
#

usbKeyboardDevInit() is the primary initialization entry point for usbKeyboardLib. Unlike some SIO drivers, the USB keyboard module does not require a separate pre-initialization step for its internal variables.

Initialization performs three major operations:

  1. Starts the typematicThread responsible for keyboard auto-repeat handling.
  2. Registers a USBD_CLIENT through usbdClientRegister().
  3. Registers a dynamic USB attachment request through usbdDynamicAttachRegister().

When a matching keyboard is detected, USBD generates a notification that is processed by the registered client and ultimately reaches usbKeyboardAttachCallback().

Required USBD initialization sequence
#

The expected dependency order is:

usbdInitialize()
      โ”‚
      โ–ผ
usbdHcdAttach()
      โ”‚
      โ–ผ
usbKeyboardDevInit()
      โ”‚
      โ”œโ”€โ”€ usbdClientRegister()
      โ”‚
      โ””โ”€โ”€ usbdDynamicAttachRegister()

A USB keyboard cannot be configured successfully until both the USBD subsystem and an appropriate HCD are available.

๐Ÿงฉ Core Data Structures
#

Two linked lists are central to usbKeyboardLib.

sioList
#

LOCAL LIST_HEAD sioList;

sioList tracks all USB keyboards currently managed by the driver. Every detected keyboard has an associated USB_KBD_SIO_CHAN structure.

When a keyboard is attached:

USB attach event
      โ”‚
      โ–ผ
createSioChan()
      โ”‚
      โ–ผ
USB_KBD_SIO_CHAN
      โ”‚
      โ–ผ
sioList

When the device is detached and no upper-layer references remain, the corresponding channel is destroyed.

reqList
#

LOCAL LIST_HEAD reqList;

reqList contains dynamic attach/detach registrations made by upper layers.

Calling:

usbKeyboardDynamicAttachRegister(usbKbdDrvAttachCallback, NULL);

creates an ATTACH_REQUEST entry. The registered callback is subsequently notified when keyboards are inserted or removed.

This design separates USB device detection from consumers that need to create or remove OS-level device instances.

โŒจ๏ธ Keyboard Input Processing
#

USB HID keyboards normally provide keyboard reports containing keyboard modifier information and up to six simultaneous scan codes. usbKeyboardLib processes these reports differently depending on the selected scan mode.

The central processing function is interpKbdReport().

RAW mode
#

When:

pSioChan->scanMode == SIO_KYBD_MODE_RAW

the driver preserves the low-level keyboard report information.

The processing sequence is:

  1. Store pReport->modifiers.
  2. Iterate over the scan-code array and store each non-zero scan code.
  3. Append 0xff as the report terminator.

This mode is appropriate when upper layers need access to keyboard scan-code information rather than translated characters.

ASCII mode
#

When:

pSioChan->scanMode == SIO_KYBD_MODE_ASCII

the driver translates scan codes into key codes through:

cvtScanCodeToKeyCode()

For each non-zero scan code:

  1. Convert the scan code and modifier state into a key code.
  2. Ignore codes already present in activeScanCodes.
  3. Queue normal characters directly.
  4. For extended keys, queue 0 followed by the lower eight bits of the key code.
  5. Detect potential sustained keypresses for typematic processing.

This separates HID-level input representation from the character-oriented interface expected by SIO consumers.

๐Ÿ”„ Scan-Code Translation and Keyboard State
#

The following internal functions implement keyboard state management and scan-code conversion.

cvtScanCodeToKeyCode()
#

LOCAL UINT16 cvtScanCodeToKeyCode(
    pUSB_KBD_SIO_CHAN pSioChan,
    UINT16 scanCode,
    UINT16 modifiers
)

Converts a USB HID scan code into a VxWorks keyboard key code. The result depends on the active keyboard state and modifier bits, including Shift, Ctrl, and Alt.

The return value can contain an ASCII character together with CapsLock, ScrollLock, and NumLock indicator flags. If no valid key mapping exists, NOKEY is returned.

isKeyPresent()
#

LOCAL BOOL isKeyPresent(pUINT16 pKeyArray, UINT16 key)

Searches an array for a specified key. It is used to determine whether a scan code is already active.

changeKbdState()
#

LOCAL VOID changeKbdState(
    pUSB_KBD_SIO_CHAN pSioChan,
    UINT16 scanCode,
    pBOOL pKeyState
)

Updates the logical state of CapsLock, NumLock, or ScrollLock and synchronizes the physical keyboard LEDs with that state.

interpScanCode()
#

LOCAL void interpScanCode(
    pUSB_KBD_SIO_CHAN pSioChan,
    UINT16 scanCode,
    UINT16 modifiers
)

Processes state-changing scan codes.

If the scan code is already present in activeScanCodes, it is ignored because the event has already been processed. Otherwise, CapsLock, NumLock, and ScrollLock events are forwarded to changeKbdState().

๐Ÿ’ก LED Control
#

The keyboard’s LED state is controlled through:

LOCAL VOID setLedReport(
    pUSB_KBD_SIO_CHAN pSioChan,
    UINT8 ledReport
)

The driver generates a HID output report and submits it to the keyboard to update the CapsLock, NumLock, and ScrollLock indicators.

During keyboard configuration, the driver initially calls:

setLedReport(pSioChan, 0);

to clear all LED indicators.

๐Ÿ“ฅ Receive Queue Management
#

usbKeyboardLib uses a circular buffer to decouple USB interrupt processing from upper-layer character consumption.

The relevant state is:

inQueue[KBD_Q_DEPTH]
        โ”‚
        โ”œโ”€โ”€ inQueueIn
        โ”œโ”€โ”€ inQueueOut
        โ””โ”€โ”€ inQueueCount

putInChar()
#

LOCAL VOID putInChar(
    pUSB_KBD_SIO_CHAN pSioChan,
    char putChar
)

Adds a character to the receive queue.

The input index advances through:

inQueue[inQueueIn++]

while inQueueCount tracks the number of queued characters.

The function assumes that the caller has already ensured sufficient queue capacity. If the queue is full, the incoming character is discarded.

nextInChar()
#

LOCAL char nextInChar(pUSB_KBD_SIO_CHAN pSioChan)

Returns the next character from:

inQueue[inQueueOut++]

It does not validate whether data is available. Callers must therefore guarantee that the queue is non-empty before invoking it.

โฑ๏ธ Typematic Key Repeat
#

USB keyboard input also implements typematic behavior for continuously held keys.

The driver uses:

  • TYPEMATIC_DELAY: 500 ms
  • Repeat interval: 66 ms

When the same key remains active beyond the initial delay, updateTypematic() places repeated characters into the input queue and invokes putRxCharCallback to deliver them to upper layers.

updateTypematic()
#

LOCAL VOID updateTypematic(pUSB_KBD_SIO_CHAN pSioChan)

Checks the current typematic state and generates repeated input once the configured delay has elapsed.

typematicThread()
#

LOCAL VOID typematicThread(pVOID param)

The dedicated thread iterates over every keyboard in sioList, invokes updateTypematic(), and then sleeps for 66 ms.

This design keeps sustained-key processing independent from USB interrupt completion callbacks.

Typematic state tracking
#

interpKbdReport() uses:

pSioChan->typematicChar

to retain the key code detected during the previous report.

When a report contains only one non-zero scan code, the driver compares it with typematicChar:

  • Same key: continue typematic processing.
  • Different key: update the timestamp and reset typematicCount.

This provides the state required to distinguish an ordinary keypress from a sustained keypress.

โšก USB Interrupt IRP Processing
#

The USB keyboard receives input through an interrupt endpoint. The driver submits an interrupt USB_IRP and waits for keyboard reports.

initKbdIrp()
#

LOCAL BOOL initKbdIrp(pUSB_KBD_SIO_CHAN pSioChan)

Initializes a USB_IRP and submits it through usbdTransfer().

The callback is assigned as:

pIrp->userCallback = usbKeyboardIrpCallback;

The submitted IRP waits for keyboard input on the interrupt pipe. USBD populates the keyboard report into pSioChan->pBootReport.

usbKeyboardIrpCallback()
#

LOCAL VOID usbKeyboardIrpCallback(pVOID p)

This callback executes when an interrupt transfer completes.

Its processing sequence is:

USB interrupt transfer completes
            โ”‚
            โ–ผ
usbKeyboardIrpCallback()
            โ”‚
            โ–ผ
interpKbdReport()
            โ”‚
            โ–ผ
Queue translated/raw data
            โ”‚
            โ–ผ
putRxCharCallback()
            โ”‚
            โ–ผ
initKbdIrp()
            โ”‚
            โ–ผ
Wait for next report

The callback therefore forms the central event loop for USB keyboard input.

After processing the current report, it resubmits the interrupt IRP so the device can continue receiving keyboard events.

๐Ÿ”ง USB Keyboard Configuration
#

configureSioChan() performs the USB-specific configuration required before keyboard input can be processed.

The sequence is:

  1. Retrieve configuration descriptor index 0 using usbdDescriptorGet().
  2. Parse the descriptor with usbDescrParse().
  3. Locate the target interface using usbDescrParseSkip().
  4. Locate the appropriate endpoint descriptors.
  5. Select the configuration with usbdConfigurationSet().
  6. Select the interface using usbdInterfaceSet().
  7. Configure the HID protocol through usbHidProtocolSet().
  8. Set the keyboard idle behavior with usbHidIdleSet().
  9. Clear the keyboard LEDs with setLedReport().
  10. Create the interrupt pipe using usbdPipeCreate().
  11. Submit the first keyboard IRP through initKbdIrp().

Conceptually:

USB descriptors
      โ”‚
      โ–ผ
Configuration
      โ”‚
      โ–ผ
Interface
      โ”‚
      โ–ผ
HID protocol
      โ”‚
      โ–ผ
Idle configuration
      โ”‚
      โ–ผ
Interrupt pipe
      โ”‚
      โ–ผ
USB_IRP
      โ”‚
      โ–ผ
Keyboard reports

๐Ÿงฑ SIO Driver Interface
#

The USB keyboard exposes standard SIO operations through sio_drv_funcs.

usbKeyboardIoctl()
#

LOCAL int usbKeyboardIoctl(
    SIO_CHAN *pChan,
    int request,
    void *someArg
)

Handles keyboard-specific control requests, including:

  • Interrupt versus polling mode.
  • RAW versus ASCII scan mode.
  • Keyboard LED control.

usbKeyboardTxStartup()
#

USB keyboards do not provide a character-oriented outbound data path, so this function returns:

EIO

usbKeyboardCallbackInstall()
#

Installs SIO callbacks on the channel.

The receive callback:

putRxCharCallback

is invoked when keyboard input has been queued.

The transmit callback:

getTxCharCallback

is unused because USB keyboards do not support character transmission through the SIO interface.

usbKeyboardPollOutput()
#

Output is unsupported and therefore returns EIO.

usbKeyboardPollInput()
#

Retrieves the next queued character by calling nextInChar().

๐Ÿ”Œ USB Hot-Plug Management
#

Hot-plugging is implemented through two distinct callback paths:

USBD physical attach/detach
            โ”‚
            โ–ผ
usbKeyboardAttachCallback()
            โ”‚
            โ–ผ
notifyAttach()
            โ”‚
            โ–ผ
reqList callbacks
            โ”‚
            โ–ผ
usrUsbKbdInit
            โ”‚
            โ”œโ”€โ”€ create device
            โ”‚
            โ””โ”€โ”€ delete device

This distinction is important because usbKeyboardIrpCallback() handles data events, whereas usbKeyboardAttachCallback() handles physical device lifecycle events.

createSioChan()
#

LOCAL pUSB_KBD_SIO_CHAN createSioChan(
    USBD_NODE_ID nodeId,
    UINT16 configuration,
    UINT16 interface
)

Allocates and initializes a USB_KBD_SIO_CHAN, appends it to sioList, and configures the associated USB interface.

It is invoked when a new keyboard is detected.

findSioChan()
#

LOCAL pUSB_KBD_SIO_CHAN findSioChan(USBD_NODE_ID nodeId)

Searches sioList for a channel associated with the specified USB node ID.

notifyAttach()
#

LOCAL VOID notifyAttach(
    pUSB_KBD_SIO_CHAN pSioChan,
    UINT16 attachCode
)

Traverses reqList and executes the registered callback functions, propagating keyboard attach or detach notifications to upper layers.

usbKeyboardAttachCallback()
#

LOCAL VOID usbKeyboardAttachCallback(
    USBD_NODE_ID nodeId,
    UINT16 attachAction,
    UINT16 configuration,
    UINT16 interface,
    UINT16 deviceClass,
    UINT16 deviceSubClass,
    UINT16 deviceProtocol
)

Handles physical keyboard insertion and removal.

For insertion:

  1. Check whether the nodeId is already represented.
  2. Create a USB_KBD_SIO_CHAN if necessary.
  3. Notify registered attach handlers.

For removal:

  1. Temporarily increment lockCount while notifying handlers.
  2. Notify registered detach handlers.
  3. Decrement the channel’s lockCount.
  4. Destroy the channel when no references remain.

The reference-counting mechanism is essential because an upper-layer task may still be holding the SIO channel while the physical USB device has already been removed.

๐Ÿ”’ SIO Channel Lifetime Management
#

usbKeyboardSioChanLock()
#

STATUS usbKeyboardSioChanLock(SIO_CHAN *pChan)

Increments lockCount, indicating that an upper-layer task currently holds a reference to the channel.

usbKeyboardSioChanUnlock()
#

STATUS usbKeyboardSioChanUnlock(SIO_CHAN *pChan)

Decrements lockCount.

If the physical keyboard has already been detached and the count reaches zero, the software channel is destroyed.

This provides a basic lifetime-management mechanism for asynchronous USB hot-plug events.

๐Ÿงน Driver Shutdown
#

destroySioChan()
#

LOCAL VOID destroySioChan(pUSB_KBD_SIO_CHAN pSioChan)

Releases the channel and its associated resources.

destroyAttachRequest()
#

LOCAL VOID destroyAttachRequest(pATTACH_REQUEST pRequest)

Removes an ATTACH_REQUEST from reqList.

doShutdown()
#

LOCAL STATUS doShutdown(int errCode)

Performs the inverse of usbKeyboardDevInit() and releases resources owned by usbKeyboardLib.

usbKeyboardDevShutdown()
#

STATUS usbKeyboardDevShutdown(void)

Invokes doShutdown() when the module’s initialization count reaches zero.

Dynamic registration APIs
#

STATUS usbKeyboardDynamicAttachRegister(
    USB_KBD_ATTACH_CALLBACK callback,
    pVOID arg
)

Registers an upper-layer attach/detach callback.

After registration, the function scans sioList so that keyboards already attached before registration can also be reported.

The corresponding unregister function:

STATUS usbKeyboardDynamicAttachUnRegister(
    USB_KBD_ATTACH_CALLBACK callback,
    pVOID arg
)

removes matching registration nodes. The implementation continues scanning to ensure duplicate registrations are completely removed.

๐Ÿ—‚๏ธ usrUsbKbdInit File-System Integration
#

While usbKeyboardLib implements the USB/SIO driver core, usrUsbKbdInit exposes that functionality through the standard VxWorks I/O system.

The abstraction allows applications to use conventional operations such as:

open
read
ioctl
close

against a device entry such as:

/usbKb/0

The resulting architecture is:

Application
    โ”‚
    โ–ผ
VxWorks I/O API
    โ”‚
    โ”œโ”€โ”€ fopen / open
    โ”œโ”€โ”€ read
    โ”œโ”€โ”€ ioctl
    โ””โ”€โ”€ close
    โ”‚
    โ–ผ
usrUsbKbdInit
    โ”‚
    โ–ผ
USB_KBD_DEV
    โ”‚
    โ–ผ
SIO_CHAN
    โ”‚
    โ–ผ
usbKeyboardLib
    โ”‚
    โ–ผ
USBD

๐Ÿ—๏ธ usbKbdDevCreate() and Device Registration
#

STATUS usbKbdDevCreate(
    char *name,
    SIO_CHAN *pSioChan
)

Creates a USB_KBD_DEV instance and registers a corresponding file-system device entry.

For example:

/usbKb/0

can represent the first detected USB keyboard.

Initialization bug and correction
#

The original implementation incorrectly reinitializes the following global objects every time usbKbdDevCreate() is called:

usbKbdMutex
usbKbdListMutex
usbKbdList

This is unsafe when multiple keyboard devices are present because creating a subsequent device can reset synchronization primitives or invalidate the existing device list.

The correct approach is to initialize these global resources once inside:

usrUsbKbdInit()

and allow usbKbdDevCreate() to operate only on per-device state.

๐Ÿงน Device Lifecycle Management
#

usbKbdDevDelete()
#

LOCAL STATUS usbKbdDevDelete(
    USB_KBD_DEV *pUsbKbdDev
)

Removes a device from the VxWorks I/O subsystem and internal keyboard list.

The operation includes:

  1. Removing pUsbKbdDev->ioDev from iosDvList.
  2. Unlinking pUsbKbdDev->pUsbKbdNode from usbKbdList.
  3. Releasing the allocated USB_KBD_DEV.

usbKbdDevFind()
#

LOCAL STATUS usbKbdDevFind(
    SIO_CHAN *pChan,
    USB_KBD_DEV **ppUsbKbdDev
)

Searches usbKbdList for the device structure associated with a specified SIO channel.

This provides the mapping between the SIO layer and the file-system device layer.

๐Ÿ”Œ File-System Hot-Plug Callback
#

usbKbdDrvAttachCallback()
#

LOCAL void usbKbdDrvAttachCallback(
    void *arg,
    SIO_CHAN *pChan,
    UINT16 attachCode
)

Receives attach and detach notifications from usbKeyboardLib.

On attachment:

USB keyboard attached
        โ”‚
        โ–ผ
usbKeyboardAttachCallback()
        โ”‚
        โ–ผ
usbKbdDrvAttachCallback()
        โ”‚
        โ–ผ
usbKbdDevCreate()
        โ”‚
        โ–ผ
/usbKb/N

On detachment:

USB keyboard removed
        โ”‚
        โ–ผ
usbKbdDrvAttachCallback()
        โ”‚
        โ”œโ”€โ”€ usbKbdDevDelete()
        โ”‚
        โ””โ”€โ”€ usbKeyboardSioChanUnlock()

The callback therefore connects the dynamic USB device lifecycle with the VxWorks file-system namespace.

๐Ÿ“‚ File Operations
#

usbKbdOpen()
#

LOCAL int usbKbdOpen(
    USB_KBD_DEV *pUsbKbdDev,
    char *name,
    int flags,
    int mode
)

Opens a USB keyboard device instance.

usbKbdClose()
#

LOCAL int usbKbdClose(USB_KBD_DEV *pUsbKbdDev)

Closes the device.

usbKbdIoctl()
#

LOCAL int usbKbdIoctl(
    USB_KBD_DEV *pUsbKbdDev,
    int request,
    void *arg
)

Forwards control requests to the underlying USB keyboard SIO implementation through:

usbKeyboardIoctl()

This keeps device-specific control logic in usbKeyboardLib while allowing applications to access it through the standard file interface.

usbKbdRead()
#

LOCAL int usbKbdRead(
    USB_KBD_DEV *pUsbKbdDev,
    UCHAR *buffer,
    UINT32 nBytes
)

Reads keyboard input in polling mode.

Each invocation retrieves one character. The nBytes parameter is not used by this implementation.

usbKbdWrite()
#

int usbKbdWrite(
    USB_KBD_DEV *pUsbKbdDev,
    UCHAR *buffer,
    UINT32 nBytes
)

Write operations are unsupported because the USB keyboard does not expose a character-oriented outbound interface.

๐Ÿš€ usrUsbKbdInit() Initialization Sequence
#

The top-level initialization function is:

void usrUsbKbdInit(void)

Its responsibilities are:

  1. Initialize usbKeyboardLib through usbKeyboardDevInit().
  2. Register usbKbdDrvAttachCallback() with usbKeyboardDynamicAttachRegister().

The resulting initialization chain is:

usrUsbKbdInit()
      โ”‚
      โ”œโ”€โ”€ usbKeyboardDevInit()
      โ”‚       โ”‚
      โ”‚       โ”œโ”€โ”€ typematicThread
      โ”‚       โ”œโ”€โ”€ USBD client
      โ”‚       โ””โ”€โ”€ dynamic USBD registration
      โ”‚
      โ””โ”€โ”€ usbKeyboardDynamicAttachRegister()
              โ”‚
              โ””โ”€โ”€ usbKbdDrvAttachCallback()

The inverse operation is provided by:

STATUS usbKbdDrvUnInit(void)

which shuts down the usrUsbKbdInit layer.

๐Ÿ” End-to-End USB Keyboard Data Flow
#

The complete input path combines USB interrupt processing, keyboard report interpretation, SIO buffering, callbacks, and VxWorks file-system access:

USB HID Keyboard
       โ”‚
       โ”‚ Interrupt IN transfer
       โ–ผ
USBD Interrupt Pipe
       โ”‚
       โ–ผ
USB_IRP
       โ”‚
       โ–ผ
usbKeyboardIrpCallback()
       โ”‚
       โ–ผ
interpKbdReport()
       โ”‚
       โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
       โ”‚               โ”‚
       โ–ผ               โ–ผ
RAW mode           ASCII mode
       โ”‚               โ”‚
       โ”‚          scan-code translation
       โ”‚               โ”‚
       โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
               โ–ผ
          inQueue[]
               โ”‚
               โ–ผ
     putRxCharCallback
               โ”‚
               โ–ผ
           SIO layer
               โ”‚
               โ–ผ
        USB_KBD_DEV
               โ”‚
               โ–ผ
        VxWorks I/O API
               โ”‚
               โ–ผ
             read()

Hot-plug events follow a separate control path:

USB attach/detach
       โ”‚
       โ–ผ
usbKeyboardAttachCallback()
       โ”‚
       โ–ผ
notifyAttach()
       โ”‚
       โ–ผ
reqList
       โ”‚
       โ–ผ
usbKbdDrvAttachCallback()
       โ”‚
       โ”œโ”€โ”€ usbKbdDevCreate()
       โ”‚
       โ””โ”€โ”€ usbKbdDevDelete()

This separation is a key architectural property of the implementation: USB data-plane processing and device lifecycle management are handled through independent callback paths while sharing the same SIO channel abstraction.

๐Ÿง  Key Design Considerations
#

The implementation illustrates several important VxWorks USB driver design principles.

Separate USB and OS-facing responsibilities
#

usbKeyboardLib is responsible for HID communication, interrupt transfers, keyboard report interpretation, SIO integration, and hot-plug notification. usrUsbKbdInit is responsible for exposing those capabilities through the VxWorks I/O subsystem.

This separation minimizes coupling between USB protocol handling and application-facing device operations.

Use asynchronous IRPs for interrupt-driven input
#

Keyboard reports are received through USB interrupt transfers rather than synchronous polling at the USB transport layer. usbKeyboardIrpCallback() processes completed reports and immediately resubmits the IRP.

This is well suited to event-driven keyboard input and avoids continuously polling the USB device from a task.

Preserve compatibility through SIO
#

The SIO_CHAN abstraction allows the USB keyboard to integrate with existing VxWorks serial-style consumers. USB-specific implementation details remain behind the SIO_DRV_FUNCS interface.

Treat hot-plugging as a lifecycle problem
#

USB devices can disappear independently of software references. The combination of lockCount, sioList, and dynamic attach callbacks ensures that a detached keyboard is not destroyed until all active software references have been released.

Initialize global state exactly once
#

The usbKbdDevCreate() initialization bug demonstrates an important driver-development rule: global synchronization primitives and global lists must not be reinitialized as a side effect of creating individual device instances.

Shared state should be initialized at module initialization time, while device creation should initialize only per-instance resources.

Keep interrupt callbacks lightweight
#

The interrupt callback primarily translates and queues input before resubmitting the IRP. More involved periodic work, such as typematic processing, is delegated to typematicThread.

This reduces the amount of work performed directly in the USB completion path and provides cleaner separation between interrupt-driven and periodic processing.

๐Ÿ“Œ Summary
#

The VxWorks USB keyboard driver provides a layered implementation that connects USB HID reports to standard VxWorks SIO and file-system interfaces.

usbKeyboardLib handles:

  • USB HID configuration and descriptor processing.
  • Interrupt endpoint and IRP management.
  • Keyboard report interpretation.
  • RAW and ASCII input modes.
  • Scan-code translation.
  • CapsLock, NumLock, and ScrollLock state management.
  • Keyboard LED updates.
  • Input buffering and typematic repetition.
  • Dynamic attach/detach notifications.
  • SIO channel lifetime management.

usrUsbKbdInit builds on this foundation by providing:

  • Device creation and deletion.
  • File-system registration.
  • Standard open/read/ioctl/close operations.
  • Hot-plug synchronization between the SIO and I/O layers.

Together, the two libraries demonstrate how a VxWorks USB application-layer driver can combine USBD/HID transport, asynchronous interrupt processing, SIO compatibility, dynamic hot-plugging, reference-counted resource management, and standard file I/O into a cohesive device-driver architecture.

Related

VxWorks USB Stack Architecture: How usbdCoreLib Works
·1680 words·8 mins
VxWorks USB UsbdCoreLib USB Driver HCD Embedded Systems RTOS 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
VxWorks USB HCD Driver Development: OHCI Layer Explained
·2368 words·12 mins
VxWorks USB USB Driver HCD OHCI UHCI Embedded Systems Real-Time OS Device Drivers