linux設(shè)備驅(qū)動歸納總結(jié)(八):2.總線、設(shè)備和驅(qū)動的關(guān)系
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
上一節(jié)介紹了總線、設(shè)備和驅(qū)動函數(shù)的注冊,這節(jié)著重介紹它們?nèi)叩年P(guān)系,和上一節(jié)一樣,我模擬一條usb總線,一個usb鼠標(biāo)設(shè)備和一個usb鼠標(biāo)驅(qū)動函數(shù),當(dāng)然,只是名字是usb,里面并沒有實(shí)質(zhì)的操作,只是通過這樣來介紹一下三者之間的關(guān)系。。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、總線、設(shè)備和驅(qū)動函數(shù)在/sys/中的框架
首先要寫三個函數(shù),bus.c、device.c和driver.c。這幾個函數(shù)其實(shí)就是上一節(jié)函數(shù)的精簡版,去掉屬性文件的創(chuàng)建,僅僅保留創(chuàng)建和注銷操作。
第一個函數(shù)是bus.c,加載模塊會創(chuàng)建了一條名叫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 }
第二個函數(shù)是device.c,加載模塊會創(chuàng)建目錄/sys/device/usb_device來管理這個usb設(shè)備。
由于該設(shè)備指定了所屬的總線是usb_bus,所有會在/sys/bus/usb/device目錄下創(chuàng)建一了指向usb_device的軟連接。
同時,在卸載模塊時,usb_deivce被刪除,內(nèi)核自動調(diào)用release函數(shù),現(xiàn)實(shí)當(dāng)中release函數(shù)應(yīng)該做一些卸載設(shè)備的相關(guān)操作,但是我的usb設(shè)備是我虛擬出來的,所以release函數(shù)只是打印了一句話。
/*8th_devModule_2/1st/device.c*/
5 extern struct bus_type usb_bus;
6
7 void usb_dev_release(struct
device *dev) //卸載函數(shù)沒有干具體的事情
8 {
9 printk("<kernel>
release\n");
10 }
11
12 struct
device usb_device = {
13
.bus_id = "usb_device",
14 .bus =
&usb_bus, //指定該設(shè)備的總線,在/sys/bus/usb
15
.release = usb_dev_release, //必須要都有release函數(shù),不然卸載時會出錯
16 };
17
18 static int __init
usb_device_init(void)
19 {
20 int ret;
21
/*設(shè)備注冊,注冊成功后在/sys/device目錄下創(chuàng)建目錄usb_device并在指定總線
22 *
usb_bus的目錄/sys/bus/usb/device創(chuàng)建/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 }
第三個函數(shù)是driver.c,加載模塊后會在指定的總線目錄的driver目錄,即/sys/bus/usb/driver目錄下創(chuàng)建一個名叫usb_driver的目錄來管理這個驅(qū)動函數(shù)。
/*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/中的驅(qū)動目錄名字
10 .bus =
&usb_bus, //必須指定驅(qū)動函數(shù)所屬總線,不然不能注冊。
11 };
12
13 static int __init
usb_driver_init(void)
14 {
15 int ret;
16
/*驅(qū)動注冊,注冊成功后在/sys/bus/usb/driver目錄下創(chuàng)建目錄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 }
接下來看看效果,因?yàn)樵O(shè)備和驅(qū)動的都指定了所屬總線,所以必須先加載總線的模塊。同樣的,在卸載總線的模塊前,必須先把設(shè)備和驅(qū)動的模塊先卸載。
[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
//但它和設(shè)備的目錄不一樣,它并不是軟連接。
[root: 1st]# lsmod //查看一下當(dāng)前加載的模塊
driver 1256 0 - Live 0xbf00c000
device 1560 0 - Live 0xbf006000
bus 1336 2
driver,device, Live 0xbf000000
//模塊的引用計(jì)數(shù),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時,內(nèi)核自動調(diào)用device結(jié)構(gòu)體中指定的release函數(shù)
<kernel>
release
usb device bye!
[root: 1st]# lsmod
//bus的引用計(jì)數(shù)為0,可以卸載bus了
bus 1336 0
- Live 0xbf012000
[root: 1st]# rmmod bus
usb bus bye!
最后來個圖:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、配對函數(shù)(match)、探測函數(shù)(probe)和卸載函數(shù)(remove)
現(xiàn)在講一下三個函數(shù):
第一個是配對函數(shù)(match),它是總線結(jié)構(gòu)體bus_type的其中一個成員:
57 int (*match)(struct device
*dev, struct device_driver *drv);
當(dāng)總線上添加了新設(shè)備或者新驅(qū)動函數(shù)的時候,內(nèi)核會調(diào)用一次或者多次這個函數(shù)。
舉例,如果我現(xiàn)在添加了一個新的驅(qū)動函數(shù),內(nèi)核就會調(diào)用所屬總線的match函數(shù),配對總線上所有的設(shè)備,如果驅(qū)動能夠處理其中一個設(shè)備,函數(shù)返回0,告訴內(nèi)核配對成功。
一般的,match函數(shù)是判斷設(shè)備的結(jié)構(gòu)體成員device->bus_id和驅(qū)動函數(shù)的結(jié)構(gòu)體成員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");
//為了配對成功,設(shè)備的bus_id和驅(qū)動的name我都更改為
10 return
1; //usb_mouse,詳細(xì)的可以查看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 };
第二個是探測函數(shù)(probe),它是驅(qū)動函數(shù)結(jié)構(gòu)體中的一個成員:
129 int (*probe) (struct device
*dev);
當(dāng)配對(match)成功后,內(nèi)核就會調(diào)用指定驅(qū)動中的probe函數(shù)來查詢設(shè)備能否被該驅(qū)動操作,如果可以,驅(qū)動就會對該設(shè)備進(jìn)行相應(yīng)的操作,如初始化。所以說,真正的驅(qū)動函數(shù)入口是在probe函數(shù)中。
所以,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
{//查詢特定設(shè)備是否存在,以及是否能夠才操作該設(shè)備,然后再進(jìn)行設(shè)備操作。
15 //check_mouse();
//自己假設(shè)一下檢查設(shè)備
16 init_mouse();
//usb鼠標(biāo)驅(qū)動的真正入口
17 return 0;
18 }
。。。。。
26 struct device_driver usb_driver
= {
27 .name = "usb_mouse",
//在/sys/中的驅(qū)動目錄名字,為了配對成功,修改為usb_mouse
28 .bus = &usb_bus,
//必須指定驅(qū)動函數(shù)所屬總線,不然不能注冊。
29 .probe
= usb_driver_probe,
30 。。。。。
31 };
第三個是卸載函數(shù)(remove),它是驅(qū)動函數(shù)結(jié)構(gòu)體中的一個成員:
130 int (*remove) (struct device
*dev);
當(dāng)該驅(qū)動函數(shù)或者驅(qū)動函數(shù)正在操作的設(shè)備被移除時,內(nèi)核會調(diào)用驅(qū)動函數(shù)中的remove函數(shù)調(diào)用,進(jìn)行一些設(shè)備卸載相應(yīng)的操作。
所以,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/中的驅(qū)動目錄名字
28 .bus = &usb_bus,
//必須指定驅(qū)動函數(shù)所屬總線,不然不能注冊。
29 .probe = usb_driver_probe,
30
.remove = usb_driver_remove,
31 };
接下來就要驗(yàn)證一下了。當(dāng)然,我的函數(shù)里面并沒有真正的硬件操作,僅僅是打印出一句話:
[root: 2nd]# insmod bus.ko
//必須先加載總線模塊
usb bus init
[root: 2nd]# insmod device.ko
usb device init
[root: 2nd]# insmod driver.ko
match
success
//當(dāng)加載了設(shè)備和驅(qū)動的模塊后,內(nèi)核調(diào)用總線的配對函數(shù)
match success
//并且配對成功。
init usb
mouse
//配對成功后內(nèi)核調(diào)用探測函數(shù)probe
usb driver init
[root: 2nd]# rmmod device
remove mouse
driver
//當(dāng)設(shè)備卸載時,內(nèi)核調(diào)用驅(qū)動函數(shù)中的remove
<kernel>
release
//同時也會調(diào)用設(shè)備中的release函數(shù)調(diào)用
usb device bye!
[root: 2nd]# rmmod driver
usb driver bye!
又到了舉例時間,程序員最喜歡就是男女關(guān)系,那就以男女關(guān)系舉例: 
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、多個設(shè)備和驅(qū)動之間的配對
上面講的內(nèi)容都是一對一的配對,但是如果系統(tǒng)中有多個配對成功,內(nèi)核會如何處理呢?
1、多個設(shè)備對應(yīng)一個驅(qū)動:
下面要講的情況是,如果多個設(shè)備與內(nèi)核中的一個驅(qū)動函數(shù)配對成功時,內(nèi)核會進(jìn)行怎么樣的操作,先看實(shí)例。
為了能夠讓多個設(shè)備配對成功,我將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的基礎(chǔ)上拷貝了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
//再加載驅(qū)動
usb driver init
[root: 3th]# insmod device.ko
//當(dāng)加載device.ko時,配對成功
match success
init usb
mouse
//內(nèi)核調(diào)用驅(qū)動中的probe
usb device init
[root: 3th]# insmod device1.ko
//再加載device1.ko,也配對成功
match
success
init usb
mouse
//內(nèi)核有調(diào)用驅(qū)動中的probe
usb device1 init
[root: 3th]# insmod device2.ko
//加載device2.ko,配對不成功
match failed
usb device2 init
上面的驗(yàn)證表明,一個驅(qū)動可以對應(yīng)多個設(shè)備。在聯(lián)想起我舉得男人女人——一個男人可以配對多個女人,哈哈。
2、一個設(shè)備對應(yīng)多個驅(qū)動
這個例子中我將driver.c拷貝多了一個driver1.c,兩個程序基本相同,都能配對成功,但看看效果:
[root: 3th]# insmod bus.ko
//先加載總線
usb bus init
[root: 3th]# insmod device.ko
//再加載設(shè)備
usb device init
[root: 3th]# insmod driver.ko
//加載driver.ko
match
success
//配對成功
match success
init usb mouse //并且調(diào)用了probe
usb driver init
[root: 3th]#
insmod driver1.ko
//再加載driver1.ko
match
success
//因?yàn)槊值那?font face="DejaVu Serif, serif">9個字母一樣,所以也會配對成功
usb
driver1 init
//但不會調(diào)用probe,因?yàn)橐呀?jīng)有一個驅(qū)動跟該設(shè)備配對了。
上面的驗(yàn)證表明,一個設(shè)備只能對應(yīng)一個驅(qū)動。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、總結(jié)
這節(jié)內(nèi)容主要介紹了總線和驅(qū)動中的幾個方法和總線、設(shè)備和驅(qū)動函數(shù)三者之間的關(guān)系。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼: 8th_devModule_2.rar
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
寫到現(xiàn)在,都差不多踏入新年了,不知道還有沒有人在看博客,預(yù)祝一下新年快樂。總結(jié)還沒寫完,年前任務(wù)沒完成。過年期間繼續(xù)復(fù)習(xí)。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx |