Skip to main content

VxBus Driver Development: A Complete Guide for VxWorks Developers

·1223 words·6 mins
VxWorks VxBus Device Driver Embedded Systems RTOS Driver Development
Table of Contents

Device driver development is at the heart of embedded systems. In VxWorks, the industry-leading real-time operating system (RTOS) from Wind River, hardware drivers are implemented using the VxBus framework.

This guide provides a comprehensive introduction to VxBus driver development, covering everything from architecture to real-world implementation. Whether you are new to VxWorks 7 or transitioning from older BSP styles, this article will help you master VxBus and build efficient, maintainable drivers.


Why VxBus Matters in VxWorks
#

In earlier versions of VxWorks, drivers were often hard-coded into the BSP (Board Support Package). This approach made maintenance difficult and limited portability when hardware changed.

To address this, Wind River introduced VxBus—a standardized, scalable, and modular driver framework.

Benefits of VxBus:

  • 🔌 Modular – Drivers are decoupled from the BSP.
  • Efficient – Device discovery and initialization are automated.
  • 🔄 Reusable – The same driver can support multiple platforms.
  • 🛠️ Maintainable – Cleaner code with reduced BSP complexity.
  • 🌍 Standardized – Device Tree support ensures industry alignment.

VxBus Architecture Overview
#

The VxBus driver model is inspired by modern OS frameworks such as Linux. It revolves around a bus–driver–device hierarchy:

  1. Bus – Represents an interconnect (e.g., PCI, I²C, SPI, USB).
  2. Device – A hardware instance connected to a bus, defined in the Device Tree (DTS).
  3. Driver – The software logic that manages the device.

At boot, VxBus:

  1. Reads the Device Tree.
  2. Enumerates devices.
  3. Matches each device’s compatible string to a registered driver.
  4. Calls the driver’s probe and attach functions.

Step-by-Step Guide to VxBus Driver Development
#

1. Define Your Device in the Device Tree
#

The Device Tree Source (DTS) is the cornerstone of hardware description. For example, to define an I²C GPIO expander:

i2c@0x10000000 {
    compatible = "nxp,pca9535";
    reg = <0x10000000 0x1000>;
    interrupts = <10 0>;  // IRQ line if applicable
};

📌 Tip: Use vendor-standard compatible strings whenever possible. This ensures your driver is portable and aligns with upstream conventions.


2. Implement the Driver Skeleton
#

A VxBus driver typically includes probe, attach, and detach methods.

LOCAL STATUS myDriverProbe(VXB_DEV_ID pDev) {
    // Check if this driver supports the device
    return OK;
}

LOCAL STATUS myDriverAttach(VXB_DEV_ID pDev) {
    // Initialize registers, memory, interrupts, etc.
    return OK;
}

LOCAL STATUS myDriverDetach(VXB_DEV_ID pDev) {
    // Free resources, disable interrupts
    return OK;
}

LOCAL VXB_DRV_METHOD myDriverMethods[] = {
    { VXB_DEVMETHOD_CALL(vxbDevProbe), myDriverProbe },
    { VXB_DEVMETHOD_CALL(vxbDevAttach), myDriverAttach },
    { VXB_DEVMETHOD_CALL(vxbDevDetach), myDriverDetach },
    VXB_DEVMETHOD_END
};

VXB_DRV myDriver = {
    { NULL },
    "myDriver",
    "Example VxBus Driver",
    VXB_BUSID_PLB,   // Bus type
    0,
    sizeof(VXB_DEV_ID),
    myDriverMethods,
    NULL,
    NULL,
    NULL
};

VXB_DRV_DEF(myDriver);

3. Register the Driver with VxBus
#

Add the driver during system initialization:

IMPORT VXB_DRV myDriver;

void sysHwInit(void) {
    vxbDevRegister(&myDriver);
}

4. Handling Interrupts
#

Many devices rely on interrupts for efficient operation. In VxBus, you can request an IRQ line like this:

LOCAL void myIsr(void * arg) {
    VXB_DEV_ID pDev = (VXB_DEV_ID) arg;
    // Handle interrupt
}

LOCAL STATUS myDriverAttach(VXB_DEV_ID pDev) {
    int irq = vxbIntConnect(pDev, 0, myIsr, pDev);
    if (irq != OK) {
        return ERROR;
    }
    vxbIntEnable(pDev, 0, myIsr, pDev);
    return OK;
}

5. DMA and Buffer Management
#

For high-performance devices such as network cards or storage controllers, Direct Memory Access (DMA) is essential.

VxBus provides APIs for DMA-safe memory allocation:

void * dmaBuf;
dmaBuf = cacheDmaMalloc(1024);
if (dmaBuf == NULL) {
    return ERROR;
}

📌 Always use cacheDmaMalloc() or similar APIs to ensure cache coherency.


6. Testing and Debugging
#

Useful VxWorks commands for driver testing:

  • -> devs – List all registered devices.
  • -> i2cShow / -> spiShow – Show bus devices.
  • -> d (0xADDRESS, 16) – Dump memory/register values.

For debugging:

  • Use printf during early development.
  • Enable Wind River Workbench or WDB debugger for breakpoints.
  • Add errnoGet() checks for error diagnosis.

Real-World Example: VxBus UART Driver
#

To make VxBus driver development more concrete, let’s walk through a simplified UART driver. This example assumes a memory-mapped UART device with basic transmit (TX) and receive (RX) registers.

1. Device Tree Entry
#

First, define the UART in the Device Tree:

uart0: serial@0x101f1000 {
    compatible = "simple,uart";
    reg = <0x101f1000 0x1000>;
    interrupts = <5 0>;
    clock-frequency = <48000000>;
};

Here:

  • reg defines the UART base address and size.
  • interrupts specifies the IRQ line.
  • clock-frequency provides the input clock rate for baud calculation.

2. Register Map
#

For this example, assume the UART has the following registers:

Offset Register Description
0x00 UART_DR Data register (R/W)
0x04 UART_SR Status register
0x08 UART_CR Control register
0x0C UART_BAUD Baud rate divider register

3. Driver Implementation
#

#define UART_DR      0x00
#define UART_SR      0x04
#define UART_CR      0x08
#define UART_BAUD    0x0C

#define UART_SR_TX_READY  (1 << 0)
#define UART_SR_RX_READY  (1 << 1)
#define UART_CR_TX_EN     (1 << 0)
#define UART_CR_RX_EN     (1 << 1)

typedef struct {
    VXB_DEV_ID  dev;
    void *      base;   // Mapped register base
    int         irq;
} UART_DEV;

LOCAL void uartIsr(void * arg) {
    UART_DEV * pDrv = (UART_DEV *) arg;
    UINT32 sr = vxbRead32(pDrv->dev, (UINT32 *) (pDrv->base + UART_SR));

    if (sr & UART_SR_RX_READY) {
        char c = vxbRead8(pDrv->dev, (UINT8 *) (pDrv->base + UART_DR));
        printf("UART RX: %c\n", c);
    }
}

LOCAL STATUS uartProbe(VXB_DEV_ID pDev) {
    return OK; // Always claim for this demo
}

LOCAL STATUS uartAttach(VXB_DEV_ID pDev) {
    UART_DEV * pDrv = (UART_DEV *) vxbMemAlloc(sizeof(UART_DEV));
    if (!pDrv) return ERROR;

    pDrv->dev  = pDev;
    pDrv->base = (void *) vxbRegMap(pDev, 0, 0);
    pDrv->irq  = vxbIntConnect(pDev, 0, uartIsr, pDrv);

    // Enable TX/RX
    vxbWrite32(pDev, (UINT32 *)(pDrv->base + UART_CR), UART_CR_TX_EN | UART_CR_RX_EN);

    // Configure baud
    vxbWrite32(pDev, (UINT32 *)(pDrv->base + UART_BAUD), 48000000 / 115200);

    vxbDevSoftcSet(pDev, pDrv);
    return OK;
}

LOCAL STATUS uartDetach(VXB_DEV_ID pDev) {
    UART_DEV * pDrv = (UART_DEV *) vxbDevSoftcGet(pDev);
    if (!pDrv) return ERROR;

    vxbIntDisconnect(pDev, pDrv->irq, uartIsr, pDrv);
    vxbMemFree(pDrv);
    return OK;
}

LOCAL VXB_DRV_METHOD uartMethods[] = {
    { VXB_DEVMETHOD_CALL(vxbDevProbe),  uartProbe },
    { VXB_DEVMETHOD_CALL(vxbDevAttach), uartAttach },
    { VXB_DEVMETHOD_CALL(vxbDevDetach), uartDetach },
    VXB_DEVMETHOD_END
};

VXB_DRV uartDriver = {
    { NULL },
    "uartDriver",
    "Simple UART VxBus Driver",
    VXB_BUSID_PLB,
    0,
    sizeof(UART_DEV),
    uartMethods,
    NULL,
    NULL,
    NULL
};

VXB_DRV_DEF(uartDriver);

4. Driver Registration
#

Add the driver to your BSP initialization:

IMPORT VXB_DRV uartDriver;

void sysHwInit(void) {
    vxbDevRegister(&uartDriver);
}

5. Testing the UART Driver
#

  1. Boot the system and run:
-> devs

You should see the UART device listed.

  1. Send data by writing to the TX register:
UART_DEV * pDrv = (UART_DEV *) vxbDevSoftcGet(<device>);
vxbWrite8(pDrv->dev, (UINT8 *)(pDrv->base + UART_DR), 'A');
  1. When characters arrive, the ISR prints them to the console.

Advanced Topics in VxBus Driver Development
#

Device Tree Overlays
#

Instead of rebuilding the BSP, you can add new hardware support by applying DTS overlays—ideal for prototyping.

Power Management
#

Modern VxBus drivers should support suspend/resume hooks for power-sensitive devices.

Multi-Instance Devices
#

Ensure your driver handles multiple instances gracefully (e.g., multiple I²C controllers).

Error Recovery
#

Implement retry logic for transient failures (e.g., bus errors, timeouts).


Best Practices for VxBus Drivers
#

Keep drivers generic – Avoid hardcoding board-specific details.
Use standard APIs – Stick to VxBus and VxWorks kernel APIs.
Document interfaces – Help future developers integrate your driver.
Follow modularity – Separate hardware init, ISR, and data handling.
Benchmark performance – Test latency and throughput under load.


Conclusion
#

Developing drivers with VxBus is a must-have skill for VxWorks engineers. By understanding its architecture, leveraging the Device Tree, and following best practices, you can build drivers that are portable, efficient, and future-proof.

From basic skeleton drivers to advanced DMA-enabled implementations, mastering VxBus opens the door to building stable, high-performance embedded systems.

Related

A Comparative Analysis of BSP Development: VxWorks vs. Linux
·831 words·4 mins
VxWorks Linux BSP Embedded Systems Device Drivers RTOS
The Ultimate VxWorks Programming Guide
·650 words·4 mins
VxWorks RTOS Embedded Systems RTP Device Drivers
Device Tree-Based Driver Development in VxWorks 7.0
·525 words·3 mins
VxWorks Device Tree Driver Development BSP RTOS GPIO