linux設(shè)備驅(qū)動歸納總結(jié)(八):1.總線、設(shè)備和驅(qū)動
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
這幾天一直在看設(shè)備模型,內(nèi)核的代碼看得我越來越沮喪,特別是kboject、kset和ktype之間的關(guān)系。但是,設(shè)備模型的歸納我打算先跳過這幾個重要結(jié)構(gòu)體,先介紹總線、設(shè)備和驅(qū)動——設(shè)備管理的相關(guān)內(nèi)容。先介紹如何使用,有機會介紹大概的原理。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、sysfs文件系統(tǒng)
設(shè)備模型是2.6內(nèi)核新引入的特征。設(shè)備模型提供了一個獨立的機制專門來表示設(shè)備,并描述其在系統(tǒng)中的拓撲結(jié)構(gòu)。
在2.4內(nèi)核中,設(shè)備的信息放在/proc中。
而在2.6內(nèi)核,內(nèi)核把設(shè)備相關(guān)的信息歸類在新增加sysfs文件系統(tǒng),并將它掛載到/sys目錄中,把設(shè)備信息歸類的同時,讓用戶可以通過用戶空間訪問。
接下來簡單介紹一些sys中的目錄:
block:用于管理塊設(shè)備,系統(tǒng)中的每一個塊設(shè)備會在該目錄下對應(yīng)一個子目錄。
bus:用于管理總線,沒注冊一條總線,在該目錄下有一個對應(yīng)的子目錄。
其中,每個總線子目錄下會有兩個子目錄:devices和drivers。
devices包含里系統(tǒng)中所有屬于該總線的的設(shè)備。
drivers包含里系統(tǒng)中所有屬于該總線的的驅(qū)動。
class:將系統(tǒng)中的設(shè)備按功能分類。
devices:該目錄提供了系統(tǒng)中設(shè)備拓撲結(jié)構(gòu)圖。
dev:該目錄已注冊的設(shè)備節(jié)點的視圖。
kernel:內(nèi)核中的相關(guān)參數(shù)。
module:內(nèi)核中的模塊信息。
fireware:內(nèi)核中的固件信息。
Fs:描述內(nèi)核中的文件系統(tǒng)。
上面的目錄,接下來的章節(jié)會常常提起bus和device。
再說說這些目錄,來個簡單的命令:
root@xiaobai-laptop:/sys# ll
class/net/eth0
lrwxrwxrwx 1 root root 0 2011-01-31
10:11 class/net/eth0 ->
../../devices/pci0000:00/0000:00:1c.5/0000:86:00.0/net/eth0/
上面的命令也可以看到class/net/eth0的路徑其實就是devices目錄中一個網(wǎng)卡設(shè)備的軟連接。
貼個書上的圖: 
由上面兩個例子看到,sys中的其他目錄都是將devvice目錄下的數(shù)據(jù)加以轉(zhuǎn)換加工而得。上面的圖中,將use設(shè)備歸類到bus總線上,又把它歸類到class。正是在sys中有很多這樣的結(jié)構(gòu),內(nèi)核就有一個完整而且復(fù)雜的拓撲結(jié)構(gòu)圖。
而維護這些關(guān)系的結(jié)構(gòu)體就包括kobject、kset、ktype和subsystem等數(shù)據(jù)結(jié)構(gòu),不過這里就先不介紹。
通過設(shè)備模型,內(nèi)核就能實現(xiàn)多種不同的任務(wù),如:
1、電源管理和系統(tǒng)關(guān)機。
2、與用戶空間通信。這個就比較容易理解,sys目錄向用戶展示了設(shè)備模型的結(jié)構(gòu)圖。
3、熱插拔設(shè)備。大概意思就是,當設(shè)備插入后,內(nèi)核會根據(jù)插入的設(shè)備安裝驅(qū)動,設(shè)備拔出后,內(nèi)核又會自動卸載驅(qū)動。
4、設(shè)備類型。將設(shè)備歸類。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
在接下來的內(nèi)容會簡單介紹總線、設(shè)備和驅(qū)動程序的概念和函數(shù)調(diào)用,以下的函數(shù)我將模擬創(chuàng)建一條ubs總線,一個usb設(shè)備和一個usb驅(qū)動。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、總線
總線是處理器和設(shè)備之間的通道,在設(shè)備模型中,所有的設(shè)備都通過總線相連,以總線來管理設(shè)備和驅(qū)動函數(shù)?偩有bus_type結(jié)構(gòu)表示。
/*linux/device.h*/
51 struct bus_type {
52 const
char *name;
53 struct
bus_attribute *bus_attrs;
54 struct device_attribute
*dev_attrs;
55 struct driver_attribute
*drv_attrs;
56
57 int
(*match)(struct device *dev, struct device_driver *drv);
58 int
(*uevent)(struct device *dev, struct kobj_uevent_env *env);
59 int
(*probe)(struct device *dev);
60 int (*remove)(struct device
*dev);
61 void (*shutdown)(struct
device *dev);
62
63 int (*suspend)(struct device
*dev, pm_message_t state);
64 int (*suspend_late)(struct
device *dev, pm_message_t state);
65 int (*resume_early)(struct
device *dev);
66 int (*resume)(struct device
*dev);
67
68 struct dev_pm_ops *pm;
69
70 struct bus_type_private *p;
71 };
紅色部分是以后將會介紹的成員,其中name是總線的名字,
bus_attrs是總線的屬性,那些函數(shù)指針的操作總線的方法,在這一章節(jié)先不講總線的方法。
總線的注冊和刪除:
總線的注冊有兩個步驟:
1、定義一個bus_type結(jié)構(gòu)體,并設(shè)置好需要設(shè)置的結(jié)構(gòu)體成員。
2、調(diào)用函數(shù)bus_register注冊總線。函數(shù)原型如下:
/*drivers/base/bus.c*/
865 int bus_register(struct
bus_type *bus)
該調(diào)用有可能失敗,所以必須檢查它的返回值,如果注冊成功,會在/sys/bus下看到指定名字的總線。
總線刪除時調(diào)用:
/*drivers/base/bus.c*/
946 void bus_unregister(struct
bus_type *bus)
接下來貼個函數(shù):
/*8th_devModule_1/1st/bus.c*/
1 #include <linux/module.h>
2 #include <linux/init.h>
3
4 #include <linux/device.h>
5
6 struct
bus_type usb_bus = {
7 .name
= "usb", //定義總線的名字為usb,注冊成功后將在/sys/bus目錄下看到
8 };
//目錄usb,如果你的系統(tǒng)已經(jīng)有usb總線,那你就要換個名字。
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 }
29
30 module_init(usb_bus_init);
31 module_exit(usb_bus_exit);
32
33 MODULE_LICENSE("GPL");
上面的函數(shù)可以看到,我僅僅定義了總線的名字為usb,其他的都沒有做?纯葱Ч
[root: 1st]# insmod bus.ko
usb bus init
[root: 1st]# ls /sys/bus/usb/
//sys/bus目錄下多了一個usb的目錄,但是里面的東西都是空的,
devices drivers_autoprobe
uevent //因為我僅僅設(shè)置了總線的名字
drivers drivers_probe
總線屬性添加和刪除:
個人理解,設(shè)置總線的屬性后,會在對應(yīng)的總線目錄下增加了一個新的文件,通過對該文件的讀寫訪問,觸發(fā)相應(yīng)的函數(shù)操作,從而實現(xiàn)/sys/的文件接口與內(nèi)核設(shè)備模型的數(shù)據(jù)交互。
/*linux/sysfs.h*/
28 struct attribute {
29 const char *name;
//設(shè)定該文件的名字
30 struct module *owner;
//設(shè)定該文件的屬主
31 mode_t mode;
//設(shè)定該文件的文件操作權(quán)限
32 };
/*linux/device.h*/
38 struct bus_attribute {
39 struct attribute attr;
40 ssize_t (*show)(struct
bus_type *bus, char *buf);
41 ssize_t (*store)(struct
bus_type *bus, const char *buf, size_t count);
42 };
bus_attribute中有兩個函數(shù)指針,show和store。
當訪問總線目錄中的name文件時,就會觸發(fā)show函數(shù),一般會將指定的信息存放到數(shù)組buf,并傳到用戶空間顯示。
當修改總線目錄中的name文件是,就會觸發(fā)stroe函數(shù),一般會將從用戶空間傳來的buf指針存放的count個字節(jié)內(nèi)容存放到內(nèi)核中。
由此可以看到,通過這樣的文件,就能實現(xiàn)sys目錄下的文件與內(nèi)核設(shè)備模型之間的數(shù)據(jù)交互。
設(shè)置總線屬性有兩個步驟:
1、創(chuàng)建并初始化bus_attribute結(jié)構(gòu),使用宏BUS_ATTR
BUS_ATTR(_name, _mode, _show,
_store)
該宏會定義一個名叫bus_attr__name(紅色部分是固定的)的bus_attibute的結(jié)構(gòu),并且成員name設(shè)置為_name,文件權(quán)限mode設(shè)置為_mode,兩個函數(shù)調(diào)用分別人show和store。
2、將bus_attibute添加到指定的總線上,使用以下調(diào)用:
/*/drivers/base/bus.c*/
123 int bus_create_file(struct
bus_type *bus, struct bus_attribute *attr)
該函數(shù)失敗時返回錯誤號。
一旦調(diào)用該函數(shù),會就在指定bus總線的目錄下新建一個名叫_name的文件,權(quán)限為_mode,當訪問和修改該文件是會分別調(diào)用show和store函數(shù)調(diào)用。
如果不需要該屬性時,使用以下函數(shù)刪除:
/*/drivers/base/bus.c*/
135 void
bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)
說了這么多,馬上來個程序:
/*8th_devModule_1/2nd/bus.c*/
1 #include <linux/module.h>
2 #include <linux/init.h>
3
4 #include <linux/device.h>
5
6 #define VER_SIZE 100
7
8 struct bus_type usb_bus = {
9 .name = "usb",
10 };
11
12 char Version[VER_SIZE] =
"xiaobai V1.0";
13
14 static
ssize_t show_bus_version(struct bus_type
*bus, char *buf)
15 {
16 return snprintf(buf,
VER_SIZE, "%s\n", Version);
17 }
18 static
ssize_t store_bus_version(struct
bus_type *bus, const char *buf, size_t count)
19 {
20 return snprintf(Version,
VER_SIZE, "%s", buf);
21 }
22
/*該宏會定義一個名叫bus_attr_version的bus_attribute的結(jié)構(gòu),并且成員name設(shè)置為
23 *
version,文件權(quán)限mode設(shè)置為S_IRUGO|S_IWUGO,并設(shè)置show函數(shù)為
24 *
show_bus_version,stror
函數(shù)為stroe_bus_version*/
25 static
BUS_ATTR(version, S_IRUGO|S_IWUGO, show_bus_version,
store_bus_version);
26
27 static int __init
usb_bus_init(void)
28 {
29 int ret;
30
31 /*總線注冊*/
32 ret =
bus_register(&usb_bus);
33 if(ret){
34 printk("bus
register failed!\n");
35 goto err1;
36 }
37
/*為總線添加屬性,調(diào)用成功后在/sys/bus/usb目錄下有一個version的文件,權(quán)限為
38 *
S_IRUGO|S_IWUGO,查看該文件時會調(diào)用函數(shù)show_bus_version,修改時調(diào)用store。*/
39 ret =
bus_create_file(&usb_bus, &bus_attr_version);
40 if(ret){
41 printk("bus creat
file failed!\n");
42 goto err2;
43 }
44 printk("usb bus
init\n");
45 return 0;
46
47 err2:
48 bus_unregister(&usb_bus);
49 err1:
50 return ret;
51 }
52
53 static void __exit
usb_bus_exit(void)
54 {
55 bus_remove_file(&usb_bus,
&bus_attr_version);
//不調(diào)用這個也可以的,刪除總線時會刪除
56 bus_unregister(&usb_bus);
57 printk("usb bus
bye!\n");
58 }
驗證一下:
[root: 2nd]# insmod bus.ko
usb bus init
[root: 2nd]# ls /sys/bus/usb/
devices drivers_autoprobe
uevent
drivers drivers_probe
version //多了一個version文件
[root: 2nd]# ls -l
/sys/bus/usb/version
//文件的權(quán)限的可讀可寫(S_IRUGO|S_IWUGO)
-rw-rw-rw-
1 root root 4096 Oct 27 23:46 /sys/bus/usb/version
[root: 2nd]#
cat /sys/bus/usb/version
//讀文件時觸發(fā)show函數(shù)
xiaobai V1.0
[root: 2nd]#
echo "haha"> /sys/bus/usb/version
//寫文件是觸發(fā)store函數(shù)
[root: 2nd]# cat
/sys/bus/usb/version
haha
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、設(shè)備
在最底層,linux系統(tǒng)中每個設(shè)備都用一個device結(jié)構(gòu)的表示,如下,我省略掉部分成員:
/*linux/device.h*/
369 struct device {
370 struct klist
klist_children;
371 struct klist_node
knode_parent; /* node in sibling list */
372 struct klist_node
knode_driver;
373 struct klist_node
knode_bus;
374 struct
device *parent;
//指定該設(shè)備的父設(shè)備,如果不指定(NULL),注冊后的設(shè)備目錄
375
///在sys/device下
376 struct kobject kobj;
377 char
bus_id[BUS_ID_SIZE]; /* position on parent bus */
//在總線生識別設(shè)備的字符串,
385
//同時也是設(shè)備注冊后的目錄名字。
386 struct
bus_type *bus; /* type of bus device is on */
//指定該設(shè)備連接的總線
387 struct
device_driver *driver; /* which driver has allocated this
388
device */
//管理該設(shè)備的驅(qū)動函數(shù)
389 void
*driver_data; /* data private to the driver */
//驅(qū)動程序的私有數(shù)據(jù)
392 struct dev_pm_info power;
422 void
(*release)(struct device *dev);
//當給設(shè)備的最后一個引用被刪除時,調(diào)用該函數(shù)
423 };
在注冊一個完整的device結(jié)構(gòu)前,至少定義parrent、bus_id、bus和release成員。但我接下來的程序僅僅定義了bus_id(指定目錄的名字)、bus(對應(yīng)的總線,不加也行)和release(這是必須的,不然卸載模塊時會出錯,不信自己試試)。
設(shè)備注冊和注銷:
與總線的注冊一樣:
1、定義結(jié)構(gòu)體device。
2、調(diào)用注冊函數(shù):
int device_register(struct device
*dev)
函數(shù)失敗返回非零,需要判斷返回值來檢查注冊是否成功。
設(shè)備注銷函數(shù):
void
device_unregister(struct device *dev)
設(shè)備屬性:
這個也是和總線屬性差不多,不細講:
設(shè)備相關(guān)的結(jié)構(gòu)體和總線的類似,處理指定文件屬性為,還定義了show和store兩個函數(shù)。
/*linux/device.h*/
300 struct device_attribute {
301 struct attribute attr;
302 ssize_t (*show)(struct
device *dev, struct device_attribute *attr,
303 char *buf);
304 ssize_t (*store)(struct
device *dev, struct device_attribute *attr,
305 const char *buf,
size_t count);
306 };
設(shè)置設(shè)備屬性有兩個步驟:
1、創(chuàng)建并初始化device_attribute結(jié)構(gòu),使用宏DEVICE_ATTR
DEVICE_ATTR(_name, _mode, _show,
_store)
該宏會定義一個名叫dev_attr__name(紅色部分是固定的)的device_attibute的結(jié)構(gòu),并且成員name設(shè)置為_name,文件權(quán)限mode設(shè)置為_mode,兩個函數(shù)調(diào)用分別人show和store。
2、將device_attibute添加到指定的設(shè)備上,使用以下調(diào)用:
/*drivers/base/core.c*/
430 int device_create_file(struct
device *dev, struct device_attribute *attr)
該函數(shù)失敗時返回錯誤號。
一旦調(diào)用該函數(shù),會就在指定dev設(shè)備的目錄下新建一個名叫_name的文件,權(quán)限為_mode,當訪問和修改該文件是會分別調(diào)用show和store函數(shù)調(diào)用。
如果不需要該屬性時,使用以下函數(shù)刪除:
/*drivers/base/core.c*/
443 void
device_remove_file(struct device *dev, struct device_attribute
*attr)
講了這么多,來個函數(shù),和總線那個函數(shù)差不多,具體功能就是新建了一個執(zhí)行名字(usb_device)的的設(shè)備目錄,并且里面有一個屬性文件version。
/*8th_devModule_1/3rd/device.c*/
1 #include <linux/module.h>
2 #include <linux/init.h>
3
4 #include <linux/device.h>
5
6 #define VER_SIZE 100
7
8 extern
struct bus_type usb_bus;
9
10 void
usb_dev_release(struct device *dev)
11 {
12
printk("<kernel> release\n");
13 }
14
15 struct
device usb_device = {
16
.bus_id = "usb_device",
17 .bus =
&usb_bus, //指定該設(shè)備的總線,這樣會在sys/bus/usb/device目錄下有一個軟連接
18
.release = usb_dev_release, //必須要都有release函數(shù),不然卸載時會出錯
19 };
11
12 char Version[VER_SIZE] =
"xiaobai V1.0";
13
14 static ssize_t
show_device_version(struct device *dev,
15 struct
device_attribute *attr, char *buf)
16 {
17 return snprintf(buf,
VER_SIZE, "%s\n", Version);
18 }
19 static ssize_t
store_device_version(struct device
*dev,
20 struct
device_attribute *attr, const char *buf, size_t count)
21 {
22 return snprintf(Version,
VER_SIZE, "%s", buf);
23 }
24
/*該宏會定義一個名叫dev_attr_version的device_attribute的結(jié)構(gòu),并且成員name設(shè)置
25 *
為version,文件權(quán)限mode設(shè)置為S_IRUGO|S_IWUGO,并設(shè)置show函數(shù)為
26
*show_device_version,
,stror函數(shù)為stroe_device_version*/
27 static
DEVICE_ATTR(version, S_IRUGO|S_IWUGO,
28
show_device_version, store_device_version);
29
30 static int __init
usb_device_init(void)
31 {
32 int ret;
33
34
/*設(shè)備注冊,注冊成功后在/sys/device目錄下創(chuàng)建目錄usb_device*/
35 ret =
device_register(&usb_device);
36 if(ret){
37 printk("device
register failed!\n");
38 goto err1;
39 }
40
/*為設(shè)備添加屬性,調(diào)用成功后在/sys/device/usb_device/目錄下有一個version的
41 *
文件,權(quán)限為S_IRUGO|S_IWUGO,查看該文件時會調(diào)用函數(shù)show_device_version,
42 *
修改時調(diào)用store_device_version。*/
43 ret =
device_create_file(&usb_device, &dev_attr_version);
44 if(ret){
45 printk("device
creat file failed!\n");
46 goto err2;
47 }
48 printk("usb device
init\n");
49 return 0;
50
51 err2:
52
device_unregister(&usb_device);
53 err1:
54 return ret;
55 }
56
57 static void __exit
usb_device_exit(void)
58 {
59
device_remove_file(&usb_device, &dev_attr_version);
60
device_unregister(&usb_device);
61 printk("usb device
bye!\n");
62 }
63
64 module_init(usb_device_init);
65 module_exit(usb_device_exit);
再看看效果,這是沒有設(shè)置總線(bus
=
&usb_bus)時的效果,代碼在8th_devModule_1/3rd/device_bak.c,我沒有編譯,如果需要的話自己編譯運行看看:
[root: 3rd]# insmod device.ko
usb device init
[root: /]#
find -name "usb_device"
//注冊后查找一下在哪里有以設(shè)備bus_id為名字的目錄
./sys/devices/usb_device
///sys/device下有一個
[root: /]#
[root: /]# ls
/sys/devices/usb_device/
//里面有一個空文件,一個屬性文件version
uevent version
[root: /]# cat
/sys/devices/usb_device/version
//查看一下version,調(diào)用shoe函數(shù)
xiaobai V1.0
[root: /]#
echo "haha" > /sys/devices/usb_device/version
//修改version,調(diào)用store函數(shù)
[root: /]# cat
/sys/devices/usb_device/version
haha
下面的是程序8th_devModule_1/3rd/device.c的效果:
[root: /]# cd
review_driver/8th_devModule/8th_devModule_1/3rd/
[root: 3rd]# insmod bus.ko
//先加載總線的模塊
usb bus init
[root: 3rd]# insmod device.ko
//再加載設(shè)備的模塊
usb device init
[root: 3rd]# cd /
[root: /]#
find -name "usb_device"
//查找一下usb_device,發(fā)現(xiàn)比沒有指定總線時多了一個路徑
./sys/devices/usb_device
//這個目錄的出現(xiàn)是因為語句(.bus
= &usb_bus,)
./sys/bus/usb/devices/usb_device
//這個目錄的出現(xiàn)是因為語句(device_register(&usb_device);)
[root: /]# cat
/sys/devices/usb_device/version
xiaobai V1.0
[root: /]# ls
-l sys/bus/usb/devices
//原來該路徑只是一個軟連接,是該設(shè)備歸類到usb_bus總線下
lrwxrwxrwx
1 root root 0 Oct 27 13:49 usb_device ->
../../../devices/usb_device
[root: /]# rmmod device
<kernel>
release
//下載時調(diào)用release函數(shù)調(diào)用,如果沒有設(shè)置release,卸載時會出錯。
usb device bye!
[root: /]# rmmod bus
usb bus bye!
[root: /]#
上面的驗證需要先加載bus.ko,bus.c源程序和1st/bus.c的函數(shù)沒什么區(qū)別,只是將usb_bus導(dǎo)出到符號表。不然的話,加載模塊時不能找到對應(yīng)的總線。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、驅(qū)動程序
設(shè)備模型跟蹤所有系統(tǒng)所知道的設(shè)備。進行跟蹤的主要原因是讓驅(qū)動程序協(xié)調(diào)與設(shè)備之間的關(guān)系。
先看驅(qū)動程序的結(jié)構(gòu)體,我僅僅貼出一些重要的成員:
/*linux/device.h*/
122 struct device_driver {
123 const
char *name;
//驅(qū)動函數(shù)的名字,在對應(yīng)總線的driver目錄下顯示
124 struct
bus_type *bus;
//指定該驅(qū)動程序所操作的總線類型,必須設(shè)置,不然會注冊失敗
125
126 struct module *owner;
127 const char *mod_name;
/* used for built-in modules */
128
129 int
(*probe) (struct device *dev);
//探測函數(shù),以后會講
130 int
(*remove) (struct device *dev);
//卸載函數(shù),當設(shè)備從系統(tǒng)中刪除時調(diào)用,以后講
131 void
(*shutdown) (struct device *dev);
//當系統(tǒng)關(guān)機是調(diào)用
132 int (*suspend) (struct
device *dev, pm_message_t state);
133 int (*resume) (struct device
*dev);
134 struct attribute_group
**groups;
135
136 struct dev_pm_ops *pm;
137
138 struct driver_private *p;
139 };
和設(shè)備不一樣的是,在注冊驅(qū)動函數(shù)是必須指定該驅(qū)動函數(shù)對應(yīng)的總線,因為驅(qū)動函數(shù)注冊成功后,會存放在對應(yīng)總線的driver目錄下,如果沒有總線,注冊當然會失敗。
與總線的注冊一樣:
1、定義結(jié)構(gòu)體device_driver。
2、調(diào)用注冊函數(shù):
214 int driver_register(struct
device_driver *drv)
函數(shù)失敗返回非零,需要判斷返回值來檢查注冊是否成功。
設(shè)備注銷函數(shù):
249 void
driver_unregister(struct device_driver *drv)
驅(qū)動函數(shù)屬性:
這個也是和總線屬性差不多,不細講:
驅(qū)動函數(shù)相關(guān)的結(jié)構(gòu)體和總線的類似,處理指定文件屬性為,還定義了show和store兩個函數(shù)。
155 struct driver_attribute {
156 struct attribute attr;
157 ssize_t (*show)(struct
device_driver *driver, char *buf);
158 ssize_t (*store)(struct
device_driver *driver, const char *buf,
159 size_t count);
160 };
設(shè)置設(shè)備屬性有兩個步驟:
1、創(chuàng)建并初始化device_attribute結(jié)構(gòu),使用宏DEVICE_ATTR
DRIVER_ATTR(_name, _mode, _show,
_store)
該宏會定義一個名叫driver_attr__name(紅色部分是固定的)的driver_attibute的結(jié)構(gòu),并且成員name設(shè)置為_name,文件權(quán)限mode設(shè)置為_mode,兩個函數(shù)調(diào)用分別人show和store。
2、將device_attibute添加到指定的驅(qū)動函數(shù)上,使用以下調(diào)用:
/*drivers/base/driver.c*/
93 int driver_create_file(struct
device_driver *drv,
struct driver_attribute *attr)
該函數(shù)失敗時返回錯誤號。
一旦調(diào)用該函數(shù),會就在指定dev設(shè)備的目錄下新建一個名叫_name的文件,權(quán)限為_mode,當訪問和修改該文件是會分別調(diào)用show和store函數(shù)調(diào)用。
如果不需要該屬性時,使用以下函數(shù)刪除:
/*drivers/base/driver.c*/
110 void
driver_remove_file(struct device_driver *drv,
struct
driver_attribute *attr)
貼上函數(shù):
/*8th_devModule_1/4th/driver.c*/
1 #include <linux/module.h>
2 #include <linux/init.h>
3
4 #include <linux/device.h>
5
6 #define VER_SIZE 100
7
8 extern struct bus_type usb_bus;
9
10 struct
device_driver usb_driver = {
11 .name
= "usb_driver",
12 .bus =
&usb_bus,
13 };
14
15 char Version[VER_SIZE] =
"xiaobai V1.0";
16
17 static ssize_t
show_driver_version(struct device_driver
*drv, char *buf)
18 {
19 return snprintf(buf,
VER_SIZE, "%s\n", Version);
20 }
21 static ssize_t
store_driver_version(struct
device_driver *drv,
22
const char *buf, size_t count)
23 {
24 return snprintf(Version,
VER_SIZE, "%s", buf);
25 }
26
/*該宏會定義一個名叫driver_attr_version的driver_attribute的結(jié)構(gòu),并且成員
27 *
name設(shè)置為version,文件權(quán)限mode設(shè)置為S_IRUGO|S_IWUGO,并設(shè)置show函數(shù)為
28 *
show_driver_version,stror函數(shù)為stroe_driver_version*/
29 static
DRIVER_ATTR(version, S_IRUGO|S_IWUGO,
30
show_driver_version, store_driver_version);
31
32 static int __init
usb_driver_init(void)
33 {
34 int ret;
35
36
/*驅(qū)動注冊,注冊成功后在/sys/bus/usb/driver目錄下創(chuàng)建目錄usb_driver*/
37 ret =
driver_register(&usb_driver);
38 if(ret){
39 printk("driver
register failed!\n");
40 goto err1;
41 }
42
/*為驅(qū)動添加屬性,調(diào)用成功后在/sys/bus/usb/dirver目錄下有一個version的
43 *
文件,權(quán)限為S_IRUGO|S_IWUGO,查看該文件時會調(diào)用函數(shù)show_driver_version,修改時
44 *
調(diào)用store_driver_version。*/
45 ret =
driver_create_file(&usb_driver, &driver_attr_version);
46 if(ret){
47 printk("driver
creat file failed!\n");
48 goto err2;
49 }
50 printk("usb driver
init\n");
51 return 0;
52
53 err2:
54
driver_unregister(&usb_driver);
55 err1:
56 return ret;
57 }
58
59 static void __exit
usb_driver_exit(void)
60 {
61
driver_remove_file(&usb_driver, &driver_attr_version);
62
driver_unregister(&usb_driver);
63 printk("usb driver
bye!\n");
64 }
看看效果,同樣必須先加載bus.ko:
[root: /]# cd
review_driver/8th_devModule/8th_devModule_1/4th/
[root: 4th]#
insmod bus.ko
//必須先加載總線
usb bus init
[root: 4th]# insmod driver.ko
usb driver init
[root: 4th]# cd /
[root: /]#
find -name "usb_driver"
//只有一處創(chuàng)建了usb_driver目錄
./sys/bus/usb/drivers/usb_driver
[root: /]# ls
/sys/bus/usb/drivers/usb_driver/
bind uevent unbind version
[root: /]# cat
/sys/bus/usb/drivers/usb_driver/version //訪問version文件是觸發(fā)show函數(shù)
xiaobai V1.0
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、總結(jié)
這節(jié)講得內(nèi)容其實不多,歸納起來就是四個函數(shù)一個屬性結(jié)構(gòu)體。
屬性結(jié)構(gòu)體:xx_attribute。
注冊函數(shù):xx_register。
注銷函數(shù):xx+unregister。
創(chuàng)建屬性文件函數(shù):xx_create_file。
刪除屬性文件函數(shù):xx_remove_file。
其中xx可以是總線(bus)、設(shè)備(device)或者驅(qū)動函數(shù)(deriver)。
一但注冊成功,就會在/sys目錄下相應(yīng)的地方創(chuàng)建一個自己命名的目錄。其中,設(shè)備和驅(qū)動函數(shù)還可以添加到指定的bus目錄下。
總線的成功注冊后會在/sys/bus目錄下創(chuàng)建相應(yīng)的目錄。
設(shè)備的成功注冊后會在/sys/device目錄下創(chuàng)建相應(yīng)的目錄,如果指定總線,會在指定總線目錄/sys/bus/xx/device下創(chuàng)建一個指向/sys/device目錄的軟連接。
驅(qū)動函數(shù)的公共注冊會在/sys/bus/xx/driver目錄下創(chuàng)建相應(yīng)的目錄。
屬性文件提供了shoe和store兩個函數(shù)調(diào)用,當讀寫文件時會觸發(fā)相應(yīng)的函數(shù)調(diào)用,實現(xiàn)內(nèi)核sysfs與用戶空間的數(shù)據(jù)交互。
三者具體的關(guān)系就在后面章節(jié)介紹。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
源代碼: 8th_devModule_1.rar
|