在 Windows 2000 以後, 微軟公司加入了新的驅動程式模型, 這就是 WDM. WDM 模式是建立在 NT 式驅動程式模型基礎上的. 因此有了前面對 NT 式驅動的理解, 對於 WDM 驅動的基本架構應該可以很容易上手.
實體裝置物件與功能裝置物件 :
在 WDM 模型中, 完成一個裝置的操作, 至少有兩個裝置物件共同完成. 其中一個是實體裝置物件 (Physical Device Object, 簡稱 PDO). 另一個是功能裝置物件 (Function Device Object, 簡稱 FDO). 其關係是 "附加" 與 "被附加" 的關係.
當 PC 插入某個裝置的時候, PDO 會自動建立. 確切的說是由匯流排驅動新建的. PDO 不能單獨操作裝置, 需要配合 FDO 一起使用. 系統會提示檢測到新裝置. 要求安裝驅動程式. 需要安裝的驅動程式指的就是 WDM 程式, 此驅動程式負責新建 FDO, 並且附加到 PDO 之上.
當一個 FDO 附加到 PDO 上的時候, PDO 裝置物件的子欄位 AttachedDevice 會記錄 FDO 的位置. PDO 被稱作底層驅動或者下層驅動, 而 FDO 被稱作高層驅動或者上層驅動. 這裡上層是指接近發出 I/O 請求的地方, 而 "下層" 指的是靠近實體裝置的地方. PDO 和 FDO 的關係可以從下圖得到更好理解 :
這是一個簡單的一種情況, 事實上要比這個複雜一些. 在 FDO 和 PDO 之間還會存在篩選驅動, 如下圖所示. 在 FDO 上面的篩選驅動被稱為上層篩選驅動. 在 FDO 的下層驅動被稱為下層篩選驅動. 另外每個裝置中, 有個 StackSize 子欄位表明操作這個裝置需要幾層才能達到最下層的實體裝置. 最上層篩選裝置物件的 StackSize 為 4, 也就是需要 4 個裝置才能到達最底層的實體裝置 :
篩選驅動可以巢狀嵌套, 也就是可以有多個高層篩選驅動, 也可以有多個底層篩選驅動. 篩選驅動不是必須存在. 在 WDM 模型中 PDO 和 FDO 是必須的. 可以看出 NT 式驅動和 WDM 驅動在設計思維上有所不同. NT 裝置是被動被裝入的, 例如當有裝置插入 PC 後, 系統不會提示, 使用者必須自己指定載入何種驅動. 而 WDM 驅動則會在插入裝置後, 系統自動新建出 PDO, 並且提示用戶安裝 FDO.
WDM 提示使用者載入 FDO, 如果該裝置已經由微軟提供, 則會自動進行安裝. 例如當 USB 滑鼠插入 PC 後, 系統就會找到對應驅動並載入. 這種設計思維導致了 WDM 模型支援隨插即用功能. 當然為了支持隨插即用功能, 這些還遠遠不夠. 匯流排驅動新建的 PDO 為程式設計員提供了很多隨插即用的服務, 這在以後的章節會接著介紹.
WDM 驅動的入口程式 :
和 NT 驅動一樣, WDM 驅動的入口程式也是 DriverEntry, 但是初始化作用被分散到其它常式中. 例如新建裝置物件的責任就不在 DriverEntry 中, 而被放在了 AddDrvice 常式中. 同時在 DriverEntry 中, 需要設置對 IRP_MJ_PNP 處理的派遣常式. 下面是 HelloWDM 的 DriverEntry 函式 :
從上面程式碼可以看出 WDM 驅動的 DriverEntry 和 NT 式驅動的 DriverEntry 有以下幾點不同 :
WDM 驅動的 AddDevice 常式 :
AddDevice 常式是 WDM 驅動所獨有的, 在 NT 驅動中沒有該常式. 在 DriverEntry 中, 需要設置 AddDevice 常式的函式位址. 設置的方式是驅動物件中有個 DriverExtension 子欄位 DriverExtension 中有個 AddDevice 子欄位, 將該欄位指向 AddDevice 常式的函式位址.
pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;
和 DriverEntry 不同, AddDevice 常式的名字可以任意命名, 在 HelloWDM 例子中, 使用的名字就是 HelloWDMAddDevice :
從上述程式碼可以看出, AddDevice 常是類似於 NT 驅動中的 DriverEntry 新建裝置物件的相關操作, 但是略有不同. AddDevice 函式有兩個輸入參數, 一個是 DriverObject, 一個是裝置物件 PhysicalDeviceObject. 驅動物件是 I/O 管理器新建的驅動物件. 裝置物件 PhysicalDeviceObject 就是底層匯流排驅動新建的 PDO 裝置物件. 傳進該參數的目的就是將 FDO 附加在 PDO 之上. 在 AddDevice 可以分為下面幾個步驟 :
前面介紹過當 FDO 附加到 PDO 上面時, PDO 會透過 AttachedDevice 子欄位知道它上面的裝置是 FDO (或是篩選裝置) . 但是 FDO 卻不知道自己的下層是什麼裝置. 解決辦法就是透過裝置擴充記錄 FDO 下層的裝置. 下面是 HelloWDM 的裝置擴充定義 :
- typedef struct _DEVICE_EXTENSION
- {
- PDEVICE_OBJECT fdo; // FDO
- PDEVICE_OBJECT NextStackDevice; //下層驅動裝置
- UNICODE_STRING ustrDeviceName; // 裝置名
- UNICODE_STRING ustrSymLinkName; // 符號連結名
- } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
DriverUnload 常式 :
在 NT 式驅動中, DriverUnload 常式主要負責作刪除裝置和取消符號連結. 而在 WDM 驅動中, 這部分的操作被 IRP_MN_REMOVE_DEVICE 的 IRP 處理函式所負責, 而 DriverUnload 常式顯得相對簡單. 如果在 DriverEntry 中有申請記憶體的操作, 可以在 DriverUnload 常式中回收. 在 HelloWDK 程式中, DriverUnload 常式除了列印兩行 log 資訊, 什麼也沒做.
對 IRP_MN_REMOVE_DEVICE IRP 的處理 :
關於 IRP 的介紹, 後面會有詳細說明. 驅動程式內部是由 IRP 所驅動. 新建 IRP 的原因有很多, IRP_MN_REMOVE_DEVICE 這個 IRP 是當裝置需要被卸載時, 由隨插即用管理器新建, 並發送到驅動程式中. IRP 一般由兩個號碼指定該 IRP 的具體意義, 一個是 IRP 號 (Major IRP), 另一個是輔 IRP 號 (Minor IRP). 每個 IRP 都由對應的派遣函式所處理, 而派遣函式是在 DriverEntry 中指定的, 例如 :
pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp;
上面這行程式就是將 IRP_MJ_PNP 的派遣函式指定為 HelloWDMPnp 函式.
當裝置需要被卸載時, 會先後發出多個 IRP_MJ_PNP. 這些 IRP 的輔 IRP 號會有所不同. 其中之一是 IRP_MN_REMOVE_DEVICE. 在 WDM 驅動程式中, 對裝置的卸載一般是對 IRP_MN_REMOVE_DEVICE 處理函式中進行卸載. 在 HelloWDM 驅動程式中, 負責處理 IRP_MN_REMOVE_DEVICE 的函式是 HandleRemoveDevice , 其程式碼如 :
在處理 IRP_MN_REMOVE_DEVICE 函式中, 它功能類似於 NT 驅動中的 DriverUnload 函式. 除了需要刪除裝置, 取消符號連結外, 在此函式還需要將 FDO 從 PDO 上的堆疊中 "摘除" 下來. 使用的函式是 IoDetachDevice.
在 FDO 從裝置鏈中被刪除後, 但 PDO 還是存在. PDO 的刪除不是由程式設計師負責, 而是由作業系統負責.
嗨 您好
回覆刪除最近在練習windows driver常參考您的網誌
但相關幾篇裡的圖片幾乎都死光了 不知是否可以重新補齊
感謝~
補充:
刪除您的網誌寫得相當清楚 很少看到這麼詳細的
謝謝~
Image host 失聯了, 圖檔沒有備份...Orz 可以支持一下原著:
刪除http://www.books.com.tw/products/0010439379
OK 感謝~
刪除