概述 #
VxWorks 是 Wind River 开发的一款实时操作系统(RTOS),为硬件接口提供了强大的支持,包括外围组件互连(PCI)设备。在 VxWorks 中编写 PCI 设备驱动涉及与 PCI 总线交互、配置设备、管理中断并为应用程序提供访问接口。本指南将带您逐步完成设计和实现一个假设设备(例如网络或存储控制器)的 PCI 驱动程序的过程。
前提条件 #
VxWorks 开发环境
:已安装 Workbench 或命令行工具。PCI 设备信息
:供应商 ID、设备 ID 和硬件文档(例如寄存器映射、中断行为)。硬件访问
:带有 PCI 设备的测试目标系统。VxWorks BSP
:为您的硬件配置的板级支持包(BSP),并启用 PCI 支持。
逐步指南 #
- 了解 PCI 设备和 VxWorks 的 PCI 支持
PCI 设备通过供应商 ID 和设备 ID 在配置空间中标识。VxWorks 提供了 PCI 库(pciConfigLib)来扫描总线、读写配置寄存器并映射设备内存。查阅设备数据手册,了解:
- 配置空间布局(例如基地址寄存器或 BAR)。
- 中断分配。
- 内存映射 I/O(MMIO)或端口 I/O 需求。
VxWorks 使用 BSP 中的 sysBusPci.c 文件初始化 PCI 总线。通过检查是否有 pciConfigLibInit() 调用,确保您的 BSP 支持 PCI。
- 初始化 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;
}
- 映射设备内存
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;
}
- 处理中断
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;
}
- 提供驱动接口
暴露函数以供应用程序与设备交互(例如读写数据)。
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);
}
- 测试与调试
- 加载驱动:将代码编译为 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 调试器跟踪执行。
- 优化与完善
- 错误处理:为失败情况添加健壮检查(例如未映射的内存、设备未找到)。
- 多任务支持:如果多个任务访问驱动,使用信号量(semMCreate())确保线程安全。
- 电源管理:如 BSP 要求,实现挂起/恢复钩子。
关键注意事项 #
- 字节序:PCI 设备可能使用小端格式;确保与 VxWorks 的字节序设置兼容。
- 性能:尽量减少寄存器访问并优化中断处理以满足实时约束。
- 设备特定逻辑:根据设备规格调整寄存器访问和中断处理。
示例集成 #
在 BSP 的 sysLib.c 或自定义初始化文件中调用:
void sysAppInit(void) {
if (myPciDriverInit() == OK) {
myPciInterruptSetup();
myPciTest();
}
}
资源 #
- VxWorks 文档:参考 VxWorks 内核程序员指南和 API 参考中的
pciConfigLib
和vxbLib
。 - 设备数据手册:寄存器级编程的关键资料。
- Wind River 支持:用于 BSP 特定问题或高级调试。
本指南为 VxWorks 中的 PCI 驱动开发奠定了基础。请根据您的具体设备调整代码,例如替换占位值(供应商/设备 ID、寄存器偏移量等)为硬件文档中的实际值。