跳过正文

VxWorks 中 PCI 设备驱动设计与编程指南

VxWorks PCI Device Driver
目录

概述
#

VxWorks 是 Wind River 开发的一款实时操作系统(RTOS),为硬件接口提供了强大的支持,包括外围组件互连(PCI)设备。在 VxWorks 中编写 PCI 设备驱动涉及与 PCI 总线交互、配置设备、管理中断并为应用程序提供访问接口。本指南将带您逐步完成设计和实现一个假设设备(例如网络或存储控制器)的 PCI 驱动程序的过程。

前提条件
#

  • VxWorks 开发环境:已安装 Workbench 或命令行工具。
  • PCI 设备信息:供应商 ID、设备 ID 和硬件文档(例如寄存器映射、中断行为)。
  • 硬件访问:带有 PCI 设备的测试目标系统。
  • VxWorks BSP:为您的硬件配置的板级支持包(BSP),并启用 PCI 支持。

逐步指南
#

  1. 了解 PCI 设备和 VxWorks 的 PCI 支持

PCI 设备通过供应商 ID 和设备 ID 在配置空间中标识。VxWorks 提供了 PCI 库(pciConfigLib)来扫描总线、读写配置寄存器并映射设备内存。查阅设备数据手册,了解:

  • 配置空间布局(例如基地址寄存器或 BAR)。
  • 中断分配。
  • 内存映射 I/O(MMIO)或端口 I/O 需求。

VxWorks 使用 BSP 中的 sysBusPci.c 文件初始化 PCI 总线。通过检查是否有 pciConfigLibInit() 调用,确保您的 BSP 支持 PCI。

  1. 初始化 PCI 驱动

首先定义一个结构来保存驱动状态,并编写初始化例程以查找和配置 PCI 设备。

#include <vxWorks.h>
#include <hwif/vxBusLib.h>
#include <hwif/buslib/pciConfigLib.h>

#define MY_VENDOR_ID  0x1234  /* Replace with your device's Vendor ID */
#define MY_DEVICE_ID  0x5678  /* Replace with your device's Device ID */

typedef struct {
    UINT32 bar0Addr;    /* Base Address Register 0 (example) */
    UINT32 irqLine;     /* Interrupt line */
    BOOL   initialized; /* Driver state */
} MyPciDevice;

MyPciDevice myDevice = {0};

/* Initialization function */
STATUS myPciDriverInit(void) {
    int busNo, devNo, funcNo;
    UINT32 devVendor;

    /* Scan PCI bus for the device */
    if (pciFindDevice(MY_DEVICE_ID, MY_VENDOR_ID, 0, &busNo, &devNo, &funcNo) == ERROR) {
        printf("Device not found!\n");
        return ERROR;
    }

    /* Read Vendor/Device ID to confirm */
    pciConfigInLong(busNo, devNo, funcNo, PCI_CFG_VENDOR_ID, &devVendor);
    printf("Found device: Vendor=0x%04X, Device=0x%04X\n", devVendor & 0xFFFF, devVendor >> 16);

    /* Get BAR0 (example memory region) */
    pciConfigInLong(busNo, devNo, funcNo, PCI_CFG_BASE_ADDRESS_0, &myDevice.bar0Addr);
    myDevice.bar0Addr &= PCI_BAR_MEM_ADDR_MASK; /* Mask off flags to get base address */

    /* Get IRQ line */
    pciConfigInByte(busNo, devNo, funcNo, PCI_CFG_INTERRUPT_LINE, (UINT8*)&myDevice.irqLine);

    myDevice.initialized = TRUE;
    return OK;
}
  1. 映射设备内存 PCI 设备通过 BAR 暴露内存区域。使用 pciDevMemMap() 或 VxWorks 的内存映射函数访问这些区域。
#include <vmLib.h>

void* myPciMapMemory(void) {
    void* mappedAddr = NULL;

    if (!myDevice.initialized) {
        printf("Device not initialized!\n");
        return NULL;
    }

    /* Map the BAR0 memory region (assuming 4KB size as an example) */
    mappedAddr = (void*)vxbPciDevMemMap(myDevice.bar0Addr, 0x1000, VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE);
    if (mappedAddr == NULL) {
        printf("Failed to map PCI memory!\n");
    } else {
        printf("Mapped BAR0 at 0x%08X\n", (UINT32)mappedAddr);
    }

    return mappedAddr;
}
  1. 处理中断

PCI 设备通常使用中断来发出事件信号。在 VxWorks 中,使用 intConnect() 连接中断服务例程(ISR)。

#include <intLib.h>

void myPciIsr(void* arg) {
    MyPciDevice* dev = (MyPciDevice*)arg;
    /* Example: Clear interrupt flag in device register (device-specific) */
    printf("Interrupt triggered on IRQ %d!\n", dev->irqLine);
}

STATUS myPciInterruptSetup(void) {
    if (!myDevice.initialized) return ERROR;

    /* Connect ISR to IRQ */
    if (intConnect(INUM_TO_IVEC(myDevice.irqLine), myPciIsr, (int)&myDevice) == ERROR) {
        printf("Failed to connect ISR!\n");
        return ERROR;
    }

    /* Enable interrupts (device-specific register write might be needed) */
    intEnable(myDevice.irqLine);
    return OK;
}
  1. 提供驱动接口

暴露函数以供应用程序与设备交互(例如读写数据)。

STATUS myPciWrite(UINT32 offset, UINT32 value, void* baseAddr) {
    if (!myDevice.initialized || baseAddr == NULL) return ERROR;
    *(volatile UINT32*)((UINT32)baseAddr + offset) = value;
    return OK;
}

UINT32 myPciRead(UINT32 offset, void* baseAddr) {
    if (!myDevice.initialized || baseAddr == NULL) return 0;
    return *(volatile UINT32*)((UINT32)baseAddr + offset);
}
  1. 测试与调试
  • 加载驱动:将代码编译为 VxWorks 内核模块(.out 文件),并在 VxWorks shell 中使用 ld < myDriver.out 加载。
  • 测试命令:添加 shell 命令(例如 myPciTest())以验证功能:
void myPciTest(void) {
    void* base = myPciMapMemory();
    if (base) {
        myPciWrite(0x10, 0xDEADBEEF, base); /* Example write */
        printf("Read back: 0x%08X\n", myPciRead(0x10, base));
    }
}
  • 调试:使用 printf()、logMsg() 或 Workbench 调试器跟踪执行。
  1. 优化与完善
  • 错误处理:为失败情况添加健壮检查(例如未映射的内存、设备未找到)。
  • 多任务支持:如果多个任务访问驱动,使用信号量(semMCreate())确保线程安全。
  • 电源管理:如 BSP 要求,实现挂起/恢复钩子。

关键注意事项
#

  • 字节序:PCI 设备可能使用小端格式;确保与 VxWorks 的字节序设置兼容。
  • 性能:尽量减少寄存器访问并优化中断处理以满足实时约束。
  • 设备特定逻辑:根据设备规格调整寄存器访问和中断处理。

示例集成
#

在 BSP 的 sysLib.c 或自定义初始化文件中调用:

void sysAppInit(void) {
    if (myPciDriverInit() == OK) {
        myPciInterruptSetup();
        myPciTest();
    }
}

资源
#

  • VxWorks 文档:参考 VxWorks 内核程序员指南和 API 参考中的 pciConfigLibvxbLib
  • 设备数据手册:寄存器级编程的关键资料。
  • Wind River 支持:用于 BSP 特定问题或高级调试。

本指南为 VxWorks 中的 PCI 驱动开发奠定了基础。请根据您的具体设备调整代码,例如替换占位值(供应商/设备 ID、寄存器偏移量等)为硬件文档中的实际值。

相关文章

VxWorks驱动及分布式编程
VxWorks Device Driver
VxWorks内核、设备驱动与BSP开发详解(第2版)
VxWorks Kernel Device Driver BSP
VxWorks下CAN设备驱动设计和应用编程
VxWorks CAN