Windows設備驅動程序編寫時的主要問題及解決方案
結合“通用高速PCI總線目標模塊”的驅動程序設計,全面地討論了Windows設備(特別是PCI設備)驅動程序編寫時所面臨的主要問題及解決方案,并提出了封裝設備驅動的方法。?
? 關鍵詞: PCI設備驅動程序 端口 內存 中斷 封裝????
???? 在設計和使用PCI設備時,經常要在PC機的軟件中訪問和控制硬件設備,但Windows操作系統(包括Windows95/98、Windows NT、Windows 2000為了保證系統的安全性、穩定性和可移植性,對應用程序訪問硬件資源加以限制,這就要求設計設備驅動程序以實現PC機的軟件對PCI設備的訪問。
?? Windows下的驅動程序不僅僅包括物理設備的驅動程序,也包括為文件系統等非物理設備編寫的虛擬設備驅動程序。為了簡化問題,下面只討論硬件物理設備的驅動程序
?? 。本文將以撏ㄓ酶咚伲校茫勺芟吣勘昴?閿[1]的驅動設計為例,探討PCI設備的驅動程序設計方案。我們開發了一套通用的PCI設備驅動程序,它可以完成一般PCI設備驅動所需的功能,可以作為其它PCI設備驅動開發的框架。?
??? 1 驅動程序的模式和開發工具的選擇??
???? 設備驅動程序是指管理某個外圍設備的一段代碼。驅動程序不會獨立地存在,而是操作系統的一部分。通過設備驅動程序,多個進程可以同時使用這些資源,從而可以實現多進程并行運行。
???
? 在下文中,將調用設備驅動程序的pc機程序稱為用戶程序。?
?????? Windows 95和Windows NT采用的驅動程序體系不同,所以大多數情況下驅動程序也不能通用。如果設備需要在Windows 9X/NT下使用,一般至少要設計Windows 9X和Windows NT兩個驅動程序版本。wINDOWS 98 可以兼容Windows 95的驅動程序,同時它又推出一個新的Win32 Drivers Mode (WDM)驅動類型。Windows 98中有些設備(如USB設備)的驅動程序必須為WDM模式。這個新的類型實際是在Windows NT的驅動模型的基礎上增加了即插即用等內容。WDM驅動也可以用在Windows 2000(先前叫Windows NT5.0)中。從長遠的角度看。今后開發人員只要開發WDM驅動就可以了,但從目前的市場情況來看,Windws 95是無法放棄的,所以WDM在近一兩年還無法替代其它類型的設備驅動。??
??? Intel 80386以上的微處理器有4個優先級別:0級、1級、2級和3級,一般操作系統運行于優先級0級上,而用戶程序運行在3級上,在對硬件操作上有一些限制(具體的限制在不同的操作系統中是不同的)。
?Windows 95支持的驅動類型很多,但針對一般硬件設備而言,主要是VxD和打印機驅動兩類。
?? VxD指的是Virtual Device Drivers。VxD運行在Intel系統的0級上,可以執行特權級指令,對任何I/O設備有全部訪問權,所以大多數硬件驅動程序都是VxD。VxD驅動通常以.vxd為擴展名,放在Windows\System目錄下,可以在Windws 95啟動時裝入,也可以在程序運行時根據需要動態地載入。
???? 動態加載有助于節約系統內存和資源。但打印機驅動程序不是VxD,它運行在3級上。同Windows 95類似,Windows NT的驅動也有可以運行在0級的內核模式(Kernel Mode)和運行在3極的用戶模式(User Mode)之分。由于Windows NT禁止用戶模式的程序訪問I/O端口(wINDOWS 95/98則允許用戶程序直接訪問I/O端口),直接控制物理設備的驅動程序都是內核模式的。而我們設計的PCI通用驅動程序要求對各種硬件資源訪問,所以應該選擇工作在0級的驅動程序模式。???
?? 開發設備驅動采用的主要開發工具是微軟為設備開發者提供的軟件包Device Driver Kit (DDK)。這個軟件包包括有關設備開發的文檔、編譯需要的頭文件和庫文件、調試工具和程序范例。在DDK中還定義了一些設備驅動可以調用的系統底層服務,象DMA服務、中斷服務、內存管理服務、可安裝文件系統服務等等。
?這些都是編寫設備驅動所必須的。但Windows 95的DDK由于主要使用匯編語言描述。開發起來比較困難。
?? 因此,我們在Windows 95操作系統中同時采用了Numega公司的產品VtoolsD。VtoolsD是基于C/C++的,支持Borland C++和Visual C++,使用和維護都較Windows DDK容易。?
?? 2 PCI驅動程序的特點???? 在設計驅動程序之前,首先要對欲控制的硬件設備進行細致地分析,更需要詳細了解硬件設備的特性。硬件設備的特性會對驅動程序設計產生重大的影響。需要了解的最主要的硬件特性包括:
???????? (1)設備的總線結構???
?? 設備采用什么總線結構非常關鍵,因為不同的總線類型(如ISA和PCI)在許多硬件工作機制上是不同的,所以驅動程序設計也不同。??
? (2)寄存器???? 要了解設置的控制寄存器、數據寄存器和狀態寄存器,以及這些寄存器工作的特性。???? (3)設備錯誤和狀態???? 要了解如何判斷設備的狀態和錯誤信號,這些信號要通過驅動程序返回給用戶。???? (4)中斷行為???? 要了解設備產生中斷的條件和使用中斷的數量。??
????? (5)數據傳輸機制???? 最常見的數據傳輸機制是通過I/O端口(port),也就是通過CPU的IN/OUT指令進行數據讀寫。PC的另一種重要的傳輸機制是DMA,但PCI規范不包括從屬DMA的說明。?
?? (6)設備內存???? 許多設備自身帶有內存,PCI設備大多是采用映射的方式映射到PC系統的物理內存。有的設備還要通過驅動程序設置設備的接口寄存器。?
?? 有關驅動程序的加載和響應用戶請求的內容,在DDK文檔中有規定,所以設計設備驅動程序主要的面臨問題是如何進行硬件操作,這是根據設備的不同而不同的。而硬件驅動程序的功能雖然千差萬別,但基本功能就是完成設備的初始化、對端口的讀寫操作、中斷的設置、響應和調用以及對內存的直接讀寫。如前面所說,Windows 9X和Windows NT的操作系統模型不同,但驅動程序所要完成的工作卻是相同的,所以下面以Windows 9X為主進行介紹,僅在需要的地方指出兩個操作系統的不同。
???
下面從這幾方面討論解決這些問題的途徑:??????????
(1)設備初始化??
????? PCI設備驅動程序要實現識別PCI器件、尋址PCI器件的資源和對PCI器件中斷的服務。PCI系統BIOS功能提供了BIOS的訪問與控制的具體特征,所有軟件(設備驅動程序、擴展ROM碼)將通過標準中斷號1AH調用BIOS功能訪問特殊部件。PCI BIOS規范有完整的有關PCI BIOS功能的描述[3]。????
在PCI設備驅動程序的初始化過程中,利用指定器件識別號(device_id)商識別號(vendor_id)、檢索號(index)搜索PCI器件,通過調用PC IBIOS確認其存在,并確定其物理位置:總線號、器件號和功能號,這是該器件/功能在系統中的唯一尋址標志。利用總線號、器件號和功能號可以尋址該器件/功能的PCI配置空間(configuration space)。???? 接下來,設備驅動就需要從配置空間獲得硬件的參數。PCI設備的許多參數,包括所用的中斷號,端口地址的范圍I/O方式、存儲器的地址?存儲器映射方式?等,都可以從PCI配置空間的各基址所對應的尋址空間中得到。讀寫配置空間可以調用BIOS中斷1AH,也可以先向配置空間地址寄存器?(0CF8H)寫入總線和設備號?在前面搜索PCI器件時得到的和寄存器號,再對配置空間數據寄存器?(0CFCH)進行讀寫。對設備驅動來說,最重要的是獲得基址寄存器(BADR),不能認為PCI器件資源總是設計設備時設置的初值,系統可能會根據硬件情況為PCI設備分配新的資源。我們所設計的PCI設備使用的基址1-3都是按I/O空間映射的,而基址4是按內存方式映射的。確定一個端口是按什么方式映射的,可以讀對應端口的配置寄存器(Configuration Register)。讀出后,判斷其0位,如果0位為數值0,表示其是按內存方式設置的,否則為I/O方式設置的。內存方式和I/O方式的配置寄存器的含義參見文獻[3]。如果要獲得基址的大小,可以向基址寄存器寫入FFFFH,然后讀基址寄存器,如果是內存方式,從第4位開始的0的數目表示基址的大小,如果是I/O方式,則從第2位開始的0的數目表示基址的大小。?
??? (2)?端口操作???? 在PC機上,I/O端口尋址空間和內存尋址空間是不同的,所以處理方法也不同。I/O空間是一個64K字節的尋址空間,它不象內存有實模式和保護模式之分,在各種模式下尋址方式相同。在Windows 9X下,用戶程序可以直接使用I/O指令,而不一定非通過專門的驅動程序來完成,所以如果軟件對硬件的操作完全是通過I/O端口操作來完成的,甚至可以不用專門設計驅動程序,直接由應用程序來完成對硬件的控制。由于PCI總線是32位的總線標準,在進行I/O操作時通常要進行雙字(DWORD)操作,而且以前大多數C/C++編譯軟件都沒有提供雙字的函數,所以需要構造雙字操作讀寫函數inpd/outpd。?
?? 在Windows NT下,系統不允許處于優先級3級的用戶程序和用戶模式驅動程序直接使用I/O指令,如果使用了I/O指令將會導致特權指令意外(privileged instruction exception)。所以任何對I/O的操作都需要借助內核模式驅動來完成。具體的做法有兩種:一是在驅動程序中使用IoReportResourceUsage報告資源占用,然后使用READ_PORT_XXX、WRITE_PORT_XXX函數讀寫,最后使用IoReportResourceUsage取消資源占用;另一種是驅動程序修改NT的I/OPermissions Map(IOPM),以使系統允許用戶程序對指定的I/O端口進行操作,這時用戶程序采用通常的I/O指令進行操作。后者的優點是速度快、用戶程序設計簡單,但犧牲了移植性,程序不能移植到非Intel的系統中,而且如果多個程序同時讀寫同一端口容易導致沖突。 ?
? 3?內存的讀寫???? Winsows工作在32位保護模式下,保護模式與實模式的根本區別在于CPU尋址方式上的不同,這也是Windows驅動程序設計中需要著重解決的問題。Windows采用了分段、分頁機制如圖1?,這樣使應用程序產生一種錯覺,好象程序中可以使用非常大的物理存儲空間。這樣做最大的好處就是一個程序可以很容易地在物理內存容量不一樣的、配置范圍差別很大的計算機上運行,編程人員使用虛擬存儲器可以寫出比任何實際配置的物理存儲器都大得多的程序。每個虛擬地址由16位的段選擇子和32位段偏移量組成。通過分段機制,系統由虛擬地址產生線性地址。再通過分頁機制,由線性地址產生物理地址。線性地址被分割成頁目錄(Page Directory)、頁表(Page Table)和頁偏移(Offset)三個部分。當建立一個新的Win32進程時,操作系統會為它分配一塊內存,并建立它自己的頁目錄、頁表,頁目錄的地址也同時放入進程的現場信息中。當計算一個地址時,系統首先從CPU控制器CR3中讀出頁目錄所在的地址,然后根據頁目錄得到頁表所在的地址,再根據頁表得到實際代碼/數據頁的頁幀,最后再根據頁偏移訪問特定的單元。硬件設備讀寫的是物理內存,但應用程序讀寫的是虛擬地址,所以存在著將物理內存地址映射到用戶程序線性地址的問題。??
? 從物理地址到線性地址的轉換工作也是由驅動程序來完成的。在Windows 95下,使用DDK的VMMCall_MapPhysToLinear進行地址映射。驅動程序的內存映射部分主要是調用VxD的系統服務MapPhysToLinear。在VtoolsD中這個函數的定義如下:
???? PVOID MapPhysToLineag(CONST VOID * PhysAddr,DWORD nBytes,DWORD Flags);???? 其中第一個參數PhysAddr就是要映射的內存的物理地址的起始位置,而nBytes是內存區域的長度,Flags必須設置為0。這個函數返回的就是這段物理地址映射的線性內存地址。如果指定的內存不能存取,函數將返回FFFFFFFFH。???? 比如要映射物理內存ED000000H開始的4096個字節,可以這樣做:????? PCHAR *PointerToPage=(PCHAR)MapPhysToLinear((PVOID)OxED000000,4096,0);???? 而將PointerToPage傳遞給調用驅動的用戶程序,在用戶程序中使用???? DWORD *pFIFOBodyBase=(DWORD*)PointerToPage;????? 而這個PFIFOBodyBase指針就可以象普通的指針一樣進行讀寫操作,而通過對這個指針的操作就可以實現對物理內存ED000000H進行讀寫。???
????? 在Windows NT下,首先調用IoReportResourceUsage請求使用設備的內存。然后調用HalTranslateBusAddress轉換與總線相關的內存為系統的物理內存地址。再使用MmMapIoSpace把設備的內存映射到虛擬空間。在設備驅動卸出時,調用MmUnmapIoSpace斷開設備的內存和虛擬空間的連接。 ?
? (4)?中斷的設置、響應與調用???? 對中斷的設置、響應與調用應該在驅動程序中完成。??
????? 對中斷的調用?象前面調用BIOS的1AH中斷讀取配置寄存空間?可以由DDK的Exec_Int完成。?
???? PCI設備驅動程序應當從PCI配置寄存器的中斷寄存器(INTLN)和中斷引腳寄存器(INTPIN)?中獲得有關中斷的信息。DDK還提供了響應中斷事件的服務。如在Windows 95中,VPICD服務用來管理所有硬件中斷事件。PC機的硬件中斷需要確定硬件中斷的IRQ,對一個特定的IRQ中斷源,VPICD或者提供缺省的中斷處理函數,或者允許其它VxD重載中斷處理函數。在VtoolsD中,要處理硬件中斷應該從VHardwareInt繼承一個類。在這個類中,VtoolsD提供了編寫中斷響應程序所需的功能。
???? 在Windows NT中,同VPICD對應的中斷服務為中斷請求層(IRQL)。設備驅動首先使用HalGetInterrupuVector將與總線有關的中斷向量轉換為系統的中斷向量,然后利用IoConnectInterrupu指定中斷服務。???
?3 設備驅動的調用????
??? 編寫設備驅動并不是最終的目的,總是需要由用戶程序來調用驅動并實現一定的功能。一般調用設備驅動是使用CreateFile函數打開設備文件,得到一個文件句柄。具體到我們的設備驅動程序中,使用如下的語句就可以打開文件。
?? hVxD=CreateFile("\\\\.\\PCIBIOS.VXD",0,0,0,???? CREATE-NEW,FILE-FLAG-DELETE-ON-CLOSE,0);???? 打開設備文件后,調用DeviceIoControl函數就可以同設備驅動程序交換數據了。???
?完成硬件操作之后,可以調用CloseHandle(hVxD);關閉設備驅動。???
? 這種調用方式也是Windows NT調用設備驅動的標準方法。對于VxD來說還有其它的調用方式,如DPMI方式,但采用DeviceIoControl的方法可以保證程序在Windows NT和Windows 9X下的兼容性,在兩個操作系統下,僅有CreateFile語句是不同的。???
?4 設備驅動的進一步封裝???? 至此,完成了對驅動程序的初步設計。但考慮到在上面調用設備驅動時使用的DeviceIoControl函數仍是比較復雜的,程序也不太容易具有通用性。而且,在有些開發工具中,如Visual Basic,不包括直接讀寫I/O端口的語句,所以可以考慮根據不同軟件的需要對驅動程序進行不同的封裝。目前,我們實現了以DLL、ActiveX、VCL和C++類庫進行封裝。DLL可以在大多數軟件環境中進行調用。ActiveX可以在Visual Basic等可視編程環境中使用。VCL可以在Delphi和C++ Builder中使用。考慮到許多用戶使用Visual C++,所以也提供了C++類庫方式。?
?? 參考文獻???
?1 馬衛國,何佩琨.通用高速PCI總線目標模塊的設計.電子技術應用,1999;25(1)?
?? 2 Art Baker.Windows NT設備驅動程序設計指南.北京:機械工業出版社,1997???
? 3 AMCC S5933 PCI Controller Data Book.Applied Micro Circuits Corporation,1996? ?? (收稿日期:1999-06-16)??
評論
查看更多