4. VxWorks I/O系统架构

VxWorks 的 I/O 子系统采用经典的层次化设计,将应用、文件系统、设备驱动与硬件解耦。理解这一架构是编写正确、高效设备驱动的前提。本章将深入剖析 I/O 子系统的层次结构、标准接口以及驱动的注册与卸载流程。

4.1 I/O 子系统层次结构

VxWorks I/O 系统从上到下分为四个清晰的层次,每一层只与相邻层交互,这种设计极大地提高了代码的可移植性和复用性。

VxWorks I/O 子系统层次结构 应用层 (Application Layer) 任务调用 open() / read() / write() / ioctl() 文件系统层 (File System Layer) dosFs / rawFs / tapeFs / socketFs / pseudoFs 驱动层 (Driver Layer) 设备驱动 xxxDrv() / 驱动函数表 (DRV_ENTRY) 硬件层 (Hardware Layer) 物理设备 / 寄存器 / 中断控制器 / DMA 引擎 数据流:应用 → 文件系统 → 驱动 → 硬件;控制流双向传递
架构要点: 应用层通过标准 I/O 接口与文件系统层交互,文件系统层将逻辑操作(如按文件名读写)转换为对设备驱动层的调用。驱动层直接操作硬件寄存器,完成实际的数据传输。这种分层使得更换文件系统或硬件设备时,只需修改对应层,其他层无需改动。

4.2 标准 I/O 接口

VxWorks 为所有设备提供了统一的 I/O 接口,包括 open()、close()、read()、write() 和 ioctl()。这些接口是应用开发者与设备驱动之间的契约。

接口 函数原型 功能描述 典型返回值
open int open(const char *name, int flags, int mode) 打开设备文件,建立文件描述符与设备的关联 文件描述符 (≥0) 或 ERROR (-1)
close int close(int fd) 关闭文件描述符,释放设备资源 OK (0) 或 ERROR (-1)
read int read(int fd, char *buffer, size_t maxBytes) 从设备读取数据到用户缓冲区 实际读取字节数 或 ERROR (-1)
write int write(int fd, char *buffer, size_t nBytes) 将用户缓冲区数据写入设备 实际写入字节数 或 ERROR (-1)
ioctl int ioctl(int fd, int command, ...) 执行设备特定的控制命令(如设置波特率、获取状态) 命令相关值 或 ERROR (-1)
关键理解: ioctl() 是设备驱动的“万能接口”,通过不同的 command 参数实现标准接口无法覆盖的特定功能。例如,串口驱动通过 ioctl() 设置波特率、数据位、停止位等参数。

4.3 设备驱动注册与卸载流程

在 VxWorks 中,设备驱动必须经过注册才能被 I/O 系统识别和使用。注册流程涉及驱动函数表的填充、设备描述符的创建以及文件描述符的关联。卸载流程则负责反向清理,释放系统资源。

4.3.1 驱动注册流程

一个典型的设备驱动注册包含以下步骤:

  1. 定义驱动函数表: 实现 xxxOpen()、xxxClose()、xxxRead()、xxxWrite()、xxxIoctl() 等函数,并填充到 DRV_ENTRY 结构体中。
  2. 调用 iosDrvInstall(): 将驱动函数表注册到 I/O 系统,返回一个驱动编号(driver number)。
  3. 创建设备: 调用 iosDevAdd() 将设备添加到 I/O 系统的设备链表中,关联驱动编号和设备名称。
  4. 初始化硬件: 在驱动初始化函数中完成硬件寄存器配置、中断注册、DMA 设置等。
设备驱动注册流程 步骤1 实现驱动函数 步骤2 iosDrvInstall() 步骤3 iosDevAdd() 步骤4 硬件初始化 注册流程详解 • iosDrvInstall() 返回驱动编号 (driver number),用于后续设备关联。 • iosDevAdd() 将设备名称(如 "/tyCo/0")与驱动编号绑定,添加到设备链表。 • 硬件初始化通常在 xxxDrv() 函数中完成,包括中断向量注册和寄存器配置。 典型注册代码框架 __COURSEGEN_PROTECTED_BLOCK_0__

4.3.2 驱动卸载流程

驱动卸载是注册的逆过程,用于在系统运行时动态移除不再需要的设备驱动,释放内存和硬件资源。

  • 关闭所有打开的文件描述符: 确保没有任务正在使用该设备。
  • 调用 iosDevDelete(): 从 I/O 系统的设备链表中移除设备节点。
  • 调用 iosDrvRemove(): 从 I/O 系统中移除驱动函数表,释放驱动编号。
  • 硬件去初始化: 禁用中断、释放 DMA 通道、恢复寄存器默认状态。
卸载注意事项: 驱动卸载必须在确保所有引用该设备的任务已经关闭文件描述符后进行。如果设备正在被使用,卸载操作可能导致系统崩溃或资源泄漏。建议在卸载前检查设备引用计数。

4.4 驱动函数表与设备描述符

驱动函数表 DRV_ENTRY 是驱动层与 I/O 系统之间的核心数据结构。每个驱动在注册时提供一组函数指针,I/O 系统在应用调用标准接口时通过驱动编号索引到对应的函数。

数据结构 成员/字段 说明
DRV_ENTRY int (*open)(...)
int (*close)(...)
int (*read)(...)
int (*write)(...)
int (*ioctl)(...)
驱动函数表,包含 5 个标准接口的函数指针。由 iosDrvInstall() 注册。
DEV_HDR DL_NODE node
short drvNum
char name[DEV_NAME_LEN]
设备描述符头部,包含设备链表节点、关联的驱动编号和设备名称。
设备私有结构 自定义字段(如寄存器基址、缓冲区指针、状态标志) 每个设备特有的数据,通常紧跟在 DEV_HDR 之后,通过 DEV_HDR 指针强制转换访问。
设计模式: 设备私有结构是驱动开发中的关键技巧。通过在 DEV_HDR 后附加自定义数据,驱动可以在 open() 或 ioctl() 中快速访问设备上下文,避免使用全局变量。例如,串口驱动可以在私有结构中保存波特率、FIFO 阈值和中断回调函数指针。

4.5 本章小结

VxWorks I/O 系统架构通过四层分离实现了高度的模块化和可移植性。标准 I/O 接口为应用提供了统一的设备访问方式,而驱动注册与卸载流程则保证了设备生命周期管理的规范性。掌握这些基础概念,是后续编写具体设备驱动(如串口、网络、存储驱动)的必备前提。