【linux kernel】Linux设备驱动模型 | bus

news2024/11/27 5:26:56

文章目录

    • 一、导读
    • 二、与总线相关的数据结构
      • (2-1)struct bus_type
      • (2-2)struct subsys_private
    • 三、总线的初始化
    • 四、总线的操作接口
      • (4-1)总线的注册
      • (4-2)总线的注销
      • (4-3)device和device_driver的添加
      • (4-4)driver的probe

一、导读

在linux设备驱动模型中,总线是一个抽象的概念,是一类特殊的设备。在设备模型的实现中,内核规定了系统中的每个设备都需要连接到一个总线上,这个总线可以是一个内部的Bus、虚拟的Bus或者Platform 总线。在内核中通过struct but_type结构来描述总线,定义在include/linux/device.h中。

本文首先描述与总线相关的数据结构,重点描述struct bus_type结构体内部各个元素的含义以及内部之间的联系。接着会描述linux设备驱动模型初始化过程中关于总线的初始化流程,这部分由buses_init()完成,最后会描述对总线的几个操作接口函数。

👀本文所有源码分析基于linux内核版本:4.1.15

文章目录

    • 一、导读
    • 二、与总线相关的数据结构
      • (2-1)struct bus_type
      • (2-2)struct subsys_private
    • 三、总线的初始化
    • 四、总线的操作接口
      • (4-1)总线的注册
      • (4-2)总线的注销
      • (4-3)device和device_driver的添加
      • (4-4)driver的probe

二、与总线相关的数据结构

(2-1)struct bus_type

总线是处理器和更多设备之间的通道,对于linux的设备模型,所有的设备都通过总线连接在一起。总线之间可以互相连接,例如:USB控制器通常是一个PCI设备,设备模型表示总线和它们控制的设备之间的实际连接。总线由struct bus_type结构表示,该结构包含了总线名称、默认属性、总线的方法、PM操作和驱动核心的私有数据。sturct bus_type定义如下:

struct bus_type {
	const char		*name;
	const char		*dev_name;
	struct device		*dev_root;
	struct device_attribute	*dev_attrs;	/* use dev_groups instead */
	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);
	void (*shutdown)(struct device *dev);

	int (*online)(struct device *dev);
	int (*offline)(struct device *dev);

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

	const struct dev_pm_ops *pm;

	const struct iommu_ops *iommu_ops;

	struct subsys_private *p;
	struct lock_class_key lock_key;
};
  • name :总线的名称。
  • dev_name : 用于子系统枚举设备等,例如(“foo%u”, dev->id)。
  • dev_root : 表示要用于父设备的默认设备。
  • dev_attrs: 设备属性组。
  • bus_groups: bus属性组。
  • dev_groups: dev属性组。
  • drv_groups: drv属性组。
  • match: 是一个需要由具体bus驱动实现的回调函数,当属于该bus的所有device和驱动添加到内核时,内核都会调用该接口函数。
  • uevent:也是一个由具体的bus驱动实现的回到函数,当属于该bus的设备,触发添加、移除或者其他动作时,bus模块核心就会调用该接口,这样可以让bus的驱动能够修改环境变量。
  • probe、 remove:这两个也是回调函数,当有新的设备或者驱动添加到这个bus时,内核则会首先调用这个bus的probe,然后再调用具体驱动程序的probe去初始化匹配设备;当有设备从这个bus上移除的时候则会调用remove,所以这个两个回调函数非常重要。
  • shutdown:在需要shutdown的时候调用该回调函数,以让设备停止工作。该函数与电源管理相关。
  • online:当设备再脱机后重新联机时调用该函数。该函数与电源管理相关。
  • offline:当让设备脱机以便进行热插拔时调用该函数。
  • suspend:当总线上的设备想要进入睡眠模式时调用。
  • resume:让这个bus上的一个设备退出睡眠模式时调用该函数。
  • pm:是与之对应的bus的电源管理操作,会去回调执行特定驱动程序的pm的ops。
  • iommu_ops:该总线的IOMMU特定操作,用于将IOMMU驱动程序实现附加到总线上,并允许驱动程序进行总线上特殊的设定操作。
  • p: 一个struct subsys_private类型的指针,是驱动核心的私有数据,只有驱动核心可以使用,
  • lock_key: 该参数供锁验证器使用。

(2-2)struct subsys_private

bus_typeclass结构中都有一个指向struct subsys_private的指针,用于保存bus_typeclass结构的驱动程序核心部分的私有数据。从命名上似乎不容易理解struct subsys_private,由于bus_typeclsss结构中都有一个struct subsys_private指针,所以可以将subsys_private理解成bus_type和class的上层,包含了bus和class。

struct subsys_private结构定义如下(/drivers/base/base.h):

struct subsys_private {
	struct kset subsys;
	struct kset *devices_kset;
	struct list_head interfaces;
	struct mutex mutex;

	struct kset *drivers_kset;
	struct klist klist_devices;
	struct klist klist_drivers;
	struct blocking_notifier_head bus_notifier;
	unsigned int drivers_autoprobe:1;
	struct bus_type *bus;

	struct kset glue_dirs;
	struct class *class;
};
  • subsys:用于描述本subsystem的kset,用于代表其自身。
  • devices_kset: 表示subsystem的device目录。
  • interfaces: interfaces是一个list_head类型数据,用于保存与之相关的interface。在内核中interface用于抽象bus下所有关联设备的一些特殊的功能。
  • mutex: mutex类型锁,用于保护设备和interface链表。
  • drivers_kset: 表示subsystem中驱动相关链表。
  • klist_devices: 设备链表,用于保存本bus下所有的device的指针,以方便查找。
  • klist_drivers: 驱动链表,用于保存本bus下所有的device_driver的指针,以方便查找。
  • bus_notifier: bus_notifier是一个总线通知列表,用于监测bus上发生的任何事情。
  • drivers_autoprobe:用于控制该bus下的drivers或者device是否具有自动probe属性。
  • bus:是一个指向与之关联的struct bus_type类型的指针。用于保存上层的bus。
  • glue_dirs:表示glue目录,用于放在父设备之间,以避免名称空间出现冲突。
  • class:是一个指向与之关联的struct class类型的指针。用于保存上层的class。

三、总线的初始化

总线属于linux驱动模型的一部分,所以在内核启动过程中,在driver_init()函数中会调用buses_init(),完成总线相关的初始化操作:


buses_init()的操作逻辑中,完成了以下几件事情:

(1)动态创建bus内核kset,并指定其事件操作函数,然后添加到sysfs中。

(2)动态创建system内核kset,并指定其父级kset为devices_kset->kobj,然后添加到sysfs中。

四、总线的操作接口

本小节描述linux内核中对总线的操作API接口:

//添加设备到总线
extern int bus_add_device(struct device *dev);
//为新的设备探测驱动
extern void bus_probe_device(struct device *dev);
//从总线中将设备移除
extern void bus_remove_device(struct device *dev);

//添加一个驱动到总线
extern int bus_add_driver(struct device_driver *drv);
//从总线将移除驱动
extern void bus_remove_driver(struct device_driver *drv);

//将驱动程序从该驱动控制的所有设备中分离
extern void driver_detach(struct device_driver *drv);
//尝试将设备和驱动程序绑定在一起
extern int driver_probe_device(struct device_driver *drv, struct device *dev);
//注册一个驱动核心总线子系统
extern int __must_check bus_register(struct bus_type *bus);

//注销驱动核心总线子系统
extern void bus_unregister(struct bus_type *bus);

/*注册总线通知器。总线通知器,用于获取当设备添加/移除或者驱动程序与
设备绑定/解绑定时的通知。*/
extern int bus_register_notifier(struct bus_type *bus,
				 struct notifier_block *nb);
         
//注销总线通知器
extern int bus_unregister_notifier(struct bus_type *bus,
				   struct notifier_block *nb);

(4-1)总线的注册

调用bus_register执行具体总线的注册操作,该函数实现在/drivers/base/bus.c中,具体执行逻辑如下:

  • (1)调用kzalloc()struct subsys_private创建内存,设置priv->bus的值为想要注册的总线类型,然后将bus->p赋值为priv。
  • (2)初始化总线通知器。
  • (3)为priv->subsys.kobj重新设置名称,即总线的名称。
  • (4)初始化priv->subsys.kobj的kset和ktype字段。
  • (5)调用kset_registerprivate->subsys.kobj注册到内核中。
  • (6)调用bus_create_file向sysfs文件系统中的bus目录下添加一个uevnet attribute:
  • (7)调用kset_create_and_add()向内核分别添加devicesdriverskset,这样便可以在sysfs中查看了:
  • (8)初始化priv指针中的mutexklist_devicesklist_drivers等变量。
  • (9)调用add_probe_files函数,在bus下添加bus_attr_drivers_probebus_attr_drivers_autoprobe两个attribute:
  • (10)调用bus_add_groups添加bus_groups属性组。

(4-2)总线的注销

调用bus_unregister执行具体总线的注销操作,该函数同样实现在/drivers/base/bus.c中:

void bus_unregister(struct bus_type *bus)
{
	pr_debug("bus: '%s': unregistering\n", bus->name);
	if (bus->dev_root)
		device_unregister(bus->dev_root);
	bus_remove_groups(bus, bus->bus_groups);
	remove_probe_files(bus);
	kset_unregister(bus->p->drivers_kset);
	kset_unregister(bus->p->devices_kset);
	bus_remove_file(bus, &bus_attr_uevent);
	kset_unregister(&bus->p->subsys);
}

(4-3)device和device_driver的添加

linux内核的驱动模型中,提供了device_register()driver_register()两个接口,供各个驱动模块使用。从linux内核多个子系统的源码中可以发现,对于各种驱动程序的注册最终都会调用到driver_register()。然而这两个接口函数的核心逻辑中,是通过调用总线的bus_add_device()bus_add_driver()实现的:在driver_register()中调用driver_find()在给定的总线中查找给定名称的驱动,如果驱动已经存在则返回-EBUSY;如果驱动在总线中不存在,则调用bus_add_driver()注册驱动。在device_register()中则首先调用device_initialize初始化设备(本质上是对sturct device结构赋值),然后调用device_add向linux内核驱动模型注册设备。

device_register()driver_register()两个接口都在/drivers/base/bus.c文件中实现。下文来具体看看这两个接口的执行逻辑:

1、bus_add_device的执行逻辑:

  • (1)从dev->bus中取得bus_type*类型的指针bus,如果获取bus不成功,则函数直接返回;如果bus获取成功,则会继续后续的第(2)步操作。
  • (2)调用device_add_attrs接口,将由bus->dev_attrs指针定义的默认attribute添加到内核中,这个操作会体现在sysfs文件系统中的/sys/devices/xxx/xxx_device/目录中。
  • (3)调用device_add_groupsbus_dev_groups添加到内核中。
  • (4)调用sysfs_create_link将该设备在sysfs中的目录,链接到该bus的devices目录下
  • (5)接着依然调用sysfs_create_link,在该设备的sysfs目录中,创建一个指向该设备所在bus目录的链接,命名为subsystem。
  • (6)前面几个操作实则是向sysfs文件系统注册关于设备的信息,向用户空间抛出接口。最后步骤则是调用klist_add_tail()将该设备指针保存到bus->p->klist_devices中。

2、bus_add_driver的执行逻辑:

  • (1)首先调用bus_get从驱动程序中获取到该驱动程序所属的bus_type指针。如果该指针为零(即获取失败)则返回-EINVAL;反之则继续执行后续操作。
  • (2)为该驱动的struct driver_private指针(priv)分配空间,并初始化其中的priv->klist_devicespriv->driverpriv->kobj.kset等变量,同时将priv保存到device_driver的p中。
  • (3)调用kobject_init_and_add,并传入该驱动的名称作为参数,向sysfs中注册driver的kobject。该操作体现在sysfs文件中的/sys/bus/xxx/drivers/目录下。
  • (4)调用klist_add_tail将驱动添加到总线的klist_drivers链表中,如果该驱动的drivers_autoprobe为真,还将调用driver_attach尝试将驱动绑定到设备。
  • (5)调用module_add_driver()将驱动添加到drv->owner中,咱不过多分析。
  • (6)调用driver_create_file,在sysfs文件系统中的driver目录下,创建uevent attribute。
  • (7)调用driver_add_groupsbus->drv_groups属性组添加到驱动中。

(4-4)driver的probe

当一个driver在进行probe的时候,大部分逻辑都会依赖总线的具体实现,核心操作是bus_probe_device()driver_attach()两个接口,这两个接口都是在drivers/base/base.h中声明,在drivers/base/bus.c中实现。

  • bus_probe_device()实现如下:

  • driver_attach()实现如下:

    从上图可知,driver_attach中会调用bus_for_each_dev遍历设备,该函数用于遍历drv->bus的设备列表,并为每个设备调用__driver_attach(),并将drv传递给__driver_attach,故而可以明确知道对每个设备执行的操作则是__driver_attach(),该函数定义在/drivers/base/dd.c文件中,具体的执行逻辑如下:

(1)使用driver_match_device()判断驱动和设备是否匹配(本质上是判断是否指定了drv->bus->match,如果指定了则执行与之对应的函数,否则返回1),如果驱动和设备已经匹配了则直接返回;否则,则继续执行后续的操作。

(2)调用driver_probe_device()尝试将驱动和设备绑定在一起。

总体上,bus_probe_device()driver_attach()这两个接口的操作流程类似,即:搜索所在的总线,比对是否有同名的device_driver(或device),如果有并且该设备没有绑定Driver(注:这一点很重要,这样可以使同一个Driver驱动相同名称的多个设备)则调用device_driver的probe接口。

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

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

相关文章

Linux的基本使用在Linux上部署程序

linux概述 Linux严格意义来说只是一个"操作系统内核",一个完整的操作系统 操作系统内核 配套的应用程序 由于 Linux 是一个完全开源免费的内核,因此有些公司/开源组织又基于 Linux 内核,提供了不同的配套程序,这就构…

GAN“家族”又添新成员——EditGAN,不但能自己修图,还修得比你我都好

导语:从风格迁移到特征解耦、语言概念解耦,研究人员正通过数学和语言逐步改善GAN的功能。作者 | 莓酊编辑 | 青暮首先想让大家猜一猜,这四张图中你觉得哪张是P过的?小编先留个悬念不公布答案,请继续往下看。生成对抗网…

【蓝桥杯】历届真题 时间显示(省赛)Java

【问题描述】 小蓝要和朋友合作开发一个时间显示的网站。在服务器上,朋友已经获取了当前的时间,用一个整数表示,值为从1970年1月1日O0:00:00到当前时刻经过的毫秒数。 现在,小蓝要在客户端显示出这个时间。小蓝不用显示出年月日&a…

Allegro如何灌铜操作指导

Allegro如何灌铜操作指导 在做PCB设计平面层的铜皮时候,会需要用到灌铜的操作,如下图 灌铜可以让铜皮自动沿着Antietch画指定网络的铜皮 具体操作如下 点击Add Line命令选择Anti Etch的层面,比如Anti Etch画在L2层,线宽设置为40mil

TCP通信的三次握手和四次挥手详解

TCP通信的三次握手和四次挥手详解 计算机网络参考模型: 应用层:例如Modbus、Http、FTP 传输层:TCP、UDP 网络层:IP 数据链路层:MAC 物理层:RS485、RS232、以太网 TCP的包头: TCP包头为至少20字节 TCP包头解释  源端口号、目的端口号,用于建立连接时,确认源端口(本机…

2.Spring 等框架简单入门了解

1.Spring 1.什么是spring? 一个轻量级Java开发框架,目的是为了解决企业级应用开发 的业务逻辑层和其他各层的耦合问题. 两个核心特性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect- oriented programming,AOP) 2.IOC(控制…

一文带你秒懂十大排序

目录 一、排序的概述 二、插入排序 1、直接插入排序 2、希尔排序 二、选择排序 1、直接选择排序 2、堆排序 三、交换排序 1、冒泡排序 2、快速排序 四、归并排序 五、计数排序 六、基数排序 七、桶排序 八、排序总结 一、排序的概述 排序就是将一组…

pod私有库

私有库制作步骤 1、在gitlab上创建一个空项目,并用source tree导到本地,便于后面代码更新上传 2、cd 到项目下 执行pod lib create 【组件名】如:pod lib create TDAlertView 输入命令后会显示下载模板,会有几秒钟等待 Cloni…

一文搞懂 python 中的 classmethod、staticmethod和普通的实例方法的使用场景

什么是类方法(classmethod)/静态方法(staticmethod)和普通成员方法? 首先看这样一个例子: class A(object):def m1(self, n):# 属于实例对象,self 指代实例对象,print("self:…

Allegro如何更改钻孔孔符以及大小操作指导

Allegro如何更改钻孔孔符以及大小操作指导 PCB设计完成时,需要放出整板的钻孔表来,有的钻孔孔符以及大小并不是需要的,Allegro支持更改钻孔符以及大小,如下图 需要更改孔符以及大小, 具体操作如下 选择Manufacture选择NC

aws parallelcluster 理解 parallelcluster 集群的配置和使用

参考资料 Setup AWS ParallelCluster 3.0 with AWS Cloud9 200 HPC For Public Sector Customers 200 HPC pcluster workshop 200 Running CFD on AWS ParallelCluster at scale 400 Tutorial on how to run CFD on AWS ParallelCluster 400 Running CFD on AWS ParallelC…

CSS 伪元素也可以被用于反爬案例?来学习一下。26

先说一下什么是 CSS 中的伪元素,CSS 伪元素的概念是指在 CSS 中使用的一些特殊的元素,它们不存在于 HTML 文档中,而是由浏览器生成的元素,用于提供额外的样式控制。这些伪元素在 HTML 代码中不存在,但可以在 CSS 中通过…

[idekCTF 2023] Malbolge I Gluttony,Typop,Cleithrophobia,Megalophobia

这些题名字我都不认识,这是什么语呀。这个比赛感觉太难了,加上春节将近比较忙,仅作了4个简单题。记录一下。Misc/Malbolge I Gluttony这是个虚拟机的题,放入misc感觉有点不可思忆,题目给了7个命令,有"…

【云原生进阶之容器】第五章容器运行时5.1节--容器运行时总述

1 Kubernetes引言 Kubernetes 已经成为容器编排调度领域的事实标准,其优良的架构不仅保证了丰富的容器编排调度功能,同时也提供了各个层次的扩展接口以满足用户的定制化需求。其中,容器运行时作为 Kubernetes 管理和运行容器的关键组件,当然也提供了简便易用的扩展…

图解二叉树的构造 | 中序 + 后序

中序后续构造二叉树 https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal/ 递归思路 递归思路很简单, 因为无论是构造一棵大树还是一棵小树, 都是重复的子问题, 思路主要麻烦在边界上 如下图所示 上述是中序和后续序列 我们要递归, 需…

BetaFlight飞控AOCODARC-F7MINI固件编译

BetaFlight飞控AOCODARC-F7MINI固件编译1. 编译目标(AOCODARC-F7MINI)2. 编译步骤Step 1 软件配置环境准备Step 2 获取开源代码Step 3 构建命令介绍Step 4 构建命令准备Step 5 厂家目标板查询Step 6 目标固件编译Step 7 目标固件清理3. 参考资料BetaFlig…

MyBatis中TypeHandler的使用教程

一.TypeHandler作用及其使用场景在我们平常开发操作数据库时,查询、插入数据等操作行为,有时会报数据类型不匹配异常,就可以得知数据的类型是不唯一的必然是多种不同的数据类型。并且我们必须要明确的一点就是java作为一门编程语言有自己的数…

如何使用ElementUI的table组件来实现单元格的行合并

前言 最近在编写一个值班的排班表,然后中间涉及到了表格应用。并且还要做出类似这种效果的行合并效果: 然后就开始找组件了。Html的table是有rowsSpan和colsSpan的属性来实现行合并和列合并的。然后就在网上找资料,发现没有几篇能把这两个属性将好的&a…

LeetCode刷题模版:111 - 120

目录 简介111. 二叉树的最小深度112. 路径总和113. 路径总和 II114. 二叉树展开为链表115. 不同的子序列116. 填充每个节点的下一个右侧节点指针117. 填充每个节点的下一个右侧节点指针 II118. 杨辉三角119. 杨辉三角 II120. 三角形最小路径和结语简介 Hello! 非常感谢您阅读海…

SWPUCTF 2022新生赛 web部分wp

😋大家好,我是YAy_17,是一枚爱好网安的小白。 本人水平有限,欢迎各位大佬指点,一起学习💗,一起进步⭐️。⭐️此后如竟没有炬火,我便是唯一的光。⭐️ 目录 [SWPUCTF 2022 新生赛]…