linux设备驱动归纳总结(八):2.match.probe.remove

Linux设备、总线和驱动之间的关系 本文通过梳理Linux内核源码中的驱动、总线以及设备来帮助理解,Linux驱动与设备之间是通过怎样的方式联系在一起,系统在检测到设备之后怎么找到对应的驱动,驱动在挂载之后如何对应到具体的设备 阅读详情
linux 设备驱动归纳总结(八): 2. 总线、设备和驱动的关系


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条usb总线,一个usb鼠标设备和一个usb鼠标驱动函数,当然,只是名字是usb,里面并没有实质的操作,只是通过这样来介绍一下三者之间的关系。。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


一、总线、设备和驱动函数在/sys/中的框架


首先要写三个函数,bus.c、device.c和driver.c。这几个函数其实就是上一节函数的精简版,去掉属性文件的创建,仅仅保留创建和注销操作。

第一个函数是bus.c,加载模块会创建了一条名叫usb的总线,总线目录放在/sys/bus/目录下:

/*8th_devModule_2/1st/bus.c*/

6 struct bus_type usb_bus = {

7 .name = "usb",         //注册成功后将在/sys/bus目录下看到目录usb

8 };

9

10 static int __init usb_bus_init(void)

11 {

12 int ret;

13 /*总线注册,必须检测返回值*/

14 ret = bus_register(&usb_bus);

15 if(ret){

16 printk("bus register failed!\n");

17 return ret;

18 }

19

20 printk("usb bus init\n");

21 return 0;

22 }

23

24 static void __exit usb_bus_exit(void)

25 {

26 bus_unregister(&usb_bus);

27 printk("usb bus bye!\n");

28 }

第二个函数是device.c,加载模块会创建目录/sys/device/usb_device来管理这个usb设备。

由于该设备指定了所属的总线是usb_bus,所有会在/sys/bus/usb/device目录下创建一了指向usb_device的软连接。

同时,在卸载模块时,usb_deivce被删除,内核自动调用release函数,现实当中release函数应该做一些卸载设备的相关操作,但是我的usb设备是我虚拟出来的,所以release函数只是打印了一句话。

/*8th_devModule_2/1st/device.c*/

5 extern struct bus_type usb_bus;

6

7 void usb_dev_release(struct device *dev) //卸载函数没有干具体的事情

8 {

9 printk("<kernel> release\n");

10 }

11

12 struct device usb_device = {

13 .bus_id = "usb_device",

14 .bus = &usb_bus,                   //指定该设备的总线,在/sys/bus/usb

15 .release = usb_dev_release, //必须要都有release函数,不然卸载时会出错

16 };

17

18 static int __init usb_device_init(void)

19 {

20 int ret;

21 /*设备注册,注册成功后在/sys/device目录下创建目录usb_device并在指定总线

22 * usb_bus的目录/sys/bus/usb/device创建/sys/device/usb_device的软连接*/

23 ret = device_register(&usb_device);

24 if(ret){

25 printk("device register failed!\n");

26 return ret;

27 }

28

29 printk("usb device init\n");

30 return 0;

31 }

32

33 static void __exit usb_device_exit(void)

34 {

35 device_unregister(&usb_device);

36 printk("usb device bye!\n");

37 }

第三个函数是driver.c,加载模块后会在指定的总线目录的driver目录,即/sys/bus/usb/driver目录下创建一个名叫usb_driver的目录来管理这个驱动函数。

/*8th_devModule_2/1st/driver.c*/

6 extern struct bus_type usb_bus;

7

8 struct device_driver usb_driver = {

9 .name = "usb_driver", //在/sys/中的驱动目录名字

10 .bus = &usb_bus,      //必须指定驱动函数所属总线,不然不能注册。

11 };

12

13 static int __init usb_driver_init(void)

14 {

15 int ret;

16 /*驱动注册,注册成功后在/sys/bus/usb/driver目录下创建目录usb_driver*/

17 ret = driver_register(&usb_driver);

18 if(ret){

19 printk("driver register failed!\n");

20 return ret;

21 }

22 printk("usb driver init\n");

23 return 0;

24 }

25

26 static void __exit usb_driver_exit(void)

27 {

28 driver_unregister(&usb_driver);

29 printk("usb driver bye!\n");

30 }

接下来看看效果,因为设备和驱动的都指定了所属总线,所以必须先加载总线的模块。同样的,在卸载总线的模块前,必须先把设备和驱动的模块先卸载。

[root: 1st]# insmod bus.ko //先加载bus.ko

usb bus init

[root: 1st]# ls /sys/bus/ //sys/bus目录下多了一个usb目录

platform scsi usb

[root: 1st]# insmod device.ko //再加载device.ko

usb device init

[root: 1st]# ls /sys/devices/ //sys/device目录下多了一个usb_device目录

platform system usb_device virtual

[root: 1st]# ls -l /sys/bus/usb/devices/ //同时将该目录软连接到指定的总线目录下

lrwxrwxrwx 1 root root 0 Oct 27 13:28 usb_device -> ../../../devices/usb_device

[root: 1st]# insmod driver.ko //加载driver.ko

usb driver init

[root: 1st]# ls /sys/bus/usb/drivers //在指定总线的driver目录下多了一个usb_driver目录

usb_driver //但它和设备的目录不一样,它并不是软连接。

[root: 1st]# lsmod //查看一下当前加载的模块

driver 1256 0 - Live 0xbf00c000

device 1560 0 - Live 0xbf006000

bus 1336 2 driver,device, Live 0xbf000000 //模块的引用计数,bus模块被device和driver引用

[root: 1st]# rmmod bus //如果你要卸载bus模块,它会提示出错,要先卸载driver和device

rmmod: remove 'bus': Resource temporarily unavailable

[root: 1st]# rmmod driver

usb driver bye!

[root: 1st]# rmmod device //卸载device时,内核自动调用device结构体中指定的release函数

<kernel> release

usb device bye!

[root: 1st]# lsmod //bus的引用计数为0,可以卸载bus了

bus 1336 0 - Live 0xbf012000

[root: 1st]# rmmod bus

usb bus bye!

最后来个图:


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


二、配对函数(match)、探测函数(probe)和卸载函数(remove)


现在讲一下三个函数:


第一个是配对函数(match),它是总线结构体bus_type的其中一个成员:

57 int (*match)(struct device *dev, struct device_driver *drv);

当总线上添加了新设备或者新驱动函数的时候,内核会调用一次或者多次这个函数。

举例,如果我现在添加了一个新的驱动函数,内核就会调用所属总线的match函数,配对总线上所有的设备,如果驱动能够处理其中一个设备,函数返回0,告诉内核配对成功。

一般的,match函数是判断设备的结构体成员device->bus_id和驱动函数的结构体成员device_driver->name是否一致,如果一致,那就表明配对成功。

所以,bus.c修改如下,贴上修改的代码:

/*8th_devModule_2/2nd/bus.c*/

6 int usb_bus_match(struct device *dev, struct device_driver *drv)

7 {

8 if(!strcmp(dev->bus_id, drv->name)){

9 printk("match success\n"); //为了配对成功,设备的bus_id和驱动的name我都更改为

10 return 1;                             //usb_mouse,详细的可以查看device.c和driver.c

11 }else{

12 printk("match failed\n");

13 return 0;

14 }

15 }

16

17 struct bus_type usb_bus = {

18 .name = "usb",                    //注册成功后将在/sys/bus目录下看到目录usb

19 .match = usb_bus_match,

20 };


第二个是探测函数(probe),它是驱动函数结构体中的一个成员:

129 int (*probe) (struct device *dev);

当配对(match)成功后,内核就会调用指定驱动中的probe函数来查询设备能否被该驱动操作,如果可以,驱动就会对该设备进行相应的操作,如初始化。所以说,真正的驱动函数入口是在probe函数中。

所以,driver.c修改如下:

/*8th_devModule_2/2nd/driver.c*/

8 void init_mouse(void)

9 {

10 printk("init usb mouse\n");

11 }

12

13 int usb_driver_probe(struct device *dev)

14 {//查询特定设备是否存在,以及是否能够才操作该设备,然后再进行设备操作。

15 //check_mouse(); //自己假设一下检查设备

16 init_mouse(); //usb鼠标驱动的真正入口

17 return 0;

18 }

。。。。。

26 struct device_driver usb_driver = {

27 .name = "usb_mouse", //在/sys/中的驱动目录名字,为了配对成功,修改为usb_mouse

28 .bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。

29 .probe = usb_driver_probe,

30 。。。。。

31 };


第三个是卸载函数(remove),它是驱动函数结构体中的一个成员:

130 int (*remove) (struct device *dev);

当该驱动函数或者驱动函数正在操作的设备被移除时,内核会调用驱动函数中的remove函数调用,进行一些设备卸载相应的操作。

所以,driver.c修改如下:

/*8th_devModule_2/2nd/driver.c*/

20 int usb_driver_remove(struct device *dev)

21 {

22 printk("remove mouse driver\n");

23 return 0;

24 }

25

26 struct device_driver usb_driver = {

27 .name = "usb_mouse", //在/sys/中的驱动目录名字

28 .bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。

29 .probe = usb_driver_probe,

30 .remove = usb_driver_remove,

31 };

接下来就要验证一下了。当然,我的函数里面并没有真正的硬件操作,仅仅是打印出一句话:

[root: 2nd]# insmod bus.ko //必须先加载总线模块

usb bus init

[root: 2nd]# insmod device.ko

usb device init

[root: 2nd]# insmod driver.ko

match success //当加载了设备和驱动的模块后,内核调用总线的配对函数

match success //并且配对成功。

init usb mouse //配对成功后内核调用探测函数probe

usb driver init

[root: 2nd]# rmmod device

remove mouse driver //当设备卸载时,内核调用驱动函数中的remove

<kernel> release //同时也会调用设备中的release函数调用

usb device bye!

[root: 2nd]# rmmod driver

usb driver bye!

又到了举例时间,程序员最喜欢就是男女关系,那就以男女关系举例:


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


三、多个设备和驱动之间的配对


上面讲的内容都是一对一的配对,但是如果系统中有多个配对成功,内核会如何处理呢?


1、多个设备对应一个驱动:


下面要讲的情况是,如果多个设备与内核中的一个驱动函数配对成功时,内核会进行怎么样的操作,先看实例。

为了能够让多个设备配对成功,我将bus.c的配对条件修改了一下:

/*8th_devModule_2/3th/bus.c */

6 int usb_bus_match(struct device *dev, struct device_driver *drv)

7 { //仅仅配对名字的前9个字母是否相同

8 if(!strncmp(dev->bus_id, drv->name, 9)){

9 printk("match success\n");

10 return 1;

11 }else{

12 printk("match failed\n");

13 return 0;

14 }

15 }

同时在device.c的基础上拷贝了device1.c和device2.c,三个程序都差不多,可以自己看看。接下来直接看效果:

[root: /]# cd /review_driver/8th_devModule/8th_devModule_2/3th

[root: 3th]# insmod bus.ko //先加载总线

usb bus init

[root: 3th]# insmod driver.ko //再加载驱动

usb driver init

[root: 3th]# insmod device.ko //当加载device.ko时,配对成功

match success

init usb mouse //内核调用驱动中的probe

usb device init

[root: 3th]# insmod device1.ko //再加载device1.ko,也配对成功

match success

init usb mouse //内核有调用驱动中的probe

usb device1 init

[root: 3th]# insmod device2.ko //加载device2.ko,配对不成功

match failed

usb device2 init

上面的验证表明,一个驱动可以对应多个设备。在联想起我举得男人女人——一个男人可以配对多个女人,哈哈。


2、一个设备对应多个驱动


这个例子中我将driver.c拷贝多了一个driver1.c,两个程序基本相同,都能配对成功,但看看效果:

[root: 3th]# insmod bus.ko //先加载总线

usb bus init

[root: 3th]# insmod device.ko //再加载设备

usb device init

[root: 3th]# insmod driver.ko //加载driver.ko

match success //配对成功

match success

init usb mouse //并且调用了probe

usb driver init

[root: 3th]# insmod driver1.ko //再加载driver1.ko

match success //因为名字的前9个字母一样,所以也会配对成功

usb driver1 init //但不会调用probe,因为已经有一个驱动跟该设备配对了。

上面的验证表明,一个设备只能对应一个驱动。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


四、总结


这节内容主要介绍了总线和驱动中的几个方法和总线、设备和驱动函数三者之间的关系。


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

源代码: 8th_devModule_2.rar   

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

写到现在,都差不多踏入新年了,不知道还有没有人在看博客,预祝一下新年快乐。总结还没写完,年前任务没完成。过年期间继续复习。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


http://blog.chinaunix.net/uid-25014876-id-110295.html

告别枯燥的Unity编辑器脚本:Odin插件如何让游戏数据配置变得像搭积木一样简单? 本文详细介绍了Odin插件如何简化Unity编辑器脚本开发,使游戏数据配置变得直观高效。通过声明式特性和丰富的可视化工具,Odin让开发者告别繁琐的OnGUI代码,实现所见即所得的开发体验。文章重点展示了Odin的核心优势、魔法特性及实战案例,帮助Unity开发者提升工作效率。 阅读详情

相关推荐

UE4载具性能调优指南:从参数设置到实战优化

本文详细解析了UE4载具性能调优的关键技巧,从WheeledVehicle类构建到轮子参数、传动系统设置,再到物理属性与操控调校。通过实战案例分享,帮助开发者优化载具的速度、加速度等核心性能,提升游戏体验。特别适合需要精细调校载具物理特性的开发者参考。

weixin_29019241的博客 469

总线,设备,驱动之间关系

总线,设备,驱动之间关系 再看I2C驱动迷茫时候看到《 从需求的角度去理解Linux系列:总线、设备和驱动 》,感觉豁然开朗,根据博客内容简单总结一下。 1、总线 总线代表同类设备需要共同遵守的时序,不同总线硬件的通信时序也是不同的,如I2c总线、USB总线、PCI总线…… 2、设备 设备代表真实存在的物理器件,每个器件有自己不同的通信时序,I2C、USB这些都代表不同的时序,这...

Kian_G 的博客 8590

C# NX二次开发:两种过滤器设置方案的实战解析与场景适配

本文深入解析C# NX二次开发中的两种过滤器设置方案:AddFilterHandler回调与MaskTriple属性设置。通过实战案例对比其性能差异与适用场景,帮助开发者根据需求选择最优方案,提升NX插件开发效率与交互体验。特别针对BlockStyler集成场景提供专业建议,是NX二次开发者的实用指南。

weixin_33739646的博客 844

设备驱动的probe、remove以及shutdown的顺序

转自http://blog.chinaunix.net/uid-29955651-id-5175102.html 系统中存在很多设备,每个设备在kernel中都有对应的驱动,那么这些驱动初始化设备以及关闭设备的顺序是怎样的呢?本文试图解答这个问题。 1 驱动probe/remove执行时机 probe的执行有两个时机,一是设备创建时,二是驱动注册时;remove相对也有两个执行时机,一是设备

qwaszx523的博客 1万+

海康VisionMaster图形定位实战:十字形Mark点精准匹配技巧

本文深入解析了海康VisionMaster图形定位模块在工业自动化中实现十字形Mark点精准匹配的实战技巧。文章从ROI设置、核心参数调优(如定位方式、尺度模式、对比度阈值)到工业场景应用与避坑指南,系统性地分享了提升匹配成功率和稳定性的关键步骤,帮助工程师应对复杂现场环境,实现高效、可靠的视觉定位。

weixin_29285605的博客 259

检索生成(RAG) vs 长文本大模型:实际应用中如何选择?

大模型的上下文理解能力直接影响到 LLMs 在复杂任务和长对话中的表现。本期内容聚焦于两种主流技术:长上下文(Large Context Windows)和检索增强生成(RAG)。这两种技术各有何优势?在实际应用中,我们又该如何权衡选择?文章不仅详细阐述了这两种技术的工作原理,还深入分析了它们在效率、准确性和实施成本等方面的差异。文章特别提到了在执行重复性任务时使用 Context Caching 优化长上下文的优势,然而,缓存策略的效果很大程度上取决于提示词的可预测性。

2401_85782938的博客 1255

关于linux-设备驱动加载卸载

linux设备驱动 devices以及driver都是在系统启动的时候加载(probe外设初始化),在关机流程里卸载(remove),在系统运行过程中,可以通过设置work驱动具体设备,比如打开了相机才去操作相关camera寄存器,退出相机,停止对camera的寄存器操作。同样的,在 /sys/firmware/devicetree/base 下设备树的一堆节点,下面是mtk平台开机部分log,各种驱动probe都发生在开机的十几秒。节点下面还有具体挂载的外设,以及该节点的compatible信息等。

weixin_39223971的博客 562

告别交越失真:手把手教你设计高性能互补输出级电路(LTspice实测)

本文深入剖析音频放大器中常见的交越失真问题,并提供基于LTspice仿真的实战解决方案。通过对比二极管偏置、Vbe倍增器和集成运放驱动三种经典方案,手把手指导设计高性能互补输出级电路,涵盖原理分析、仿真验证及实物调试要点,助力消除音频失真,提升音质。

weixin_29190573的博客 552

linux删除设备驱动,linux设备驱动归纳总结(八):2.match.probe.remove

linux设备驱动归纳总结(八):2.总线、设备和驱动的关系xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条usb总线,一个usb鼠标设备和一个usb鼠标驱动函数,当然,只是名字是us...

weixin_32841641的博客 705

【操作系统】实验楼操作系统实验三——系统调用

操作系统实验3——系统调用 环境配置 ​ 首先同样是配置环境 cd /home/shiyanlou/oslab tar -zxvf hit-oslab-linux-20110823.tar.gz -C /home/shiyanlou 实验步骤 添加函数声明 cd ~/oslab/linux-0.11/include/linux gedit sys.h //添加内容: extern int sys_whoami(); extern int sys_iam(); fn_ptr_sys_call_table

qq_46612803的博客 6357

Unity布娃娃系统 Ragdoll Mecanim Mixer使用

6. 选完之后,左键下面的hip等角色各个部位不松开鼠标,拖拽连接到上面Amature的各个部位,如图,下面所有部位都连接完之后,第二步点击Configure Selected Bones确认你的部位连接选择。要做一个布娃娃和动画混合的项目,这个插件是比较不错的,搜了一圈文章教程也是抄来抄去,抄的没有头没有尾,自己摸索着用了一下,记录使用过程吧。8. 之后就点击步骤7图一中的Create Ragdoll,会发现player结构发生变化,如图。4. 添加完脚本之后会显示你的player结构,如图。

SuShengQuanshiW的博客 1649

总线,设备,驱动——(2)match,probe,remove函数

linux设备驱动归纳总结(八):2.总线、设备和驱动的关系 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条usb总线,一个usb鼠标设备和一个usb鼠标驱动函数,当然

manchestermi的博客 1130

财务惊呼“不可能”!Python 自动识别 PDF 发票,数据秒级录入系统

电子发票(原生 PDF):这种最爽,文字是可以选中的。我们用 PyPDF2 直接提取文本,速度快,准确率 100%。 纸质扫描/拍照发票:这种是图片。必须上 OCR(光学字符识别)。我们要用百度开源的 PaddleOCR,这是目前中文识别效果最好的开源库之一,吊打早已过气的 Tesseract。

BUG猿的博客 1024

linux设备驱动归纳总结(八):2.总线、设备和驱动的关系

原文地址:linux设备驱动归纳总结(八):2.match.probe.remove http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3631834&fromuid=28801784 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

yasinEmb的专栏 836

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

linux设备驱动归纳总结(八):2.总线、设备和驱动的关系【转】

本文转载自:http://blog.chinaunix.net/uid-25014876-id-110295.html linux设备驱动归纳总结(八):2.总线、设备和驱动的关系 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 上一节介绍了总线、设备...

152

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

linux设备驱动归纳总结:2.match.probe.remove

http://blog.chinaunix.net/uid-25014876-id-110295.html xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条u

idea的博客 446

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

上一篇: 使用dialog写图形自动化shell脚本
下一篇: Qt学习之路(17): Qt标准对话框之QMessageBox
常用技术
博客等级 码龄17年 91粉丝 100原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值