3. QNX SPI 驱动架构:资源管理器模型与设备注册流程

QNX 的 SPI 驱动体系建立在 资源管理器 (Resource Manager) 框架之上。理解这一架构是进行参数配置与 DMA 协同调试的前提。本章将深入剖析 SPI 驱动的分层模型、spi-master 与 spi-slave 的注册流程,以及它们如何通过路径空间与用户态交互。

3.1 资源管理器模型在 SPI 驱动中的角色

在 QNX 中,所有设备驱动都以资源管理器的形式存在。SPI 驱动也不例外——它作为一个用户态或内核态的进程,向系统注册一个路径(如 /dev/spi0),并通过 POSIX 标准接口(open、close、read、write、devctl())与上层应用通信。

核心概念: SPI 资源管理器将硬件寄存器操作封装为消息处理函数,应用层无需关心底层 SPI 时序,只需通过文件描述符发起传输请求。
调试提示: 使用 ls -l /dev/spi* 可查看系统已注册的 SPI 设备节点。若节点缺失,说明驱动未成功注册或资源管理器未启动。
QNX SPI 资源管理器分层架构图 QNX SPI 资源管理器分层架构 应用层 (Application) open /dev/spi0 → ioctl / devctl → read / write 资源管理器框架 (Resource Manager Framework) io_open / io_read / io_write / io_devctl 消息分发 SPI 驱动层 (SPI Driver) spi_master_init / spi_slave_init / 寄存器配置 / 中断处理 硬件抽象层 (HAL / BSP) SPI 控制器寄存器映射 / GPIO 片选 / DMA 通道绑定 SPI 硬件控制器 (SoC SPI IP) 消息传递路径

3.2 spi-master 与 spi-slave 设备注册流程

SPI 驱动在资源管理器框架下,分为 spi-master(控制器端)和 spi-slave(从设备端)两个注册阶段。Master 负责管理 SPI 总线时序与片选,Slave 则代表挂载在总线上的具体外设。

3.2.1 spi-master 注册流程

  1. 分配并初始化 master 结构体:填充总线号、时钟频率范围、DMA 能力标志、片选极性等参数。
  2. 注册资源管理器路径:调用 spi_master_register() 或直接使用 resmgr_attach() 绑定 /dev/spiN 路径。
  3. 绑定消息处理函数:将 io_devctl 路由到 SPI 配置命令(如 SPI_SET_MODE、SPI_SET_SPEED)。
  4. 硬件初始化:配置控制器寄存器、时钟门控、中断向量、DMA 通道。
  5. 挂载到系统设备树:在 QNX 的 devb-* 或 io-sock 体系下注册,使上层可发现。
常见错误: 若 master 注册时未正确设置 max_speed_hz 或 mode_bits,后续 slave 配置可能因参数校验失败而无法 attach。

3.2.2 spi-slave 注册流程

  1. 创建 slave 实例:为每个 SPI 从设备分配 spi_slave_t 结构体,指定片选 GPIO、工作模式、位宽、最大时钟。
  2. 绑定到 master:调用 spi_slave_attach(),将 slave 挂载到已注册的 master 总线上。
  3. 注册从设备路径:通常以 /dev/spiN.M 格式创建节点(N=总线号,M=片选号)。
  4. 配置传输参数:通过 devctl() 下发 SPI_CS_CONFIG、SPI_XFER_MODE 等命令。
  5. 准备 DMA 缓冲区:若启用 DMA,需注册物理连续内存并绑定 DMA 通道。
SPI Master 与 Slave 注册时序图 SPI Master 与 Slave 注册流程时序 驱动初始化 SPI Master SPI Slave 1 分配并初始化 master 结构体 2 注册 /dev/spi0 资源管理器路径 3 配置 SPI 控制器寄存器与时钟 Master 就绪 4 spi_slave_attach() 绑定到 master 5 注册 /dev/spi0.0 从设备节点 Slave 就绪

3.3 关键数据结构与参数映射

在注册过程中,驱动开发者需要填充以下核心数据结构。这些结构体直接决定了 SPI 的传输能力与兼容性。

数据结构 关键字段 说明
spi_master_t bus_num, max_speed_hz, mode_bits, dma_support 定义总线编号、最高时钟、支持的模式位掩码、DMA 能力
spi_slave_t cs_gpio, mode, bits_per_word, speed_hz 片选 GPIO、SPI 模式 (0/1/2/3)、字长、通信速率
spi_ioc_transfer tx_buf, rx_buf, len, cs_change, delay_usecs 单次传输描述符,支持 tx/rx 分离、片选保持、帧间隔
spi_msg transfers, num_transfers, complete 消息队列,包含多个 transfer,支持异步完成回调
架构要点: spi-master 是总线资源的拥有者,spi-slave 是总线资源的消费者。一个 master 可以挂载多个 slave,每个 slave 通过独立的片选线区分。在资源管理器模型中,master 负责仲裁并发访问,slave 只负责配置自身参数。

3.4 路径空间与用户态交互

注册完成后,用户态程序通过标准文件 I/O 与 SPI 驱动交互。典型的路径命名规则如下:

  • /dev/spi0 — SPI 总线 0 的 master 控制节点(用于全局配置)
  • /dev/spi0.0 — 总线 0 上的从设备 0(片选 CS0)
  • /dev/spi0.1 — 总线 0 上的从设备 1(片选 CS1)

应用层通过 devctl(fd, SPI_CS_CONFIG, &config) 下发配置,通过 devctl(fd, SPI_XFER, &msg) 发起传输。驱动在 io_devctl 处理函数中解析命令,调用底层硬件接口完成操作。

调试技巧: 在驱动注册完成后,使用 pidin -p spi-driver -v 可查看资源管理器线程状态;使用 ioctl -v /dev/spi0.0 可测试设备节点是否响应。

3.5 注册流程中的常见问题与避坑

问题现象 可能原因 解决方案
/dev/spiN 节点未出现 资源管理器未 attach 成功,或路径名冲突 检查 resmgr_attach() 返回值,确认 /dev/shmem 权限
slave attach 返回 ENODEV master 未就绪或总线号不匹配 确认 master 的 bus_num 与 slave 指定的 master 一致
片选 GPIO 无输出波形 GPIO 未正确配置为 SPI 片选功能 检查 BSP 中 GPIO mux 设置,确认片选极性
DMA 传输卡死 DMA 通道未绑定或缓冲区未对齐 确保使用 mmap() 分配物理连续内存,检查 DMA 描述符链
架构约束: 在 QNX 中,spi-master 和 spi-slave 的注册顺序有严格依赖——必须先完成 master 注册并进入就绪状态,才能进行 slave 的 attach 操作。若在驱动初始化阶段并发注册,需使用同步原语(如 pthread_barrier)保证时序。