linux内核中竟有如此“高冷”的driver_register
相关文件:
/drivers/base/driver.c
1、简介
向linux内核注册驱动由driver_register()
完成。它将驱动程序的信息添加到内核的驱动程序列表中,使得内核能够在需要时与该驱动程序进行交互。
当调用driver_register()
函数时,内核会将驱动程序添加到内核驱动程序列表中,并在需要时使用该驱动程序来匹配和初始化设备。驱动程序的探测函数(probe)将在设备与驱动程序匹配时调用,以便进行设备的初始化。移除函数(remove)将在设备从系统中移除时调用,以进行相关的清理操作。
通过调用driver_register()
函数,驱动程序可以将自身注册到内核中,从而使得内核能够管理和与驱动程序进行交互。这为设备的探测、初始化、配置和移除提供了必要的框架和支持。
内核中,几乎所有的驱动子系统都会以该函数进行封装,开放出对应驱动的注册函数,例如PCI驱动框架,在/drivers/pic/pci-driver.c文件中会调用该函数:
再比如对应i2c设备驱动,在i2c驱动框架下的/driver/i2c/i2c-core.c文件中有如下代码:
综上可知,driver_register()在几乎所有的驱动子系统中都会使用到。兜兜转转,最后都会调用到该函数。
2、driver_register分析
在linux内核中,struct device_driver结构体用于表示一个设备驱动程序。它包含了驱动程序的相关信息,如名称、总线类型、探测函数、移除函数等,用于与设备进行匹配、初始化和清理操作。struct device_driver
结构体提供了设备驱动程序的基本信息和回调函数,用于与设备进行匹配、初始化、清理和管理。通过使用该结构体,驱动程序能够在设备与驱动程序匹配时进行初始化操作,并在设备移除或系统关机时进行相应的清理操作。此外,还可以通过属性组和电源管理操作等扩展功能来增强驱动程序的功能和灵活性。该结构定义如下:
struct device_driver {
const char *name; //
struct bus_type *bus;//驱动程序所属的总线类型。
struct module *owner; //模块拥有者
const char *mod_name; //在构建内建模块时使用
bool suppress_bind_attrs; //是否禁用通过sysfs bound/unbound操作
const struct of_device_id *of_match_table; //设备树匹配表
const struct acpi_device_id *acpi_match_table; //ACPI匹配表。
int (*probe) (struct device *dev); //驱动程序的探测函数,用于在设备与驱动程序匹配时进行初始化。
int (*remove) (struct device *dev);//驱动程序的移除函数,用于在设备从系统中移除时进行清理。
void (*shutdown) (struct device *dev);// 驱动程序的关机函数,用于在系统关机时进行相关的清理操作。
int (*suspend) (struct device *dev, pm_message_t state);// 驱动程序的挂起函数,用于在设备进入挂起状态时进行相关的操作。
int (*resume) (struct device *dev);//驱动程序的恢复函数,用于在设备从挂起状态恢复时进行相关的操作。
const struct attribute_group **groups;// 驱动程序的属性组,用于提供设备的特定属性。
const struct dev_pm_ops *pm;// 驱动程序的电源管理操作,用于控制设备的电源管理。
struct driver_private *p;//驱动核心的私有数据。驱动核心能访问。
};
driver_register
函数用于向设备驱动模型注册一个设备驱动,实现在/drivers/base/driver.c文件中:
int driver_register(struct device_driver *drv)
{
int ret;
struct device_driver *other;
BUG_ON(!drv->bus->p);
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
printk(KERN_WARNING "Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
printk(KERN_ERR "Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
ret = bus_add_driver(drv);
if (ret)
return ret;
ret = driver_add_groups(drv, drv->groups);
if (ret) {
bus_remove_driver(drv);
return ret;
}
kobject_uevent(&drv->p->kobj, KOBJ_ADD);
return ret;
}
driver_register()
具体执行流程如下:
- (1)调用
driver_find()
通过名字找到bus上的driver。 - (2)调用
bus_add_driver()
添加一个driver到bus。 - (3)调用
driver_add_groups()
将属性组(attribute_group
)添加到驱动程序中。 - (4)调用
kobject_uevent()
触发内核KOBJ_ADD
事件,用于向用户空间发送KOBJ_ADD
事件通知。
下文将展开driver_find()和bus_add_driver()进行分析。
(2-1)driver_find分析
该函数接收两个参数:
- (1)name:驱动程序的名称。
- (2)bus:待被扫描的bus。
函数实现如下:
调用kset_find_obj()
根据name寻找是否有kobject
,如果找到了,则使用to_driver()
返解出struct driver_private
,然后将driver_private->driver
作为参数返回;否则返回NULL。
该行代码中:
struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
bus->p->drivers_kset
本质是struct kset
,struct kset
是linux内核对象的集合,添加的设备驱动将作为内核对象添加到对应的kset集合中。kset_find_obj()
则用于在kset中搜索出对应name
的内核对象,该函数实现如下:
回到driver_register()
中,如果driver_find()
找到了对应的device_driver
,则证明该设备驱动已经注册过了,这时候则返回退出driver_register()
;否则继续执行后续的bus_add_driver()
操作。
(2-2)bus_add_driver分析
bus_add_driver()
实现在/drivers/base/bus.c中,将执行下列具体的逻辑:
- (1)从
drv->bus
中解析出bus,如果bus为NULL,则返回退出bus_add_driver:
bus = bus_get(drv->bus);
if (!bus)
return -EINVAL;
- (2)调用
kzalloc()
分配一个struct driver_private
内存空间:
- (3)调用
klist_init()
初始化设备链表klist_devices
:
klist_init(&priv->klist_devices, NULL, NULL);
- (4)设置驱动私有数据的
driver
和kobj.kset
字段,并将驱动私有数据设置到驱动程序的->p
字段:
priv->driver = drv;
drv->p = priv;
priv->kobj.kset = bus->p->drivers_kset;
- (5)调用
kobject_init_and_add()
初始化设备私有数据中的内核对象,并指定驱动类型driver_ktype
:
error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
"%s", drv->name);
if (error)
goto out_unregister;
driver_ktype
定义如下:
static struct kobj_type driver_ktype = {
.sysfs_ops = &driver_sysfs_ops,
.release = driver_release,
};
- (6)使用
klist_add_tail()
将priv->knode_bus
节点添加到bus->p->klist_drivers
总线的驱动链表中。 - (7)如果设置了驱动所属的bus的drivers_autoprobe,则调用drvier_attach()尝试将驱动程序绑定到设备:
- (8)使用
moudle_add_drvier
将设备驱动程序添加到内核模块系统中,使之可以与设备进行关联。通过调用这个函数,可以将设备驱动程序注册到内核的设备驱动程序列表中,以便在设备被发现时自动加载并与之匹配。 - (9)调用
driver_create_file()
为设备驱动创建sysfs中的属性文件。该文件将显示在/sys/bus/<bus_name>/drivers/<driver_name>/目录下,其中<bus_name>是设备所属的总线类型,<driver_name>是设备驱动程序的名称。 - (10)调用
driver_add_groups()
将设备驱动程序的bus
的属性组添加到sysfs中,这些属性组将显示在/sys/bus/<bus_name>/drivers/<driver_name>/目录下,其中<bus_name>是设备所属的总线类型,<driver_name>是设备驱动程序的名称。
3、总结
driver_register()
是linux 内核中用于注册设备驱动程序的函数。它属于linux设备驱动模型的一部分,用于将驱动程序添加到内核的设备驱动程序列表中,以便内核可以与相应的硬件设备进行交互。在内核中的大部分驱动都会形成自己的驱动框架核心,例如:USB、i2c、spi等,这些驱动框架核心也一般都会封装出自己的注册函数,但是这些注册函数本质上都是调用driver_register()
实现驱动的注册。例如下图所示:
文章转载自公众号嵌入式小生
- 分享
- 举报
-
浏览量:3160次2022-05-27 10:41:52
-
浏览量:342次2023-08-22 11:07:14
-
浏览量:257次2023-11-15 17:25:34
-
2020-07-29 18:32:39
-
浏览量:318次2023-11-29 12:31:57
-
浏览量:214次2023-08-18 17:31:53
-
浏览量:1097次2023-06-12 14:34:18
-
浏览量:213次2024-02-05 11:02:54
-
浏览量:3129次2020-04-27 16:58:40
-
浏览量:669次2023-08-18 10:36:32
-
浏览量:327次2023-08-18 14:08:55
-
浏览量:2433次2017-11-20 16:03:39
-
浏览量:198次2024-01-25 14:44:37
-
浏览量:1040次2023-05-16 15:43:05
-
浏览量:2602次2022-05-26 10:04:36
-
浏览量:1836次2020-08-28 15:08:37
-
浏览量:2170次2020-08-12 20:09:12
-
浏览量:407次2023-08-11 18:18:49
-
2023-08-14 16:26:07
-
广告/SPAM
-
恶意灌水
-
违规内容
-
文不对题
-
重复发帖
tomato
感谢您的打赏,如若您也想被打赏,可前往 发表专栏 哦~
举报类型
- 内容涉黄/赌/毒
- 内容侵权/抄袭
- 政治相关
- 涉嫌广告
- 侮辱谩骂
- 其他
详细说明