1. Linux 驱动开发基础:ioctl 与 Platform 总线
在嵌入式 Linux 开发中,设备驱动是连接硬件和操作系统的关键桥梁。本文将深入探讨两个核心概念:ioctl 系统调用和 Platform 总线驱动模型,并通过一个完整的 LED 驱动实现案例,展示如何将它们结合起来开发高效可靠的设备驱动。
1.1 ioctl 系统调用详解
ioctl(Input/Output Control)是 Linux 系统提供的一个强大工具,它允许用户空间程序与设备驱动进行定制化的交互。与标准的 read/write 操作不同,ioctl 提供了更灵活的控制方式,特别适合那些需要复杂控制逻辑的设备。
ioctl 的核心在于其命令(cmd)参数的设计。这个 32 位整数被精心划分为多个字段,每个字段都有特定的含义:
code复制| 31...30 | 29...16 | 15...8 | 7...0 |
|---------|---------|--------|-------|
| dir | size | nr | type |
-
type(8位):设备类型标识符,通常用一个 ASCII 字符表示,用于区分不同类别的设备。例如,'L' 可以表示 LED 设备,'K' 表示键盘设备等。
-
nr(8位):命令编号,在特定设备类型下的具体操作编号。例如,对于 LED 设备,0x01 可能表示打开,0x02 表示关闭。
-
dir(2位):数据流向标志:
- _IOC_NONE(00):无数据传输
- _IOC_READ(01):从设备读取数据
- _IOC_WRITE(10):向设备写入数据
- _IOC_READ|_IOC_WRITE(11):双向数据传输
-
size(14位):参数数据的大小(以字节为单位),确保内核和用户空间传递的数据大小一致。
内核提供了一系列宏来简化命令定义:
c复制#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
实际开发中,建议为每个设备定义一个唯一的 magic number(类型标识符),并将所有命令集中在一个头文件中,方便驱动和应用层共享。
1.2 Platform 总线驱动模型解析
Platform 总线是 Linux 内核为那些没有物理总线的嵌入式设备(如 GPIO、ADC、LED 等)设计的一种虚拟总线机制。它的核心思想是将设备描述(硬件资源)与驱动实现(控制逻辑)分离,提高代码的可重用性和可维护性。
Platform 模型包含三个关键组件:
-
platform_device:描述硬件设备及其资源(如寄存器地址、中断号等)。在较新的内核中,通常通过设备树(Device Tree)来定义,但在旧版本内核中仍需要手动注册。
-
platform_driver:实现设备的驱动逻辑,包括初始化、资源分配、操作接口等。驱动通过名称或兼容性列表与设备匹配。
-
platform_bus_type:内核提供的虚拟总线类型,负责设备和驱动的匹配工作。
Platform 总线的工作流程如下:
- 内核启动时注册 platform 总线
- 设备(platform_device)向总线注册
- 驱动(platform_driver)向总线注册
- 总线核心根据名称或兼容性匹配设备和驱动
- 匹配成功后调用驱动的 probe 函数进行初始化
这种分离的设计使得:
- 同一驱动可以支持多个硬件变体
- 硬件变更不需要修改驱动代码
- 资源管理更加规范和安全
2. LED 驱动实战:从设备到应用
2.1 设备端实现详解
在 Platform 模型中,设备端主要负责描述硬件资源。对于我们的 LED 示例,关键资源是控制 LED 的寄存器地址。以下是完整的设备端实现:
c复制#include <linux/platform_device.h>
#include <linux/io.h>
#define DEV_NAME "led" // 必须与驱动名称一致
/* LED 控制寄存器物理地址 */
#define IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 0x020E0068
#define IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 0x020E02F4
#define GPIO1_GDIR 0x0209C004
#define GPIO1_DR 0x0209C000
/* 资源释放函数 */
static void release(struct device *dev) {
printk(KERN_INFO "led device released\n");
}
/* 定义硬件资源数组 */
static struct resource res[] = {
{
.start = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03,
.end = IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 + 3,
.name = "iomuxc_mux",
.flags = IORESOURCE_MEM,
},
{
.start = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03,
.end = IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO03 + 3,
.name = "iomuxc_pad",
.flags = IORESOURCE_MEM,
},
{
.start = GPIO1_GDIR,
.end = GPIO1_GDIR + 3,
