Linux字元設備驅動程序的設計

admin @ 2014-03-25 , reply:0

摘 要  介紹了Linux字元設備驅動程序中建立設備 ,初始化設備、設備的資源分配和如何訪問設備的方法及相關函數的實現.

關鍵詞  Linux 字元設備  設備驅動程序

      設備驅動程序實質上是一組完成不同任務的函數的集合 ,通過這些函數所提供的功能可以使得從設備接受輸入和將輸出送到設備就象讀寫文件一樣 ,因此 ,Linux中的每一個設備都具有文件的外在特徵 ,都能使用open() ,close() ,read() ,write()等系統調用.

Linux設備驅動程序的主要功能有:
a 初始化設備;
b 提供各類設備服務;
c 負責內核和設備之間的數據交換;
d 檢測和處理設備工作過程中出現的錯誤.

1  命名規則
每個設備的驅動程序都有一組實質上相同的函數 ,並且都需添加至內核原碼中以重新生成內
核 ,因此為了防止不同驅動程序之間函數名的衝突 ,必須確保名稱的唯一性 ,最好的方法是在各驅
動程序的函數前加一以設備名為字元串的前綴.
本文約定要開發驅動程序的設備名為“mydev”.

2  設備文件的建立
為了使對設備的讀寫操作象文件的存取一樣處理 ,Linux所有的設備在目錄樹中的適當位置都有相對應的文件名稱 ,這樣才能對它們進行open() 、close()等系統調用;這些文件稱為字元設備特殊文件或塊設備特殊文件 ,一般存放在/ dev目錄中 ,如主硬碟第一分區的塊設備特殊文件為/ dev/hda0 ,第一虛擬控制終端的字元設備特殊文件為/ dev/tty1.設備特殊文件的建立使用mknod命令或mknod()系統調用(只有root 帳號才有權建立) ,命令格式為:mknod filename type major minor ,其中filename 為要建立的設備特殊文件名(含路徑) ,type 說明
設備類型(c 為字元設備 ,b為塊設備) ,major 和minor 說明與該文件結合的主設備號和次設備號.

如: # mknod/ dev/mydev c 40 1 命令 ,用主設備號 40 和次設備號 1 建立了字元設備特殊文件/dev/mydev.

3  init 函數
init 函數用來完成對所控設備的初始化工作 ,以及調用 register- chrdev()函數來註冊字元設備.
根據命名規則和本文約定 ,設備“mydev”的init 函數為:
void mydev- init(void)
{
 if(register- chrdev(40“, mydev”,&mydev- fops))
   TRACE- TXT“( Device(40) driver registered error”)
 else
   TRACE- TXT“( Device(40) driver registered successfully”)
∥以下為對設備進行初始化的代碼 ,略

}
其中 ,register- chrdev函數中的參數40 為主設備號“, mydev”為設備名 ,mydev- fops 為以下所要介紹的包含基本入口點的結構 ,類型為file- operations;當執行 myder- init 時 ,它將調用內核函數 reg2ister- chrdev,把驅動程序的基本入口點指針存放在內核的字元設備地址表中 ,在用戶進程對該設備執行系統調用時提供入口地址.

4  基本入口點
每個設備驅動程序都有一個稱為file- operation 的數據結構 ,其中包含指向驅動程序內部大多數函數的入口(函數指針) ,完整結構如下:
struct file- operations {
 int ( 3lseek) () ;
 int ( 3 read) () ;
 int ( 3write) () ;
 int ( 3 readdir) () ;
 int ( 3 select) () ;
 int ( 3ioctl) () ;
 int ( 3mmap) () ;
 int ( 3open) () ;
 void ( 3 release) () ;
 int ( 3fsync) () ;
 int ( 3fasync) () ;
 int ( 3check- media- change) () ;
 void ( 3 revalidate) () ;

};
大多數的驅動程序只是利用了其中的一部分(對於驅動程序中不提供的功能 ,把相應位置的值設為NULL) ,對於字元設備來說 ,要提供的主要入口有:open() 、release() 、read() 、write() 、ioctl() ,現分別說明如下:

open()函數  對設備特殊文件進行open()系統調用時 ,將調用驅動程序的open()函數:int open(struct inode 3inode ,struct file 3file) ,其中參數inode 為設備特殊文件的 inode(索引結點)結構的指針 ,參數file 是指向這一設備的文件結構的指針.open()的主要任務是確定硬體處在就緒狀態、驗證次設備號的合法性(次設備號可以用MINOR(inode - >i- rdev)取得) 、控制使用設備的進程數、根據執行情況返回狀態碼(0 表示成功 ,負數表示存在錯誤)等.

release()函數 當最後一個打開設備的用戶進程執行 close()系統調用時 ,內核將調用驅動程序的 release()函數:void release(struct inode 3inode ,struct file 3file) ,參數說明同上.release 函數的主要任務是清理未結束的輸入/輸出操作、釋放資源、用戶自定義排他標誌的複位等.

read()函數 當對設備特殊文件進行read()系統調用時 ,將調用驅動程序read()函數:void read(struct inode 3inode ,struct file 3file ,char 3 buf ,int count) ;inode 和file 參數定義同上 ,參數 buf 是指向用戶空間緩衝區的指針 ,由用戶進程給出 ,count 為用戶進程要求讀取的位元組數 ,也由用戶給出.read()函數的功能就是從硬設備或內核內存中讀取或複製 count 個位元組到 buf 指定的緩衝區中;在複製數據時要注意 ,驅動程序運行在內核中 ,而 buf 指定的緩衝區在用戶內存區中 ,是不能直接在內核中訪問使用的 ,因此 ,必須使用特殊的複製函數來完成複製工作 ,這些函數在〈asm/ segment. h〉
中定義:
void put- user- byte(char data- byte ,char 3 u- addr) ;
void put- user- word(short data- word ,short 3 u- addr) ;
void put- user- long(long data- long,long 3 u- addr) ;
void memcpy- tofs(void 3 u- addr ,void 3 k- addr ,unsigned long cnt) ;
參數 u- addr 為用戶空間地址 ,k- addr 為內核空間地址 ,cnt 為位元組數.
    另外 ,在複製數據前 ,必須檢驗用戶對指定內存空間的許可權以確定是否予以完成操作 ,可以用內核函數verify- area(定義在 linux/mm. h 中)來完成;void verify- area(int access,void 3 u- addr ,un2signed long size) ,其中 ,參數 access 為訪問類型 ,VERIFY- WRITE為寫訪問 ,VERIFY- READ 為讀訪問 ,參數 u- addr 為用戶空間的起始地址 ,參數 size 是內存塊的位元組數.如果允許按指定方式訪問指定內存空間 ,函數verify- area 返回0 ,否則 - EFAULT.

write()函數  當設備特殊文件進行 write()系統調用時 ,將調用驅動程序的 write()函數:void write(struct inode 3inode ,struct file 3file ,char 3 buf ,int count) ;參數說明同上.write()的功能是將參數buf 指定的緩衝區中的count 個位元組內容複製到硬體或內核內存中;和read()一樣 ,複製工作也需要由特殊函數來完成:
unsigned char- get- user- byte(char 3 u- addr) ;
unsigned char- get- user- word(short 3 u- addr) ;
unsigned char- get- user- long(long 3 u- addr) ;
unsigned memcpy- - fromfs(void 3 k- addr ,void 3 u- addr ,unsigned long cnt) ;

ioctl()函數  該函數是特殊的控制函數 ,可以通過它向設備傳遞控制信息或從設備取得狀態信息 ,函數原型為:int ioctl(struct inode 3inode ,struct file 3file ,unsigned int cmd ,unsigned long arg) ,參數inode 和file 的含義同上 ,參數cmd為設備驅動程序要執行的命令的代碼 ,由用戶自定義(0x5421 ,0x5450 ,0x5451 ,0x5452系統保留) ,參數arg為相應的命令提供參數 ,類型可以是整型、指針等.

     同樣 ,在驅動程序中 ,這些函數的定義也必須符合命名規則 ,按照本文約定 ,設備“mydev”的驅動程序的這些函數應分別命名為 mydev- open ,mydev- release ,mydev- read ,mydev- write ,mydev- ioctl ,因此設備“mydev”的基本入口點結構變數 mydev- fops賦值如下:
struct file- operations mydev- fops {
 NULL ,
 mydev- read ,
 mydev- write ,
 NULL ,
 NULL ,
 mydev- ioctl ,
 NULL ,
 mydev- open ,
 mydev- release ,
 NULL ,
 NULL ,
 NULL ,
 NULL
};

5  內存分配
      和其他應用程序一樣 ,設備驅動程序在運行時也需要一定的內存空間來存放臨時數據 ,但由於驅動程序是在內核運行 ,所以它的內存管理有它的特殊性 ,在Linux中有幾種分配內存的方法:
(1) 採用固定內存分配法;即在源程序中就定義一固定大小的數據緩衝區 ,如:static char my-buffer1000 ;

(2) 使用內核的動態內存分配 ,由函數對 kmalloc()和 kfree()來實現. kmalloc()函數的原型為:vold kmalloc(size- t size ,int priority) ,參數 size 為要分配內存容量的位元組數 ,返回時指向已分配內存的起始地址 ,出錯時 ,返回0;參數priority說明若 kmalloc()不能馬上分配內存時用戶進程要採用的動作 ,GFP- KERNEL 表示等待 ,等 kmalloc()函數將一些內存安排到交換區來滿足你的內存需要 ,GFP- ATOMIC表示不等待 ,如不能立即分配到內存則返回 0 值.用 kmalloc()分配的內存塊可以用kfree()函數來釋放 ,在〈linux/malloc. h〉中 kfree()被定義為: # define kfree(n) kfree- s((n) ,0) ,其中kfree- s()函數原型為:void kfree- s(void 3ptr ,int size) ,參數ptr 為 kmalloc()返回的已分配內存的指針 ,size 是要釋放的以位元組計數的內存容量 ,若為0 時 ,由內核自動確定內存塊的大小.


6    中斷
       由於大多數設備驅動程序是用來監視和控制硬體設備工作的 ,因此需要對硬體產生的中斷請求提供中斷服務子程序;與登記基本入口點一樣 ,驅動程序也要請求內核將特定的中斷請求和中斷服務子程序聯繫在一起.在Linux中 ,用request- irq()函數(在Linux/ signal.h中定義)來實現請求:intrequest- irq(unsigned int irq ,void( 3 handler)int ,unsigned long type ,char 3 name) ,參數 irq 為要截取的中斷請求號 ,參數 handler為指向中斷服務子程序的指針 ,參數 type 用來確定是正常中斷還是快速,所謂正常中斷就是中斷服務子程序返回后 ,內核可以執行調度程序來確定將運行哪一個進程 ,而快速中斷是指中斷服務子程序返回后 ,立即執行被中斷程序 ,正常中斷 type 取值為 0 ,快速中斷type 取值為 SA- INTERRUPT,參數 name 是設備驅動程序的名稱.

7  驅動程序的安裝
     當你編寫完了驅動程序后 ,就要對它進行編譯和裝入內核 ,步驟如下:
(1) 將任何與設備驅動程序有關的.c 和.h文件複製到包含字元設備驅動程序源碼的目錄中 ,一般在Linux源程序目錄中的 drivers/char子目錄中.
(2) 在chr- dev- init()函數的最後加上調用驅動程序 init()函數的源碼 ,如本文約定 ,需加上mydev- init() ,chrdev- init()函數在 drivers/char/mem.c 中.
(3) 編輯 drivers/char目錄中的makefile ,將驅動程序的 OBJ 文件名稱(.o)放在 OBJS定義的後面 ,並將源文件名(.c)放在 SRCS定義的後面.
(4) 編譯、連接並重新安裝內核.
請注意 ,在改變內核代碼時 ,請備份你的舊系統 ,以避免由於新內核的工作混亂造成永久性的破壞.

8  結束語
       設備驅動程序是操作系統內核和機器硬體之間的介面 ,通過它可以使得設備文件化.本文只是簡單介紹了編寫一個字元設備驅動程序的基本規則和方法 ,如讀者欲了解更多、更詳細的內容 ,請參閱Linux的 HowTo 文檔.




[admin via 研發互助社區 ] Linux字元設備驅動程序的設計已經有1279次圍觀

http://cocdig.com/docs/show-post-42732.html