摘要:闡述了在嵌入式Linux系統(tǒng)環(huán)境下設(shè)備驅(qū)動(dòng)程序的開發(fā),詳細(xì)探討了基于linux下設(shè)備驅(qū)動(dòng)程序具體開發(fā)過(guò)程,最后說(shuō)明了如何使用設(shè)備驅(qū)動(dòng)程序。
關(guān)鍵詞:嵌入式系統(tǒng);linux;驅(qū)動(dòng)程序
引言
Linux是一個(gè)遵循POSIX標(biāo)準(zhǔn)的免費(fèi)操作系統(tǒng)。具有BSD和SYSV的擴(kuò)展特性。與其他操作系統(tǒng)相比,嵌入式Linux系統(tǒng)以其可應(yīng)用于多種硬件平臺(tái)、內(nèi)核高效穩(wěn)定、源碼開放、軟件豐富、網(wǎng)絡(luò)通信和文件管理機(jī)制完善等優(yōu)良特性而正被作為研究熱點(diǎn),越來(lái)越多的研究人員采用Linux平臺(tái)來(lái)開發(fā)自己的產(chǎn)品。Linux設(shè)備驅(qū)動(dòng)程序在Linux內(nèi)核源代碼中占有很大比例,從2.0、2.2到2.4版本的內(nèi)核,源代碼的長(zhǎng)度日益增加,其實(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)是它對(duì)硬件設(shè)備的管理抽象化,系統(tǒng)中的每一個(gè)設(shè)備都用一個(gè)特殊的文件來(lái)表示。所有的硬件設(shè)備都像普通的文件一樣看待,使用與操作系統(tǒng)相同的標(biāo)準(zhǔn)系統(tǒng)來(lái)進(jìn)行打開、讀寫和關(guān)閉。
在Linux操作系統(tǒng)下有3類主要的設(shè)備文件類型:塊設(shè)備、字符設(shè)備、網(wǎng)絡(luò)設(shè)備。字符設(shè)備是指存取時(shí)沒有緩存的設(shè)備。可像文件一樣訪問(wèn)字符設(shè)備,字符設(shè)備驅(qū)動(dòng)程序負(fù)責(zé)實(shí)現(xiàn)這些行為。系統(tǒng)的控制臺(tái)和并口就是字符設(shè)備的例子,它們可以很好地用“流”來(lái)描述。塊設(shè)備是文件系統(tǒng)的宿主,如磁盤。Linux允許像字符設(shè)備那樣讀取塊設(shè)備——允許一次傳輸任意數(shù)目的字節(jié)。結(jié)果是,字符設(shè)備和塊設(shè)備讀取數(shù)方式一致。而網(wǎng)絡(luò)設(shè)備不同于字符設(shè)備和塊設(shè)備,它面向的上一層不是文件系統(tǒng)而是網(wǎng)絡(luò)協(xié)議層,是通過(guò)BSD套接口訪問(wèn)數(shù)據(jù)。與設(shè)備相對(duì)應(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)核的不斷升級(jí),file_operations結(jié)構(gòu)也越來(lái)越大,不同的版本的內(nèi)核會(huì)稍有不同。


應(yīng)用程序只有通過(guò)對(duì)設(shè)備文件的open、release、read、write、ioctl等才能訪問(wèn)字符設(shè)備和塊設(shè)備。用戶自己定義好file_operations結(jié)構(gòu)后,編寫出設(shè)備實(shí)際所需要的各操作函數(shù),對(duì)于不需要的操作函數(shù)用NULL初始化,這些操作函數(shù)將被注冊(cè)到內(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)處理。
對(duì)于字符設(shè)備而言,llseek(),read(),write(),ioctl(),open(),release()這些函數(shù)是不可缺的;對(duì)十塊設(shè)備,open(),release(),ioctl(),check_media_change(),revalidate()是不可缺少的。
網(wǎng)絡(luò)設(shè)備結(jié)構(gòu)體net_device定義在include\linuxhletdevice.h里,如下所示:


定義好net_device結(jié)構(gòu)體后,根據(jù)實(shí)際情況編寫操作函數(shù),其中hard_start_xmit()函數(shù)是用來(lái)發(fā)送數(shù)據(jù)的,set mac address()是進(jìn)行網(wǎng)絡(luò)參數(shù)設(shè)置的。
當(dāng)linux初始化時(shí)將調(diào)用初始化函數(shù)intdevice_init(),該函數(shù)包括以下內(nèi)容:
注冊(cè)所用設(shè)備。linux用設(shè)備號(hào)來(lái)標(biāo)識(shí)字符設(shè)備和塊設(shè)備。設(shè)備號(hào)分為主設(shè)備號(hào)和從設(shè)備號(hào),最終形成設(shè)備接點(diǎn)。設(shè)備節(jié)點(diǎn)在訪問(wèn)字符設(shè)備和塊設(shè)備的設(shè)備驅(qū)動(dòng)程序時(shí)將使用。通常主設(shè)備號(hào)標(biāo)識(shí)設(shè)備對(duì)應(yīng)的驅(qū)動(dòng)程序,大多數(shù)設(shè)備是“一個(gè)主設(shè)備號(hào)對(duì)應(yīng)一個(gè)驅(qū)動(dòng)程序”,如:虛擬控制臺(tái)和串口終端由驅(qū)動(dòng)程序4管理。次設(shè)備號(hào)由內(nèi)核使用,用于確定設(shè)備文件所指的設(shè)備。字符設(shè)備和塊設(shè)備注冊(cè)時(shí)必須先定義好設(shè)備號(hào)。
字符設(shè)備注冊(cè)函數(shù)如下:

int register_chrdev(unsigned int major,constchar*name,struct file_oprations*fops);其中major是主設(shè)備號(hào)。
由于對(duì)網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的訪問(wèn)不需要設(shè)備節(jié)點(diǎn),它的注冊(cè)函數(shù)如下:
int register_netdev(struct net_device*dev)
注冊(cè)設(shè)備所用的中斷。中斷在現(xiàn)代計(jì)算機(jī)結(jié)構(gòu)中有重要的地位,操作系統(tǒng)必須提供程序響應(yīng)中斷的能力。一般是把一個(gè)中斷處理程序注冊(cè)到系統(tǒng)中去。操作系統(tǒng)在硬件中斷發(fā)生后調(diào)用驅(qū)動(dòng)程序的處理程序。
注冊(cè)中斷所用的函數(shù)如下:其中,irq是中斷向量;handler是中斷處理函數(shù);flags是中斷處理中的掩碼;devices是設(shè)備名;dev_id是在中斷共享使用的id。
當(dāng)linux不使用該設(shè)備時(shí),就要調(diào)用清除函

編寫服務(wù)子程序
服務(wù)于I/O請(qǐng)求的子程序,又稱為驅(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)來(lái)接收硬件中斷,再由系統(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)識(shí)請(qǐng)求服務(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.
對(duì)內(nèi)核進(jìn)行重新編譯時(shí),進(jìn)行相關(guān)的配置,比如要使用AT91RM9200的UART,就要如下配置:
Character devices->Serial drivers.>AT91RM9200 serial port suppot
將驅(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é)語(yǔ)
設(shè)備驅(qū)動(dòng)程序的開發(fā)是在Linux環(huán)境中最復(fù)雜的編程任務(wù)之一。它需要和硬件打交道,容易引起系統(tǒng)崩潰,而且很難調(diào)試。掌握設(shè)備驅(qū)動(dòng)程序的開發(fā)技術(shù),將使得開發(fā)嵌入式Linux的系統(tǒng)更為迅速和有效。