Linux驱动学习(1)

Linux 驱动学习:基于 Platform 架构编写驱动

之前编写 LED 字符设备驱动时,需要自己申请设备号、创建设备节点,并且将寄存器地址直接写在驱动代码中。这种方式虽然可以实现功能,但是不方便管理和移植。

为了让驱动结构更加清晰,Linux 引入了 platform 总线模型,并结合设备树、pinctrl 子系统和 gpio 子系统,实现设备信息与驱动代码分离。

一、混杂设备驱动(misc)

之前编写字符设备驱动时,需要通过 alloc_chrdev_region() 申请设备号,再通过 cdev 注册字符设备。

实际上,Linux 已经为一些通用设备预留了主设备号。例如,混杂设备(misc)使用的主设备号为 10,蜂鸣器、LED 等简单设备可以根据实际情况使用混杂设备框架。

使用 misc 框架可以省去部分字符设备注册流程,主要通过以下结构体和函数完成注册:

  • struct miscdevice:描述混杂设备。

  • misc_register():注册混杂设备。

  • misc_deregister():注销混杂设备。

不过,misc 只是简化字符设备注册的一种方式,并不是所有 LED、蜂鸣器都必须使用 misc 驱动。

二、设备树驱动

之前编写驱动时,外设的寄存器地址、GPIO 编号等信息可能直接写在驱动代码中。如果更换开发板或者引脚,这些代码就需要修改,不利于驱动移植。

因此,引入了设备树(Device Tree)。

设备树主要负责描述硬件信息,例如:

  • 外设使用的寄存器地址。

  • GPIO 引脚信息。

  • 中断信息。

  • 时钟、电源等硬件资源。

驱动代码则负责获取这些信息,并实现设备的初始化和操作方法。

以 IMX6ULL 为例,设备树源码通常位于:

arch/arm/boot/dts/imx6ull-alientek-emmc.dts

修改设备树后,需要重新编译对应的 DTB 文件,并确保开发板实际加载了更新后的设备树。

需要注意,设备树并不是自动让驱动适配所有外设。驱动还需要通过设备树匹配相应的设备,并正确解析其中的硬件资源。

三、Platform 驱动框架

Platform 是 Linux 中用于管理片上外设的一种总线模型,常用于 GPIO 控制器、串口、LED 等 SoC 内部设备。

它的核心是将设备和驱动分开管理。

1. 设备与驱动分离

  • 设备端(platform_device):描述设备的硬件资源。

  • 驱动端(platform_driver):实现设备的操作方法。

在使用设备树的情况下,设备信息通常定义在 DTS 文件中,由内核将设备树节点转换为相应的 platform 设备。

驱动通过 compatible 属性与设备树节点进行匹配。匹配成功后,内核调用驱动中的 probe() 函数,完成设备初始化。

当设备被移除时,内核可以调用 remove() 函数执行清理工作。

常用成员和函数:

  • struct platform_driver:定义 platform 驱动。

  • probe():设备匹配成功后执行。

  • remove():设备移除时执行。

  • platform_driver_register():注册 platform 驱动。

  • platform_driver_unregister():注销 platform 驱动。

2. Platform 驱动的基本流程

  1. 在设备树中描述硬件信息。

  2. 在驱动中编写 platform_driver。

  3. 设置驱动的 of_match_table,通过 compatible 匹配设备树。

  4. 注册 platform 驱动。

  5. 设备匹配成功后,执行 probe()。

  6. 在 probe() 中获取 GPIO、寄存器等资源,并完成设备初始化。

  7. 编写文件操作接口,实现用户空间与驱动之间的交互。

如果使用 misc 框架,还可以在 probe() 中注册 misc 设备,在 remove() 中注销。

四、驱动分层思想

Linux 驱动开发不仅强调设备与驱动分离,也强调分层设计。

对于 GPIO 控制的 LED,可以使用 pinctrl 和 GPIO 两个子系统,避免直接操作底层寄存器。

1. Pinctrl 子系统

pinctrl 主要负责引脚复用和电气属性配置,例如:

  • 设置引脚的复用功能。

  • 配置上下拉。

  • 设置驱动能力等电气属性。

以 IMX6ULL 为例,在:

arch/arm/boot/dts/imx6ull-alientek-emmc.dts

文件的 iomuxc 节点中,可以添加引脚配置方案。

例如,下面是一个简化的 GPIO 引脚配置示例,具体引脚复用值需要根据实际硬件和芯片手册确定:

复制代码
&iomuxc {
    pinctrl_led: ledgrp {
        fsl,pins = <
            /* 在这里填写实际的引脚复用配置 */
        >;
    };
};

这里的 pinctrl_led 是引脚配置节点的标签,后续可以通过 pinctrl-0 引用它。

2. GPIO 子系统

GPIO 子系统主要负责 GPIO 的输入输出控制。

例如:

  • 将 GPIO 配置为输入或输出。

  • 读取 GPIO 输入电平。

  • 设置 GPIO 输出高低电平。

在设备树中,可以将引脚配置与 GPIO 设备信息关联起来:

复制代码
led {
    compatible = "alientek,myled";
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_led>;

    /* 根据实际硬件填写 GPIO 控制器和引脚编号 */
    led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;

    status = "okay";
};

以上代码仅为结构示例,GPIO 控制器、引脚编号以及宏的定义必须根据实际硬件进行调整。使用 GPIO_ACTIVE_LOW 表示该 GPIO 采用低电平有效的逻辑。

驱动中可以通过 GPIO 描述符接口获取并控制 GPIO,例如 devm_gpiod_get() 和 gpiod_set_value_cansleep()。

3. Pinctrl 和 GPIO 的区别

简单理解:

  • pinctrl:负责配置引脚应该使用什么功能,以及相关电气属性。

  • GPIO:负责控制引脚的输入输出状态。

例如,LED 所连接的引脚需要先配置为 GPIO 功能,再由 GPIO 子系统控制输出高低电平。

在使用设备树和标准 GPIO 接口时,通常不需要在驱动中直接操作 IOMUXC、GPIO 的物理寄存器地址。

五、总结

学习 Linux 驱动时,可以将整个过程理解为三个方面:

  1. 设备树:描述硬件信息,解决硬件信息写死在驱动中的问题。

  2. Platform 框架 :实现设备与驱动分离,通过匹配机制调用 probe()。

  3. pinctrl 和 GPIO 子系统:分别负责引脚配置和 GPIO 输入输出控制。

在此基础上,再结合 misc 或其他字符设备框架,就可以完成从设备树解析、硬件初始化到用户空间访问的完整驱动流程。

这种方式能够减少重复代码,也更方便驱动移植和维护。

相关推荐
HelloWorld工程师1 小时前
不改动任何历史配置,也能让 SSL 证书自动续期
linux·服务器·网络协议·https·ssl
啦啦啦啦啦zzzz1 小时前
分布式存储----缓存组
linux·服务器·c++·分布式·缓存
余额瞒着我当琳1 小时前
C++算法学习篇5--二分查找法
c++·学习·算法
华允物联-HUAIOT1 小时前
串口服务器支持 WiFi 吗?有线与无线串口联网科普
运维·服务器·单片机
水饺编程1 小时前
第2章:Linux 基础,whoami 命令
linux·c语言·汇编
北京靠谱的GEO优化机构1 小时前
为什么优质百科词条需要修改更新?常态化运维实操指南
运维
鱼很腾apoc2 小时前
【MySQL】 第2期 查询与函数系统详解:从基础 CRUD 到复合多表查询
linux·数据库·学习·mysql·dml
IMPYLH2 小时前
Linux 运维基础:从入门到实战的完整指南
linux·运维·php
幼儿园蛋小黄2 小时前
嵌入式学习总结:从 I2C 中断驱动到 DHT11 温湿度采集实战
单片机·嵌入式硬件·学习