如果阅读了《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(Wdf中提出的概念,其实对应于wdm之前的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函数。