云计算百科
云计算领域专业知识百科平台

IMX6ULL 杂项设备 LED 驱动

从用户空间到 GPIO 寄存器:IMX6ULL 杂项设备 LED 驱动完整解析

这篇博客整理自我们之前逐行拆解的 LED 驱动代码。它实现的是:用户空间向 /dev/led_misc 写入 0 或 1,驱动通过 GPIO 寄存器控制 IMX6ULL 上 LED 的亮灭。代码没有硬编码寄存器地址,而是通过设备树节点和 of_iomap 完成地址映射。

一、为什么用 misc 设备?

Linux 设备分为字符设备、块设备和网络设备。LED 属于典型的字符设备:按字节流读写,不需要随机寻址。

传统字符设备需要自己申请主设备号、创建设备节点、管理 cdev。而 misc 设备是字符设备的简化版本,所有 misc 设备共享主设备号 10,只需指定次设备号和设备名,注册后内核会自动在 /dev 下生成设备节点。

对于 LED、蜂鸣器、按键这类功能简单的外设,misc 框架能省去大量模板代码。

二、设备树:硬件信息的“说明书”

驱动里最关键的转变是:不再硬编码寄存器地址。我们在设备树中定义一个 /myled0 节点,用 reg 属性描述四个寄存器:

/myled0 {
reg = <0x020E0068 0x04 /* 引脚复用控制寄存器 */
0x020E02F4 0x04 /* 引脚电气属性寄存器 */
0x0209C004 0x04 /* GPIO1_GDIR 方向寄存器 */
0x0209C000 0x04>; /* GPIO1_DR 数据寄存器 */
};

reg 里每一对 <地址 长度> 是一段。驱动通过 of_iomap(plednode, index) 按索引取出对应段,完成物理地址到内核虚拟地址的映射。

三、驱动初始化:注册设备 + 配置硬件

模块加载时,led_misc_init 依次完成:

  • 注册 misc 设备:misc_register(&led_misc) 生成 /dev/led_misc。
  • 查找设备树节点:of_find_node_by_path("/myled0") 定位硬件描述。
  • 映射四个寄存器:通过 of_iomap 分别映射复用控制、PAD 配置、方向、数据寄存器。
  • 初始化 GPIO:
    • 将引脚复用为 GPIO 模式;
    • 配置 PAD 电气属性;
    • 设置 GPIO1_IO03 为输出;
    • 默认输出高电平,LED 熄灭。
  • 四、led_write:用户空间到硬件的桥梁

    这是整个驱动的核心交互函数。它的原型是:

    static ssize_t led_write(struct file *fp, const char __user *puser, size_t n, loff_t *off)

    这些参数不是驱动自己传的,而是内核 VFS 层在用户调用 write() 时自动填充的:

    参数来源
    fp 内核根据文件描述符找到的 struct file
    puser 用户空间 write(fd, buf, count) 中的 buf
    n 用户空间 write 中的 count
    off 内核维护的文件读写偏移

    调用链如下:

    用户: write(fd, &val, sizeof(val))
    ↓
    glibc: 触发 write 系统调用
    ↓
    内核: sys_write → vfs_write → fops->write
    ↓
    驱动: led_write(fp, puser, n, off)

    函数内部通过 copy_from_user 从用户态拷贝数据,然后根据值操作 GPIO 数据寄存器的 bit3:

    • 写入 1:清除 bit3,输出低电平,点亮 LED;
    • 写入 0:置位 bit3,输出高电平,熄灭 LED。

    五、file_operations 与函数指针回调

    驱动通过 file_operations 把 led_write 挂到 .write 上:

    static struct file_operations fops = {
    .owner = THIS_MODULE,
    .write = led_write,
    };

    fops 是一组函数指针的集合,定义了设备对外暴露的操作接口。 用户空间调用 write() 时,VFS 沿着指针找到你的 led_write 并传入参数。这就是 Linux 驱动中回调函数 + 函数指针的核心设计模式。

    六、代码改进建议

    问题建议
    失败返回 -1 改为返回 -EFAULT
    成功返回 0 建议返回 n,表示实际写入字节数
    of_iomap 无检查 每次映射后判断返回值是否为 NULL
    退出未释放映射 led_misc_exit 中应对四个地址调用 iounmap
    节点引用未释放 用完后调用 of_node_put(plednode)

    七、测试方法

    insmod led_misc.ko
    ls /dev/led_misc
    echo -ne "\\x01" > /dev/led_misc # 点亮
    echo -ne "\\x00" > /dev/led_misc # 熄灭
    rmmod led_misc

    八、总结

    这套代码串联了 Linux 驱动开发的几个核心概念:

    • misc 框架简化字符设备注册;
    • 设备树实现硬件描述与驱动代码解耦;
    • of_iomap 完成物理地址到虚拟地址的安全映射;
    • file_operations 通过函数指针建立用户空间与驱动的交互通道;
    • copy_from_user 保证内核态与用户态之间的数据安全拷贝。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » IMX6ULL 杂项设备 LED 驱动
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!