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结构体提供了设备驱动程序的基本信息和回调函数,用于与设备进行匹配、初始化、清理和管理。
通过使用该结构体,驱动程序能够在设备与驱动程序匹配时进行初始化操作,并在设备移除或系统关机时进行相应的清理操作。此外,还可以通过属性组和电源管理操作等扩展功能来增强驱动程序的功能和灵活性。该结构定义如下:
structdevice_driver{ constchar*name;// structbus_type*bus;//驱动程序所属的总线类型。 structmodule*owner;//模块拥有者 constchar*mod_name;//在构建内建模块时使用 boolsuppress_bind_attrs;//是否禁用通过sysfsbound/unbound操作 conststructof_device_id*of_match_table;//设备树匹配表 conststructacpi_device_id*acpi_match_table;//ACPI匹配表。 int(*probe)(structdevice*dev);//驱动程序的探测函数,用于在设备与驱动程序匹配时进行初始化。 int(*remove)(structdevice*dev);//驱动程序的移除函数,用于在设备从系统中移除时进行清理。 void(*shutdown)(structdevice*dev);//驱动程序的关机函数,用于在系统关机时进行相关的清理操作。 int(*suspend)(structdevice*dev,pm_message_tstate);//驱动程序的挂起函数,用于在设备进入挂起状态时进行相关的操作。 int(*resume)(structdevice*dev);//驱动程序的恢复函数,用于在设备从挂起状态恢复时进行相关的操作。 conststructattribute_group**groups;//驱动程序的属性组,用于提供设备的特定属性。 conststructdev_pm_ops*pm;//驱动程序的电源管理操作,用于控制设备的电源管理。 structdriver_private*p;//驱动核心的私有数据。驱动核心能访问。 };
driver_register函数用于向设备驱动模型注册一个设备驱动,实现在/drivers/base/driver.c文件中:
intdriver_register(structdevice_driver*drv) { intret; structdevice_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'needsupdating-pleaseuse" "bus_typemethods ",drv->name); other=driver_find(drv->name,drv->bus); if(other){ printk(KERN_ERR"Error:Driver'%s'isalreadyregistered," "aborting... ",drv->name); return-EBUSY; } ret=bus_add_driver(drv); if(ret) returnret; ret=driver_add_groups(drv,drv->groups); if(ret){ bus_remove_driver(drv); returnret; } kobject_uevent(&drv->p->kobj,KOBJ_ADD); returnret; }
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。
该行代码中:
structkobject*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) gotoout_unregister;
driver_ktype定义如下:
staticstructkobj_typedriver_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/
(10)调用driver_add_groups()将设备驱动程序的bus的属性组添加到sysfs中,这些属性组将显示在/sys/bus/
3、总结
driver_register()是linux 内核中用于注册设备驱动程序的函数。它属于linux设备驱动模型的一部分,用于将驱动程序添加到内核的设备驱动程序列表中,以便内核可以与相应的硬件设备进行交互。在内核中的大部分驱动都会形成自己的驱动框架核心,例如:USB、i2c、spi等,这些驱动框架核心也一般都会封装出自己的注册函数,但是这些注册函数本质上都是调用driver_register()实现驱动的注册。例如下图所示:
审核编辑:刘清
-
USB接口
+关注
关注
9文章
699浏览量
55570 -
电源管理
+关注
关注
115文章
6154浏览量
144207 -
LINUX内核
+关注
关注
1文章
316浏览量
21614 -
I2C驱动
+关注
关注
0文章
9浏览量
7034 -
ADD
+关注
关注
1文章
20浏览量
9404
原文标题:linux内核中竟有如此“高冷”的driver_register
文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论