VxBus Driver Development: A Complete Guide for VxWorks Developers

Introduction

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

This guide will provide a comprehensive overview of VxBus driver development, covering all aspects from architecture to practical implementation. Whether you are a newcomer to VxWorks 7 or migrating from an older BSP style, this article will help you master VxBus and build efficient, maintainable drivers.

Why VxBus is So Important in VxWorks

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

To address this issue, Wind River introduced VxBusβ€”a standardized, scalable, and modular driver framework.

Advantages 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 – Code is clearer, reducing the complexity of the BSP.
  • β€’ 🌍 Standardized – Supports Device Tree, ensuring alignment with industry standards.

Overview of VxBus Architecture

The VxBus driver model draws from frameworks of modern operating systems like Linux. It revolves around a bus-driver-device hierarchy:

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

At startup, VxBus performs the following steps:

  1. 1. Reads the Device Tree.
  2. 2. Enumerates devices.
  3. 3. Matches the <span>compatible</span> string of each device with registered drivers.
  4. 4. Calls the driver’s <span>probe</span> and <span>attach</span> 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, you can do it like this:

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

πŸ“Œ Tip: Use vendor-standard <span>compatible</span> strings whenever possible. This ensures your driver is portable and consistent 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) {
    // Release 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. Handle Interrupts

Many devices rely on interrupts for efficient operation. In VxBus, you can request an IRQ interrupt 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 network cards or storage controllers, Direct Memory Access (DMA) is crucial.

VxBus provides APIs for DMA-safe memory allocation:

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

πŸ“Œ Always use <span>cacheDmaMalloc()</span> or similar APIs to ensure cache coherence.

6. Testing and Debugging

Common VxWorks commands for driver testing:

  • β€’ <span>-> devs</span> – List all registered devices.
  • β€’ <span>-> i2cShow</span> / <span>-> spiShow</span> – Display bus devices.
  • β€’ <span>-> d (0xADDRESS, 16)</span> – Dump memory/register values.

Debugging methods:

  • β€’ Use <span>printf</span> in early development stages.
  • β€’ Enable Wind River Workbench or WDB debugger to set breakpoints.
  • β€’ Add <span>errnoGet()</span> checks to diagnose errors.

Real Case: VxBus UART Driver

To make VxBus driver development more concrete, let’s create 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:

  • β€’ <span>reg</span> defines the base address and size of the UART.
  • β€’ <span>interrupts</span> specifies the IRQ interrupt line.
  • β€’ <span>clock-frequency</span> provides the input clock frequency for baud rate calculations.

2. Register Mapping

This example assumes the UART has the following registers:

Offset Register Description
0x00 UART_DR Data Register (read/write)
0x04 UART_SR Status Register
0x08 UART_CR Control Register
0x0C UART_BAUD Baud Rate Divisor 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 address
    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 support in 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 rate
    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 the BSP initialization:

IMPORT VXB_DRV uartDriver;

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

5. Testing the UART Driver

  1. 1. Boot the system and run:
-> devs

You should see the UART device listed.

  1. 2. 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. 3. When the character arrives, the ISR will print it to the console.

Advanced Topics in VxBus Driver Development

Device Tree Overlays

You can add new hardware support by applying DTS overlays without rebuilding the BSPβ€”ideal for prototyping.

Power Management

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

Multi-Instance Devices

Ensure your driver can gracefully handle multiple instances (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 – Follow VxBus and VxWorks kernel APIs.βœ… Document interfaces – Help future developers integrate your driver.βœ… Follow modularity – Separate hardware initialization, ISR, and data processing.βœ… Benchmark performance – Test latency and throughput under load.

Conclusion

For VxWorks engineers, developing drivers using VxBus is an essential skill. By understanding its architecture, leveraging the Device Tree, and following best practices, you can build portable, efficient, and future-proof drivers.

From basic driver skeletons to advanced implementations supporting DMA, mastering VxBus will open the door to building stable, high-performance embedded systems.

For more VxWorks learning resources, please click “Read Original”

Leave a Comment