硬體與驅動程式的安裝過程
1. 假設新加入一塊PCI網路卡之後,重新開機。
2. Bus Driver(PCI.sys)最先偵測到硬體存在,由PCI.sys通知PnP Manager準備啟動這個裝置。
3. 如果kernel mode PnP Manager找不到適當的驅動程式載入,則進一步要求user mode PnP Manager準備安裝驅動程式。
4. 如果user mode PnP Manager無法在系統中找到適當的驅動程式,則啟動client installation,由使用者自行安裝。如果user mode PnP Manager能從系統中找到驅動程式,則啟動server installation安裝驅動程式。
5. 以上兩者安裝模式都需要Setup API和CfgMgr API的協助,透過INF檔案的安裝指示,以及Cat檔案的數位簽名,將合法的驅動程式安裝到OS中。
6. 在安裝過程,透過Co-Installer、Class Installer與Device Installation API將執行的設定與資訊寫入登錄資料庫(Registry)。
7. 最後,PnP Manager會載入驅動程式(Function Driver),對它送出IRP_MN_START_DEVICE。
驅動程式用到的登錄資料庫
1. HKEY_LOCAL_MACHINE\System\CurrentControlSet\路徑下有Control、Enum、Services目錄
2. HKEY_LOCAL_MACHINE\Hardware
在Control目錄下有Class子目錄,針對不同類型的硬體裝置分類,給予不同的CLSID值。硬體裝置的規格資料都被存放在每個CLSID目錄底下。
在Enum目錄下是根據硬體裝置的已知名稱來分類,如︰PCI、IDE、USB等,屬於USB型別的裝置就被歸類在USB目錄底下。另外,當OS開機後,底層的bus driver會進行硬體裝置掃描,硬體裝置也會回應特定裝置識別碼,然後bus driver再比對Enum目錄下的註冊碼,以判斷系統是否支援該裝置。
Services目錄下的資料有某些目的,例如希望在OS開機之後某些應用程式能在背景執行,類似常駐程式。目錄下也會提供驅動程式的路經,通常系統根目錄底下有個子目錄System\Drivers,存放驅動程式(*.sys),可以根據Start機碼值決定是否要Auto、Manual、Disable…執行驅動程式。
驅動程式的類型
1. Bus Driver︰最底層的驅動程式,包含各種匯流排(IDE、PCI、USB、IEEE1394…),是必備的驅動程式。Windows隨機就附贈相關的bus driver。
2. Function Driver︰通常由硬體廠商所開發,定義硬體能提供哪些功能。它能支援多個同類型的硬體裝置運作,假設有三張同樣功能的PCI卡,所有的功能運作都由這個function driver負責提供驅動。
3. Filter Driver︰屬於可有可無的驅動程式。分為兩種filter driver,一種介於bus driver與function driver之間,稱為下層過濾式驅動程式。另一種在function driver之上,稱為上層過濾式驅動程式。這類驅動程式也像其他驅動程式一樣,具備DeviceEntry、AddDevice、Dispatch…等函式。
上層過濾式驅動程式通常用於篩選IRP封包流向。假設原本的function driver僅提供基本功能,針對一些附加功能需要處理的話,在不修改原本驅動程式情況下,只要寫個上層過濾式驅動程式處理IRP封包。另外,還有個用途就是修正硬體或者function driver的臭蟲。
下層過濾式驅動程式用於協助我們寫個無關匯流排的function driver。假設有個設備可支援三種不同匯流排,但因為功能都相同,function driver應該一致才對。因此利用下層過濾式驅動程式將bus driver與function driver切開,我們僅需要寫三個filter driver對應到不同的bus,而function driver發送IRP封包給對應的filter driver即可。
Q︰應用程式如何呼叫硬體裝置呢?
一旦作業系統開機成功之後,硬體裝置建立好Device Object,我們可以用工具看到所有裝置的資訊。這類工具像Object Viewer(WinObj.exe)或DevView公用程式,可以從網路下載得到。用WinObj工具打開後,所看到的裝置如下
當應用程式呼叫CreateFile函式以便開啟某一裝置時,這個函式需要輸入裝置名稱,例如\\.\TritonGenericDevice。I/O Manager看到“\\.\”這個符號會改成“\??\”,對應到上圖所示,就是\GLOBAL??\目錄下找一個名稱為TritonGenericDevice的裝置,然後開啟它,以便user mode的應用程式可以操控。
Driver的中斷處理
中斷的等級分為軟體中斷與硬體中斷,而軟體中斷的優先權低於硬體中斷。軟體中斷又分三種︰依優先權高低為Dispatch level、APC(Async Procedure Call)level、Passive level。硬體中斷有很多種類,不過優先權都高於軟體。
當偵測到硬體發出中斷之後,I/O Manager將中斷封裝成特定的資料結構,傳給該硬體裝置的驅動程式,由這個驅動程式的ISR服務常式決定是否要處理這個中斷。如果需要處理,I/O Manager會再呼叫驅動程式的DPC(Deferred Procedure Call)函式,這個函式才真正處理中斷。
Passive level︰thread執行的等級,執行於這個等級的函式有DriverEntry、AddDevice、ReInitialize、Unload、Dispatch、user mode的程式也在這個等級執行。
APC level︰是作業系統核心所使用的,用於喚醒thread或者執行某些特定函式。
Dispatch level︰在作業系統核心有一個主要thread負責分配其他threads執行的順序。取得這個等級的thread將有優先權執行本身的工作,其他的thread則處於passive等級等待執行。常見的函式有StartIo、Cancel、IoTimer、DPC…,因為這些函式必須存取DeviceObject或其他欄位,在這個等級執行確保不會被其他驅動程式所干擾。
Q︰如何建立ISR服務常式與DPC函式呢?
驅動程式的DriverEntry之後,必須先註冊一個ISR,以便支援硬體發出的中斷處理。所以要呼叫IoConnectInterrupt函式註冊驅動程式的ISR,這是I/O Manager內建用來支援中斷處理的函式之一。因為硬體中斷的等級高於軟體執行的等級,所以ISR服務常式執行的工作要越少越好,以免佔用CPU太久的資源。ISR的工作有幾個要點,如下︰
1. 檢查收到的中斷是不是我們的硬體所發出的,如果不是則返回FALSE,如果是則繼續下列步驟。
2. 設定硬體停止發出中斷。
3. 如果需要與驅動程式的其他成員存取shared data,則需要執行Critical Section。
4. 呼叫IoRequestDpc,等到ISR完成工作後,I/O Manager將會呼叫DPC函式。
前面提到DriverEntry要註冊ISR,還要在AddDevice建立一個DPC函式以便ISR之後呼叫。在AddDevice函式中,呼叫IoInitializeDpcRequest函式來建立DPC函式。底下的範例是以DriverWorks開發的驅動程式,