0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

linux内核中的driver_register介绍

嵌入式小生 来源:嵌入式小生 2023-07-14 09:17 次阅读

1、简介

linux内核注册驱动由driver_register()完成。它将驱动程序的信息添加到内核的驱动程序列表中,使得内核能够在需要时与该驱动程序进行交互。

当调用driver_register()函数时,内核会将驱动程序添加到内核驱动程序列表中,并在需要时使用该驱动程序来匹配和初始化设备。驱动程序的探测函数(probe)将在设备与驱动程序匹配时调用,以便进行设备的初始化。移除函数(remove)将在设备从系统中移除时调用,以进行相关的清理操作。

通过调用driver_register()函数,驱动程序可以将自身注册到内核中,从而使得内核能够管理和与驱动程序进行交互。这为设备的探测、初始化、配置和移除提供了必要的框架和支持。

内核中,几乎所有的驱动子系统都会以该函数进行封装,开放出对应驱动的注册函数,例如PCI驱动框架,在/drivers/pic/pci-driver.c文件中会调用该函数:

78dbf582-21db-11ee-962d-dac502259ad0.png

再比如对应i2c设备驱动,在i2c驱动框架下的/driver/i2c/i2c-core.c文件中有如下代码:

793a14b4-21db-11ee-962d-dac502259ad0.png

综上可知,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。

函数实现如下:

79690530-21db-11ee-962d-dac502259ad0.png

调用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的内核对象,该函数实现如下:

79855bcc-21db-11ee-962d-dac502259ad0.png

回到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内存空间:

79c2e424-21db-11ee-962d-dac502259ad0.png

(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()尝试将驱动程序绑定到设备:

79ec2f28-21db-11ee-962d-dac502259ad0.png

(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()实现驱动的注册。例如下图所示:

7a21d18c-21db-11ee-962d-dac502259ad0.png





审核编辑:刘清

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

    关注

    9

    文章

    699

    浏览量

    55570
  • 电源管理
    +关注

    关注

    115

    文章

    6154

    浏览量

    144207
  • LINUX内核
    +关注

    关注

    1

    文章

    316

    浏览量

    21614
  • I2C驱动
    +关注

    关注

    0

    文章

    9

    浏览量

    7034
  • ADD
    ADD
    +关注

    关注

    1

    文章

    20

    浏览量

    9404

原文标题:linux内核中竟有如此“高冷”的driver_register

文章出处:【微信号:嵌入式小生,微信公众号:嵌入式小生】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Linux内核开发工具介绍

    进行嵌入式Linux产品开发,往往需要对内核进行裁剪和定制,以满足嵌入式产品的功能和性能需求。本文介绍几种阅读Linux内核源码的工具和方法
    发表于 12-29 15:20 4687次阅读
    <b class='flag-5'>Linux</b><b class='flag-5'>内核</b>开发工具<b class='flag-5'>介绍</b>

    Linux内核开发工具介绍

    进行嵌入式Linux产品开发,往往需要对内核进行裁剪和定制,以满足嵌入式产品的功能和性能需求。本文介绍几种阅读Linux内核源码的工具和方法
    发表于 01-06 17:20

    总线设备驱动模型浅析

    时,最终都会调用到:int driver_register(structdevice_driver *drv){ // 将驱动绑定在对应的总线上,主要工作把驱动(device_driver)添加到总线
    发表于 08-22 16:19

    iMX6Q开发板设备树内核-注册驱动例程介绍

    `文档主要讲解在 iMX6Q/D/PLUS 开发板的设备树内核(4.1.15)源码,设备树注册驱动和非设备树的类似。 1 注册驱动源码分析 设备树的内核驱动,platform_
    发表于 07-18 15:42

    Linux内核教程

    本章学习目标掌握LINUX内核版本的含义理解并掌握进程的概念掌握管道的概念及实现了解内核的数据结构了解LINUX内核的算法掌握
    发表于 04-10 16:59 0次下载

    Linux内核学习起步课件

    Linux内核学习起步介绍
    发表于 04-10 17:22 0次下载

    Linux内核配置系统详解

    随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 Linux 内核级的开发。面对日益庞
    发表于 11-01 15:45 4次下载

    Linux设备模型:device和device driver

    device和device driverLinux驱动开发的基本概念。Linux kernel的思路很简单:驱动开发,就是要开发指定的软件(driver)以驱动指定的设备,所以ker
    发表于 05-10 11:21 2442次阅读

    你了解Embeded linux的probe

    所谓的"probe”,是指在Linux内核,如果存在相同名称的device和device_driver内核就会执行device_
    发表于 05-14 16:18 3920次阅读
    你了解Embeded <b class='flag-5'>linux</b><b class='flag-5'>中</b>的probe

    linux内核参数设置_linux内核的功能有哪些

    本文主要阐述了linux内核参数设置及linux内核的功能。
    发表于 09-17 14:40 1362次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>内核</b>参数设置_<b class='flag-5'>linux</b><b class='flag-5'>内核</b>的功能有哪些

    Linux内核的编译与运行

    本文档的主要内容详细介绍的是Linux内核的编译与运行免费下载。
    发表于 03-25 13:48 11次下载

    Linux内核结构介绍

    通常情况下,Linux内核的结构被认为包含以下11个主要层次。
    的头像 发表于 04-14 11:59 1231次阅读

    万千设备,linux内核如何知道?

    linux内核设备的注册由device_register()函数完成,这个函数是linux设备驱动模型的核心函数
    的头像 发表于 07-12 08:52 796次阅读
    万千设备,<b class='flag-5'>linux</b><b class='flag-5'>内核</b>如何知道?

    使用 PREEMPT_RT 在 Ubuntu 构建实时 Linux 内核

    的实时内核补丁来完成。简介我们曾介绍过在Ubuntu22.04启用实时Linux内核有多简单,因为Canonical已将该
    的头像 发表于 04-12 08:36 2203次阅读
    使用 PREEMPT_RT 在 Ubuntu <b class='flag-5'>中</b>构建实时 <b class='flag-5'>Linux</b> <b class='flag-5'>内核</b>

    linux内核通用HID触摸驱动

    linux内核,为HID触摸面板实现了一个通用的驱动程序,位于/drivers/hid/hid-multitouch.c文件。hid触摸驱动是以struct hid_
    的头像 发表于 10-29 10:55 235次阅读
    <b class='flag-5'>linux</b><b class='flag-5'>内核</b><b class='flag-5'>中</b>通用HID触摸驱动