既然我永遠都是被動者,那我怎麼知道你呼叫我的時候,我的狀態是什麼?舉個例子來說,當 OS 跟我說, 要我 clean up 的時候,我怎麼知道我要 clean up 什麼東西,有哪些東西是需要等待?Global 變數嗎?這是個好主意。 不過這種方式實在太下流了,連 MS 都不屑使用。這時,當然是 Driver 兩大主角要出場啦:Drvier Object 以及 Device Object。
typedef struct _DEVICE_OBJECT { CSHORT Type; USHORT Size; LONG ReferenceCount; struct _DRIVER_OBJECT *DriverObject; struct _DEVICE_OBJECT *NextDevice; struct _DEVICE_OBJECT *AttachedDevice; struct _IRP *CurrentIrp; PIO_TIMER Timer; ULONG Flags; ULONG Characteristics; __volatile PVPB Vpb; PVOID DeviceExtension; DEVICE_TYPE DeviceType; CCHAR StackSize; union { LIST_ENTRY ListEntry; WAIT_CONTEXT_BLOCK Wcb; } Queue; ULONG AlignmentRequirement; KDEVICE_QUEUE DeviceQueue; KDPC Dpc; ULONG ActiveThreadCount; PSECURITY_DESCRIPTOR SecurityDescriptor; KEVENT DeviceLock; USHORT SectorSize; USHORT Spare1; struct _DEVOBJ_EXTENSION *DeviceObjectExtension; PVOID Reserved; } DEVICE_OBJECT;
typedef struct _DRIVER_OBJECT { CSHORT Type; CSHORT Size; // The following links all of the devices created by a single driver together on a list, and the Flags word provides an extensible flag // location for driver objects. PDEVICE_OBJECT DeviceObject; ULONG Flags; // The following section describes where the driver is loaded. The count // field is used to count the number of times the driver has had its // registered reinitialization routine invoked. PVOID DriverStart; ULONG DriverSize; PVOID DriverSection; PDRIVER_EXTENSION DriverExtension; // The driver name field is used by the error log thread // determine the name of the driver that an I/O request is/was bound. UNICODE_STRING DriverName; // The following section is for registry support. Thise is a pointer // to the path to the hardware information in the registry PUNICODE_STRING HardwareDatabase; // The following section contains the optional pointer to an array of // alternate entry points to a driver for "fast I/O" support. Fast I/O // is performed by invokzing the driver routine directly with separate // parameters, rather than using the standard IRP call mechanism. Note // that these functions may only be used for synchronous I/O, and when // the file is cached. PFAST_IO_DISPATCH FastIoDispatch; // The following section describes the entry points to this particular // driver. Note that the major function dispatch table must be the last // field in the object so that it remains extensible. PDRIVER_INITIALIZE DriverInit; PDRIVER_STARTIO DriverStartIo; PDRIVER_UNLOAD DriverUnload; PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1]; } DRIVER_OBJECT;
typedef struct _DRIVER_EXTENSION { // Back pointer to Driver Object struct _DRIVER_OBJECT *DriverObject; // The AddDevice entry point is called by the Plug & Play manager // to inform the driver when a new device instance arrives that this // driver must control. PDRIVER_ADD_DEVICE AddDevice; // The count field is used to count the number of times the driver has // had its registered reinitialization routine invoked. ULONG Count; // The service name field is used by the pnp manager to determine // where the driver related info is stored in the registry. UNICODE_STRING ServiceKeyName; // Note: any new shared fields get added here. } DRIVER_EXTENSION, *PDRIVER_EXTENSION;
很複雜對不對?不用擔心啦,先看 DEVICE_OBJECT,有沒有看到 DeviceExtension 啊?這個就是 Windows Driver 的奧妙啦(搞不好大家都是這樣玩)。 它的型別是 PVOID,也就是 void *。為甚麼要宣告成 PVOID,那是因為你的東西都可以放在裡面,而且不管是那一種 Driver,都可以把它自己需要紀錄的東西放到 DeviceExtension 內。 而且 Windows 保證,每當呼叫到你的 Driver 所提供的 function 時,一定會把 DEVICE_OBJECT 放到參數內,所以你可以不必擔心怎樣知道你的 Driver 的狀態啦。
接下來得進入正題,Driver 的 entry point:
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
DriverEntry 就跟 C 的 main 一樣,只是 C 的 runtime 認 main,而 Windows 認 DriverEntry 罷了。 在 DriverEntry 內要做什麼事呢?記住,IN 表示這個參數是 OS 給的,表示我們要利用這個參數作某些事情,或是把這個參數給設定好。
回頭看 DRIVER_OBJECT,我們要填的東西可多了:有 DriverUnload、AddDevice function、以及 MajorFunction function pointer array。
DriverUnload 很簡單,就是 undo DriverEntry()。而 AddDevice 會等到 OS 真的要把你的 Driver instantiate 的時候,才會呼叫。MajorFunction array 可多了,基本上,OS 會透過這個 function pointer array 來呼叫你填入的 function。
DriverEntry() 呼叫完,OS 會接著呼叫你剛剛在 DRIVER_OBJECT 內填入的 AddDevice():
NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo)
DriverObject 就是 DriverEntry() 傳入的 DriverObject。pdo 呢,就是你的下家的 DeviceObject。 要知道,WDM driver stack 是由下往上長的,所以你會有下家幫你處理 IRP,而你的上家也會把 IRP 丟給你處理。 而要把 IRP 丟給下家,得先把這個 pdo 給記起來才行。記到哪裡?當然是萬惡的 DeviceExtension 啦。
問題來啦,DRIVER_OBJECT 是 OS 給的,那 DEVICE_OBJECT 呢?當然是呼叫 IoCreateDevice() 啦:
NTSTATUS IoCreateDevice( IN PDRIVER_OBJECT DriverObject, IN ULONG DeviceExtensionSize, IN PUNICODE_STRING DeviceName OPTIONAL, IN DEVICE_TYPE DeviceType, IN ULONG DeviceCharacteristics, IN BOOLEAN Exclusive, OUT PDEVICE_OBJECT *DeviceObject );
注意一下,DeviceExtensionSize 是指 OS allocate 出來的 DEVICE_OBJECT 所含的 DeviceExtension 大小。 換句話說,DeviceExtension 是 OS 幫你配置出來的。取得 DEVICE_OBJECT 之後,呼叫 IoAttachDeviceToDeviceStack(),把你的 DeviceObject 放到你的下家之上。大功告成。
當然,在 AddDevice() 內還要初始化許多東西,包括 spinlock/event、Dpc for ISR、double-linked list、DeviceExtension 等等的 resource。
No comments:
Post a Comment