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 驱动的基本流程在设备树中描述硬件信息。在驱动中编写platform_driver。设置驱动的of_match_table通过compatible匹配设备树。注册 platform 驱动。设备匹配成功后执行probe()。在probe()中获取 GPIO、寄存器等资源并完成设备初始化。编写文件操作接口实现用户空间与驱动之间的交互。如果使用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 驱动时可以将整个过程理解为三个方面设备树描述硬件信息解决硬件信息写死在驱动中的问题。Platform 框架实现设备与驱动分离通过匹配机制调用probe()。pinctrl 和 GPIO 子系统分别负责引脚配置和 GPIO 输入输出控制。在此基础上再结合 misc 或其他字符设备框架就可以完成从设备树解析、硬件初始化到用户空间访问的完整驱动流程。这种方式能够减少重复代码也更方便驱动移植和维护。