嵌入式Linux设备驱动:角色、运行时洞察与开发指南
编者注:嵌入式Linux持续跻身嵌入式系统设计最热门操作系统行列。随着物联网爆发式增长,其跨IoT应用所有层的通用性不可或缺。精通嵌入式Linux让工程师能快速开发复杂、可靠系统。在《精通嵌入式Linux编程 第2版》中,Chris Simmonds以清晰实战例子引导读者驾驭这一强大OS。
本节选自第9章,作者解释内核设备驱动如何与硬件接口、开发者如何编写和使用这些驱动、为何此知识对嵌入式工程师重要。以下章节拆解关键概念:
- 理解其角色
- 运行时读取驱动状态
- 用户空间设备驱动
- 编写内核设备驱动
- 发现硬件配置
改编自《精通嵌入式Linux编程 第2版》,Chris Simmonds著。
第9章. 与设备驱动接口
内核设备驱动向系统其余部分暴露底层硬件。作为嵌入式系统开发者,您必须理解这些驱动如何融入整体架构、如何从用户空间应用访问它们。大多数系统包含常见外设驱动,允许您操控硬件——如GPIO引脚、LED、SPI、I²C——无需编写内核代码。但遇到自定义硬件时,您需编写或改编驱动。
本章涵盖:
- 设备驱动角色
- 字符设备
- 块设备
- 网络设备
- 运行时了解驱动
- 寻找正确设备驱动
- 用户空间设备驱动
- 编写内核设备驱动
- 发现硬件配置
设备驱动角色
第4章所述,内核封装系统无数硬件接口,以一致方式呈现给用户空间。设备驱动在内核层与下方物理硬件间调解。它们可控制真实设备(如UART、MMC)或代表虚拟设备(如/dev/null、ramdisk)。单个驱动可管理同类设备多实例。
内核驱动以全特权运行,访问处理器寄存器、处理中断、执行DMA。利用内核健壮同步和内存管理基础设施。但权力伴随风险:有Bug驱动可崩溃整个系统。因此驱动应保持简单,提供数据和操作而将策略决策留给用户空间。例如,响应插拔事件加载模块由用户空间守护进程udev处理,非内核。
Linux将驱动分三主类:
- 字符 – 无缓冲I/O、薄接口;自定义驱动默认选择。
- 块 – 面向大容量存储块I/O、大量缓冲;不适用他用。
- 网络 – 类块设备但处理收发包。
第四类涉及伪文件系统,通过文件组暴露驱动功能(如GPIO经/sys/class/gpio访问)。本章先深入探索前三类。
字符设备
字符设备在用户空间呈现为称为设备节点的特殊文件。每节点通过主设备号映射到驱动、通过次设备号标识特定接口。例如ARM Versatile PB上首个串口为/dev/ttyAMA0,主设备号204、次设备号64;第二口共享相同主设备号但用次设备号65。四端口完整列表如下:
# ls -l /dev/ttyAMA*
crw-rw---- 1 root root 204, 64 Jan 1 1970 /dev/ttyAMA0
crw-rw---- 1 root root 204, 65 Jan 1 1970 /dev/ttyAMA1
crw-rw---- 1 root root 204, 66 Jan 1 1970 /dev/ttyAMA2
crw-rw---- 1 root root 204, 67 Jan 1 1970 /dev/ttyAMA3
标准主/次设备号列于Documentation/devices.txt,自定义设备可用内核源码定义值。例如PL011 UART驱动声明:
#define SERIAL_AMBA_MAJOR 204
#define SERIAL_AMBA_MINOR 64
驱动支持多实例时,设备节点名通常追加实例号到基础名(如ttyAMA + 0‑3)。
设备节点可自动或手动创建:
- devtmpfs – 驱动注册接口,守护进程创建节点。
- udev / mdev – 用户空间守护进程从sysfs提取名创建节点。
- mknod – 系统构建时手动创建静态节点。
Linux 2.6起,主设备号12位(1–4095)、次设备号20位(0–1,048,575)。扩展范围支持海量设备。
嵌入式