引言
Linux是一個(gè)遵循POSIX標(biāo)準(zhǔn)的免費(fèi)操作系統(tǒng),。具有BSD和SYSV的擴(kuò)展特性,。與其他操作系統(tǒng)相比,嵌入式Linux系統(tǒng)以其可應(yīng)用于多種硬件平臺,、內(nèi)核高效穩(wěn)定,、源碼開放、軟件豐富,、網(wǎng)絡(luò)通信和文件管理機(jī)制完善等優(yōu)良特性而正被作為研究熱點(diǎn),,越來越多的研究人員采用Linux平臺來開發(fā)自己的產(chǎn)品。Linux設(shè)備驅(qū)動(dòng)程序在Linux內(nèi)核源代碼中占有很大比例,,從2.0,、2.2到 2.4版本的內(nèi)核,源代碼的長度日益增加,,其實(shí)主要是設(shè)備驅(qū)動(dòng)程序在增加,。
設(shè)備驅(qū)動(dòng)程序的編寫
設(shè)備驅(qū)動(dòng)程序是linux內(nèi)核的一部分,是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口,它由一組函數(shù)和一些私有數(shù)據(jù)組成,是連接應(yīng)用程序與具體硬件的橋梁。Linux的一個(gè)基本特點(diǎn)是它對硬件設(shè)備的管理抽象化,系統(tǒng)中的每一個(gè)設(shè)備都用一個(gè)特殊的文件來表示,。所有的硬件設(shè)備都像普通的文件一樣看待,使用與操作系統(tǒng)相同的標(biāo)準(zhǔn)系統(tǒng)來進(jìn)行打開,、讀寫和關(guān)閉。
在Linux 操作系統(tǒng)下有3類主要的設(shè)備文件類型:塊設(shè)備,、字符設(shè)備,、網(wǎng)絡(luò)設(shè)備。字符設(shè)備是指存取時(shí)沒有緩存的設(shè)備,??上裎募粯釉L問字符設(shè)備,字符設(shè)備驅(qū)動(dòng)程序負(fù)責(zé)實(shí)現(xiàn)這些行為。系統(tǒng)的控制臺和并口就是字符設(shè)備的例子,,它們可以很好地用“流”來描述,。塊設(shè)備是文件系統(tǒng)的宿主,如磁盤,。 Linux允許像字符設(shè)備那樣讀取塊設(shè)備——允許一次傳輸任意數(shù)目的字節(jié),。結(jié)果是,字符設(shè)備和塊設(shè)備讀取數(shù)方式一致,。而網(wǎng)絡(luò)設(shè)備不同于字符設(shè)備和塊設(shè)備, 它面向的上一層不是文件系統(tǒng)而是網(wǎng)絡(luò)協(xié)議層,是通過BSD套接口訪問數(shù)據(jù),。與設(shè)備相對應(yīng)的是三類設(shè)備驅(qū)動(dòng)程序,字符設(shè)備驅(qū)動(dòng)程序、塊設(shè)備驅(qū)動(dòng)程序,、網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序,。
字符設(shè)備驅(qū)動(dòng)程序、塊設(shè)備驅(qū)動(dòng)程序與網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的結(jié)構(gòu)體是不同的,。
在linux 源代碼linux/ include / linux/ fs. h中定義了字符設(shè)備和塊設(shè)備驅(qū)動(dòng)程序中必須使用的file_operations結(jié)構(gòu),每個(gè)設(shè)備驅(qū)動(dòng)都實(shí)現(xiàn)這個(gè)接口所定義的部分或全部函數(shù),。隨著內(nèi)核的不斷升級, file_operations結(jié)構(gòu)也越來越大,不同的版本的內(nèi)核會(huì)稍有不同。file_operations定義如下:
struct file_operations{
int( * lseek) ( struct inode * , struct file * , off_t , int) ; int( *release) ( struct inode * , struct file * ) ;
int( * read) ( struct inode * , struct file * , char * , int) ; int( * fsync) ( struct inode *, struct file * ) ;
int( *write) ( struct inode * , struct file * , const char *,, int) ; int( * fasync) ( struct inode * , struct file *,, int) ;
int( * readdir) ( struct inode , struct file , void * , dilldir) ; int( *check_media_change) ( kdev_t dev) ;
int(*select) ( struct inode *, struct file * , int, select_table * ) ; int( * revalidate) ( kdev_t dev) ; };
int ( * ioctl) ( struct inode * , struct file *, unsigned int, unsigned long) ;
int( *mmap) ( struct inode * , struct file * , struct vm_area_struct * ) ;
int( * open) ( struct inode *, struct file *) ;
應(yīng)用程序只有通過對設(shè)備文件的open、release,、read,、write、ioctl等才能訪問字符設(shè)備和塊設(shè)備,。用戶自己定義好 file_operations結(jié)構(gòu)后,,編寫出設(shè)備實(shí)際所需要的各操作函數(shù),對于不需要的操作函數(shù)用NULL初始化,,這些操作函數(shù)將被注冊到內(nèi)核,,當(dāng)應(yīng)用程序?qū)υO(shè)備相應(yīng)的設(shè)備文件進(jìn)行文件操作時(shí),內(nèi)核會(huì)找到相應(yīng)的操作函數(shù),,并進(jìn)行調(diào)用,。如果操作函數(shù)使用NULL,操作函數(shù)就進(jìn)行默認(rèn)處理,。
對于字符設(shè)備而言,,llseek( ),read( ),,write(),,ioctl( ),open( ),,release( )這些函數(shù)是不可缺的,;對于塊設(shè)備,,open( ),release( ),,ioctl(),,check_media_change( ),revalidate( )是不可缺少的,。
網(wǎng)絡(luò)設(shè)備結(jié)構(gòu)體 net_device 定義在 includelinuxnetdevice.h 里,,如下所示:
struct net_device
{
char name ; int (*init)(struct
net_device *dev);
unsigned short flags ; int (*open)
(struct net_device *dev);
unsigned long base_addr; int
(*stop)(struct net_device *dev)
unsigned int irq ; int
(*hard_start_xmit)(struct sk_buff *skb,
unsigned char dev_addr; struct
net_device *dev);
unsigned char addr_len; int
(*set_mac_address)( struct net_device
unsigned long trans_start; *dev,void* addr);
……
}
定義好net_device結(jié)構(gòu)體后,根據(jù)實(shí)際情況編寫操作函數(shù),,其中hard_start_xmit()函數(shù)是用來發(fā)送數(shù)據(jù)的,,set_mac_address()是進(jìn)行網(wǎng)絡(luò)參數(shù)設(shè)置的。
當(dāng)linux初始化時(shí)將調(diào)用初始化函數(shù)int device_init( ),,該函數(shù)包括以下內(nèi)容:
注冊所用設(shè)備,。linux用設(shè)備號來標(biāo)識字符設(shè)備和塊設(shè)備。設(shè)備號分為主設(shè)備號和從設(shè)備號,,最終形成設(shè)備接點(diǎn),。設(shè)備節(jié)點(diǎn)在訪問字符設(shè)備和塊設(shè)備的設(shè)備驅(qū)動(dòng)程序時(shí)將使用。通常主設(shè)備號標(biāo)識設(shè)備對應(yīng)的驅(qū)動(dòng)程序,,大多數(shù)設(shè)備是“一個(gè)主設(shè)備號對應(yīng)一個(gè)驅(qū)動(dòng)程序”,,如:虛擬控制臺和串口終端由驅(qū)動(dòng)程序4管理,。次設(shè)備號由內(nèi)核使用,,用于確定設(shè)備文件所指的設(shè)備。字符設(shè)備和塊設(shè)備注冊時(shí)必須先定義好設(shè)備號,。
字符設(shè)備注冊函數(shù)如下:
int register_chrdev(unsigned int major ,const char *name, struct file_oprations *fops);
其中 major是主設(shè)備號,。
由于對網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的訪問不需要設(shè)備節(jié)點(diǎn),它的注冊函數(shù)如下:
int register_netdev(struct net_device *dev)
注冊設(shè)備所用的中斷,。中斷在現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)中有重要的地位,操作系統(tǒng)必須提供程序響應(yīng)中斷的能力,。一般是把一個(gè)中斷處理程序注冊到系統(tǒng)中去。操作系統(tǒng)在硬件中斷發(fā)生后調(diào)用驅(qū)動(dòng)程序的處理程序,。
注冊中斷所用的函數(shù)如下:
int request_irq (unsigned irq,void(*handler)(int,void*,struct pt_regs*),unsigned long flags,const char*device,void* dev_id);
其中,,irq是中斷向量;handler是中斷處理函數(shù);flags是中斷處理中的掩碼,;devices是設(shè)備名,;dev_id是在中斷共享使用的id。
當(dāng)linux不使用該設(shè)備時(shí),就要調(diào)用清除函數(shù)void_devicie_exit ( ),它同初始化函數(shù)相對應(yīng)的,,主要是:
注銷設(shè)備,字符設(shè)備注銷函數(shù)如下:
int unregister_chrdev(unsigned int major ,const char *name, struct file_oprations *fops),;
注銷中斷,注銷中斷所用的函數(shù)如下:
int free_irq (unsigned irq,void(*handler)(int,,void*,,struct pt_regs*),,unsigned long flags,const char*device,,void* dev_id);
釋放資源,模塊初始化和清除函數(shù)采用module_init(device_init),,module_exit(device_exit) 形式
編寫服務(wù)子程序
服務(wù)于I/O請求的子程序,又稱為驅(qū)動(dòng)程序的上半部分,。調(diào)用這部分是由于系統(tǒng)調(diào)用的結(jié)果,。這部分程序在執(zhí)行的時(shí)候.系統(tǒng)仍認(rèn)為是和進(jìn)行調(diào)用的進(jìn)程屬于同一個(gè)進(jìn)程. 只是用戶態(tài)變成了核心態(tài),具有進(jìn)行此系統(tǒng)調(diào)用的用戶程序的運(yùn)行環(huán)境.因此可以在其中調(diào)用sleep等與進(jìn)程運(yùn)行環(huán)境有關(guān)的函數(shù),。
中斷服務(wù)子程序,,又稱為驅(qū)動(dòng)程序的下半部分。在Linux系統(tǒng)中.并不是直接從中斷向量表中調(diào)用設(shè)備驅(qū)動(dòng)程序的中斷服務(wù)子程序,,而是由Linux系統(tǒng)來接收硬件中斷,,再由系統(tǒng)調(diào)用中斷服務(wù)子程序。中斷可以產(chǎn)生在任何一個(gè)進(jìn)程運(yùn)行的時(shí)候,,因此在中斷服務(wù)程序被調(diào)用的時(shí)候.不能依賴于仟何進(jìn)程的狀態(tài),,也就不能調(diào)用任何與進(jìn)程運(yùn)行環(huán)境相關(guān)的函數(shù)。因?yàn)樵O(shè)備驅(qū)動(dòng)程序一般支持同一類型的若干設(shè)備,,所以一般在系統(tǒng)調(diào)用中斷服務(wù)程序的時(shí)候,,都帶有一個(gè)或多個(gè)參數(shù),以唯一標(biāo)識請求服務(wù)的設(shè)備,。
設(shè)備驅(qū)動(dòng)程序的使用
直接將驅(qū)動(dòng)程序編譯進(jìn)linux內(nèi)核
將設(shè)備驅(qū)動(dòng)程序復(fù)制到 linux/drivers相關(guān)的子目錄下,比如字符設(shè)備驅(qū)動(dòng)程序 就放在linux/drivers/char下,。
修改linux/drivers相關(guān)的子目錄的Makefile,
如obj-$(config_dev_driver) +=dev_driver.o,這樣在編譯內(nèi)核時(shí)將會(huì)編譯dev_driver.c,生成 dev_driver.o.
對內(nèi)核進(jìn)行重新編譯時(shí),,進(jìn)行相關(guān)的配置,,比如要使用AT91RM9200的UART,就要如下配置:
Character devices -> Serial drivers ->AT91RM9200 serial port support
將驅(qū)動(dòng)程序編譯成驅(qū)動(dòng)模塊
在設(shè)備驅(qū)動(dòng)程序中要有兩個(gè)重要函數(shù):
module_init(dev_init),,module_exit(dev_exit)
利用相應(yīng)的交叉編譯器以及編譯命令將驅(qū)動(dòng)程序dev_driver.c編譯成dev_driver.o 這樣的動(dòng)態(tài)驅(qū)動(dòng)模塊,。利用insmod命令給系統(tǒng)安裝驅(qū)動(dòng)模塊,如果在/dev目錄下沒有相應(yīng)的設(shè)備文件,,就可以使用mknod創(chuàng)建一個(gè)設(shè)備文件,。利用 rmmod命令卸載驅(qū)動(dòng)模塊,設(shè)備文件的刪除可以用rm命令,。
結(jié)語
設(shè)備驅(qū)動(dòng)程序的開發(fā)是在Linux環(huán)境中最復(fù)雜的編程任務(wù)之一 ,。它需要和硬件打交道,容易引起系統(tǒng)崩潰,,而且很難調(diào)試,。掌握設(shè)備驅(qū)動(dòng)程序的開發(fā)技術(shù),將使得開發(fā)嵌入式Linux的系統(tǒng)更為迅速和有效,。
參考文獻(xiàn):
1. AlessandroRubmi,,JonathanCorbet.Linux設(shè)備驅(qū)動(dòng)程序,,第3版,中國電力出版社,,2006.2
2. 毛德操,、胡希明,Linux內(nèi)核源代碼情景分析,,浙江大學(xué)出版社,,2001
3. 周立功、陳明記,、陳渝,,ARM嵌入式linux系統(tǒng)構(gòu)建與驅(qū)動(dòng)開發(fā),北京航空航天大學(xué)出版社,,2006年1月第1版
4. 李祥兵,、鄭扣根,Linux中I2C總線驅(qū)動(dòng)程序的開發(fā),,計(jì)算機(jī)工程與設(shè)計(jì),,2005;26(1)
5. 鄭偉、王欽若,,Linux內(nèi)核空間設(shè)備驅(qū)動(dòng)程序的開發(fā),,微計(jì)算機(jī)信息,2003;19(12)