嵌入式數(shù)據(jù)采集系統(tǒng)中的ADS8364驅(qū)動程序設(shè)計
4 ADS8364的驅(qū)動程序設(shè)計概述
4.1 驅(qū)動程序的LKM實現(xiàn)機制
Linux內(nèi)核提供了兩種機制來開發(fā)設(shè)備驅(qū)動程序,一種是直接把驅(qū)動程序編譯進內(nèi)核,成為Linux內(nèi)核的一部分。另一種是通過LKM(Loadable Kernel Module,即可加載模塊化機制)來開發(fā)可動態(tài)加載和卸載的驅(qū)動模塊[3]。驅(qū)動程序如果編譯進內(nèi)核的話,會增加內(nèi)核的大小,還要改動內(nèi)核的源文件,而且不能動態(tài)的卸載,不利于調(diào)試,所以本系統(tǒng)把ADS8364的驅(qū)動程序編寫成了 LKM型驅(qū)動程序(就是把ADS8364的驅(qū)動程序作為一個獨立的單元模塊,在使用時可以使用insmod命令加載到核心中,用完后使用rmmod命令卸載的那種)。
4.2 驅(qū)動程序的注冊與注銷
向系統(tǒng)增加一個驅(qū)動程序則意味著要賦予它一個主設(shè)備號,這一賦值過程是在驅(qū)動程序模塊的初始化中完成的。ADS8364的初始化入口函數(shù)定義如下:
int _init ads8364_init_module(void)全功能版J-LINK ARM仿真器V6.0
{ …..
ret = register_chrdev(11, ADS8364, ad_fops);
……}
在用insmod命令將編譯好的模塊調(diào)入內(nèi)存時,ads8364_init_module( )函數(shù)被調(diào)用。在這里,ads8364_init_module( )只做了一件事,它調(diào)用函數(shù)register_chrdev( )向內(nèi)核注冊該字符設(shè)備。函數(shù)register_chrdev()定義在linux/fs-h> 中。register_chrdev需要三個參數(shù),參數(shù)一是希望獲得的設(shè)備號,如果是零的話,系統(tǒng)將自動選擇一個沒有被占用的設(shè)備號作為該字符設(shè)備的主設(shè)備號并返回。參數(shù)二是設(shè)備文件名,這個名字必須插入到/dev目錄中,并與驅(qū)動程序的主設(shè)備號和次設(shè)備號相連。參數(shù)三用來登記驅(qū)動程序?qū)嶋H執(zhí)行操作的函數(shù)的指針。如果登記成功,返回設(shè)備的主設(shè)備號,不成功,返回一個負值。在此,我們選用當前不用的設(shè)備號11作為ADS8364的主設(shè)備號,設(shè)備名為 ADS8364。
在關(guān)閉字符設(shè)備或塊設(shè)備時,還需要通過unregister_chrdev( )從內(nèi)核中注銷設(shè)備,并釋放主設(shè)備號。在用rmmod卸載該驅(qū)動模塊時,cleanup_module函數(shù)被調(diào)用,它釋放字符設(shè)備ADS8364在系統(tǒng)字符設(shè)備表中占有的表項。
void ads8364_cleanup_module(void)
{ unregister_chrdev(11, ADS8364); info("ADS8364 cleanup module ok!"); }
4.3 file_operations結(jié)構(gòu)體的設(shè)計
在Linux中,字符設(shè)備向內(nèi)核提供的接口函數(shù)集就是文件操作集file_operations結(jié)構(gòu)體。在Linux系統(tǒng)中,打開的設(shè)備在內(nèi)核內(nèi)部由設(shè)備文件file結(jié)構(gòu)標識,內(nèi)核使用file_operations(文件操作)結(jié)構(gòu)訪問驅(qū)動程序的函數(shù)。每個文件都與自己的函數(shù)集相關(guān)聯(lián)(通過包含指向file_operations結(jié)構(gòu)的f_ops指針段實現(xiàn)),這些操作主要負責系統(tǒng)調(diào)用的實現(xiàn)[4]。為了使驅(qū)動程序在結(jié)構(gòu)的定義發(fā)生變化時更具可移植性,并且使得代碼更加緊湊且易讀,我們首先采用標記化格式聲明ADS8364的file_operations結(jié)構(gòu):
static struct file_operations ad_fops = {全功能版J-LINK ARM仿真器V6.0
owner: THIS_MODULE,/* ad_fops所屬的設(shè)備模塊 */
read:ads8364_read,/*從設(shè)備中讀數(shù)據(jù)*/
poll:ads8364_poll, /*查詢設(shè)備狀態(tài)*/
ioctl:ads8364_ioctl,/*設(shè)備I/O控制*/全功能版J-LINK ARM仿真器V6.0
open:ads 8364_open,/*打開設(shè)備操作*/
release:ads 8364_release,/*釋放操作*/
};
file_operations結(jié)構(gòu)體把系統(tǒng)調(diào)用和驅(qū)動程序關(guān)聯(lián)起來。這個結(jié)構(gòu)的每一個成員的名字都對應著一個系統(tǒng)調(diào)用。用戶進程利用系統(tǒng)調(diào)用在對設(shè)備文件進行諸如read、write、open等操作時,系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號找到相應的設(shè)備驅(qū)動程序,然后讀取這個數(shù)據(jù)結(jié)構(gòu)相應的函數(shù)指針,接著把控制權(quán)交給該函數(shù)。這就是ADS8364驅(qū)動程序工作的基本原理。既然是這樣,則編寫設(shè)備驅(qū)動程序的主要工作就是編寫子函數(shù),并填充 file_operations的各個域。下面我們就把file_operations的各個域的功能和主要調(diào)用函數(shù)加以介紹。
1)設(shè)備I/O控制操作函數(shù)
與普通文件相比,設(shè)備文件的操作要復雜得多,不可能簡單地通過 read、write 和llseek 等來實現(xiàn)。所有其它類型的操作都可以通過VFS的ioctl調(diào)用來執(zhí)行,函數(shù)定義如下:
static int ads8364_ioctl(struct inode * inode, struct file *filp, unsigned int cmd, unsigned long arg)
ads8364_ioctl是ADS8364驅(qū)動程序中對設(shè)備的I/O通道進行管理的函數(shù)。其中參數(shù)inode就是用戶程序打開ADS8364時使用open函數(shù)返回的文件標示符,cmd就是用戶程序?qū)DS8364的控制命令,它是唯一聯(lián)系用戶程序命令和驅(qū)動程序支持的途徑,該函數(shù)通過cmd區(qū)分操作,通過arg傳遞參數(shù)和結(jié)果。在驅(qū)動程序中實現(xiàn)的ads8364_ioctl函數(shù)體內(nèi),有一個switch{case}結(jié)構(gòu),每一個case對應一個命令碼,做出一些相應的操作。 ads8364_ioctl的設(shè)計的關(guān)鍵就是,如何將cmd命令碼在用戶程序里生成以及在驅(qū)動程序里的解析,所以switch{case}結(jié)構(gòu)在 ads8364_ioctl中至關(guān)重要,因為對設(shè)備的I/O控制都是通過這一部分的代碼實現(xiàn)的。
linux操作系統(tǒng)文章專題:linux操作系統(tǒng)詳解(linux不再難懂)
評論