Kernel Mode Driver Framework >> C/C++ (PNP)
AddDevice()
當系統找到對應的裝置(透過INF檔案安裝)且驅動程式被系統載入後,AddDevice()就會被系統呼叫,而AddDevice()是在DriverEntry()裡面註冊的,所以系統才會知道AddDevice()位於何處,名稱不一定要用AddDevice,但是參數跟回傳值必須遵照Microsoft的定義,否則會有問題。
AddDevice()副程式定義如下:
NTSTATUS AddDevice(WDFDRIVER, PWDFDEVICE_INIT);
相較於WDM驅動程式,KMDF驅動程式傳入的參數已經改成WDF專用的Object,WDFDRIVER是在DriverEntry()時產生的Object,而PWDFDEVICE_INIT則是一個由WDF配置以及初始化的Object,相關初始化的設定都依賴這個Object做設定,雖然Microsoft官方並沒有對此Object內部做說明,不過司徒翻了一下定義,發現只有一個PVOID定義。
那在AddDevice()需要做什麼事情呢?
1. 初始化File相關旗標以及Callback,如:FileOpen、FileClose
2. 產生新的Device Object
3. 建造一條Symbolic Link
4. 初始化其餘Callback,如:FileRead、FileWrite、IOCTL
該Symbolic Link就是提供給User Application開啟(僅能使用CreateFile() API開啟),還記得呼叫CreateFile()時會提供一個名字嗎?若記得的話,此名字就是驅動程式的Symbolic Link名稱,那問題又來了,有沒有可能其它驅動程式也使用同一個Symbolic Link名字呢?答案是,肯定會發生的,所以Microsoft建議大家使用GUID的方式註冊,使用者可以使用工具產生新的GUID名稱,並使用該GUID註冊裝置,避免名稱衝突,那User Application又該如何開啟該驅動程式呢?這時候就必須使用Setup API做GUID列舉並取得Symbolic Link名稱,哪一種方式比較好呢?如果是使用Symbolic Link註冊名稱,User Application比較好寫,因為名稱已經知道了,反之,使用GUID註冊的話,User Application需要列舉判斷後才能開啟,所以會比較不好寫,但是優點則是名稱不會衝突。
範例:
#define DEV_NAME L"\\Device\\MyDriver" #define SYM_NAME L"\\DosDevices\\MyDriver" NTSTATUS AddDevice(WDFDRIVER Driver, PWDFDEVICE_INIT pDeviceInit) { WDFDEVICE device; UNICODE_STRING suDevName; UNICODE_STRING szSymName; WDF_FILEOBJECT_CONFIG file_cfg; WDF_IO_QUEUE_CONFIG ioqueue_cfg; // Step 1 WdfDeviceInitSetIoType(pDeviceInit, WdfDeviceIoBuffered); WDF_FILEOBJECT_CONFIG_INIT(&file_cfg, IrpFileCreate, IrpFileClose, NULL); WdfDeviceInitSetFileObjectConfig(pDeviceInit, &file_cfg, WDF_NO_OBJECT_ATTRIBUTES); // Step 2 RtlInitUnicodeString(&suDevName, DEV_NAME); RtlInitUnicodeString(&szSymName, SYM_NAME); WdfDeviceInitAssignName(pDeviceInit, &suDevName); WdfDeviceCreate(&pDeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &device); WdfDeviceCreateSymbolicLink(device, &szSymName); // Step 3 WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(&ioqueue_cfg, WdfIoQueueDispatchSequential); ioqueue_cfg.EvtIoRead = IrpRead; ioqueue_cfg.EvtIoWrite = IrpWrite; return WdfIoQueueCreate(device, &ioqueue_cfg, WDF_NO_OBJECT_ATTRIBUTES, WDF_NO_HANDLE); }
Step 1:初始化File Callback以及相關旗標,比較需要注意的是WdfDeviceIoBuffered旗標,因為在做裝置讀寫時,User Application跟驅動程式是否共用同一塊Buffer是取決於該旗標,如果使用者設定成WdfDeviceIoBuffered,則代表驅動程式有自己獨立一塊Buffer,驅動程式讀取完硬體資料後,會複製到它自己的Buffer,然後再複製到User Application的Buffer,所以速度會比較慢一些,如果要共用同一塊Buffer的話,則把旗標設定成WdfDeviceIoDirect即可。
Step 2:產生一個Device Object(可自己決定名稱),然後建立一條Symbolic Link(可自己決定名稱),Device Object名稱一般是放在Windows特殊資料夾中的Device資料夾,使用者可以使用WinObj程式去查看有哪些Device Object,而Symbolic Link的名稱則是放在DosDevices資料夾(GLOBAL??),那應用程式該如何把完整路徑名稱給CreateFile()呢?答案是加上\.\\關鍵字,有印象開啟COM Port時,需要使用這樣CreateFile("\\.\\\\COM1", ...);的方式嗎?這就是代表完整路徑的意思,在寫COM Port程式時,不一定說是要大於COM9才能加\.\\路徑,其實從COM1就可以開始使用,因為那是Global的名稱表示方式。
Step 3:初始化其餘Callback。