小華的部落格: 2007/8/26 - 2007/9/2

搜尋此網誌

星期四, 8月 30, 2007

WDM 筆記2

自己學習WDM果然還是有許多問題無法解決,也只能邊學邊做筆記,做的筆記也不知道是對是錯,只能說WDM真難搞,難怪外面的WDM課程都那麼貴。

//----------------------
// ACP CreateFile
//----------------------
CreateFile() 可以去取得某個Driver的Handle,而這個API使用的是WDM Driver中的外部名稱,但是一般Driver命名方式除了直接給名稱之外還可以使用GUID(一串數字),而這兩種差別在下面比較:
外部名稱:
(1) 直接命名 : \\DeviceDos\\MyDriver
(2) GUID : DEFINE_GUID GUID_DEVICE_MYDRIVER 1234-ABCD-7890123ABCD-....
所以如果Driver是使用GUID,則必須透過一些方式轉換成CreateFile()可以使用的格式。

//------------------------
// 外部名稱的取得
//------------------------
想要對某個Driver送IRP,首先就要利用CreateFile()取得Driver Handle,而這個函數又需要知道Driver的外部名稱,那如何知道Driver的外部名稱是什麼?
這邊又要把Driver分成兩種:
(1) Inbox Driver: 這種型態的驅動程式指的是OS本身提供的,那這些Driver幾乎都是GUID命名,而這個GUID可以在DDK中找到,找xxx.h內的宣告,或是直接搜尋關鍵字 "DEFINE_GUID" or "GUID_DEVICE_HDD"
其中"GUID_DEVICE_HDD"也可以是"GUID_DEVICE_BATTERY",看你是哪個設備的Driver你就打什麼名稱,如果找不到就要另外想辦法囉。

(2) Non-Inbox Driver : 這類的驅動程式就是指廠商自己寫的,例如ATI...etc. 而這類的驅動程式一般廠商會提供GUID,或是在廠商提供的xxx.inf 內搜尋。

//-------------------------------
// Release Driver
//-------------------------------
一般Release Driver會給3個檔案:
(1) xxx.inf : 這是類似Script的東西,用來把Driver的一些資訊告訴OS,也就是會安裝一些資訊到Registry之中(例如 : ...\Service\{ServiceName}),而INF檔案裡面有其格式,可參考MSDN說明,其中就包含Driver的GUID,另外在[DeviceList]下面宣告的就是所謂的Hardware ID。
(2) xxx.sys : Driver COnpiler好之後的檔案,也就是實際的程式碼檔案。
(3) xxx.cat : 當經過WHQL後會有這個檔案可以使用,OS就會認定這個Driver是屬於經過認證的驅動程式,當然也可以做一個假的來測試。

//--------------------------------
// Driver Stack
//--------------------------------
Filter-DO: 過濾Driver
Function-DO : 一般設備的驅動程式都稱為Function-DO
PDO: Bus Driver

一般PDO還可以看做下一層的Driver,因為OS會把Driver堆疊起來,所謂的堆疊就是利用DeviceObject內的某個LowerDeviceObject欄位指向另一個DeviceObject,而LowerDeviceObject 指過去的Driver就是PDO,也就是下一層Driver,如果剛好是Bus Driver,那麼PDO=Bus Driver...

NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
{
NTSTATUS ntStatus = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
NULL, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo);
....
pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo);
....
}

由範例程式可以看到當PnP Manager 通知Driver去執行AddDevice()函數時,會跟我們的Driver說下一層的PDO是誰(當成參數傳給AddDevice()),然後我們先去為我們的Driver建立一個DeviceObject,這個DO也就是FDO,然後把我們的FDO 連接(Attach)在某個PDO上面,這樣子是不是就長的很像Stack ?

星期三, 8月 29, 2007

WDM 筆記

最近為了撰寫ACPI Driver所以先把之前WDM Driver的筆記整理一下:
//--------------------------
// IRP 用途
//--------------------------
IRP, I/O Request Packet : 內含command, data的一組封包。系統和Driver溝通時使用。

例如AP端送出IRP,內涵IOCTL_XXXX指令,以及一些參數
對應的Driver會去分析指令然後做對應的動作:
Switch(IRP->Command)
{
case IOCTL_XXXX :
Do Somethings here
case: ...
}
//---------------------------
//WDM Driver的Entry Point
//---------------------------
NTSTATUS
DriverEntry( IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath)
{
...
//在這邊去註冊你的Routine到DriverObject資料結構內
DriverObject->DriverUnload = MyIo_Unload;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = MyIo_CreateClose;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = MyIo_Dispatcher;
...
}

//---------------------------
// WDM Driver 命名方式有兩種:
//---------------------------
1.內部名稱 (在你使用IoCreateDevice會用到) : Kernel 使用
\\Devices Kernel Mode定義
2.外部名稱(symbolic link name,給User應用程式的CreateFile使用) : User端使用
\\DosDevices

#define MYIO_NT_DEVICE_NAME L"\\Device\\MyIO"
#define MYIO_DOS_DEVICE_NAME L"\\DosDevices\\MyIO"

簡單的說就是OS 會把我們寫的Driver載入到記憶體中,然後會幫我們產生一個Driver Object資料結構在記憶體中,然後把這個資料結構位址當作參數傳給我們的DriverEntry(),接著我們就會設定資料結構中的一些函數指標指向我們Driver記憶體中的函數位址。EX: DriverObject->DriverUnload = MyIo_Unload;

當Driver Object結構設定好之後(或稱註冊)我們會去呼叫IoCreateDevice()函數,這個函數會在OS管理的Namespace中插入一個名稱,然後Kernel Driver層如果要找我們Driver時候就會去找這個名稱,而這個名稱就是內部名稱。

另外當應用程式需要對我們撰寫driver送出一些IRP的時候,其實也是透過"名稱"的方式來找到我們註冊進去的函數,然後OS才會把我們的IRP送給Driver Object內指標所指向的函數來處理,因此為了不要混亂掉,所以用另一種命名方式給應用程式使用,這就是外部名稱。

//-----------------------
// WDM Driver被載入的時機
//-----------------------
1. OpenSCM 方式
我們自己寫的Driver可以不用到到Windows目錄下,當我們需要這個Driver來使用的時候,可以透過SCM所提供的一些服務,先在登入檔SYSTEM\CurrentControlSet\Services 下面替我們的Driver建立一個鍵值,然後利用SCM 的服務去把我們的Driver載入到記憶體中,接著呼叫DriverEntry(),之後我們就可以透過AP對這個Driver 送出IRP

2. PnP Driver方式
對照 Registry 和 INF 檔,找到對應的driver,因為每個PnP Device都有自己的ID,類似PCI ID或是Vendor ID...等,當系統偵測到Device時,就會去登入檔內找到對應的Driver,所以自己撰寫的Driver也要撰寫一個xxx.inf 用來安裝這些資訊。
(1) 當系統偵測到device,就會將對應的driver載入到記憶體中,接著呼叫driver的DeviceEntry()
(2) 接著系統的PnP Manager 會去呼叫driver中的AddDevice() (註冊在Driver Object中)
(3) 處理AP的IRP或是PnP Manager 的IRP,或是Device 的INT 請求...等

3.非 PnP Driver
也是對照Registry 和INF 檔找到對應的Driver,但是與PnP Driver差別在於沒有PnP ID,因此OS不會自動找到Driver且載入Driver,而是User自己要利用新增設備(Add New Hardware Wizard)來增加Device,接著OS也是對照Registry 和INF 檔找到對應的Driver來載入到記憶體中。