linux设备驱动归纳总结:总线、设备和驱动的关系(续)
xxxxxxxxxxxxxxxxxxxxxxxx
上一节介绍了总线、设备和驱动函数的注册,这节着重介绍它们三者的关系,和上一节一样,我模拟一条usb总线,一个usb鼠标设备和一个usb鼠标驱动函数,当然,只是名字是usb,里面并没有实质的操作,只是通过这样来介绍一下三者之间的关系。。
xxxxxxxxxxxxxxxxxxxxxxxx
一、总线、设备和驱动函数在/sys/中的框架
首先要写三个函数,bus.c、device.c和driver.c。这几个函数其实就是上一节函数的精简版,去掉属性文件的创建,仅仅保留创建和注销操作。
第一个函数是bus.c,加载模块会创建了一条名叫usb的总线,总线目录放在/sys/bus/目录下:
6 structbus_type usb_bus = {
7 .name ="usb",
8};
9
10static int __init usb_bus_init(void)
11{
12int ret;
13
14 ret =bus_register(&usb_bus);
15if(ret){
16printk("bus register failed!\n");
17return ret;
18}
19
20printk("usb bus init\n");
21return 0;
22}
23
24static void __exit usb_bus_exit(void)
25{
26bus_unregister(&usb_bus);
27printk("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函数只是打印了一句话。
5extern struct bus_type usb_bus;
6
7void usb_dev_release(struct device *dev) //卸载函数没有干具体的事情
8{
9printk("<kernel>release\n");
10}
11
12 structdevice usb_device = {
13 .bus_id ="usb_device",
14 .bus =&usb_bus,
15 .release =usb_dev_release, //必须要都有release函数,不然卸载时会出错
16};
17
18static int __init usb_device_init(void)
19{
20int ret;
21
23 ret =device_register(&usb_device);
24if(ret){
25printk("device register failed!\n");
26return ret;
27}
28
29printk("usb device init\n");
30return 0;
31}
32
33static void __exit usb_device_exit(void)
34{
35device_unregister(&usb_device);
36printk("usb device bye!\n");
37}
第三个函数是driver.c,加载模块后会在指定的总线目录的driver目录,即/sys/bus/usb/driver目录下创建一个名叫usb_driver的目录来管理这个驱动函数。
6extern struct bus_type usb_bus;
7
8 structdevice_driver usb_driver = {
9 .name ="usb_driver", //在/sys/中的驱动目录名字
10 .bus =&usb_bus,
11};
12
13static int __init usb_driver_init(void)
14{
15int ret;
16
17 ret =driver_register(&usb_driver);
18if(ret){
19printk("driver register failed!\n");
20return ret;
21}
22printk("usb driver init\n");
23return 0;
24}
25
26static void __exit usb_driver_exit(void)
27{
28driver_unregister(&usb_driver);
29printk("usb driver bye!\n");
30}
接下来看看效果,因为设备和驱动的都指定了所属总线,所以必须先加载总线的模块。同样的,在卸载总线的模块前,必须先把设备和驱动的模块先卸载。
[root: 1st]#insmod bus.ko //先加载bus.ko
usbbus init
[root: 1st]# ls/sys/bus/ //sys/bus目录下多了一个usb目录
platformscsi
[root: 1st]#insmod device.ko //再加载device.ko
usbdevice init
[root: 1st]# ls/sys/devices/ //sys/device目录下多了一个usb_device目录
platformsystem
[root: 1st]# ls-l /sys/bus/usb/devices/ //同时将该目录软连接到指定的总线目录下
lrwxrwxrwx 1root root 0 Oct 27 13:28
[root: 1st]#insmod driver.ko //加载driver.ko
usbdriver 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
bus1336
[root: 1st]#rmmod bus //如果你要卸载bus模块,它会提示出错,要先卸载driver和device
rmmod: remove'bus': Resource temporarily unavailable
[root: 1st]#rmmod driver
usbdriver bye!
[root: 1st]#rmmod device //卸载device时,内核自动调用device结构体中指定的release函数
<kernel>release
usbdevice bye!
[root: 1st]#lsmod //bus的引用计数为0,可以卸载bus了
bus1336
[root: 1st]#rmmod bus
usbbus bye!
最后来个图:
xxxxxxxxxxxxxxxxxxxxxxxx
二、配对函数(match)、探测函数(probe)和卸载函数(remove)
现在讲一下三个函数:
第一个是配对函数(match),它是总线结构体bus_type的其中一个成员:
57int (*match)(struct device *dev, struct device_driver*drv);
当总线上添加了新设备或者新驱动函数的时候,内核会调用一次或者多次这个函数。
举例,如果我现在添加了一个新的驱动函数,内核就会调用所属总线的match函数,配对总线上所有的设备,如果驱动能够处理其中一个设备,函数返回0,告诉内核配对成功。
一般的,match函数是判断设备的结构体成员device->bus_id和驱动函数的结构体成员device_driver->name是否一致,如果一致,那就表明配对成功。
所以,bus.c修改如下,贴上修改的代码:
6int
7{
8if(!strcmp(dev->bus_id,drv->name)){
9printk("match success\n"); //为了配对成功,设备的bus_id和驱动的name我都更改为
10return 1;
11}else{
12printk("match failed\n");
13return 0;
14}
15}
16
17struct bus_type usb_bus = {
18.name = "usb",
19.match =usb_bus_match,
20};
第二个是探测函数(probe),它是驱动函数结构体中的一个成员:
129int (*probe) (struct device *dev);
当配对(match)成功后,内核就会调用指定驱动中的probe函数来查询设备能否被该驱动操作,如果可以,驱动就会对该设备进行相应的操作,如初始化。所以说,真正的驱动函数入口是在probe函数中。
所以,driver.c修改如下:
8void init_mouse(void)
9{
10printk("init usb mouse\n");
11}
12
13 intusb_driver_probe(struct device *dev)
14{//查询特定设备是否存在,以及是否能够才操作该设备,然后再进行设备操作。
15//check_mouse(); //自己假设一下检查设备
16init_mouse(); //usb鼠标驱动的真正入口
17return 0;
18}
。。。。。
26struct device_driver usb_driver = {
27.name = "usb_mouse",//在/sys/中的驱动目录名字,为了配对成功,修改为usb_mouse
28.bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。
29
30
31};
第三个是卸载函数(remove),它是驱动函数结构体中的一个成员:
130int (*remove) (struct device *dev);
当该驱动函数或者驱动函数正在操作的设备被移除时,内核会调用驱动函数中的remove函数调用,进行一些设备卸载相应的操作。
所以,driver.c修改如下:
20int
21{
22printk("remove mouse driver\n");
23return 0;
24}
25
26struct device_driver usb_driver = {
27.name = "usb_mouse", //在/sys/中的驱动目录名字
28.bus = &usb_bus, //必须指定驱动函数所属总线,不然不能注册。
29.probe = usb_driver_probe,
30
31};
接下来就要验证一下了。当然,我的函数里面并没有真正的硬件操作,仅仅是打印出一句话:
[root: 2nd]#insmod bus.ko //必须先加载总线模块
usbbus init
[root: 2nd]#insmod device.ko
usbdevice init
[root: 2nd]#insmod driver.ko
matchsuccess
match success//并且配对成功。
init usbmouse
usbdriver init
[root: 2nd]#rmmod device
remove mousedriver
<kernel>release //同时也会调用设备中的release函数调用
usbdevice bye!
[root: 2nd]#rmmod driver
usbdriver bye!
又到了举例时间,程序员最喜欢就是男女关系,那就以男女关系举例:
xxxxxxxxxxxxxxxxxxxxxxxx
三、多个设备和驱动之间的配对
上面讲的内容都是一对一的配对,但是如果系统中有多个配对成功,内核会如何处理呢?
1、多个设备对应一个驱动:
下面要讲的情况是,如果多个设备与内核中的一个驱动函数配对成功时,内核会进行怎么样的操作,先看实例。
为了能够让多个设备配对成功,我将bus.c的配对条件修改了一下:
6int usb_bus_match(struct device *dev, struct device_driver*drv)
7 {//仅仅配对名字的前9个字母是否相同
8if(!strncmp(dev->bus_id,drv->name, 9)){
9printk("match success\n");
10return 1;
11}else{
12printk("match failed\n");
13return 0;
14}
15}
同时在device.c的基础上拷贝了device1.c和device2.c,三个程序都差不多,可以自己看看。接下来直接看效果:
[root: /]# cd/review_driver/8th_devModule/8th_devModule_2/3th
[root: 3th]#insmod bus.ko //先加载总线
usbbus init
[root: 3th]#insmod driver.ko //再加载驱动
usbdriver init
[root: 3th]#insmod device.ko //当加载device.ko时,配对成功
matchsuccess
init usbmouse
usbdevice init
[root: 3th]#insmod device1.ko //再加载device1.ko,也配对成功
match success
init usbmouse
usbdevice1 init
[root: 3th]#insmod device2.ko //加载device2.ko,配对不成功
matchfailed
usbdevice2 init
上面的验证表明,一个驱动可以对应多个设备。在联想起我举得男人女人——一个男人可以配对多个女人,哈哈。
2、一个设备对应多个驱动
这个例子中我将driver.c拷贝多了一个driver1.c,两个程序基本相同,都能配对成功,但看看效果:
[root: 3th]#insmod bus.ko //先加载总线
usbbus init
[root: 3th]#insmod device.ko //再加载设备
usbdevice init
[root: 3th]#insmod driver.ko //加载driver.ko
matchsuccess
matchsuccess
init usb mouse//并且调用了probe
usbdriver init
[root: 3th]#insmod driver1.ko //再加载driver1.ko
match success//因为名字的前9个字母一样,所以也会配对成功
usb driver1init
上面的验证表明,一个设备只能对应一个驱动。
xxxxxxxxxxxxxxxxxxxxxxxx
四、总结
这节内容主要介绍了总线和驱动中的几个方法和总线、设备和驱动函数三者之间的关系。
xxxxxxxxxxxxxxxxxxxxxxxx
源代码: