找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

3820

积分

1

好友

531

主题
发表于 13 小时前 | 查看: 3| 回复: 0

继续走读基于 Linux 6.6.123 内核的ttyserial串口驱动之serial_core抽象层。

学习驱动开发,走读驱动源码的一个核心内容是要理解这种驱动设计的意图,而不只是看代码怎么实现的。

serial_core.c 是 Linux内核 中串口驱动的核心框架代码,它抽象了串口(UART)驱动的通用逻辑,为具体硬件的 UART 驱动(如 8250、ttyAMA 等)提供统一的上层接口和通用功能实现。本文从核心功能、关键设计理念、核心数据结构、核心函数流程四个维度展开分析。

一 核心功能定位

serial_core.c 的核心目标是解耦串口通用逻辑与硬件专属逻辑:

  • 向上:对接 Linux TTY 子系统,提供符合 TTY 框架的操作接口(如 tty_operations 实现);
  • 向下:定义 uart_ops 硬件操作接口,让具体 UART 驱动只需实现硬件相关逻辑,无需关心 TTY 层的通用流程;
  • 中间层:实现串口通用功能(波特率计算、流控、中断调度、PM 管理、RS485 配置、状态管理等)。

二 核心数据结构

[1]. 核心结构体关系

TTY与UART数据结构层级关系图

[2]. 关键结构体解析

UART核心结构体功能表

三 核心功能模块解析

[1]. TTY 子系统对接

serial_core 实现了 TTY 子系统要求的 tty_operations 接口,核心函数包括:

  • uart_start / uart_stop:启动 / 停止串口发送(对接 TTY 层的发送控制);
  • uart_write:将用户数据写入串口发送缓冲区(环形缓冲区 circ_buf);
  • uart_write_room:返回发送缓冲区剩余空间;
  • uart_throttle / uart_unthrottle:流控(XON/XOFF/CTS/RTS)的节流 / 解除节流;
  • uart_flush_buffer:清空发送缓冲区;
  • uart_tiocmget / uart_tiocmset:获取/设置 Modem 控制线(DTR/RTS/CTS 等)。

关键逻辑:发送缓冲区管理

串口发送采用环形缓冲区(circ_buf) 设计:

  • 用户数据先写入 uart_state->xmit 环形缓冲区;
  • 硬件发送中断触发时,驱动从缓冲区取数据写入硬件 FIFO;
  • uart_write_wakeup:当缓冲区数据量低于阈值时,唤醒 TTY 层继续写数据;
  • __uart_start:核心发送启动逻辑,包含 PM 运行时管理(确保硬件处于激活状态)。

[2]. 波特率与参数配置

波特率计算

  • uart_get_baud_rate:解析 termios 中的波特率配置,处理特殊速率(如 38400 对应自定义速率),保底 9600;
  • uart_get_divisor:根据 UART 时钟(uartclk)和目标波特率,计算硬件分频系数(uartclk/(16*baud));
  • uart_update_timeout:根据帧大小(数据位 + 校验位 + 停止位)和波特率,计算帧超时时间(用于 FIFO 超时判断)。

串口参数设置

  • uart_change_line_settings:根据 termios 更新硬件参数(波特率、数据位、校验位、流控等),调用硬件 set_termios 接口;
  • uart_set_mctrl / uart_clear_mctrl:设置 / 清除 Modem 控制线(DTR/RTS),处理 RS485 模式下的控制线特殊逻辑。

[3]. 串口启停与生命周期管理

启动流程(uart_startup)

UART启动过程流程图

关闭流程(uart_shutdown)

  • 清除 TTY 错误标记,停止发送;
  • 关闭 DTR/RTS(若配置 HUPCL);
  • 释放发送缓冲区(避免锁死,缓冲区释放不持有端口锁);
  • 重置 PM 状态,清理运行时资源。

[4]. 流控(Flow Control)管理

支持软件流控(XON/XOFF) 和硬件流控(CTS/RTS):

  • uart_throttle:节流时,若开启硬件流控则清除 RTS,若开启软件流控则发送 XOFF;
  • uart_unthrottle:解除节流时,恢复 RTS 或发送 XON;
  • uart_send_xchar:发送高优先级的 XON/XOFF 字符(绕过普通发送缓冲区);
  • 硬件流控额外逻辑:检测 CTS 状态变化,通过 hw_stopped 标记控制发送启停。

[5]. RS485 模式支持

RS485 是串口的半双工总线模式,serial_core 提供通用配置框架:

  • uart_check_rs485_flags:校验 RS485 配置标志的合法性(驱动是否支持);
  • uart_sanitize_serial_rs485:sanitize 配置(如 RTS 延时上限、默认 RTS 模式);
  • uart_set_rs485_termination:通过 GPIO 控制 RS485 总线终端电阻;
  • uart_set_rs485_rx_during_tx:控制是否允许发送时接收(半双工 / 全双工);
  • 硬件驱动需实现 rs485_config 接口,完成具体的 RS485 硬件配置。

[6]. 中断与状态统计

  • uart_icount:统计串口各类中断事件(收 / 发字节、帧错误、溢出、Modem 状态变化等);
  • uart_get_icount:向用户态暴露中断统计数据;
  • uart_wait_modem_status:等待 Modem 状态(DCD/RI/DSR/CTS)变化,用于用户态同步等待;
  • uart_break_ctl:控制串口 Break 信号(硬件专属实现)。

[7]. 电源管理(PM)

集成 Linux 运行时 PM 框架,确保串口仅在活跃时耗电:

  • __uart_start:调用 pm_runtime_get 增加 PM 引用计数,激活硬件;
  • pm_runtime_put_autosuspend:使用自动挂起机制,闲置时自动进入低功耗;
  • uart_change_pm:切换串口 PM 状态(ON/SUSPEND)。

[8]. 并发与锁机制

串口是共享资源,serial_core 设计了多层锁保证并发安全:

串口驱动并发锁机制表

核心宏封装

  • uart_port_lock:加锁时自动增加引用计数,解锁时释放;
  • uart_port_unlock:解锁 + 引用计数减一,避免锁与引用计数不一致。

四 设计理念

[1]. 分层与抽象(Separation of Concerns)

  • 通用逻辑与硬件解耦:serial_core 实现 TTY 对接、缓冲区管理、波特率计算、流控等通用逻辑,具体硬件驱动只需实现 uart_ops 接口(如 start_tx / set_termios);
  • TTY子系统 适配:向上屏蔽串口特性,以标准 TTY 接口对接,符合 Linux 字符设备统一框架;
  • 硬件无关性:通过 uart_port 抽象硬件属性,支持不同总线(IO/MMIO)、不同架构的 UART。

[2]. 模块化与可扩展

  • 接口化设计uart_ops 定义硬件驱动的最小实现集,驱动可按需实现扩展接口(如 rs485_configverify_port);
  • RS485 扩展:通过 serial_rs485 结构体和 GPIO 控制,兼容不同硬件的 RS485 实现;
  • PM 扩展:集成 runtime PM,驱动无需关心 PM 框架细节,只需适配核心的 PM 调用。

[3]. 健壮性与容错

  • 边界检查:关键函数(如 uart_write_wakeup)通过 BUG_ON 检查非法状态(如端口已关闭);
  • 资源清理:缓冲区释放、PM 引用计数、锁的释放都做了容错处理(如 pm_runtime_put_noidle 处理 PM 错误);
  • 权限控制:修改串口硬件配置(如 IO 基址、中断)需 CAP_SYS_ADMIN 权限,避免普通用户篡改;
  • 降级处理:波特率配置非法时,自动降级到旧配置或 9600 保底。

[4]. 性能优化

  • 环形缓冲区:减少数据拷贝,提高发送效率;
  • 自旋锁 + 引用计数:中断上下文使用自旋锁(低延迟),进程上下文使用互斥锁,兼顾性能与安全;
  • runtime PM:仅在活跃时激活硬件,降低功耗;
  • 锁粒度优化port->lock 为端口级自旋锁,而非全局锁,减少并发冲突。

[5]. 兼容性

  • legacy 兼容:支持旧版 RS485 标志、自定义波特率(UPF_SPD_MASK)等历史特性;
  • TTY 标准兼容:严格遵循 TTY 子系统接口规范,保证用户态工具(如 stty)可正常使用;
  • 架构兼容:通过 uart_portiotype(IO/MMIO)、regshift 等字段,适配不同架构的硬件寻址方式。

五 总结

serial_core.c 是 Linux 串口驱动的“骨架”,它的设计核心是抽象通用逻辑、暴露硬件接口、对接上层框架:

  • 对上层(TTY):提供标准的字符设备接口,隐藏串口硬件细节;
  • 对下层(硬件驱动):封装通用流程(缓冲区、流控、PM、锁),只需实现硬件专属操作;
  • 对用户:保证串口操作的一致性(如 setserialstty 工具通用),同时支持 RS485 等扩展特性。

这种设计既符合 Linux 内核“复用、解耦、可扩展”的核心思想,也兼顾了串口驱动的高性能、高可靠性和硬件兼容性。

以上是关于 Linux 6.6.123 内核中 serial_core.c源码分析内容。




上一篇:Sam Altman算起「人肉成本」:AI训练2小时真比人类成长20年更环保?
下一篇:Windows 11 26H2 商业预览版发布:集成Microsoft 365 Copilot的Ask Copilot助力生产力升级
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-2-25 19:37 , Processed in 0.473202 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表