1。 I/O Port。 和硬件打交道離不開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(int 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)存的物理地址。 2。內(nèi)存操作 在設(shè)備驅(qū)動程序中動態(tài)開辟內(nèi)存,不是用malloc,而是kmalloc,或者用get_free_pages直接申請頁。釋放內(nèi)存用的是kfree,或free_pages。 請注意,kmalloc等函數(shù)返回的是物理地址! 注意,kmalloc最大只能開辟128k-16,16個字節(jié)是被頁描述符結(jié)構(gòu)占用了。 內(nèi)存映射的I/O口,寄存器或者是硬件設(shè)備的RAM(如顯存)一般占用F0000000以上的地址空間。在驅(qū)動程序中不能直接訪問,要通過kernel函數(shù)vremap獲得重新映射以后的地址。 另外,很多硬件需要一塊比較大的連續(xù)內(nèi)存用作DMA傳送。這塊程序需要一直駐留在內(nèi)存,不能被交換到文件中去。但是kmalloc最多只能開辟128k的內(nèi)存。 這可以通過犧牲一些系統(tǒng)內(nèi)存的方法來解決。 3。中斷處理 同處理I/O端口一樣,要使用一個中斷,必須先向系統(tǒng)登記。 int request_irq(unsigned int irq ,void(*handle)(int,void *,struct pt_regs *), unsigned int long flags, const char *device); irq: 是要申請的中斷。 handle:中斷處理函數(shù)指針。 flags:SA_INTERRUPT 請求一個快速中斷,0 正常中斷。 device:設(shè)備名。 如果登記成功,返回0,這時(shí)在/proc/interrupts文件中可以看你請求的中斷。 4。一些常見的問題。 對硬件操作,有時(shí)時(shí)序很重要。但是如果用C語言寫一些低級的硬件操作的話,gcc往往會對你的程序進(jìn)行優(yōu)化,這樣時(shí)序會發(fā)生錯誤。如果用匯編寫呢,gcc同樣會對匯編代碼進(jìn)行優(yōu)化,除非用volatile關(guān)鍵字修飾。最保險(xiǎn)的辦法是禁止優(yōu)化。這當(dāng)然只能對一部分你自己編寫的代碼。如果對所有的代碼都不優(yōu)化,你會發(fā)現(xiàn)驅(qū)動程序根本無法裝載。這是因?yàn)樵诰幾g驅(qū)動程序時(shí)要用到gcc的一些擴(kuò)展特性,而這些擴(kuò)展特性必須在加了優(yōu)化選項(xiàng)之后才能體現(xiàn)出來。
由于設(shè)備種類繁多,相應(yīng)的設(shè)備驅(qū)動程序也非常之多。盡管設(shè)備驅(qū)動程序是內(nèi)核的一部分,但設(shè)備驅(qū)動程序的開發(fā)往往由很多人來完成,如業(yè)余編程高手、設(shè)備廠商等。為了讓設(shè)備驅(qū)動程序的開發(fā)建立在規(guī)范的基礎(chǔ)上,就必須在驅(qū)動程序和內(nèi)核之間有一個嚴(yán)格定義和管理的接口,例如SVR4提出了DDI/DDK規(guī)范,其含義就是設(shè)備與驅(qū)動程序接口/設(shè)備驅(qū)動程序與內(nèi)核接口(Device-Driver Interface/Driver-Kernel Interface)。通過這個規(guī)范,可以規(guī)范設(shè)備驅(qū)動程序與內(nèi)核之間的接口。
Linux的設(shè)備驅(qū)動程序與外接的接口與DDI/DKI規(guī)范相似,可以分為三部分: (1) 驅(qū)動程序與內(nèi)核的接口,這是通過數(shù)據(jù)結(jié)構(gòu)file_operations來完成的。 (2) 驅(qū)動程序與系統(tǒng)引導(dǎo)的接口,這部分利用驅(qū)動程序?qū)υO(shè)備進(jìn)行初始化。 (3) 驅(qū)動程序與設(shè)備的接口,這部分描述了驅(qū)動程序如何與設(shè)備進(jìn)行交互,這與具體設(shè)備密切相關(guān)。
根據(jù)功能,驅(qū)動程序的代碼可以分為如下幾個部分: (1)驅(qū)動程序的注冊和注銷 (2)設(shè)備的打開與釋放 (3)設(shè)備的讀和寫操作 (4)設(shè)備的控制操作 (5)設(shè)備的中斷和查詢處理 前三點(diǎn)我們已經(jīng)給予簡單說明,后面我們還會結(jié)合具體程序給出進(jìn)一步的說明。關(guān)于設(shè)備的控制操作可以通過驅(qū)動程序中的ioctl()來完成,例如,對光驅(qū)的控制可以使用cdrom_ ioctl()。 與讀寫操作不同,ioctl()的用法與具體設(shè)備密切相關(guān),例如,對于軟驅(qū)的控制可以使用floppy_ioctl(),其調(diào)用形式為: static int floppy_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long param) 其中cmd的取值及含義與軟驅(qū)有關(guān),例如,F(xiàn)DEJECT表示彈出軟盤。 除了ioctl(),設(shè)備驅(qū)動程序還可能有其他控制函數(shù),如lseek()等。 對于不支持中斷的設(shè)備,讀寫時(shí)需要輪流查詢設(shè)備的狀態(tài),以便決定是否繼續(xù)進(jìn)行數(shù)據(jù)傳輸,例如,打印機(jī)驅(qū)動程序在缺省時(shí)輪流查詢打印機(jī)的狀態(tài)。如果設(shè)備支持中斷,則可按中斷方式進(jìn)行 |
|