4. Linux内核UART子系统架构

全志T113的UART串口DMA驱动开发,必须深入理解Linux内核UART子系统的分层架构。该架构采用经典的分层设计思想,将数据通路、协议处理、硬件控制与DMA传输解耦,为开发者提供了清晰的接口和扩展点。本章将逐层剖析tty核心层、线路规程层、串口驱动层以及DMA引擎接口层的职责、交互关系与关键数据结构。

4.1 四层架构总览

Linux UART子系统从上至下依次为:tty核心层、线路规程层、串口驱动层和DMA引擎接口层。用户空间通过标准tty设备节点与内核交互,数据流经各层完成协议解析、硬件控制与DMA传输。

架构核心思想:每一层只关注单一职责,通过标准接口与相邻层通信。这种设计使得更换底层硬件或调整上层协议时,无需修改其他层代码。
Linux UART子系统四层架构 Linux UART 子系统四层架构 用户空间 (应用程序 / dev/ttyS*) tty核心层 (tty_io.c / tty_port) 设备节点管理、数据缓冲、tty操作函数集 线路规程层 (n_tty.c / line discipline) 数据格式化、行编辑、协议转换 串口驱动层 (uart_ops / uart_port) 硬件寄存器操作、中断处理、波特率设置 DMA引擎接口层 (dmaengine / dma_slave_config) DMA通道管理、描述符提交、传输完成回调 全志T113 UART硬件 + DMA控制器 数据流向 用户 → 内核 → 硬件 控制流向 硬件 → 中断 → 用户
开发提示:在全志T113平台上,串口驱动层需要实现 uart_ops 结构体中的 start_tx()、stop_tx()、start_rx() 等回调,并在其中调用DMA引擎接口层的API来启动DMA传输。

4.2 tty核心层

tty核心层是UART子系统与用户空间的桥梁。它负责管理tty设备节点(如 /dev/ttyS0)、维护tty端口数据结构、处理打开/关闭/读写等系统调用,并将数据传递给线路规程层。

核心文件 关键数据结构 主要职责
drivers/tty/tty_io.c struct tty_struct 表示一个tty设备实例,包含读写缓冲区、线路规程指针、端口操作函数集
drivers/tty/tty_port.c struct tty_port 管理端口状态、引用计数、唤醒队列,支持异步操作
include/linux/tty.h struct tty_operations 定义 open()、close()、write()、ioctl() 等回调接口

在DMA模式下,tty核心层不直接参与DMA传输,但它提供的 tty_flip_buffer_push() 机制是数据从内核空间传递到用户空间的关键路径。串口驱动层通过DMA接收数据后,需要将数据填充到tty flip buffer中,然后调用该函数通知tty核心层。

4.3 线路规程层

线路规程层位于tty核心层与串口驱动层之间,负责对原始数据进行协议处理。默认的线路规程 n_tty 实现了规范模式(行编辑)和非规范模式(原始数据)两种工作方式。

关键点:在DMA传输场景中,线路规程层通常配置为非规范模式(termios.c_lflag & ~ICANON),以避免行编辑对DMA数据流造成不必要的延迟和缓冲。
线路规程层数据流 线路规程层数据流(非规范模式) tty核心层 n_tty 线路规程 串口驱动层 非规范模式:数据不经过行编辑,直接透传 DMA接收的数据以原始字节流形式传递给用户空间

线路规程层通过 struct tty_ldisc_ops 结构体注册,其中 receive_buf() 回调是串口驱动层将接收数据送入线路规程的入口。在DMA驱动中,此回调接收的数据已经是经过DMA搬运的完整数据块。

4.4 串口驱动层

串口驱动层是UART子系统的核心硬件抽象层。它通过 struct uart_driver 和 struct uart_port 结构体描述一个具体的UART控制器,并实现 struct uart_ops 中的回调函数来操作硬件寄存器。

数据结构 关键字段 说明
uart_driver driver_name, dev_name, nr 注册到tty核心的驱动实例,指定设备名称和端口数量
uart_port membase, irq, uartclk, ops 表示一个UART端口,包含寄存器基地址、中断号、时钟频率和操作函数集
uart_ops startup(), shutdown(), set_termios(), start_tx(), stop_tx() 硬件操作回调,驱动开发者必须实现这些函数
DMA模式关键点:在全志T113平台上,start_tx() 回调中需要配置DMA发送通道,提交发送描述符;start_rx()(通常通过 uart_ops 的 throttle()/unthrottle() 或自定义接口)中需要启动DMA接收循环。中断处理函数中应减少字符级操作,改为检查DMA完成状态。

4.5 DMA引擎接口层

DMA引擎接口层是Linux内核提供的通用DMA控制器抽象层,位于 drivers/dma/ 目录下。它屏蔽了不同DMA控制器的硬件差异,为串口驱动层提供统一的API接口。

串口驱动层通过以下步骤使用DMA引擎:

  1. 获取DMA通道:调用 dma_request_slave_channel() 或 dma_request_chan() 获取发送和接收DMA通道。
  2. 配置DMA参数:填充 struct dma_slave_config 结构体,设置源/目标地址、数据宽度、突发长度等。
  3. 准备描述符:调用 dmaengine_prep_slave_sg() 或 dmaengine_prep_dma_cyclic() 创建DMA传输描述符。
  4. 提交并启动:调用 dmaengine_submit() 提交描述符,然后调用 dma_async_issue_pending() 启动传输。
  5. 处理完成回调:在描述符的 callback 函数中处理传输完成事件,如唤醒等待队列或重新提交接收描述符。
DMA引擎接口层交互流程 DMA引擎接口层交互流程 串口驱动层 DMA引擎接口层 DMA控制器硬件 ①请求通道 ②配置寄存器 ③DMA传输 ④中断/回调 ⑤完成通知 关键数据结构 • dma_slave_config: 配置源/目标地址、数据宽度、突发长度 • dma_async_tx_descriptor: 描述符,包含回调函数指针 • dma_chan: DMA通道句柄,用于提交和取消传输
全志T113平台适配:全志T113的DMA控制器支持 dmaengine_prep_dma_cyclic() 循环传输模式,非常适合UART串口的连续接收。在接收描述符的回调中,只需更新环形缓冲区的写指针,无需重新提交描述符,大大降低了CPU开销。

4.6 四层协作数据流

以一次完整的DMA接收为例,数据在各层之间的流动如下:

步骤 操作层 动作描述
1 DMA控制器硬件 UART接收FIFO数据达到阈值,触发DMA请求,DMA控制器将数据搬运到内存缓冲区
2 DMA引擎接口层 DMA传输完成,调用描述符回调函数,通知串口驱动层
3 串口驱动层 在回调中调用 tty_insert_flip_string() 将数据填入tty flip buffer
4 线路规程层 n_tty_receive_buf() 处理数据(非规范模式下直接传递)
5 tty核心层 tty_flip_buffer_push() 将数据推送到用户空间读取队列
6 用户空间 应用程序通过 read() 系统调用获取数据
性能优势:在整个DMA接收流程中,CPU仅在步骤2(回调)和步骤3(数据搬运到flip buffer)参与,且数据搬运是批量进行的。相比传统中断模式每个字节触发一次中断,DMA模式将中断频率降低了数个数量级,显著提升了全志T113平台的CPU利用率。