PCI設備Windows通用驅動程序設計
下面從這幾方面討論解決這些問題的途徑:
(1)設備初始化
PCI設備驅動程序要實現識別PCI器件、尋址PCI器件的資源和對PCI器件中斷的服務。PCI系統BIOS功能提供了BIOS的訪問與控制的具體特征,所有軟件(設備驅動程序、擴展ROM碼)將通過標準中斷號1AH調用BIOS功能訪問特殊部件。PCI BIOS規范有完整的有關PCI BIOS功能的描述[3]。
在PCI設備驅動程序的初始化過程中,利用指定器件識別號(device_id)、廠商識別號(vendor_id)、檢索號(index)搜索PCI器件,通過調用PCI BIOS確認其存在,并確定其物理位置:總線號、器件號和功能號,這是該器件/功能在系統中的唯一尋址標志。利用總線號、器件號和功能號可以尋址該器件/功能的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的數目表示基址的大小。
在Windows NT下,查找PCI設備的工作是由HalGetBusData完成的,也可以使用前述的辦法讀取配置寄存器,但DDK推薦使用HaiGetBusDataOffset函數。
(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)內存的讀寫
Windows工作在32位保護模式下,保護模式與實模式的根本區別在于CPU尋址方式上的不同,這也是Windows驅動程序設計中需要著重解決的問題。Windows采用了分段、分頁機制(圖1),這樣使應用程序產生一種錯覺,好象程序中可以使用非常大的物理存儲空間。這樣做最大的好處就是一個程序可以很容易地在物理內存容量不一樣的、配置范圍差別很大的計算機上運行,編程人員使用虛擬存儲器可以寫出比任何實際配置的物理存儲器都大得多的程序。每個虛擬地址由16位的段選擇子和32位段偏移量組成。通過分段機制,系統由虛擬地址產生線性地址。再通過分頁機制,由線性地址產生物理地址。線性地址被分割成頁目錄(Page Directory)、頁表(Page Table)和頁偏移(Offset)三個部分。當建立一個新的Win32進程時,操作系統會為它分配一塊內存,并建立它自己的頁目錄、頁表,頁目錄的地址也同時放入進程的現場信息中。當計算一個地址時,系統首先從CPU控制器CR3中讀出頁目錄所在的地址,然后根據頁目錄得到頁表所在的地址,再根據頁表得到實際代碼/數據頁的頁幀,最后再根據頁偏移訪問特定的單元。硬件設備讀寫的是物理內存,但應用程序讀寫的是虛擬地址,所以存在著將物理內存地址映射到用戶程序線性地址的問題。
評論