基于企业网站的网络营销方法室内设计师找图片的网站
2026/2/4 9:25:06 网站建设 项目流程
基于企业网站的网络营销方法,室内设计师找图片的网站,东营市建设网,供灯放生网站开发Linux 总线设备驱动模型学习笔记 1. 详细介绍总线设备驱动模型 1.1 产生背景#xff1a;为什么要引入它#xff1f; 在传统的字符设备驱动编写中#xff0c;开发者通常将“硬件资源#xff08;引脚、地址#xff09;”和“软件逻辑#xff08;操作流程#xff09;”混写…Linux 总线设备驱动模型学习笔记1. 详细介绍总线设备驱动模型1.1 产生背景为什么要引入它在传统的字符设备驱动编写中开发者通常将“硬件资源引脚、地址”和“软件逻辑操作流程”混写在一起。这种方式存在两个主要缺陷代码冗余如果有多个相同的硬件如 10 个相同的 LED 灯开发者可能需要拷贝多份几乎相同的驱动代码。可移植性差一旦硬件引脚发生变动必须修改并重新编译整个驱动程序。为了解决这些问题Linux 引入了分离Separation的思想即“总线-设备-驱动”模型。1.2 核心思想分离与分层总线设备驱动模型将一个驱动程序的核心任务拆分为两个独立的模块设备Device和驱动Driver并通过总线Bus将它们撮合在一起。① 设备 (platform_device)描述“有什么”职责负责提供硬件资源。内容包括硬件的基地址、中断号、GPIO 引脚编号等物理参数。特点当硬件更换引脚或地址时只需要修改设备端的代码而不需要动业务逻辑。② 驱动 (platform_driver)描述“怎么做”职责负责实现具体的业务逻辑。内容包括如何初始化硬件init、如何点亮 LEDctl、如何处理文件操作file_operations等。特点驱动程序通过内核提供的接口动态地从“设备端”获取资源从而实现“一套代码驱动多个板卡”的目标。③ 总线 (Bus)负责“撮合”职责管理设备和驱动的匹配。机制总线维护着两张链表设备链表和驱动链表。每当一个新的设备或驱动注册到总线上时总线就会自动触发“匹配Match”动作。1.3 平台总线 (Platform Bus)对于嵌入式系统中的 SoC 内部控制器如 I.MX6ULL 的 GPIO、UART、I2C 等它们通常直接挂在 CPU 内部总线上并没有类似 USB 或 PCI 这种可以被物理感知的实际总线。为此Linux 内核虚拟出了一种总线称为平台总线 (Platform Bus)。结构体内核中使用struct platform_device来代表硬件资源使用struct platform_driver来代表驱动逻辑。匹配标志默认情况下它们通过.name属性字符串进行匹配。1.4 模型的优势总结维度传统方式总线驱动模型代码结构硬件资源与逻辑耦合资源与逻辑完全分离可移植性差换硬件需改源码编译强只需更换设备端资源管理效率低容易造成驱动代码冗余高支持多实例匹配2. 匹配规则当我们在内核中注册一个platform_device或一个platform_driver时系统会自动调用平台总线的匹配函数通常是platform_match。匹配的目的是为了让驱动程序找到它能控制的硬件资源。内核遵循一套严格的优先级顺序进行匹配主要分为以下三个阶段2.1 第一优先级强制选择 (driver_override)这是优先级最高的一种方式允许开发者手动“指定”匹配关系。规则比较platform_device.driver_override和platform_driver.driver.name。原理如果platform_device结构体中的driver_override成员被设置了某个驱动的名字那么这个设备将无视其他匹配规则只与名字相符的驱动进行绑定。用途常用于调试或者在系统中有多个兼容驱动时强制指定使用某一个驱动。2.2 第二优先级ID 表匹配 (id_table)如果第一阶段没有匹配成功内核会检查驱动程序是否支持一系列设备。规则比较platform_device.name和platform_driver.id_table[i].name。原理platform_driver结构体中包含一个id_table数组类型为struct platform_device_id。这个数组就像是一个“白名单”列出了该驱动能够支持的所有设备名称。内核会遍历这个数组如果发现某个{.name xxx}与设备的name一致则匹配成功。优势一个驱动程序可以支持多个名字不同的设备。例如一个 LED 驱动可以同时支持名字为led_red和led_blue的两个设备。私有数据id_table还可以携带driver_data为不同的匹配项提供不同的私有配置数据。2.3 第三优先级名字匹配 (Name Match)这是最常用、也是最简单的匹配方式当id_table为空时使用。规则比较platform_device.name和platform_driver.driver.name。原理直接比对设备结构体中的名字和驱动结构体内嵌的device_driver中的名字。代码体现在设备端.name 100ask_led。在驱动端.driver { .name 100ask_led }。局限性这种方式要求名字必须完全一致且一个驱动通常只能对应一种名称的设备。匹配规则优先级表优先级比较对象 A比较对象 B说明1 (最高)device.driver_overridedriver.name强制覆盖规则2 (中等)device.namedriver.id_table[i].name支持多设备列表匹配3 (最低)device.namedriver.driver.name最基础的同名匹配2.4 特殊说明设备树匹配 (Device Tree)虽然你提到的规则主要针对物理代码定义的匹配但现代内核如 4.9.88最常用的其实是设备树匹配它的优先级通常非常高规则比较设备树节点的compatible属性与驱动的of_match_table列表。位置在上述三种规则之前或之中执行取决于具体的内核版本实现。这是 Linux 总线设备驱动模型学习笔记的第三部分重点分析内核是如何将设备和驱动联结在一起并触发业务代码的。3. 函数调用关系在总线模型中函数调用不再是线性的而是由内核总线框架根据“注册”行为触发的事件驱动。3.1 注册阶段的调用关系① 驱动注册 (platform_driver_register)当我们在led_drv.c的入口函数中调用platform_driver_register时内核会发生以下调用platform_driver_register(drv): 驱动入口函数发起请求。driver_register: 进入内核通用驱动层。bus_add_driver: 将驱动添加到platform_bus的驱动链表中。driver_attach: 尝试在总线上寻找匹配的设备。bus_for_each_dev: 遍历总线上的设备链表对每个设备调用__driver_attach。② 设备注册 (platform_device_register)在传统的设备端代码如led_dev.c中注册设备时platform_device_register(pdev): 向内核声明硬件资源。device_add: 将设备添加到platform_bus的设备链表中。bus_probe_device: 尝试为该新设备寻找匹配的驱动。device_initial_probe-__device_attach: 遍历驱动链表进行匹配。3.3 匹配与探测的核心Match 与 Probe无论是先加载驱动还是先加载设备最终都会汇聚到匹配 (Match)和探测 (Probe)这两个核心步骤。① 匹配 (Match) 过程内核调用总线定义的.match函数对于平台总线是platform_match该函数会按照上一章提到的优先级driver_override-id_table-name进行字符串比对。如果返回 1表示匹配成功进入下一步。如果返回 0表示不匹配继续查找下一个。② 探测 (Probe) 过程一旦 Match 成功内核会启动probe调用链really_probe: 内核确认匹配后准备调用驱动的具体实现。drv-probe(即imx6ull_led_probe):获取资源驱动程序通过platform_get_resource拿到设备定义的寄存器基地址。地址映射执行ioremap得到虚拟地址。注册接口调用register_chrdev并创建/dev下的设备节点。3.4 卸载阶段的调用关系 (Remove)当执行rmmod卸载驱动或设备被拔出时platform_driver_unregister: 发起注销请求。driver_unregister-__device_release_driver: 找到已绑定的设备。drv-remove(即imx6ull_led_remove):执行iounmap释放虚拟地址。销毁设备节点和类。注销字符设备号。3.5 逻辑关系总结表动作触发函数内核行为驱动层响应装载驱动platform_driver_register找设备名并匹配匹配成功则执行probe装载设备platform_device_register找驱动名并匹配匹配成功则执行probe匹配逻辑platform_match执行三级匹配规则(内核自动执行)卸载逻辑platform_driver_unregister解除绑定关系执行remove这是 Linux 总线设备驱动模型学习笔记的第四部分重点解析驱动开发中频繁调用的内核 API。4. 常用函数注册、反注册与资源获取在平台总线模型中开发者不再直接操作底层的链表而是通过内核提供的标准 API 来完成设备资源的声明和驱动逻辑的加载。4.1 设备端常用函数 (Platform Device)设备端代码如led_dev.c的主要任务是定义struct resource并注册设备。① 注册与反注册int platform_device_register(struct platform_device *pdev)作用将定义好的平台设备注册到内核中。行为将设备加入platform_bus的设备链表并触发总线的匹配过程。void platform_device_unregister(struct platform_device *pdev)作用注销设备。行为从链表中移除设备并触发已绑定驱动的remove函数。② 静态定义宏struct platform_device_register_simple(...)作用这是一个快捷函数用于一次性完成分配、设置和注册一个简单的平台设备。4.2 驱动端常用函数 (Platform Driver)驱动端代码如led_drv.c负责实现业务逻辑并提取资源。① 注册与反注册int platform_driver_register(struct platform_driver *drv)作用将驱动程序注册到内核。行为将驱动加入总线的驱动链表并搜索匹配的设备。void platform_driver_unregister(struct platform_driver *drv)作用注销驱动程序。module_platform_driver(drv)作用宏定义。它可以代替init和exit函数中冗长的注册/注销代码是一个一键注册宏。② 获取资源 (Resource Management)这是probe函数中最核心的操作struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)参数说明dev由内核传给probe的设备指针。type资源类型如IORESOURCE_MEM代表内存/寄存器地址IORESOURCE_IRQ代表中断号。num同类资源的索引比如有两组内存资源第 0 组和第 1 组。示例获取寄存器物理基地址。4.3 资源类型定义 (struct resource)资源结构体是设备向驱动传递物理参数的载体。structresource{resource_size_tstart;// 资源的起始位置如物理地址 0x020AC000resource_size_tend;// 资源的结束位置constchar*name;// 资源的名字unsignedlongflags;// 资源类型标志IORESOURCE_MEM, IORESOURCE_IO, IORESOURCE_IRQ};4.4 函数功能对照表函数分类函数名称典型使用位置核心目的设备注册platform_device_registerled_dev.c(init)声明硬件资源存在。驱动注册platform_driver_registerled_drv.c(init)声明操作逻辑就绪。资源获取platform_get_resourceprobe函数内部动态获取硬件物理地址。地址映射devm_ioremap_resourceprobe函数内部将获取的资源自动进行映射更高级的ioremap。驱动注销platform_driver_unregisterled_drv.c(exit)停止驱动并释放资源。这是 Linux 总线设备驱动模型学习笔记的最后一部分将设备端、驱动端以及内核总线的交互逻辑完整串联。5. 写程序的流程编写一个基于总线模型的驱动程序本质上是完成“资源定义”与“逻辑实现”的解耦。整个流程分为设备端Resource和驱动端Logic两个独立文件的编写。5.1 设备端流程分配 / 设置 / 注册platform_device目标告诉内核硬件的物理参数但不涉及任何控制逻辑。定义资源 (struct resource)指定寄存器的起始地址、长度及类型IORESOURCE_MEM。指定中断号等其他资源如果需要。设置platform_device结构体.name指定设备名称如100ask_led这是与驱动匹配的唯一“暗号”。.resource关联刚才定义的资源数组。.num_resources资源的个数。注册设备在模块入口函数中调用platform_device_register()。在模块出口函数中调用platform_device_unregister()。5.2 驱动端流程分配 / 设置 / 注册platform_driver目标编写通用的控制逻辑动态地根据拿到的资源进行操作。定义file_operations结构体实现标准的open,write等接口。实现probe函数核心逻辑点获取资源调用platform_get_resource()拿到设备端传来的物理地址。地址映射调用ioremap()将物理地址转为虚拟地址。注册字符设备调用register_chrdev()并创建类与设备节点。实现remove函数执行iounmap()释放地址映射。注销字符设备并销毁设备节点。设置platform_driver结构体.probe和.remove指向刚才实现的函数。.driver.name必须与设备端的.name完全一致才能触发匹配。注册驱动在模块入口函数调用platform_driver_register()。5.3 整体运行逻辑串联表步骤模块动作内核行为结果1设备端 (led_dev.ko)insmod将设备加入总线设备链表等待驱动匹配2驱动端 (led_drv.ko)insmod将驱动加入总线驱动链表触发匹配检查3总线系统匹配name发现name一致调用驱动的probe4驱动端 (probe)获取资源并映射注册字符设备驱动/dev/100ask_led0出现5用户空间open/write经过系统调用到达驱动层LED 物理状态改变6驱动端 (remove)rmmod释放资源注销设备系统恢复清洁5.4 为什么这样写更高效解耦如果你的 LED 从 GPIO5_3 换到了 GPIO3_3你只需要修改并重新编译设备端文件而处理逻辑复杂的驱动端 (led_drv.c) 一行代码都不用动。多实例如果你有两组 LED 硬件只需注册两个platform_device使用相同名字内核就会自动调用两次驱动的probe为你生成两个设备节点。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询