linux内核中的driver_register介绍

描述

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
", drv->name);

 other = driver_find(drv->name, drv->bus);
 if (other) {
  printk(KERN_ERR "Error: Driver '%s' is already registered, "
   "aborting...
", 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//drivers//目录下,其中是设备所属的总线类型,是设备驱动程序的名称。

(10)调用driver_add_groups()将设备驱动程序的bus的属性组添加到sysfs中,这些属性组将显示在/sys/bus//drivers//目录下,其中是设备所属的总线类型,是设备驱动程序的名称。

3、总结

driver_register()是linux 内核中用于注册设备驱动程序的函数。它属于linux设备驱动模型的一部分,用于将驱动程序添加到内核的设备驱动程序列表中,以便内核可以与相应的硬件设备进行交互。在内核中的大部分驱动都会形成自己的驱动框架核心,例如:USB、i2c、spi等,这些驱动框架核心也一般都会封装出自己的注册函数,但是这些注册函数本质上都是调用driver_register()实现驱动的注册。例如下图所示:

电源管理





审核编辑:刘清

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分