現在的位置: 首頁 > 綜合 > 正文

linux設備驅動歸納總結(八):2.匯流排、設備和驅動的關係

2018年03月16日 ⁄ 綜合 ⁄ 共 7634字 ⁄ 字型大小 評論關閉

原文地址:linux設備驅動歸納總結(八):2.match.probe.remove
http://bbs.chinaunix.net/forum.php?mod=viewthread&tid=3631834&fromuid=28801784

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

上一節介紹了匯流排、設備和驅動函數的註冊,這節著重介紹它們三者的關係,和上一節一樣,我模擬一條usb匯流排,一個usb滑鼠設備和一個usb滑鼠驅動函數,當然,只是名字是usb,裡面並沒有實質的操作,只是通過這樣來介紹一下三者之間的關係。。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

一、匯流排、設備和驅動函數在/sys/中的框架

首先要寫三個函數,bus.cdevice.cdriver.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:28usb_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模塊被devicedriver引用

[root: 1st]# rmmod bus //如果你要卸載bus模塊,它會提示出錯,要先卸載driverdevice

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.cdriver.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.cdevice2.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

四、總結

這節內容主要介紹了匯流排和驅動中的幾個方法和匯流排、設備和驅動函數三者之間的關係。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

寫到現在,都差不多踏入新年了,不知道還有沒有人在看博客,預祝一下新年快樂。總結還沒寫完,年前任務沒完成。過年期間繼續複習。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

抱歉!評論已關閉.