如何編寫Linux操作系統(tǒng)下的設(shè)備驅(qū)動程序_第1頁
如何編寫Linux操作系統(tǒng)下的設(shè)備驅(qū)動程序_第2頁
如何編寫Linux操作系統(tǒng)下的設(shè)備驅(qū)動程序_第3頁
如何編寫Linux操作系統(tǒng)下的設(shè)備驅(qū)動程序_第4頁
如何編寫Linux操作系統(tǒng)下的設(shè)備驅(qū)動程序_第5頁
已閱讀5頁,還剩3頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、如何編寫 Linux 操作系統(tǒng)下的設(shè)備驅(qū)動程序 版本: 1.0目錄:序言一 . Linux device driver 的概念 二.實(shí)例剖析 三.設(shè)備驅(qū)動程序中的一些具體問題原作: (若無特別說明,文中各部份內(nèi)容出處相同)發(fā)信人 : Roy_G轉(zhuǎn)載:SMTH編輯:江雄 <jxiong> 定版 1.0如何編寫 Linux 操作系統(tǒng)下的設(shè)備驅(qū)動程序Roy G序言Linux 是 Unix 操作系統(tǒng)的一種變種 ,在 Linux 下編寫驅(qū)動程序的原理和思想完全類似于其他的 Unix 系統(tǒng) ,但它 dos 或 window 環(huán)境下的驅(qū)動程序有很大的區(qū)別.在 Linux 環(huán)境下設(shè)計(jì)驅(qū)動程序 ,思

2、想簡潔 ,操作方便 ,功能也很強(qiáng)大 ,但是支持函數(shù)少 ,只能依賴 kernel 中的函數(shù) ,有些常用的操 作要自己來編寫 ,而且調(diào)試也不方便 .本人這幾周來為實(shí)驗(yàn)室自行研制的一塊多媒體卡編制了 驅(qū)動程序 , 獲得了一些經(jīng)驗(yàn) , 愿與 Linux fans 共享 ,有不當(dāng)之處 ,請予指正 .以下的一些文字主要來源于 khg,johnsonm 的 Write linux device driver,Brennans Guide toInline Assembly,The Linux A-Z還有清華 BBS上的有關(guān) device driver的一些資料.這些資料有的 已經(jīng)過時 ,有的還有一些錯誤 ,

3、我依據(jù)自己的試驗(yàn)結(jié)果進(jìn)行了修正.一. Linux device driver 的概念系統(tǒng)調(diào)用是操作系統(tǒng)內(nèi)核和應(yīng)用程序之間的接口,設(shè)備驅(qū)動程序是操作系統(tǒng)內(nèi)核和機(jī)器硬件之間的接口 .設(shè)備驅(qū)動程序?yàn)閼?yīng)用程序屏蔽了硬件的細(xì)節(jié),這樣在應(yīng)用程序看來 ,硬件設(shè)備只.設(shè)備驅(qū)動程序是是一個設(shè)備文件 , 應(yīng)用程序可以象操作普通文件一樣對硬件設(shè)備進(jìn)行操作 內(nèi)核的一部分 ,它完成以下的功能 :1. 對設(shè)備初始化和釋放 .2. 把數(shù)據(jù)從內(nèi)核傳送到硬件和從硬件讀取數(shù)據(jù) .3. 讀取應(yīng)用程序傳送給設(shè)備文件的數(shù)據(jù)和回送應(yīng)用程序請求的數(shù)據(jù) .4. 檢測和處理設(shè)備出現(xiàn)的錯誤 .在 Linux 操作系統(tǒng)下有兩類主要的設(shè)備文件類型,

4、一種是字符設(shè)備 ,另一種是塊設(shè)備 .字符設(shè)備和塊設(shè)備的主要區(qū)別是 :在對字符設(shè)備發(fā)出讀 /寫請求時 ,實(shí)際的硬件 I/O 一般就緊接著發(fā)生了 , 塊設(shè)備則不然 ,它利用一塊系統(tǒng)內(nèi)存作緩沖區(qū) ,當(dāng)用戶進(jìn)程對設(shè)備請求讀 /寫時 ,它首先察看緩 沖區(qū)的內(nèi)容 ,如果緩沖區(qū)的數(shù)據(jù)能滿足用戶的要求,就返回請求的數(shù)據(jù) ,如果不能 ,就調(diào)用請求函數(shù)來進(jìn)行實(shí)際的 I/O 操作 .塊設(shè)備是主要針對磁盤等慢速設(shè)備設(shè)計(jì)的,以免耗費(fèi)過多的 CPU時間來等待 .已經(jīng)提到 ,用戶進(jìn)程是通過設(shè)備文件來與實(shí)際的硬件打交道.每個設(shè)備文件都都有其文件屬性(c/b), 表示是字符設(shè)備還蔤強(qiáng)檣璞 ?另外每個文件都有兩個設(shè)備號 , 第一

5、個是主設(shè)備號 ,標(biāo)識驅(qū) 動程序 ,第二個是從設(shè)備號 ,標(biāo)識使用同一個設(shè)備驅(qū)動程序的不同的硬件設(shè)備,比如有兩個軟盤,就可以用從設(shè)備號來區(qū)分他們 .設(shè)備文件的的主設(shè)備號必須與設(shè)備驅(qū)動程序在登記時申請 的主設(shè)備號一致 ,否則用戶進(jìn)程將無法訪問到驅(qū)動程序.最后必須提到的是 ,在用戶進(jìn)程調(diào)用驅(qū)動程序時 ,系統(tǒng)進(jìn)入核心態(tài) ,這時不再是搶先式調(diào)度 .也 就是說 ,系統(tǒng)必須在你的驅(qū)動程序的子函數(shù)返回后才能進(jìn)行其他的工作.如果你的驅(qū)動程序陷入死循環(huán) ,不幸的是你只有重新啟動機(jī)器了 ,然后就是漫長的 fsck./hehe(請看下節(jié) ,實(shí)例剖析 ) 來源 : 中國科大 BBS 站 如何編寫 Linux 操作系統(tǒng)下的

6、設(shè)備驅(qū)動程序Roy G二.實(shí)例剖析我們來寫一個最簡單的字符設(shè)備驅(qū)動程序.雖然它什么也不做 ,但是通過它可以了解 Linux 的設(shè)備驅(qū)動程序的工作原理 .把下面的 C 代碼輸入機(jī)器 ,你就會獲得一個真正的設(shè)備驅(qū)動程序.不過我的 kernel 是 2.0.34, 在低版本的 kernel 上可能會出現(xiàn)問題 ,我還沒測試過 ./xixi#define _NO_VERSION_#include#includechar kernel_version = UTS_RELEASE;這一段定義了一些版本信息 ,雖然用處不是很大 ,但也必不可少 .Johnsonm 說所有的驅(qū)動程序的開頭都要包含 ,但我看倒是未

7、必由于用戶進(jìn)程是通過設(shè)備文件同硬件打交道,對設(shè)備文件的操作方式不外乎就是一些系統(tǒng)調(diào)用,如 open,read,write,close, 注意 ,不是 fopen, fread., 但是如何把系統(tǒng)調(diào)用和驅(qū)動程序關(guān)聯(lián)起來呢 ?這需要了解一個非常關(guān)鍵的數(shù)據(jù)結(jié)構(gòu) :struct file_operations int (*seek) (struct inode * ,struct file *, off_t ,int);int (*read) (struct inode * ,struct file *, char ,int);int (*write) (struct inode * ,struct

8、file *, off_t ,int);int (*readdir) (struct inode * ,struct file *, struct dirent *,int);int (*select) (struct inode * ,struct file *, int ,select_table *);int (*ioctl) (struct inode * ,struct file *, unsined int ,unsignedlong);int (*mmap) (struct inode * ,struct file *, struct vm_area_struct*);int (

9、*open) (struct inode * ,struct file *);int (*release) (struct inode * ,struct file *);int (*fsync) (struct inode * ,struct file *);int (*fasync) (struct inode * ,struct file *,int);int (*check_media_change) (struct inode * ,struct file *);int (*revalidate) (dev_t dev);這個結(jié)構(gòu)的每一個成員的名字都對應(yīng)著一個系統(tǒng)調(diào)用.用戶進(jìn)程利用系

10、統(tǒng)調(diào)用在對設(shè)備文件進(jìn)行諸如 read/write 操作時 ,系統(tǒng)調(diào)用通過設(shè)備文件的主設(shè)備號找到相應(yīng)的設(shè)備驅(qū)動程序,然后讀取這個數(shù)據(jù)結(jié)構(gòu)相應(yīng)的函數(shù)指針,接著把控制權(quán)交給該函數(shù) .這是 linux 的設(shè)備驅(qū)動程序工作的基本原理 .既然是這樣 , 則編寫設(shè)備驅(qū)動程序的主要工作就是編寫子函數(shù),并填充file_operations 的各個域 .相當(dāng)簡單 ,不是嗎 ?下面就開始寫子程序 .#include#include#include#include#includeunsigned int test_major = 0;static int read_test(struct inode *node,st

11、ruct file *file,char *buf,int count)int left;if (verify_area(VERIFY_WRITE,buf,count) = -EFAULT )return -EFAULT;for(left = count ; left > 0 ; left-)_put_user(1,buf,1);buf+;return count;這個函數(shù)是為read調(diào)用準(zhǔn)備的當(dāng)調(diào)用read時,read_test()被調(diào)用,它把用戶的緩沖區(qū)全部寫1.buf 是 read 調(diào)用的一個參數(shù) .它是用戶進(jìn)程空間的一個地址.但是在 read_test 被調(diào)用時 ,系統(tǒng)進(jìn)入核心態(tài)

12、 所以不能使用buf 這個地址 ,必須用 _put_user(),這是 kernel 提供的一個函數(shù) ,用于向用戶傳送數(shù)據(jù) 另外還有很多類似功能的函數(shù)請參考 在向用戶空間拷貝數(shù)據(jù)之前,必須驗(yàn)證buf是否可用.這就用到函數(shù)verify_area.static int write_tibet(struct inode *inode,struct file *file,const char *buf,int count)return count;static int open_tibet(struct inode *inode,struct file *file )MOD_INC_USE_COUNT

13、;return 0;static void release_tibet(struct inode *inode,struct file *file )MOD_DEC_USE_COUNT;這幾個函數(shù)都是空操作實(shí)際調(diào)用發(fā)生時什么也不做,他們僅僅為下面的結(jié)構(gòu)提供函數(shù)指針。struct file_operations test_fops = NULL,read_test,write_test,NULL, /* test_readdir */NULL,NULL, /* test_ioctl */NULL, /* test_mmap */open_test,release_test,NULL, /* te

14、st_fsync */NULL, /* test_fasync */* nothing more, fill with NULLs */;設(shè)備驅(qū)動程序的主體可以說是寫好了。 現(xiàn)在要把驅(qū)動程序嵌入內(nèi)核。 驅(qū)動程序可以按照兩種 方式編譯。一種是編譯進(jìn) kernel,另一種是編譯成模塊(modules),如果編譯進(jìn)內(nèi)核的話,會 增加內(nèi)核的大小,還要改動內(nèi)核的源文件,而且不能動態(tài)的卸載, 不利于調(diào)試,所以推薦使 用模塊方式。int init_module(void)int result;result = register_chrdev(0, "test", &test_fo

15、ps);if (result < 0) printk(KERN_INFO "test: cant get major numbern");return result;if (test_major = 0) test_major = result; /* dynamic */return 0;在用 insmod 命令將編譯好的模塊調(diào)入內(nèi)存時, init_module 函數(shù)被調(diào)用。 在這里, init_module 只做了一件事,就是向系統(tǒng)的字符設(shè)備表登記了一個字符設(shè)備。register_chrdev 需要三個參數(shù),參數(shù)一是希望獲得的設(shè)備號, 如果是零的話, 系統(tǒng)將選擇一

16、個沒有被占用的設(shè)備號返回。 參數(shù)二是設(shè)備文件名, 參數(shù)三用來登記驅(qū)動程序?qū)嶋H執(zhí)行操作的函數(shù)的指針。 如果登記成功, 返回設(shè)備的主設(shè)備號,不成功,返回一個負(fù)值。void cleanup_module(void)unregister_chrdev(test_major, "test");在用 rmmod 卸載模塊時, cleanup_module 函數(shù)被調(diào)用,它釋放字符設(shè)備 test 在系統(tǒng)字符設(shè)備表中占有的表項(xiàng)。一個極其簡單的字符設(shè)備可以說寫好了,文件名就叫 test.c 吧。下面編譯$ gcc -O2 -DMODULE -D_KERNEL_ -c test.c得到文件 te

17、st.o 就是一個設(shè)備驅(qū)動程序。 如果設(shè)備驅(qū)動程序有多個文件,把每個文件按上面的命令行編譯,然后ld -r file1.o file2.o -o modulename.驅(qū)動程序已經(jīng)編譯好了,現(xiàn)在把它安裝到系統(tǒng)中去。$ insmod -f test.o如果安裝成功,在 /proc/devices 文件中就可以看到設(shè)備 test, 并可以看到它的主設(shè)備號 ,。要卸載的話,運(yùn)行$ rmmod test下一步要創(chuàng)建設(shè)備文件。mknod /dev/test c major minorc 是指字符設(shè)備, major 是主設(shè)備號,就是在 /proc/devices 里看到的。 用 shell 命令$ cat

18、 /proc/devices | awk "$2="test" print $1" 就可以獲得主設(shè)備號,可以把上面的命令行加入你的 shell script 中去。 minor 是從設(shè)備號,設(shè)置成 0 就可以了。我們現(xiàn)在可以通過設(shè)備文件來訪問我們的驅(qū)動程序。寫一個小小的測試程序。 #include#include#include#includemain()int testdev;int i;char buf10;testdev = open("/dev/test",O_RDWR);if ( testdev = -1 )printf(&

19、quot;Cannt open file n"); exit(0);read(testdev,buf,10);for (i = 0; i < 10;i+) printf("%dn",bufi); close(testdev);編譯運(yùn)行,看看是不是打印出全 1 ?以上只是一個簡單的演示。真正實(shí)用的驅(qū)動程序要復(fù)雜的多,要處理如中斷, DMA,I/O port 等問題。這些才是真正的難點(diǎn)。請看下節(jié),實(shí)際情況的處理。 來源 : 中國科大 BBS 站 如何編寫 Linux 操作系統(tǒng)下的設(shè)備驅(qū)動程序Roy G三 設(shè)備驅(qū)動程序中的一些具體問題。1. I/O Port.和硬

20、件打交道離不開I/O Port,老的ISA設(shè)備經(jīng)常是占用實(shí)際的I/O端口,在 linux 下,操作系統(tǒng)沒有對 I/O 口屏蔽,也就是說,任何驅(qū)動程序都可以 對任意的 I/O 口操作,這樣就很容易引起混亂。每個驅(qū)動程序應(yīng)該自己避免 誤用端口。有兩個重要的 kernel 函數(shù)可以保證驅(qū)動程序做到這一點(diǎn)。1) check_region(int io_port, int off_set)這個函數(shù)察看系統(tǒng)的I/O表,看是否有別的驅(qū)動程序占用某一段I/O 口。參數(shù) 1: io 端口的基地址,參數(shù) 2:io 端口占用的范圍。返回值: 0 沒有占用, 非 0,已經(jīng)被占用。2) request_region(i

21、nt io_port, int off_set,char *devname)如果這段 I/O 端口沒有被占用,在我們的驅(qū)動程序中就可以使用它。在使用之前,必須向系 統(tǒng)登記,以防止被其他程序占用。登記后,在 /proc/ioports 文件中可以看到你登記的 io 口。 參數(shù) 1: io 端口的基地址。參數(shù) 2:io 端口占用的范圍。參數(shù) 3:使用這段 io 地址的設(shè)備名。在對 I/O 口登記后,就可以放心地用 inb(), outb() 之類的函來訪問了。在一些 pci 設(shè)備中, I/O 端口被映射到一段內(nèi)存中去,要訪問這些端口就相當(dāng)于訪問一段內(nèi) 存。經(jīng)常性的,我們要獲得一塊內(nèi)存的物理地址。在

22、dos環(huán)境下,(之所以不說是 dos操作系統(tǒng)是因?yàn)槲艺J(rèn)為 DOS 根本就不是一個操作系統(tǒng),它實(shí)在是太簡單,太不安全了)只要用 段:偏移就可以了。在 window95中,95ddk提供了一個 vmm 調(diào)用 _MapLinearToPhys,用以 把線性地址轉(zhuǎn)化為物理地址。但在 Linux 中是怎樣做的呢?2 內(nèi)存操作在設(shè)備驅(qū)動程序中動態(tài)開辟內(nèi)存,不是用 malloc,而是kmalloc,或者用get_free_pages直 接申請頁。釋放內(nèi)存用的是kfree,或free_pages.請注意,kmalloc等函數(shù)返回的是物理地址!而 malloc 等返回的是線性地址! 關(guān)于 kmalloc 返回的

23、是物理地址這一點(diǎn)本人有點(diǎn)不太明 白:既然從線性地址到物理地址的轉(zhuǎn)換是由 386cpu 硬件完成的,那樣匯編指令的操作數(shù)應(yīng) 該是線性地址, 驅(qū)動程序同樣也不能直接使用物理地址而是線性地址。但是事實(shí)上kmalloc 返回的確實(shí)是物理地址,而且也可以直接通過它訪問實(shí)際的RAM,我想這樣可以由兩種解釋,一種是在核心態(tài)禁止分頁,但是這好像不太現(xiàn)實(shí);另一種是 linux 的頁目錄和頁表項(xiàng)設(shè)計(jì)得正好使得物 理地址等同于線性地址。我的想法不知對不對,還請高手指教。言歸正傳,要注意 kmalloc 最大只能開辟 128k-16 , 16 個字節(jié)是被頁描述符結(jié)構(gòu)占用了。 kmalloc用法參見khg.內(nèi)存映射的I/O 口,寄存器或者是硬件設(shè)備的RAM(如顯存)一般占用F0000000 以上的地址空間。 在驅(qū)動程序中不能直接訪問, 要通過 kernel 函數(shù) vremap 獲得重 新映射以后的地址。另外,很多硬件需要一塊比較大的連續(xù)內(nèi)存用作DMA傳送。這塊內(nèi)存需要一直駐留在內(nèi)存,不能被交換到文件中去。但是 kmall

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論