如果阅读了《WDF队列分析(1)--序幕》,可能你还记得在那篇文章的结尾部分提到过进入DispatchWorker函数后,框架根据IRP类型选择不同的Package类型,进而有了不同的Dispatch函数:
FxPackage*
GetDispatchPackage(
__in UCHAR MajorFunction
)
{
switch (MajorFunction) {
case IRP_MJ_CREATE:
return (FxPackage*) m_PkgGeneral;
case IRP_MJ_READ:
return (FxPackage*) m_PkgIo;
case IRP_MJ_SYSTEM_CONTROL:
return (FxPackage*) m_PkgWmi;
case IRP_MJ_PNP:
case IRP_MJ_POWER:
if (m_PkgPnp != NULL) {
return (FxPackage*) m_PkgPnp;
}
else {
return (FxPackage*) m_PkgDefault;
}
break;
default:
return (FxPackage*) m_PkgDefault;
}
}
当时我们的关注点其实集中在DriverObject上(派遣函数属于驱动对象的一部分),在本篇中,我们会把关注点转移到DeviceObject。
开始之前先回顾一下函数DispatchWorker的结尾部分,MS用到了链式表达式:
return Device->GetDispatchPackage(
irp.GetMajorFunction()
)->Dispatch(Irp);
GetDispatchPackage返回4种FxPackage的派生类对象,因此上面的语句可以看作是下列语句的变种:
return Device->m_PkgPnp->Dispath(Irp);
链式语句的前半部分:Device->m_PkgPnp暗示我们m_PkgPnp是FxDevice的类成员,同理像m_PkgGeneral/m_PkgIo等变量都是FxDevice的类成员。
class FxDevice : public FxDeviceBase {
...
public:
FxPkgIo* m_PkgIo;
FxPkgPnp* m_PkgPnp;
FxPkgGeneral* m_PkgGeneral;
FxWmiIrpHandler* m_PkgWmi;
FxDefaultIrpHandler* m_PkgDefault;
...
}; //framework\shared\inc\private\common\FxDevice.cpp
既然这几个成员变量是指针类型的,那么,在设备对象创建时它们应该执行了初始化操作(否则早蓝屏了),以m_PkgIo/m_PkgPnp为关键字在SourceInsight中查找引用,将在FxDevice::Initialize/FxDevice::FdoInitialize等函数中找到对这些变量的初始化赋值。
_Must_inspect_result_
NTSTATUS
FxDevice::_Create(
__in PFX_DRIVER_GLOBALS FxDriverGlobals,
__in PWDFDEVICE_INIT* DeviceInit,
__in_opt PWDF_OBJECT_ATTRIBUTES DeviceAttributes,
__out FxDevice** Device
)
{
...
status = pDevice->Initialize(pInit, DeviceAttributes);
switch (pInit->InitType) {
case FxDeviceInitTypeFdo:
status = pDevice->FdoInitialize(pInit);
break;
case FxDeviceInitTypePdo:
status = pDevice->PdoInitialize(pInit);
break;
case FxDeviceInitTypeControlDevice:
status = pDevice->ControlDeviceInitialize(pInit);
break;
default:
break;
}
...
if (pDevice->IsPnp()) {
...
pDevice->m_PkgPnp->FinishInitialize(pInit);
}
pInit->CreatedDevice = pDevice;
Done:
*Device = pDevice;
return status;
}
在函数FxDevice::_Create中,框架将新创建的FxDevice对象通过FxDevice::Initialize函数做了简单的初始化工作,然后根据调用WdfDeviceCreate时传入的WDFDEVICE_INIT结构,按PDO/FDO/CDO/FiDO的分类对FxDevice再次设置。
_Must_inspect_result_
NTSTATUS
FxDevice::Initialize(
__in PWDFDEVICE_INIT DeviceInit,
__in_opt PWDF_OBJECT_ATTRIBUTES DeviceAttributes
)
{
...
m_PkgDefault = new (pGlobals) FxDefaultIrpHandler(pGlobals, (CfxDevice*)this);
if (m_PkgDefault == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
InstallPackage(m_PkgDefault);
if (DeviceInit->InitType == FxDeviceInitTypeControlDevice) {
m_Legacy = TRUE;
}
...
Mx::MxInitializeNPagedLookasideList(&m_RequestLookasideList,
NULL,
NULL,
0,
m_RequestLookasideListElementSize,
pGlobals->Tag,
0);
// Init device's auto_forward_cleanup_close.
ConfigureAutoForwardCleanupClose(DeviceInit);
//
// Create, close, cleanup, shutdown
//
m_PkgGeneral = new(pGlobals) FxPkgGeneral(pGlobals, this);
if (m_PkgGeneral == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
InstallPackage(m_PkgGeneral);
...
// IO package handles reads, writes, internal and external IOCTLs
m_PkgIo = new(pGlobals) FxPkgIo(pGlobals, (CfxDevice*) this);
if (m_PkgIo == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
InstallPackage(m_PkgIo);
...
return STATUS_SUCCESS;
}
FxDevice::Initialize初始化了m_PkgDefault/m_PkgIo/m_PkgGeneral 3个成员变量,换言之,目前驱动程序已经具备除了IRP_MJ_Pnp/Power之外的所有IRP处理能力。但是,这只是通用的配置,就同一个IRP,位于同一设备栈上的不同类型的设备对象有不同的处理方式:如对于IRP_MJ_PNP&IRP_MN_QUERY事件,FiDo和FDO可能选择向下传递该IRP,而PDO会枚举并返回总线上的设备信息。所以,WDF这种一揽子的做法肯定不合适,所以需要根据设备类型进一步细分。
划分的依据用到了WDFDEVICE_INIT结构,结构中的DeviceType域用于上述目的。
typedef struct WDFDEVICE_INIT *PWDFDEVICE_INIT;
在创建设备对象的时候,会设置WDFDEVICE_INIT结构。默认情况下WDFDEVICE_INIT!DeviceType是Fdo(附注:这句话我没有在源码中核对过,但是toast使用的是默认设置,所以这么猜测)。默认值往往不够应付多变的情况,这时候就可以通过其他函数为WDFDEVICE_INIT!DeviceType指定其他类型,如toast的过滤驱动在调用WdfDeviceCreate前如此操作:
NTSTATUS
FilterEvtDeviceAdd(
IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit
)
{
PAGED_CODE ();
//查看WdfFdoInitSetFilter定义,它仅仅将DeviceInit->Fdo.Filter置为TRUE;
WdfFdoInitSetFilter(DeviceInit);
WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, FILTER_EXTENSION);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
}
至于Pdo和Cdo(cdo是Wdf中提出的概念,其实对应于内核服务"legacy device"),又可分别参考toast\kmdf\static\buspdo.c的实现:
NTSTATUS
Bus_CreatePdo(
__in WDFDEVICE Device,
__in PWCHAR HardwareIds,
__in ULONG SerialNo
)
{
pDeviceInit = WdfPdoInitAllocate(Device);
WdfDeviceInitSetDeviceType(pDeviceInit, FILE_DEVICE_BUS_EXTENDER);
}
以及ioctl\kmdf\nonpnp.c
NTSTATUS
DriverEntry(
IN OUT PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
)
{
status = WdfDriverCreate(DriverObject,
RegistryPath,
&attributes,
&config,
&hDriver);
pInit = WdfControlDeviceInitAllocate(
hDriver,
&SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R
);
status = NonPnpDeviceAdd(hDriver, pInit);
}
现在WDF可根据WDFDEVICE_INIT!DeviceType的值,进行二度加工了。由于IRP_MJ_PNP消息过于复杂,所以我将在下一篇以IRP_MJ_READ为列,尝试分析m_PkgIo变量及相应的FdoInitialize函数。