Linux下的設(shè)備驅(qū)動(dòng)程序被組織為一組完成不同任務(wù)的函數(shù)的集合,通過這些函數(shù)使得linux的設(shè)備操作猶如文件一般。在應(yīng)用程序看來,硬件設(shè)備只是一個(gè)設(shè)備文件,應(yīng)用程序可以象操作普通文件一樣對(duì)硬件設(shè)備進(jìn)行操作,如open()、close()、read()、write() 等。
Linux主要將設(shè)備分為二類:字符設(shè)備和塊設(shè)備。字符設(shè)備是指設(shè)備發(fā)送和接收數(shù)據(jù)以字符的形式進(jìn)行;而塊設(shè)備則以整個(gè)數(shù)據(jù)緩沖區(qū)的形式進(jìn)行。字符設(shè)備的驅(qū)動(dòng)相對(duì)比較簡單。
下面我們來假設(shè)一個(gè)非常簡單的虛擬字符設(shè)備:這個(gè)設(shè)備中只有一個(gè)4個(gè)字節(jié)的全局變量int global_var,而這個(gè)設(shè)備的名字叫做"globalvar"。對(duì)"globalvar"設(shè)備的讀寫等操作即是對(duì)其中全局變量global_var的操作。
驅(qū)動(dòng)程序是內(nèi)核的一部分,因此我們需要給其添加模塊初始化函數(shù),該函數(shù)用來完成對(duì)所控設(shè)備的初始化工作,并調(diào)用register_chrdev() 函數(shù)注冊(cè)字符設(shè)備:
|
其中,register_chrdev函數(shù)中的參數(shù)MAJOR_NUM為主設(shè)備號(hào), "globalvar"為設(shè)備名,globalvar_fops為包含基本函數(shù)入口點(diǎn)的結(jié)構(gòu)體,類型為file_operations。當(dāng)globalvar模塊被加載時(shí),globalvar_init被執(zhí)行,它將調(diào)用內(nèi)核函數(shù)register_chrdev,把驅(qū)動(dòng)程序的基本入口點(diǎn)指針存放在內(nèi)核的字符設(shè)備地址表中,在用戶進(jìn)程對(duì)該設(shè)備執(zhí)行系統(tǒng)調(diào)用時(shí)提供入口地址。
與模塊初始化函數(shù)對(duì)應(yīng)的就是模塊卸載函數(shù),需要調(diào)用register_chrdev()的"反函數(shù)"
|
隨著內(nèi)核不斷增加新的功能,file_operations結(jié)構(gòu)體已逐漸變得越來越大,但是大多數(shù)的驅(qū)動(dòng)程序只是利用了其中的一部分。對(duì)于字符設(shè)備來說,要提供的主要入口有:open()、release()、read()、write()、ioctl()、llseek()、poll()等。
open()函數(shù) 對(duì)設(shè)備特殊文件進(jìn)行open()系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動(dòng)程序的open() 函數(shù):
int (*open)(struct inode * ,struct file *);
其中參數(shù)inode為設(shè)備特殊文件的inode (索引結(jié)點(diǎn)) 結(jié)構(gòu)的指針,參數(shù)file是指向這一設(shè)備的文件結(jié)構(gòu)的指針。open()的主要任務(wù)是確定硬件處在就緒狀態(tài)、驗(yàn)證次設(shè)備號(hào)的合法性(次設(shè)備號(hào)可以用 MINOR(inode-> i - rdev) 取得)、控制使用設(shè)備的進(jìn)程數(shù)、根據(jù)執(zhí)行情況返回狀態(tài)碼(0表示成功,負(fù)數(shù)表示存在錯(cuò)誤)等;
release()函數(shù) 當(dāng)最后一個(gè)打開設(shè)備的用戶進(jìn)程執(zhí)行close ()系統(tǒng)調(diào)用時(shí),內(nèi)核將調(diào)用驅(qū)動(dòng)程序的release() 函數(shù):
void (*release) (struct inode * ,struct file *) ;
release 函數(shù)的主要任務(wù)是清理未結(jié)束的輸入/輸出操作、釋放資源、用戶自定義排他標(biāo)志的復(fù)位等。
read()函數(shù) 當(dāng)對(duì)設(shè)備特殊文件進(jìn)行read() 系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動(dòng)程序read()函數(shù):
ssize_t (*read) (struct file *, char *, size_t, loff_t *);
用來從設(shè)備中讀取數(shù)據(jù)。當(dāng)該函數(shù)指針被賦為NULL 值時(shí),將導(dǎo)致read 系統(tǒng)調(diào)用出錯(cuò)并返回-EINVAL("Invalid argument,非法參數(shù)")。函數(shù)返回非負(fù)值表示成功讀取的字節(jié)數(shù)(返回值為"signed size"數(shù)據(jù)類型,通常就是目標(biāo)平臺(tái)上的固有整數(shù)類型)。
globalvar_read函數(shù)中內(nèi)核空間與用戶空間的內(nèi)存交互需要借助第2節(jié)所介紹的函數(shù):
|
write( ) 函數(shù) 當(dāng)設(shè)備特殊文件進(jìn)行write () 系統(tǒng)調(diào)用時(shí),將調(diào)用驅(qū)動(dòng)程序的write () 函數(shù):
ssize_t (*write) (struct file *, const char *, size_t, loff_t *);
向設(shè)備發(fā)送數(shù)據(jù)。如果沒有這個(gè)函數(shù),write 系統(tǒng)調(diào)用會(huì)向調(diào)用程序返回一個(gè)-EINVAL。如果返回值非負(fù),則表示成功寫入的字節(jié)數(shù)。
globalvar_write函數(shù)中內(nèi)核空間與用戶空間的內(nèi)存交互需要借助第2節(jié)所介紹的函數(shù):
|
ioctl() 函數(shù) 該函數(shù)是特殊的控制函數(shù),可以通過它向設(shè)備傳遞控制信息或從設(shè)備取得狀態(tài)信息,函數(shù)原型為:
int (*ioctl) (struct inode * ,struct file * ,unsigned int ,unsigned long);
unsigned int參數(shù)為設(shè)備驅(qū)動(dòng)程序要執(zhí)行的命令的代碼,由用戶自定義,unsigned long參數(shù)為相應(yīng)的命令提供參數(shù),類型可以是整型、指針等。如果設(shè)備不提供ioctl 入口點(diǎn),則對(duì)于任何內(nèi)核未預(yù)先定義的請(qǐng)求,ioctl 系統(tǒng)調(diào)用將返回錯(cuò)誤(-ENOTTY,"No such ioctl fordevice,該設(shè)備無此ioctl 命令")。如果該設(shè)備方法返回一個(gè)非負(fù)值,那么該值會(huì)被返回給調(diào)用程序以表示調(diào)用成功。