其它对象
设备可能还有其它一些需要在AddDevice中初始化的对象。这些对象可能包括各种同步对象,各种队列头(queue anchors),聚集/分散列表缓冲区,等等。事实上,在本书的其它地方讨论这些对象的初始化更合适。
初始化设备标志
设备对象中有两个标志位需要在AddDevice中初始化,并且它们在以后也不会改变,它们是DO_BUFFERED_IO和DO_DIRECT_IO标志。你只能设置并使用其中一个标志,它将决定你以何种方式处理来自用户模式的内存缓冲区。(我将在第七章中讨论这两种缓冲模式的不同,以及你如何选择) 由于任何在后面装入的上层过滤器驱动程序将复制你的标志设置,所以在AddDevice中做这个选择十分重要。如果你在过滤器驱动程序装入后改变了设置,它们可能会不知道。
设备对象中有两个标志位属于电源管理范畴。与前两个缓冲区标志不同,这两个标志在任何时间都可以被改变。我将在第八章中详细讨论它们,但这里我先介绍一下。DO_POWER_PAGABLE意味着电源管理器将在PASSIVE_LEVEL级上向你发送IRP_MJ_POWER请求。DO_POWER_INRUSH意味着你的设备在上电时将汲取大量电流,因此,电源管理器将确保没有其它INRUSH设备同时上电。
设置初始电源状态
大部分设备一开始就进入全供电状态。如果你知道你的设备的初始电源状态,应该告诉电源管理器:
POWER_STATE state; state.DeviceState = PowerDeviceD0; PoSetPowerState(fdo, DevicePowerState, state); |
---|
电源管理的细节请见第八章。
建立设备堆
每个过滤器驱动程序和功能驱动程序都有责任把设备对象放到设备堆栈上,从PDO开始一直向上。你可以调用IoAttachDeviceToDeviceStack完成你那部分工作:
NTSTATUS AddDevice(..., PDEVICE_OBJECT pdo) { PDEVICE_OBJECT fdo; IoCreateDevice(..., &fdo); pdx->LowerDeviceObject = IoAttachDeviceToDeviceStack(fdo, pdo); } |
---|
IoAttachDeviceToDeviceStack的第一个参数是新创建的设备对象的地址。第二个参数是PDO地址。AddDevice的第二个参数也是这个地址。返回值是紧接着你下面的任何设备对象的地址,它可以是PDO,也可以是其它低级过滤器设备对象。如果该函数失败则返回一个NULL指针,因此你的AddDevice函数也是失败的,应返回STATUS_DEVICE_REMOVED。
清除DO_DEVICE_INITIALIZING标志
在AddDevice中最后一件需要做的事是清除设备对象中的DO_DEVICE_INITIALIZING标志:
fdo->Flags &= ~DO_DEVICE_INITIALIZING; |
---|
当这个标志设置时,I/O管理器将拒绝任何打开该设备句柄的请求或向该设备对象上附着其它设备对象的请求。在驱动程序完成初始化后,必须清除这个标志。在以前版本的Windows NT中,大部分驱动程序在DriverEntry中创建所有需要的设备对象。当DriverEntry返回时,I/O管理器自动遍历设备对象列表并清除该标志。但在WDM驱动程序中,设备对象在DriverEntry返回后才创建,所以I/O管理器不会自动清除这个标志,驱动程序必须自己清除它。