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


<script>window._bd_share_config={"common":{"bdSnsKey":{},"bdText":"","bdMini":"2","bdMiniList":false,"bdPic":"","bdStyle":"0","bdSize":"16"},"share":{}};with(document)0[(getElementsByTagName('head')[0]||body).appendChild(createElement('script')).src='http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion='+~(-new Date()/36e5)];</script>

阅读(62) | 评论(0) | 转发(0) |

linux设备驱动归纳总结(八):2.match.probe.remove的更多相关文章

  1. linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  2. 【Linux】linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授 ...

  3. linux设备驱动归纳总结(八):4.总线热插拔【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110774.html linux设备驱动归纳总结(八):4.总线热插拔 xxxxxxxxxxxxxxx ...

  4. linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-110738.html linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想 xxxxxx ...

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

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

  6. linux设备驱动归纳总结(八):1.总线、设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-109733.html linux设备驱动归纳总结(八):1.总线.设备和驱动 xxxxxxxxxxxx ...

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

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

  8. 【Linux开发】linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想

    linux设备驱动归纳总结(八):3.设备管理的分层与面向对象思想 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

  9. 【Linux开发】linux设备驱动归纳总结(八):4.总线热插拔

    linux设备驱动归纳总结(八):4.总线热插拔 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ...

随机推荐

  1. zabbix4.2学习笔记--zabbix安装

    环境 系统信息 发行版 版本 ip 关系 主机名 centos 7.5 192.168.181.135 服务端 server centos 7.5 192.168.181.136 客户端 client ...

  2. C# WebService 的缓存机制

    C# WebService 的缓存机制   [转]WebService的缓存机制 2008年02月19日 星期二 11:22 WebService的缓存分为两种,一种是简单的输出缓存,一种是强大的数据 ...

  3. js判断图片是否有效

    var ImgObj=new Image(); ImgObj.src= 'http://192.168.10.6:8082/3D/SERVER_1_DELL_880.jpg'; if(ImgObj.f ...

  4. BZOJ 3876 支线剧情 有源汇有上下界最小费用可行流

    题意: 给定一张拓扑图,每条边有边权,每次只能从第一个点出发沿着拓扑图走一条路径,求遍历所有边所需要的最小边权和 分析: 这道题乍一看,可能会想到什么最小链覆盖之类的,但是仔细一想,会发现不行,一是因 ...

  5. 用SQLyog或Navicat远程连接数据库

    以SQLyog为例(Navicat同理): 登录远程数据库服务器查看当前存在用户:即点击用户管理器(人像图标),查看用户. 1)如果某一用户 主机一栏中是"%",则表示本用户是开放 ...

  6. Centos7配置ThinkPHP5.0完整过程(一)

    在Centos中配置PHP服务器环境,首先要安装Apache的http服务,然后安装php解析环境,最后再配置ThinkPHP5.0. 首先安装HTTP sudo yum install httpd ...

  7. Spring接收web请求参数的几种方式

    1 查询参数 请求格式:url?参数1=值1&参数2=值2...同时适用于GET和POST方式spring处理查询参数的方法又有几种写法: 方法一:方法参数名即为请求参数名 // 查询参数1 ...

  8. [Python3网络爬虫开发实战] 1.3.4-tesserocr的安装

    在爬虫过程中,难免会遇到各种各样的验证码,而大多数验证码还是图形验证码,这时候我们可以直接用OCR来识别. 1. OCR OCR,即Optical Character Recognition,光学字符 ...

  9. MySQL-----连表

    连表: **拿到两张表的信息** select * from userinfo,department 弊端是数据会乱,出现重复,不建议这样. **使userinfo表的part_id列与departm ...

  10. 语法,if,while循环,for循环

    目录 一.语法 二.while循环 三.for循环 一.语法 if: if判断其实是在模拟人做判断.就是说如果这样干什么,如果那样干什么.对于ATM系统而言,则需要判断你的账号密码的正确性. if 条 ...