在Linux系統(tǒng)上編寫驅(qū)動程序,說簡單也簡單,說難也難。難在于對算法的編寫和設(shè)備的控制方面,是比較讓人頭疼的;說它簡單是因為在Linux下已經(jīng)有一套驅(qū)動開發(fā)的模式,編寫的時候只需要按照這個模式寫就可以了,而這個模式就是它事先定義好的一些結(jié)構(gòu)體,在驅(qū)動編寫的時候,只要對這些結(jié)構(gòu)體根據(jù)設(shè)備的需求進(jìn)行適當(dāng)?shù)奶畛洌蛯崿F(xiàn)了驅(qū)動的編寫。
首先在Linux下,視一切事物皆為文件,它同樣把驅(qū)動設(shè)備也看成是文件,對于簡單的文件操作,無非就是open/close/read/write,在Linux對于文件的操作有一個關(guān)鍵的數(shù)據(jù)結(jié)構(gòu):file_operation,它的定義在源碼目錄下的include/linux/fs.h中,內(nèi)容如下:
[cpp]view plaincopy
1.structfile_operations{
2.structmodule*owner;
3.loff_t(*llseek)(structfile*,loff_t,int);
4.ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);
5.ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);
6.ssize_t(*aio_read)(structkiocb*,conststructiovec*,unsignedlong,loff_t);
7.ssize_t(*aio_write)(structkiocb*,conststructiovec*,unsignedlong,loff_t);
8.int(*readdir)(structfile*,void*,filldir_t);
9.unsignedint(*poll)(structfile*,structpoll_table_struct*);
10.int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);
11.long(*unlocked_ioctl)(structfile*,unsignedint,unsignedlong);
12.long(*compat_ioctl)(structfile*,unsignedint,unsignedlong);
13.int(*mmap)(structfile*,structvm_area_struct*);
14.int(*open)(structinode*,structfile*);
15.int(*flush)(structfile*,fl_owner_tid);
16.int(*release)(structinode*,structfile*);
17.int(*fsync)(structfile*,intdatasync);
18.int(*aio_fsync)(structkiocb*,intdatasync);
19.int(*fasync)(int,structfile*,int);
20.int(*lock)(structfile*,int,structfile_lock*);
21.ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);
22.unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);
23.int(*check_flags)(int);
24.int(*flock)(structfile*,int,structfile_lock*);
25.ssize_t(*splice_write)(structpipe_inode_info*,structfile*,loff_t*,size_t,unsignedint);
26.ssize_t(*splice_read)(structfile*,loff_t*,structpipe_inode_info*,size_t,unsignedint);
27.int(*setlease)(structfile*,long,structfile_lock**);
28.};
對于這個結(jié)構(gòu)體中的元素來說,大家可以看到每個函數(shù)名前都有一個“*”,所以它們都是指向函數(shù)的指針。目前我們只需要關(guān)心
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
這幾條,因為這篇文章就叫簡單驅(qū)動。就是讀(read)、寫(write)、控制(ioctl)、打開(open)、卸載(release)。這個結(jié)構(gòu)體在驅(qū)動中的作用就是把系統(tǒng)調(diào)用和驅(qū)動程序關(guān)聯(lián)起來,它本身就是一系列指針的集合,每一個都對應(yīng)一個系統(tǒng)調(diào)用。
但是畢竟file_operation是針對文件定義的一個結(jié)構(gòu)體,所以在寫驅(qū)動時,其中有一些元素是用不到的,所以在2.6版本引入了一個針對驅(qū)動的結(jié)構(gòu)體框架:platform,它是通過結(jié)構(gòu)體platform_device來描述設(shè)備,用platform_driver描述設(shè)備驅(qū)動,它們都在源代碼目錄下的include/linux/platform_device.h中定義,內(nèi)容如下:
[cpp]view plaincopy
1.structplatform_device{
2.constchar*name;
3.intid;
4.structdevicedev;
5.u32num_resources;
6.structresource*resource;
7.conststructplatform_device_id*id_entry;
8./*archspecificadditions*/
9.structpdev_archdataarchdata;
10.};
11.structplatform_driver{
12.int(*probe)(structplatform_device*);
13.int(*remove)(structplatform_device*);
14.void(*shutdown)(structplatform_device*);
15.int(*suspend)(structplatform_device*,pm_message_tstate);
16.int(*resume)(structplatform_device*);
17.structdevice_driverdriver;
18.conststructplatform_device_id*id_table;
19.};
對于第一個結(jié)構(gòu)體來說,它的作用就是給一個設(shè)備進(jìn)行登記作用,相當(dāng)于設(shè)備的身份證,要有姓名,身份證號,還有你的住址,當(dāng)然其他一些東西就直接從舊身份證上copy過來,這就是其中的struct device dev,這是傳統(tǒng)設(shè)備的一個封裝,基本就是copy的意思了。對于第二個結(jié)構(gòu)體,因為Linux源代碼都是C語言編寫的,對于這里它是利用結(jié)構(gòu)體和函數(shù)指針,來實現(xiàn)了C語言中沒有的“類”這一種結(jié)構(gòu),使得驅(qū)動模型成為一個面向?qū)ο蟮慕Y(jié)構(gòu)。對于其中的struct device_driver driver,它是描述設(shè)備驅(qū)動的基本數(shù)據(jù)結(jié)構(gòu),它是在源代碼目錄下的include/linux/device.h中定義的,內(nèi)容如下:
[cpp]view plaincopy
1.structdevice_driver{
2.constchar*name;
3.structbus_type*bus;
4.structmodule*owner;
5.constchar*mod_name;/*usedforbuilt-inmodules*/
6.boolsuppress_bind_attrs;/*disablesbind/unbindviasysfs*/
7.#ifdefined(CONFIG_OF)
8.conststructof_device_id*of_match_table;
9.#endif
10.int(*probe)(structdevice*dev);
11.int(*remove)(structdevice*dev);
12.void(*shutdown)(structdevice*dev);
13.int(*suspend)(structdevice*dev,pm_message_tstate);
14.int(*resume)(structdevice*dev);
15.conststructattribute_group**groups;
16.conststructdev_pm_ops*pm;
17.structdriver_private*p;
18.};
依然全部都是以指針的形式定義的所有元素,對于驅(qū)動這一塊來說,每一項肯定都是需要一個函數(shù)來實現(xiàn)的,如果不把它們集合起來,是很難管理的,而且很容易找不到,而且對于不同的驅(qū)動設(shè)備,它的每一個功能的函數(shù)名必定是不一樣的,那么我們在開發(fā)的時候,需要用到這些函數(shù)的時候,就會很不方便,不可能在使用的時候去查找對應(yīng)的源代碼吧,所以就要進(jìn)行一個封裝,對于函數(shù)的封裝,在C語言中一個對好的辦法就是在結(jié)構(gòu)體中使用指向函數(shù)的指針,這種方法其實我們在平時的程序開發(fā)中也可以使用,原則就是體現(xiàn)出一個“類”的感覺,就是面向?qū)ο蟮乃枷搿?/p>
在Linux系統(tǒng)中,設(shè)備可以大致分為3類:字符設(shè)備、塊設(shè)備和網(wǎng)絡(luò)設(shè)備,而每種設(shè)備中又分為不同的子系統(tǒng),由于具有自身的一些特殊性質(zhì),所以有不能歸到某個已經(jīng)存在的子類中,所以可以說是便于管理,也可以說是為了達(dá)到同一種定義模式,所以linux系統(tǒng)把這些子系統(tǒng)歸為一個新類:misc ,以結(jié)構(gòu)體miscdevice描述,在源代碼目錄下的include/linux/miscdevice.h中定義,內(nèi)容如下:
[cpp]view plaincopy
1.structmiscdevice{
2.intminor;
3.constchar*name;
4.conststructfile_operations*fops;
5.structlist_headlist;
6.structdevice*parent;
7.structdevice*this_device;
8.constchar*nodename;
9.mode_tmode;
10.};
對于這些設(shè)備,它們都擁有一個共同主設(shè)備號10,所以它們是以次設(shè)備號來區(qū)分的,對于它里面的元素,大應(yīng)該很眼熟吧,而且還有一個我們更熟悉的list_head的元素,這里也可以應(yīng)證我之前說的list_head就是一個橋梁的說法了。
其實對于上面介紹的結(jié)構(gòu)體,里面的元素的作用基本可以見名思意了,所以不用贅述了。其實寫一個驅(qū)動模塊就是填充上述的結(jié)構(gòu)體,根據(jù)設(shè)備的功能和用途寫相應(yīng)的函數(shù),然后對應(yīng)到結(jié)構(gòu)體中的指針,然后再寫一個入口一個出口(就是模塊編程中的init和exit)就可以了,一般情況下入口程序就是在注冊platform_device和platform_driver(當(dāng)然,這樣說是針對以platform模式編寫驅(qū)動程序)。
-
嵌入式
+關(guān)注
關(guān)注
5068文章
19019瀏覽量
303299
原文標(biāo)題:搞嵌入式Linux驅(qū)動說簡單也簡單,說難也難!嵌入式驅(qū)動的結(jié)構(gòu)分析
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論