设备驱动模型:总线-设备-驱动

news2024/12/26 18:17:15

1 设备驱动模型简介

参考 以下内容:

  1. Linux 笔记:
    https://xuesong.blog.csdn.net/article/details/109522945?spm=1001.2014.3001.5502
  2. 正点原子-左盟主 驱动开发
  3. 网络资料:https://www.cnblogs.com/lizhuming/category/1859545.html

1.1 概念

  • 开发过程中,一般驱动已经由半导体厂家编写好了,而设备驱动一般也由设备器件的厂家编写好了,我们只需要提供设备信息即可,比如 I2C设备的话提供设备连接到了哪个 I2C 接口上,I2C 的速度是多少等等。
  • 相当于将设备信息从设备驱动中剥离开来,驱动使用标准方法去获取到设备信息(比如从设备树中获取到设备信息),然后根据获取到的设备信息来初始化设备。这样就相当于驱动只负责驱动,设备只负责设备,想办法将两者进行匹配即可。这个就是 Linux中的总线(bus)、驱动(driver)和设备(device)模型,也就是常说的驱动分离。总线就是驱动和设备信息的月老,负责给两者牵线搭桥,如下图所示;

在这里插入图片描述

1.2 总线-设备-驱动

总线(bus):负责管理挂载对应总线的设备以及驱动;
设备(device):挂载在某个总线的物理设备;
驱动(driver):与特定设备相关的软件,负责初始化该设备以及提供一些操作该设备的操作方式;
以下只说 总线-设备-驱动 模式下的操作

  1. 总线管理着两个链表:设备链表 和 驱动链表。
  2. 当我们向内核注册一个驱动时,便插入到总线的驱动链表。
  3. 当我们向内核注册一个设备时,便插入到总线的设备链表。
  4. 在插入的同时,总线会执行一个 bus_type 结构体中的 match 方法对新插入的 设备/驱动进行匹配。(例如以名字的方式匹配。方式有很多总,下面再详细分析。)
  5. 匹配成功后,会调用 驱动 device_driver 结构体中的 probe 方法。(通常在 probe中获取设备资源。具体有开发人员决定。)
  6. 在移除设备或驱动时,会调用 device_driver 结构体中的 remove 方法;

2 总线

2.1 介绍:

总线是连接处理器和设备之间的桥梁
代表着同类设备需要共同遵循的工作时序。
总线驱动:

负责实现总线行为,管理两个链表。

struct bus_type {
    const char              *name;
    const struct attribute_group **bus_groups;
    const struct attribute_group **dev_groups;
    const struct attribute_group **drv_groups;

    int (*match)(struct device *dev, struct device_driver *drv);
    int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
    int (*probe)(struct device *dev);
    int (*remove)(struct device *dev);

    int (*suspend)(struct device *dev, pm_message_t state);
    int (*resume)(struct device *dev);

    const struct dev_pm_ops *pm;

    struct subsys_private *p;
};
  • name:指定总线的名称,当新注册一种总线类型时,会在 /sys/bus 目录创建一个新的目录,目录名就是该参数的值;
  • bus_groups、dev_groups、drv_groups:分别表示 总线、设备、驱动的属性。
    通常会在对应的 /sys 目录下在以文件的形式存在,对于驱动而言,在目录 /sys/bus//driver/ 存放了驱动的默认属性;设备则在目录 /sys/bus//devices/ 中。这些文件一般是可读写的,用户可以通过读写操作来获取和设置这些 attribute 的值。
  • match:当向总线注册一个新的设备或者是新的驱动时,会调用该回调函数。该设备主要负责匹配工作。
  • uevent:总线上的设备发生添加、移除或者其它动作时,就会调用该函数,来通知驱动做出相应的对策。
  • probe:当总线将设备以及驱动相匹配之后,执行该回调函数,最终会调用驱动提供的probe 函数。
  • remove:当设备从总线移除时,调用该回调函数。
  • suspend、resume:电源管理的相关函数,当总线进入睡眠模式时,会调用suspend回调函数;而resume回调函数则是在唤醒总线的状态下执行。
  • pm:电源管理的结构体,存放了一系列跟总线电源管理有关的函数,与 device_driver 结构体中的 pm_ops 有关。
  • p:该结构体用于存放特定的私有数据,其成员 klist_devices 和 klist_drivers 记录了挂载在该总线的设备和驱动。

match 函数,此函数很重要,单词 match 的意思就是“匹配、相配”,因此此函数就是完成设备和驱动之间匹配的,总线就是使用 match 函数来根据注册的设备来查找对应的驱动,或者根据注册的驱动来查找相应的设备,因此每一条总线都必须实现此函数。match 函数有两个参数:dev 和 drv,这两个参数分别为 device 和 device_driver 类型,也就是设备和驱动。

platform 总线是 bus_type 的一个具体实例,定义在文件 drivers/base/platform.c,platform 总
线定义如下

struct bus_type platform_bus_type = {
	.name = "platform",
	.dev_groups = platform_dev_groups,
	.match = platform_match,
	.uevent = platform_uevent,
	.pm = &platform_dev_pm_ops,
};

platform_bus_type 就是 platform 平台总线,其中 platform_match 就是匹配函数。我们来看
一下驱动和设备是如何匹配的,platform_match 函数定义在文件 drivers/base/platform.c 中,函
数内容如下所示:

1 static int platform_match(struct device *dev,struct device_driver *drv)
2 {
3 struct platform_device *pdev = to_platform_device(dev);
4 struct platform_driver *pdrv = to_platform_driver(drv);
5
6 /*When driver_override is set,only bind to the matching driver*/
7 if (pdev->driver_override)
8 return !strcmp(pdev->driver_override, drv->name);
9
10 /* Attempt an OF style match first */
11 if (of_driver_match_device(dev, drv))
12 return 1;
13
14 /* Then try ACPI style match */
15 if (acpi_driver_match_device(dev, drv))
16 return 1;
/* Then try to match against the id table */
19 if (pdrv->id_table)
20 return platform_match_id(pdrv->id_table, pdev) != NULL;
21
22 /* fall-back to driver name match */
23 return (strcmp(pdev->name, drv->name) == 0);
24 }

驱动和设备的匹配有四种方法:

  1. 第一种匹配方式, OF 类型的匹配,也就是设备树采用的匹配方式, of_driver_match_device 函数定义在文件 include/linux/of_device.h 中。device_driver 结构体(表示设备驱动)中有个名为of_match_table的成员变量,此成员变量保存着驱动的compatible匹配表,设备树中的每个设备节点的compatible 属性会和 of_match_table 表中的所有成员比较,查看是否有相同的条目,如果有的话就表示设备和此驱动匹配,设备和驱动匹配成功以后 probe 函数 就会执行。
  2. 第二种匹配方式,ACPI 匹配方式。 第 19~20 行,
  3. 第三种匹配方式,id_table 匹配,每个 platform_driver 结构体有一个 id_table成员变量,顾名思义,保存了很多 id 信息。这些 id 信息存放着这个 platformd 驱动所支持的驱 动类型。
  4. 第四种匹配方式,如果第三种匹配方式的 id_table 不存在的话就直接比较驱动和 设备的 name字段,看看是不是相等,如果相等的话就匹配成功。

对于支持设备树的 Linux 版本号,一般设备驱动为了兼容性都支持设备树和无设备树两种匹配方式。也就是第一种匹配方式一般都会存在,第三种和第四种只要存在一种就可以,一般用的最多的还是第四种,也就是直接比较驱动和设备的 name 字段,毕竟这种方式最简单了。

其实,我看这个还是主要因为设备树;

当我们成功注册总线时,会在 /sys/bus/ 目录下创建一个新目录,目录名为我们新注册的总线名。

3 devices

在 /sys/devices 目录记录了系统中所有的设备。
/sys 下的所有设备文件和 /sys/dev 下的所有设备节点都是链接文件,实际上都指向了对应的设备文件。

device 结构体:

struct device 
{
        const char *init_name;
        struct device           *parent;
        struct bus_type *bus;
        struct device_driver *driver;
        void            *platform_data;
        void            *driver_data;
        struct device_node      *of_node;
        dev_t                   devt;
        struct class            *class;
        void (*release)(struct device *dev);
        const struct attribute_group **groups;  /* optional groups */
        struct device_private   *p;
};

内核源码路径:内核源码/include/linux/device.h。

  • init_name:指定该设备的名称,总线匹配时,一般会根据比较名字来进行配对。
  • parent:表示该设备的父对象,旧版本的设备之间没有任何联系,引入 Linux 设备驱动模块后,设备之间呈现树状结构,便于管理各种设备。
  • bus:归属与哪个总线。当我们注册设备时,内核便会将该设备注册到对应的总线。
  • of_node:存放设备树中匹配的设备节点。当内核使能设备树,总线负责将驱动的 of_match_table 以及设备树的
    compatible 属性进行比较之后,将匹配的节点保存到该变量。
  • platform_data:特定设备的私有数据,通常定义在板级文件中。
  • driver_data:驱动层可以通过 dev_set/get_drvdata 函数来获取该成员变量。
  • class:指向该设备对应类。
  • dev:设备号。dev_t 类型。
  • release:回调函数。当设备被注销时,该函数被调用。
  • group:指向 struct attribute_group 类型指针指定该设备属性。

driver 结构体:


struct device_driver 
{
        const char              *name;
        struct bus_type         *bus;

        struct module           *owner;
        const char              *mod_name;      /* used for built-in modules */

        bool suppress_bind_attrs;       /* disables bind/unbind via sysfs */

        const struct of_device_id       *of_match_table;
        const struct acpi_device_id     *acpi_match_table;

        int (*probe) (struct device *dev);
        int (*remove) (struct device *dev);

        const struct attribute_group **groups;
        struct driver_private *p;
};

内核源码路径:内核源码/include/linux/device.h

  • name:指定驱动名称,总线进行匹配时,利用该成员与设备名进行比较。
  • bus:归属与哪个总线。内核需要保证在驱动执行之前,对应的总线能够正常工作。
  • suppress_bind_attrs:布尔量,用于指定是否通过 sysfs 导出 bind 与 unbind文件,bind 与
    unbind 文件是驱动用于绑定/解绑关联的设备。
  • owner:表示该驱动的拥有者,一般设置为 THIS_MODULE。
  • of_match_table:指定该驱动支持的设备类型。当内核使能设备树时,会利用该成员与设备树中的 compatible 属性进行比较。
  • remove:当设备从操作系统中拔出或者是系统重启时,会调用该回调函数。
  • probe:当驱动以及设备匹配后,会执行该回调函数,对设备进行初始化。通常的代码,都是以main函数开始执行的,但是在内核的驱动代码,都是从
    probe 函数开始的。
  • group:指向 struct attribute_group 类型的指针,指定该驱动的属性。

调用关系

platform_device_register
platform_device_add
    device_add
        bus_add_device // 放入链表
        bus_probe_device // probe 枚举设备,即找到匹配的(dev, drv)
            device_initial_probe
                __device_attach
                    bus_for_each_drv(...,__device_attach_driver,...)
                        __device_attach_driver
                            driver_match_device(drv, dev) // 是否匹配
                            driver_probe_device // 调用 drv 的 probe                   
platform_driver_register
__platform_driver_register
    driver_register
        bus_add_driver // 放入链表
            driver_attach(drv)
                bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
                    __driver_attach
                        driver_match_device(drv, dev) // 是否匹配
                        driver_probe_device // 调用 drv 的 probe


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/496108.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Github的加速访问

文章目录 概述Steam的下载Steam的安装使用 概述 GitHub打开访问速度比较慢,这儿介绍一种加速访问的方式,是正规的方式,采用 Steam 来加速。 Steam的下载 浏览器输入框输入Watt Toolkit进行搜索, 选择官网进入,网址 …

蚂蚁安全科技 Nydus 与 Dragonfly 镜像加速实践 | 龙蜥技术

编者按:本文详细介绍蚂蚁安全科技使用龙蜥社区技术进行镜像加速的实践过程,可以让您了解如何基于龙蜥社区推出的容器镜像,Nydus 与 Dragonfly 镜像加速技术和 LifseaOS 为容器的启动加速。文章转自金融级分布式架构,以下为全文。 …

计算材料学有哪些SCI期刊推荐? - 易智编译EaseEditing

以下是一些计算材料学领域的SCI期刊推荐: Computational Materials Science: 该期刊发表计算材料科学的理论、计算和实验研究,包括材料结构、热力学、物理和化学性质以及材料的设计、制备和性能等方面的内容。 Materials Horizons&#xff1…

JavaWeb——HTML中的常用标签详解

目录 一、HTML 1、HTML标签结构 2、HTML文件结构 (1)、定义 (2)、标签层次结构 二、HTML常见标签 1、注释标签 2、标题标签 3、段落标签 4、换行标签 5、格式化标签 6、图片标签 (1)、定义 &a…

4。计算机组成原理(3)指令系统

嵌入式软件开发,非科班专业必须掌握的基本计算机知识 核心知识点:数据表示和运算、存储系统、指令系统、总线系统、中央处理器、输入输出系统 指令系统(Instruction Set)是计算机体系结构的关键组成部分之一,它定义了处…

Java面试题复习(1)

目录 1.mysql使用innodb引擎,请简述mysql索引的最左前缀,如何优化order by语句 2.在JVM内存模型中,为什么要区分新生去和老年代,对于新生代为什么要区分eden区和survial区? 3.常见的远程调用有几种 4.对于外部衔接的…

ChatGPT总是答非所问?如何使用chatgpt定义角色

一、📝 定义角色:ChatGPT 的角色设定 背景信息:提供详细、准确的背景信息和前提条件,以便 ChatGPT 提供有针对性的回答和建议 任务目标:清晰地描述希望 ChatGPT 完成的任务 输出要求:告知 ChatGPT 如何完…

chatgpt应用市场

简介:利用ChatGPT的模型能力可以开发出垂直领域的应用市场。例如,可以使用ChatGPT来构建一个智能医疗助手,该助手可以回答患者关于疾病、症状、治疗方案等方面的问题。另外,也可以使用ChatGPT来构建一个智能金融助手,该助手可以回答用户关于投资、理财、贷款等方面的问题。…

MySQL的两个原则,两个优化,一个bug

背景:因为间隙锁在可重复读隔离级别下才有效,所以本篇文章接下来的描述,若没有特殊说明,默认 是可重复读隔离级别。 1.加锁规则里面,包含了两个“原则”、两个“优化”和一个“bug”。 (1). 原则1:加锁的基本单位是n…

家用洗地机有什么推荐的吗?家用洗地机分享

洗地机是解决清洁问题的智能设备。多年的市场验证已证明,它比传统清洁方法更加经济、高效和环保。相比传统方式,洗地机可以以更小的成本清洁更大的空间。同时,它的清洁效果也更好,采用先进的技术和设备,包括高压喷洒、…

第二十一章 备忘录模式

文章目录 前言一、备忘录模式基本介绍二、备忘录模式原理代码实现备忘录对象 Memento发起者 Originator管理备忘录类 CaretakerClint 测试类 三、游戏角色恢复状态实例备忘录对象游戏角色管理备忘录对象测试 Clint 四、备忘录模式的注意事项和细节 前言 一、备忘录模式基本介绍…

顺序表(二)

文章目录 前言一.什么是链表二.线性表的链表结构2.1链表的初始2.2 链表的分类2.3 单链表2.4 双链表 三.java里面的LinkedList API3.1 基础操作3.2 链表的遍历操作 四.模拟实现LinkedList的相关操作创建一个链表头插法尾插法任意位置插入,第一个数据节点为0号下标查找是否包含关…

【网络安全】记一次杀猪盘渗透实战

看起来非常假的网站,这个网站是没有 cdn 的用的是 thinkphpk 框架搭建的。 先打一波 poc 没有效果 访问一下后台直接在 url 后面加/admin。 一个开源的 cms 还没有验证码尝试用 burp 进行爆破,首先在火狐上设置代理 ip 为 127.0.0.1 代理端口为 8081。 B…

性能测试----负载测试、压力测试、并发测试

性能测试:检测一个软件的性能。 性能测试的指标: 响应时间:用户从请求到服务器响应的时间 吞吐量:单位时间内成功地传送数据的数量 并发数:在线并且在操作的用户数 负载测试:加负载,找到让系…

内网:定位域管理员

在域渗透中,获取域内的一个支点后,需要获取管理员权限。在一个域中,当计算机加入域后,会默认给域管理员组赋予本地系统管理员权限。当计算机被添加到域中,成为域的成员主机时,系统会自动将域管理员组添加到…

( “ 图 “ 之 拓扑排序 ) 207. 课程表 ——【Leetcode每日一题】

❓207. 课程表 难度:中等 你这个学期必须选修 numCourses 门课程,记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出,其中 prerequisites[i] [ai, bi] ,表示如果要学习课…

C#_委托简述

一.委托的分类 1.delegate关键字 直接通过delegate关键字定义委托类,定义示例如下: delegate void Calculate(int x, int y, int z); 此时可将返回值类型为void、参数类型为(int, int, int)的函数委托给Calculate,示例: Calcu…

今日立夏 — 初夏已至,万物并秀。

晴日暖风生麦气, 绿阴幽草胜花时。 ——《初夏即事》王安石 告别春日和煦的风,立夏缓缓走到眼前。 古人云:“四月立夏为节者,夏,大也,至此之时物已长大,故以为名。”万物生长,人有…

Java—JDK8新特性—Lambda表达式

目录 JDK8新特性 2.Lambda表达式 2.1 什么是Lambda表达式 2.2 为什么使用Lamdba表达式 2.3 Lambda表达式基本语法 JDK8新特性 官网提供网址:JDK 8 Features 2.Lambda表达式 2.1 什么是Lambda表达式 Lamdba是一个匿名函数,可以把Lamdba表达式理解为是…

社群人脉系统源码软件开发

社群人脉系统源码软件技术主要包括以下方面: 后端开发:使用Java、Python等编程语言,采用Spring、Django等框架,实现后台管理系统和API接口开发。 前端开发:使用Vue、React等前端框架,实现用户界面开…