linux 基礎復習(9)設備驅動入門
struct inode提供了關于設備文件/dev/driver(假設此設備名為driver)的信息。struct file 提供關于被打開的文件信息,主要用于與文件系統對應的設備驅動程序使用。struct file 較為重要,這里列出了它的定義:
struct file {
mode_t f_mode;/*標識文件是否可讀或可寫,FMODE_READ或FMODE_WRITE*/
dev_t f_rdev; /* 用于/dev/tty */
off_t f_pos; /* 當前文件位移 */
unsigned short f_flags; /* 文件標志,如O_RDONLY、O_NONBLOCK和O_SYNC */
unsigned short f_count; /* 打開的文件數目 */
unsigned short f_reada;
struct inode *f_inode; /*指向inode的結構指針 */
struct file_operations *f_op;/* 文件索引指針 */
};
設備驅動程序主要組成
(1)設備注冊
設備注冊使用函數register_chrdev,調用該函數后就可以向系統申請主設備號,如果register_chrdev操作成功,設備名就會出現在/proc/devices 文件里。
register_chrdev等函數語法要點所需頭文件 #i nclude 函數原型 int register_chrdev(unsigned int major, const char *name,struct file_operations *fops) major:設備驅動程序向系統申請的主設備號 如果為0 則系統為此驅動程序動態地分配一個主設備號函數傳入值 name:設備名 fops:對各個調用的入口點函數返回值 成功:如果是動態分配主設備號,此返回所分配的主設備號 且設備名就會出現在/proc/devices文函數返回值 件里
出錯:-1
(2)設備解除注冊
在關閉設備時,通常需要解除原先的設備注冊,此時可使用函數unregister_chrdev,此后該設備就會從/proc/devices 里消失。
unregister_chrdev等函數語法要點
所需頭文件 #i nclude
函數原型 int unregister_chrdev(unsigned int major, const char *name)
major:設備的主設備號,必須和注冊時的主設備號相同。
函數傳入值 name:設備名
函數返回值 成功:0,且設備名從/proc/devices文件里消失。
出錯:-1
(3)打開設備
打開設備的接口函數是open,根據設備的不同,open函數完成的功能也有所不同,但通常情況下在open函數中要完成如下工作。
· 遞增計數器。
· 檢查特定設備的特殊情況。
· 初始化設備。
· 識別次設備號
其中遞增計數器是用于設備計數的。由于設備在使用時通常會打開較多次數,也可以由不同的進程所使用,所以若有一進程想要關閉該設備,則必須保證其他設備沒有使用該設備。因此使用計數器就可以很好地完成這項功能。
這里,實現計數器操作的是用在中定義的3 個宏如下。
· MOD_INC_USE_COUNT:計數器加一。
· MOD_DEC_USE_COUNT:計數器減一。
· MOD_IN_USE:計數器非零時返回真。
另外,當有多個物理設備時,就需要識別次設備號來對各個不同的設備進行不同的操作,在有些驅動程序中并不需要用到。
雖然這是對設備文件執行的第一個操作,但卻不是驅動程序一定要聲明的操作。若這個函數的入口為NULL,那么設備的打開操作將永遠成功,但系統不會通知驅動程序。
(4)釋放設備
釋放設備的接口函數是release。要注意釋放設備和關閉設備是完全不同的。當一個進程釋放設備時,其他進程還能繼續使用該設備,只是該進程暫時停止對該設備的使用;而當一個進程關閉設備時,其他進程必須重新打開此設備才能使用。
釋放設備時要完成的工作如下。
· 遞減計數器MOD_DEC_USE_COUNT。
· 在最后一次釋放設備操作時關閉設備。
(5)讀寫設備
讀寫設備的主要任務就是把內核空間的數據復制到用戶空間,或者從用戶空間復制到內核空間,也就是將內核空間緩沖區里的數據復制到用戶空間的緩沖區中或者相反。這里首先解釋一個read和write函數的入口函數,如下表所示。
read、write函數語法要點
所需頭文件 #i nclude
函數原型 ssize_t (*read) (struct file *filp, char *buff, size_t count, loff_t *offp)
ssize_t (*write) (struct file *filp, const char *buff, size_t count, loff_t *offp)
filp:文件指針
函數傳入值 buff:指向用戶緩沖區
count:傳入的數據長度
offp:用戶在文件中的位置
函數返回值 成功:寫入的數據長度
雖然這個過程看起來很簡單,但是內核空間地址和應用空間地址是有很大區別的,其中之一就是用戶空間的內存是可以被換出的,因此可能會出現頁面失效等情況。所以就不能使用諸如memcpy 之類的函數來完成這樣的操作。在這里就要使用copy_to_user 或copy_from_user 函數,它們就是用來實現用戶空間和內核空間的數據交換的。copy_to_user 和copy_from_user 的格式如下表
所需頭文件 #i nclude
函數原型 Unsigned long copy_to_user(void *to, const void *from, unsigned long count)
Unsigned long copy_from_user(void *to, const void *from, unsigned long count)
To:數據目的緩沖區
函數傳入值 From:數據源函數傳入值 緩沖區
count:數據長度
函數返回值 成功:寫入的數據長度
失敗:-EFAULT
這兩個函數不僅實現了用戶空間和內核空間的數據轉換,而且還會檢查用戶空間指針的有效性。如果指針無效,那么就不進行復制
(6)獲取內存
在應用程序中獲取內存通常使用函數malloc,但在設備驅動程序中動態開辟內存可以有基于內存地址和基于頁面為單位兩類。其中,基于內存地址的函數有kmalloc,注意的是,kmalloc函數返回的是物理地址,而malloc 等返回的是線性地址,因此在驅動程序中不能使用malloc函數。與malloc()不同,kmalloc()申請空間有大小限制。長度是2的整次方,并且不會對所獲取的內存空間清零。
基于頁為單位的內存有函數族有如下。
· get_zeroed_page:獲得一個已清零頁面。
評論