--- /dev/null
+/*++
+
+Copyright (c) Microsoft Corporation
+
+Module Name:
+
+ FxDevice.cpp
+
+Abstract:
+
+ This is the class implementation for the base Device class.
+
+Author:
+
+
+
+Environment:
+
+ Both kernel and user mode
+
+Revision History:
+
+--*/
+
+#include "coreprivshared.hpp"
+
+extern "C" {
+#include "FxDevice.tmh"
+}
+
+//
+// This table contains the mapping between device type and the
+// default priority boost used by the the framework when
+// an I/O request is completed. The DeviceObject->DeviceType
+// is used as an index into this table.
+//
+const CHAR FxDevice::m_PriorityBoosts[] = {
+ IO_NO_INCREMENT, // FILE_DEVICE_UNDEFINED 0x00000000
+ IO_NO_INCREMENT, // FILE_DEVICE_BEEP 0x00000001
+ IO_CD_ROM_INCREMENT, // FILE_DEVICE_CD_ROM 0x00000002
+ IO_CD_ROM_INCREMENT, // FILE_DEVICE_CD_ROM_FILE_SYSTEM 0x00000003
+ IO_NO_INCREMENT, // FILE_DEVICE_CONTROLLER 0x00000004
+ IO_NO_INCREMENT, // FILE_DEVICE_DATALINK 0x00000005
+ IO_NO_INCREMENT, // FILE_DEVICE_DFS 0x00000006
+ IO_DISK_INCREMENT, // FILE_DEVICE_DISK 0x00000007
+ IO_DISK_INCREMENT, // FILE_DEVICE_DISK_FILE_SYSTEM 0x00000008
+ IO_NO_INCREMENT, // FILE_DEVICE_FILE_SYSTEM 0x00000009
+ IO_NO_INCREMENT, // FILE_DEVICE_INPORT_PORT 0x0000000a
+ IO_KEYBOARD_INCREMENT, // FILE_DEVICE_KEYBOARD 0x0000000b
+ IO_MAILSLOT_INCREMENT, // FILE_DEVICE_MAILSLOT 0x0000000c
+ IO_SOUND_INCREMENT, // FILE_DEVICE_MIDI_IN 0x0000000d
+ IO_SOUND_INCREMENT, // FILE_DEVICE_MIDI_OUT 0x0000000e
+ IO_MOUSE_INCREMENT, // FILE_DEVICE_MOUSE 0x0000000f
+ IO_NO_INCREMENT, // FILE_DEVICE_MULTI_UNC_PROVIDER 0x00000010
+ IO_NAMED_PIPE_INCREMENT,// FILE_DEVICE_NAMED_PIPE 0x00000011
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_NETWORK 0x00000012
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_NETWORK_BROWSER 0x00000013
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_NETWORK_FILE_SYSTEM 0x00000014
+ IO_NO_INCREMENT, // FILE_DEVICE_NULL 0x00000015
+ IO_PARALLEL_INCREMENT, // FILE_DEVICE_PARALLEL_PORT 0x00000016
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_PHYSICAL_NETCARD 0x00000017
+ IO_NO_INCREMENT, // FILE_DEVICE_PRINTER 0x00000018
+ IO_NO_INCREMENT, // FILE_DEVICE_SCANNER 0x00000019
+ IO_SERIAL_INCREMENT, // FILE_DEVICE_SERIAL_MOUSE_PORT 0x0000001a
+ IO_SERIAL_INCREMENT, // FILE_DEVICE_SERIAL_PORT 0x0000001b
+ IO_VIDEO_INCREMENT, // FILE_DEVICE_SCREEN 0x0000001c
+ IO_SOUND_INCREMENT, // FILE_DEVICE_SOUND 0x0000001d
+ IO_SOUND_INCREMENT, // FILE_DEVICE_STREAMS 0x0000001e
+ IO_NO_INCREMENT, // FILE_DEVICE_TAPE 0x0000001f
+ IO_NO_INCREMENT, // FILE_DEVICE_TAPE_FILE_SYSTEM 0x00000020
+ IO_NO_INCREMENT, // FILE_DEVICE_TRANSPORT 0x00000021
+ IO_NO_INCREMENT, // FILE_DEVICE_UNKNOWN 0x00000022
+ IO_VIDEO_INCREMENT, // FILE_DEVICE_VIDEO 0x00000023
+ IO_DISK_INCREMENT, // FILE_DEVICE_VIRTUAL_DISK 0x00000024
+ IO_SOUND_INCREMENT, // FILE_DEVICE_WAVE_IN 0x00000025
+ IO_SOUND_INCREMENT, // FILE_DEVICE_WAVE_OUT 0x00000026
+ IO_KEYBOARD_INCREMENT, // FILE_DEVICE_8042_PORT 0x00000027
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_NETWORK_REDIRECTOR 0x00000028
+ IO_NO_INCREMENT, // FILE_DEVICE_BATTERY 0x00000029
+ IO_NO_INCREMENT, // FILE_DEVICE_BUS_EXTENDER 0x0000002a
+ IO_SERIAL_INCREMENT, // FILE_DEVICE_MODEM 0x0000002b
+ IO_NO_INCREMENT, // FILE_DEVICE_VDM 0x0000002c
+ IO_DISK_INCREMENT, // FILE_DEVICE_MASS_STORAGE 0x0000002d
+ IO_NETWORK_INCREMENT, // FILE_DEVICE_SMB 0x0000002e
+ IO_SOUND_INCREMENT, // FILE_DEVICE_KS 0x0000002f
+ IO_NO_INCREMENT, // FILE_DEVICE_CHANGER 0x00000030
+ IO_NO_INCREMENT, // FILE_DEVICE_SMARTCARD 0x00000031
+ IO_NO_INCREMENT, // FILE_DEVICE_ACPI 0x00000032
+ IO_NO_INCREMENT, // FILE_DEVICE_DVD 0x00000033
+ IO_VIDEO_INCREMENT, // FILE_DEVICE_FULLSCREEN_VIDEO 0x00000034
+ IO_NO_INCREMENT, // FILE_DEVICE_DFS_FILE_SYSTEM 0x00000035
+ IO_NO_INCREMENT, // FILE_DEVICE_DFS_VOLUME 0x00000036
+ IO_SERIAL_INCREMENT, // FILE_DEVICE_SERENUM 0x00000037
+ IO_NO_INCREMENT, // FILE_DEVICE_TERMSRV 0x00000038
+ IO_NO_INCREMENT, // FILE_DEVICE_KSEC 0x00000039
+ IO_NO_INCREMENT, // FILE_DEVICE_FIPS 0x0000003A
+ IO_NO_INCREMENT, // FILE_DEVICE_INFINIBAND 0x0000003B
+};
+
+NTSTATUS
+FxDevice::_CompletionRoutineForRemlockMaintenance(
+ __in MdDeviceObject DeviceObject,
+ __in MdIrp Irp,
+ __in PVOID Context
+ )
+/*++
+
+Routine Description:
+
+ A completion routine for the IRPs for which we acquired opt-in remove lock.
+
+Arguments:
+ DeviceObject - Pointer to deviceobject
+ Irp - Pointer to the Irp for which we acquired opt-in remove lock.
+ Context - NULL
+Return Value:
+
+ NT Status is returned.
+
+--*/
+
+{
+ FxIrp irp(Irp);
+
+ UNREFERENCED_PARAMETER(Context);
+
+ //
+ // Let the irp continue on its way.
+ //
+ irp.PropagatePendingReturned();
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+ Mx::MxReleaseRemoveLock(&((FxDevice::_GetFxWdmExtension(
+ DeviceObject))->IoRemoveLock), Irp);
+#else
+ UNREFERENCED_PARAMETER(DeviceObject);
+#endif
+
+ return STATUS_CONTINUE_COMPLETION;
+}
+
+
+FxDevice::FxDevice(
+ __in FxDriver *ArgDriver
+ ) :
+ FxDeviceBase(ArgDriver->GetDriverGlobals(), ArgDriver, FX_TYPE_DEVICE, sizeof(FxDevice)),
+ m_ParentDevice(NULL)
+{
+ SetInitialState();
+}
+
+VOID
+FxDevice::SetInitialState(
+ VOID
+ )
+{
+ //
+ // Set the initial device state
+ //
+ m_CurrentPnpState = WdfDevStatePnpObjectCreated;
+ m_CurrentPowerState = WdfDevStatePowerObjectCreated;
+ m_CurrentPowerPolicyState = WdfDevStatePwrPolObjectCreated;
+
+ //
+ // Set the default IO type to "buffered"
+ //
+ m_ReadWriteIoType = WdfDeviceIoBuffered;
+
+ RtlZeroMemory(&m_DeviceName, sizeof(m_DeviceName));
+ RtlZeroMemory(&m_SymbolicLinkName, sizeof(m_SymbolicLinkName));
+ RtlZeroMemory(&m_MofResourceName, sizeof(m_MofResourceName));
+
+ m_Filter = FALSE;
+ m_Exclusive = FALSE;
+ m_PowerPageableCapable = FALSE;
+ m_ParentWaitingOnChild = FALSE;
+ m_Legacy = FALSE;
+ m_DeviceObjectDeleted = FALSE;
+ m_PdoKnown = FALSE;
+ m_Legacy = FALSE;
+ m_AutoForwardCleanupClose = FALSE;
+ m_SelfIoTargetNeeded = FALSE;
+ m_DeviceTelemetryInfoFlags = 0;
+
+ //
+ // Clear all packages by default
+ //
+
+ m_PkgIo = NULL;
+ m_PkgPnp = NULL;
+ m_PkgGeneral = NULL;
+ m_PkgWmi = NULL;
+ m_PkgDefault = NULL;
+
+ InitializeListHead(&m_PreprocessInfoListHead);
+ InitializeListHead(&m_CxDeviceInfoListHead);
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+ m_FileObjectClass = WdfFileObjectNotRequired;
+#else // UMDF
+ //
+ // In UMDF file object is always required. So indicate that now.
+ //
+ m_FileObjectClass = WdfFileObjectWdfCannotUseFsContexts;
+#endif
+
+ m_DefaultPriorityBoost = IO_NO_INCREMENT;
+
+ InitializeListHead(&m_FileObjectListHead);
+
+ m_RequestLookasideListElementSize = 0;
+ RtlZeroMemory(&m_RequestLookasideList, sizeof(m_RequestLookasideList));
+ RtlZeroMemory(&m_RequestAttributes, sizeof(m_RequestAttributes));
+
+#if (FX_CORE_MODE == FX_CORE_USER_MODE)
+ //
+ // Init UMDF specific members
+ //
+ m_CleanupFromFailedCreate = FALSE;
+ m_Dispatcher = NULL;
+ m_DevStack = NULL;
+ m_PdoDevKey = NULL;
+ m_DeviceKeyPath = NULL;
+ m_KernelDeviceName = NULL;
+ m_DeviceInstanceId = NULL;
+
+ m_RetrievalMode = UMINT::WdfDeviceIoBufferRetrievalDeferred;
+ m_IoctlIoType = WdfDeviceIoBuffered;
+ m_DirectTransferThreshold = 0;
+
+ m_DirectHardwareAccess = FX_DIRECT_HARDWARE_ACCESS_DEFAULT;
+ m_RegisterAccessMode = FX_REGISTER_ACCESS_MODE_DEFAULT;
+ m_FileObjectPolicy = FX_FILE_OBJECT_POLICY_DEFAULT;
+ m_FsContextUsePolicy = FX_FS_CONTEXT_USE_POLICY_DEFAULT;
+ m_InteruptThreadpool = NULL;
+#endif
+}
+
+FxDevice::~FxDevice()
+{
+ PLIST_ENTRY next;
+
+ // Make it always present right now even on free builds
+ if (IsDisposed() == FALSE) {
+ DoTraceLevelMessage(
+ GetDriverGlobals(), TRACE_LEVEL_FATAL, TRACINGDEVICE,
+ "FxDevice 0x%p not disposed: this maybe a driver reference count "
+ "problem with WDFDEVICE %p", this, GetObjectHandleUnchecked());
+
+ FxVerifierBugCheck(GetDriverGlobals(),
+ WDF_OBJECT_ERROR,
+ (ULONG_PTR) GetObjectHandleUnchecked(),
+ (ULONG_PTR) this);
+ }
+
+ //
+ // Execute mode-specific destructor. Noop for KMDF, but does
+ // does detach and delete of UM device object for UMDF. Therefore
+ // can be done before other cleanup.
+ //
+ DestructorInternal();
+
+ //
+ // If the device has been initialized but hasn't yet been
+ // destroyed, destroy it now.
+ //
+
+
+
+
+
+
+
+ ASSERT(m_DeviceObject.GetObject() == NULL);
+
+ ASSERT(m_DeviceName.Buffer == NULL);
+
+#if FX_CORE_MODE == FX_CORE_KERNEL_MODE
+ //
+ // Assert only applicable to KM because FxDevice can get destroyed in UMDF
+ // without going through normal pnp remove path, for example, when an
+ // AddDevice failure is done in reflector, after all um drivers have
+ // succeeded AddDevice. KMDF and host use fake remove irp to handle
+ // AddDevice failure in KMDF and host respectively, but reflector does not
+ // do that for AddDevice failure that happens in reflector.
+ // Note that symbolicName buffer will anyway be deleted in this destructor
+ // later on so the symbolic link buffer doesn't leak out.
+ //
+ ASSERT(m_SymbolicLinkName.Buffer == NULL);
+#endif
+
+ ASSERT(m_MofResourceName.Buffer == NULL);
+
+ if (m_PkgIo != NULL) {
+ m_PkgIo->RELEASE(NULL);
+ m_PkgIo = NULL;
+ }
+
+ if (m_PkgPnp != NULL) {
+ m_PkgPnp->RELEASE(NULL);
+ m_PkgPnp = NULL;
+ }
+
+ if (m_PkgGeneral != NULL) {
+ m_PkgGeneral->RELEASE(NULL);
+ m_PkgGeneral = NULL;
+ }
+
+ if (m_PkgWmi != NULL) {
+ m_PkgWmi->RELEASE(NULL);
+ m_PkgWmi = NULL;
+ }
+
+ if (m_PkgDefault != NULL) {
+ m_PkgDefault->RELEASE(NULL);
+ m_PkgDefault = NULL;
+ }
+
+ while (!IsListEmpty(&m_PreprocessInfoListHead)) {
+ next = RemoveHeadList(&m_PreprocessInfoListHead);
+ FxIrpPreprocessInfo* info;
+ info = CONTAINING_RECORD(next, FxIrpPreprocessInfo, ListEntry);
+ InitializeListHead(next);
+ delete info;
+ }
+
+ while (!IsListEmpty(&m_CxDeviceInfoListHead)) {
+ next = RemoveHeadList(&m_CxDeviceInfoListHead);
+ FxCxDeviceInfo* info;
+ info = CONTAINING_RECORD(next, FxCxDeviceInfo, ListEntry);
+ InitializeListHead(next);
+ delete info;
+ }
+
+ //
+ // Clean up any referenced objects
+ //
+ if (m_DeviceName.Buffer != NULL) {
+ FxPoolFree(m_DeviceName.Buffer);
+ RtlZeroMemory(&m_DeviceName, sizeof(m_DeviceName));
+ }
+
+ DeleteSymbolicLink();
+
+ if (m_MofResourceName.Buffer != NULL) {
+ FxPoolFree(m_MofResourceName.Buffer);
+ RtlZeroMemory(&m_MofResourceName, sizeof(m_DeviceName));
+ }
+
+ //
+ // m_RequestLookasideListElementSize will be set to non zero if we have
+ // initialized the request lookaside list.
+ //
+ if (m_RequestLookasideListElementSize != 0) {
+ Mx::MxDeleteNPagedLookasideList(&m_RequestLookasideList);
+ m_RequestLookasideListElementSize = 0;
+ }
+
+ if (m_ParentDevice != NULL) {
+ m_ParentDevice->RELEASE(this);
+ }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::_Create(
+ __in PFX_DRIVER_GLOBALS FxDriverGlobals,
+ __in PWDFDEVICE_INIT* DeviceInit,
+ __in_opt PWDF_OBJECT_ATTRIBUTES DeviceAttributes,
+ __out FxDevice** Device
+ )
+{
+ PWDFDEVICE_INIT pInit;
+ FxDevice* pDevice;
+ NTSTATUS status;
+ WDFOBJECT object;
+ PLIST_ENTRY pNext;
+ PWDFCXDEVICE_INIT pCxInit;
+ FxWdmDeviceExtension* wdmDeviceExtension;
+
+ *Device = NULL;
+ pInit = *DeviceInit;
+
+ pDevice = new (FxDriverGlobals, DeviceAttributes)
+ FxDevice(pInit->Driver);
+
+ if (pDevice == NULL) {
+ status = STATUS_INSUFFICIENT_RESOURCES;
+ goto Done;
+ }
+
+ status = pDevice->Initialize(pInit, DeviceAttributes);
+ if (!NT_SUCCESS(status)) {
+ goto Done;
+ }
+
+ 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:
+ //
+ // Should not drop here
+ //
+ ASSERT(FALSE);
+ break;
+ }
+ if (!NT_SUCCESS(status)) {
+ goto Done;
+ }
+
+ //
+ // Ok, we have created the device. Now lets create a handle for it.
+ //
+ status = pDevice->PostInitialize();
+ if (!NT_SUCCESS(status)) {
+ goto Done;
+ }
+
+ //
+ // Can't use the PDO's FxDevice m_Parent as the object hierarchy parent
+ // because the Fx object hierarchy lifetime rules do not match the
+ // rules for a pnp PDO lifetime vs its FDO.
+ //
+ status = pDevice->Commit(DeviceAttributes,
+ &object,
+ pDevice->GetDriver());
+ if (!NT_SUCCESS(status)) {
+ goto Done;
+ }
+
+ //
+ // NOTE: ---> DO NOT FAIL FROM HERE FORWARD <---
+ //
+
+ //
+ // Up until now we have not reassigned any of the allocations in pInit
+ // and assigned them to the underlying objects. We are now at the point
+ // of "no return", ie we cannot fail. If we reassigned the allocations
+ // before this point and the driver retried to create the device (let's
+ // say with a different name), we would have freed those allocations
+ // and the driver writer would have thought that particular settings
+ // we valid, but were not b/c we freed them on error. So, to avoid a
+ // huge tracking mess, we only grab the allocations once we know for
+ // *sure* we are going to return success.
+ //
+ if (pInit->DeviceName != NULL) {
+ pInit->DeviceName->ReleaseString(&pDevice->m_DeviceName);
+ }
+
+ //
+ // Check for driver preprocess requirements.
+ //
+ if (pInit->PreprocessInfo != NULL) {
+ ASSERT( pInit->PreprocessInfo->ClassExtension == FALSE);
+ ASSERT(IsListEmpty(&pDevice->m_PreprocessInfoListHead));
+ InsertTailList(&pDevice->m_PreprocessInfoListHead,
+ &pInit->PreprocessInfo->ListEntry);
+ pInit->PreprocessInfo = NULL;
+
+ //
+ // If the driver is preprocessing requests on this device, they need
+ // their own stack location so that they can set their own completion
+ // routine.
+ //
+ pDevice->SetStackSize(pDevice->GetStackSize()+1);
+ }
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+ wdmDeviceExtension = _GetFxWdmExtension(pDevice->GetDeviceObject());
+ if (wdmDeviceExtension->RemoveLockOptionFlags &
+ WDF_REMOVE_LOCK_OPTION_ACQUIRE_FOR_IO) {
+ //
+ // We will use a completion routine for remlock maintenance
+ //
+ pDevice->SetStackSize(pDevice->GetStackSize()+1);
+ }
+
+ //
+ // Note: In case of UMDF StackSize is incremented prior to attaching
+ // the device to stack. See the comment in FxDeviceUm.cpp
+ //
+ if (pDevice->m_SelfIoTargetNeeded) {
+ pDevice->SetStackSize(pDevice->GetStackSize()+1);
+ }
+
+#else
+ UNREFERENCED_PARAMETER(wdmDeviceExtension);
+#endif
+
+ //
+ // Check for any class-extensions' preprocess requirements.
+ //
+ for (pNext = pInit->CxDeviceInitListHead.Flink;
+ pNext != &pInit->CxDeviceInitListHead;
+ pNext = pNext->Flink) {
+
+ pCxInit = CONTAINING_RECORD(pNext, WDFCXDEVICE_INIT, ListEntry);
+
+ if (pCxInit->PreprocessInfo != NULL) {
+ ASSERT(pCxInit->PreprocessInfo->ClassExtension);
+ InsertTailList(&pDevice->m_PreprocessInfoListHead,
+ &pCxInit->PreprocessInfo->ListEntry);
+ pCxInit->PreprocessInfo = NULL;
+
+ //
+ // If the class extension is preprocessing requests on this
+ // device, it needs its own stack location so that it can
+ // set its own completion routine.
+ //
+ pDevice->SetStackSize(pDevice->GetStackSize()+1);
+ }
+ }
+
+ if (pDevice->IsPnp()) {
+ //
+ // Take all of the allocations out of pInit related to pnp. This
+ // will also transition the pnp state machine into the added state.
+ //
+ pDevice->m_PkgPnp->FinishInitialize(pInit);
+ }
+
+ pInit->CreatedDevice = pDevice;
+
+ //
+ // Clear out the pointer, we freed it on behalf of the caller
+ //
+ *DeviceInit = NULL;
+
+ if (pInit->CreatedOnStack == FALSE) {
+ delete pInit;
+ }
+
+Done:
+ if (!NT_SUCCESS(status) && pDevice != NULL) {
+ //
+ // We want to propagate the original error code
+ //
+ (void) pDevice->DeleteDeviceFromFailedCreate(status, FALSE);
+ pDevice = NULL;
+ }
+
+ *Device = pDevice;
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::DeleteDeviceFromFailedCreateNoDelete(
+ __in NTSTATUS FailedStatus,
+ __in BOOLEAN UseStateMachine
+ )
+{
+ //
+ // Cleanup the device, the driver may have allocated resources
+ // associated with the WDFDEVICE
+ //
+ DoTraceLevelMessage(
+ GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
+ "WDFDEVICE %p !devobj %p created, but EvtDriverDeviceAdd returned "
+ "status %!STATUS! or failure in creation",
+ GetObjectHandleUnchecked(), GetDeviceObject(), FailedStatus);
+
+ //
+ // We do not let filters affect the building of the rest of the stack.
+ // If they return error, we convert it to STATUS_SUCCESS, remove the
+ // attached device from the stack, and cleanup.
+ //
+ if (IsFilter()) {
+ DoTraceLevelMessage(
+ GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
+ "WDFDEVICE %p, !devobj %p is a filter, converting %!STATUS! to"
+ " STATUS_SUCCESS", GetObjectHandleUnchecked(), GetDeviceObject(),
+ FailedStatus);
+ FailedStatus = STATUS_SUCCESS;
+ }
+
+ if (UseStateMachine) {
+ MxEvent waitEvent;
+
+ //
+ // See comments for m_CleanupFromFailedCreate in class definition file
+ // for use of this statement.
+ //
+ SetCleanupFromFailedCreate(TRUE);
+
+
+
+
+
+ waitEvent.Initialize(SynchronizationEvent, FALSE);
+ m_PkgPnp->CleanupDeviceFromFailedCreate(waitEvent.GetSelfPointer());
+ }
+ else {
+ //
+ // Upon certain types of failure, like STATUS_OBJECT_NAME_COLLISION, we
+ // could keep the pDevice around and the caller retry after changing
+ // a property, but the simpler route for now is to just recreate
+ // everything from scratch on the retry.
+ //
+ // Usually the pnp state machine will do this and the FxDevice destructor
+ // relies on it running b/c it does some cleanup.
+ //
+ EarlyDispose();
+ DestroyChildren();
+
+ //
+ // Wait for all children to drain out and cleanup.
+ //
+ if (m_DisposeList != NULL) {
+ m_DisposeList->WaitForEmpty();
+ }
+
+ //
+ // We keep a reference on m_PkgPnp which is released in the destructor
+ // so we can safely touch m_PkgPnp after destroying all of the child
+ // objects.
+ //
+ if (m_PkgPnp != NULL) {
+ m_PkgPnp->CleanupStateMachines(TRUE);
+ }
+ }
+
+ //
+ // This will detach and delete the device object
+ //
+ Destroy();
+
+ return FailedStatus;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::DeleteDeviceFromFailedCreate(
+ __in NTSTATUS FailedStatus,
+ __in BOOLEAN UseStateMachine
+ )
+{
+ NTSTATUS status;
+
+ status = DeleteDeviceFromFailedCreateNoDelete(FailedStatus, UseStateMachine);
+
+ //
+ // Delete the Fx object now
+ //
+ DeleteObject();
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::Initialize(
+ __in PWDFDEVICE_INIT DeviceInit,
+ __in_opt PWDF_OBJECT_ATTRIBUTES DeviceAttributes
+ )
+/*++
+
+Routine Description:
+ Generic initialization for an FxDevice regardless of role (pdo, fdo, control).
+
+Arguments:
+
+
+Return Value:
+
+
+ --*/
+
+{
+ PFX_DRIVER_GLOBALS pGlobals;
+ PLIST_ENTRY next;
+ NTSTATUS status;
+ BOOLEAN wmiTracing;
+ size_t reqCtxSize;
+ PWDFCXDEVICE_INIT cxInit;
+ CCHAR cxIndex;
+ FxCxDeviceInfo* cxDeviceInfo;
+
+ pGlobals = GetDriverGlobals();
+ wmiTracing = FALSE;
+ m_Exclusive = DeviceInit->Exclusive;
+ cxIndex = 0;
+
+ MarkDisposeOverride(ObjectDoNotLock);
+
+ //
+ // Configure device constraints.
+ //
+ status = ConfigureConstraints(DeviceAttributes);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ //
+ // Generic catch all
+ //
+ 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;
+ }
+
+ //
+ // Size will be set to a non zero if the driver wants request attributes
+ // associated with each created request.
+ //
+ if (DeviceInit->RequestAttributes.Size != 0) {
+ ASSERT(DeviceInit->RequestAttributes.Size == sizeof(WDF_OBJECT_ATTRIBUTES));
+ RtlCopyMemory(&m_RequestAttributes,
+ &DeviceInit->RequestAttributes,
+ sizeof(DeviceInit->RequestAttributes));
+ }
+
+ reqCtxSize = FxGetContextSize(&m_RequestAttributes);
+
+ //
+ // If present, setup a I/O class extensions info chain.
+ //
+ for (next = DeviceInit->CxDeviceInitListHead.Flink;
+ next != &DeviceInit->CxDeviceInitListHead;
+ next = next->Flink) {
+
+ cxInit = CONTAINING_RECORD(next, WDFCXDEVICE_INIT, ListEntry);
+
+ cxDeviceInfo = new(pGlobals) FxCxDeviceInfo(pGlobals);
+ if (NULL == cxDeviceInfo) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+
+ cxDeviceInfo->Index = ++cxIndex; // 1-based.
+ cxDeviceInfo->Driver = cxInit->CxDriverGlobals->Driver;
+ cxDeviceInfo->IoInCallerContextCallback.m_Method =
+ cxInit->IoInCallerContextCallback;
+ cxDeviceInfo->RequestAttributes = cxInit->RequestAttributes;
+
+ InsertTailList(&m_CxDeviceInfoListHead, &cxDeviceInfo->ListEntry);
+
+ //
+ // Set weak ref to this run-time cx struct to help file-object logic later on.
+ //
+ cxInit->CxDeviceInfo = cxDeviceInfo;
+
+ //
+ // Find the max size for the request context. Used below.
+ //
+ ASSERT(cxInit->RequestAttributes.Size == 0 ||
+ cxInit->RequestAttributes.Size == sizeof(WDF_OBJECT_ATTRIBUTES));
+
+ reqCtxSize = MAX(FxGetContextSize(&cxInit->RequestAttributes),
+ reqCtxSize);
+ }
+
+ //
+ // Memory layout for memory backing FxRequest which is allocated from the
+ // lookaside list:
+ //
+ // If we are tracking memory, the allocation layout is
+ // 0x0 - FX_POOL_TRACKER
+ // 0x0 + sizeof(FX_POOL_TRACKER) - FX_POOL_HEADER
+ // 0x0 + sizeof(FX_POOL_TRACKER) + FX_POOL_HEADER_SIZE - start of FxRequest
+ //
+ // if no tracking is occuring, the allocation layout is
+ // 0x0 - FX_POOL_HEADER
+ // 0x0 + FX_POOL_HEADER_SIZE - start of FxRequest
+ //
+ // NOTE: If the computation of m_RequestLookasideListElementSize changes,
+ // FxDevice::AllocateRequestMemory and FxDevice::FreeRequestMemory will also
+ // need to be updated to reflect the changes made.
+ //
+ status = FxCalculateObjectTotalSize2(pGlobals,
+ sizeof(FxRequest),
+ 0,
+ reqCtxSize,
+ &m_RequestLookasideListElementSize);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ status = FxPoolAddHeaderSize(pGlobals,
+ m_RequestLookasideListElementSize,
+ &m_RequestLookasideListElementSize);
+
+ if (!NT_SUCCESS(status)) {
+ //
+ // FxPoolAddHeaderSize will log to the IFR on error
+ //
+ return status;
+ }
+
+ 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);
+
+#if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
+
+
+
+
+
+
+ m_PkgWmi = new(pGlobals) FxWmiIrpHandler(pGlobals, this);
+ if (m_PkgWmi == NULL) {
+ return STATUS_INSUFFICIENT_RESOURCES;
+ }
+ InstallPackage(m_PkgWmi);
+#endif
+
+ //
+ // 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);
+
+ //
+ // Configure I/O package.
+ //
+ m_PkgIo->SetIoInCallerContextCallback(DeviceInit->IoInCallerContextCallback);
+
+ if (DeviceInit->RequiresSelfIoTarget) {
+ m_SelfIoTargetNeeded = TRUE;
+ }
+
+ return STATUS_SUCCESS;
+}
+
+VOID
+FxDevice::ConfigureAutoForwardCleanupClose(
+ __in PWDFDEVICE_INIT DeviceInit
+ )
+{
+ WDF_TRI_STATE autoForwardCleanupClose;
+ PLIST_ENTRY next;
+ BOOLEAN checkClientDriver;
+
+ autoForwardCleanupClose = WdfUseDefault;
+ checkClientDriver = TRUE;
+
+ //
+ // Device-wide configuration for auto forwarding cleanup and close requests:
+ // . Use WdfFalse if one of the devices in the chain use this setting with a create
+ // callback (this means it will complete all create IRPs).
+ // . Else use lowest driver's setting in the chain (order of cx chain: lower to higher).
+ // . If no settings are present, use default.
+ //
+ for (next = DeviceInit->CxDeviceInitListHead.Blink;
+ next != &DeviceInit->CxDeviceInitListHead;
+ next = next->Blink) {
+
+ PWDFCXDEVICE_INIT cxInit;
+
+ cxInit = CONTAINING_RECORD(next, WDFCXDEVICE_INIT, ListEntry);
+
+ if (cxInit->FileObject.Set) {
+ autoForwardCleanupClose = cxInit->FileObject.AutoForwardCleanupClose;
+
+ if (autoForwardCleanupClose == WdfFalse &&
+ cxInit->FileObject.Callbacks.EvtCxDeviceFileCreate != NULL) {
+
+ checkClientDriver = FALSE;
+ break;
+ }
+ }
+ }
+
+ if (checkClientDriver && DeviceInit->FileObject.Set) {
+ autoForwardCleanupClose = DeviceInit->FileObject.AutoForwardCleanupClose;
+ }
+
+ switch (autoForwardCleanupClose) {
+ case WdfTrue:
+
+ m_AutoForwardCleanupClose = TRUE;
+ //
+ // If the device is legacy then set it to false because you can't forward
+ // requests.
+ //
+ if(m_Legacy) {
+ m_AutoForwardCleanupClose = FALSE;
+ }
+ break;
+
+ case WdfFalse:
+ m_AutoForwardCleanupClose = FALSE;
+ break;
+
+ case WdfUseDefault:
+ //
+ // For filters (which must be FDOs), we default to TRUE. All other
+ // device roles (FDO, PDO, control) default to FALSE. We cannot check
+ // m_Filter yet because it is set in FdoInitialize which occurs later.
+ //
+ if (DeviceInit->IsFdoInit() && DeviceInit->Fdo.Filter) {
+ m_AutoForwardCleanupClose = TRUE;
+ }
+ else {
+ m_AutoForwardCleanupClose = FALSE;
+ }
+ }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::PostInitialize(
+ VOID
+ )
+{
+ NTSTATUS status;
+
+ status = FxDisposeList::_Create(GetDriverGlobals(),
+ m_DeviceObject.GetObject(),
+ &m_DisposeList);
+
+ return status;
+}
+
+
+
+
+
+
+
+
+#if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::CreateDevice(
+ __in PWDFDEVICE_INIT DeviceInit
+ )
+{
+ MdDeviceObject pNewDeviceObject;
+ ULONG characteristics;
+ NTSTATUS status;
+ DEVICE_TYPE devType;
+
+ status = m_PkgGeneral->Initialize(DeviceInit);
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ devType = DeviceInit->DeviceType;
+ if (devType < ARRAY_SIZE(m_PriorityBoosts)) {
+ m_DefaultPriorityBoost= m_PriorityBoosts[devType];
+ }
+
+ characteristics = DeviceInit->Characteristics;
+
+ //
+ // You can only create secure device objects which have a name. All other
+ // device objects rely on the PDO's security
+ //
+ if (DeviceInit->ShouldCreateSecure()) {
+ PUNICODE_STRING pName, pSddl;
+ LPGUID pGuid;
+
+ if (DeviceInit->DeviceName != NULL) {
+ pName = DeviceInit->DeviceName->GetUnicodeString();
+ }
+ else {
+ pName = NULL;
+ }
+
+ if (DeviceInit->Security.DeviceClassSet) {
+ pGuid = &DeviceInit->Security.DeviceClass;
+ }
+ else {
+ pGuid = NULL;
+ }
+
+ if (DeviceInit->Security.Sddl != NULL) {
+ pSddl = DeviceInit->Security.Sddl->GetUnicodeString();
+ }
+ else {
+ //
+ // Always provide an SDDL if one is not supplied.
+ //
+ // SDDL_DEVOBJ_SYS_ALL_ADM_ALL = "D:P(A;;GA;;;SY)(A;;GA;;;BA)"
+ //
+ // SDDL_DEVOBJ_SYS_ALL_ADM_ALL allows the kernel, system, and
+ // administrator complete control over the device. No other users
+ // may access the device.
+ //
+ pSddl = (PUNICODE_STRING) &SDDL_DEVOBJ_SYS_ALL_ADM_ALL;
+ }
+
+ status = Mx::MxCreateDeviceSecure(
+ m_Driver->m_DriverObject.GetObject(),
+ sizeof(FxWdmDeviceExtension),
+ pName,
+ devType,
+ characteristics,
+ m_Exclusive,
+ pSddl,
+ pGuid,
+ &pNewDeviceObject);
+ }
+ else {
+ status = Mx::MxCreateDevice(
+ m_Driver->m_DriverObject.GetObject(),
+ sizeof(FxWdmDeviceExtension),
+ NULL,
+ devType,
+ characteristics,
+ m_Exclusive,
+ &pNewDeviceObject);
+ }
+
+ if (NT_SUCCESS(status)) {
+ FxWdmDeviceExtension* pWdmExt;
+
+ pWdmExt = _GetFxWdmExtension(pNewDeviceObject);
+
+ //
+ // We reassign DeviceExtension below and then use the knowledge that
+ // we can always retrieve DeviceExtension by adding sizeof(DEVICE_OBJECT)
+ // to pNewDeviceObject. ASSERT that this assumption is correct.
+ //
+ MxDeviceObject newDeviceObject(pNewDeviceObject);
+ ASSERT(pWdmExt == newDeviceObject.GetDeviceExtension());
+
+ Mx::MxInitializeRemoveLock(&pWdmExt->IoRemoveLock,
+ GetDriverGlobals()->Tag,
+ 0, // max min
+ 0 // highwater mark
+ );
+
+ //
+ // Option for remove lock is stored in device extension
+ // since this option may be examined after FxDevice is destroyed
+ // (if an Irp is sent after removal of device).
+ // We combine the flags from DeviceInit with what's set through registry
+ //
+ pWdmExt->RemoveLockOptionFlags = DeviceInit->RemoveLockOptionFlags |
+ GetDriverGlobals()->RemoveLockOptionFlags;
+
+ //
+ // We assign the first context assigned to this object as the
+ // DeviceExtension for compatibility reasons. This allows existing
+ // WDM extensions to work as well as any stack which exports a known
+ // structure for the extension (ie the FDO knows the extension of its
+ // PDO and casts it and accesses it directly).
+ //
+ newDeviceObject.SetDeviceExtension(&GetContextHeader()->Context[0]);
+ m_DeviceObject.SetObject(pNewDeviceObject);
+
+ //
+ // Set some device object flags based on properties of DeviceInit.
+ //
+ // If we are a filter, we will set these flags later
+ // (in FxDevice::FdoInitialize) based on the device we are attached to.
+ //
+ if (m_Filter == FALSE) {
+ if (DeviceInit->ReadWriteIoType == WdfDeviceIoBuffered) {
+ m_DeviceObject.SetFlags(m_DeviceObject.GetFlags() | DO_BUFFERED_IO);
+ }
+ else if (DeviceInit->ReadWriteIoType == WdfDeviceIoDirect) {
+ m_DeviceObject.SetFlags(m_DeviceObject.GetFlags() | DO_DIRECT_IO);
+ }
+
+ m_ReadWriteIoType = DeviceInit->ReadWriteIoType;
+ m_PowerPageableCapable = DeviceInit->PowerPageable;
+ }
+ }
+
+ return status;
+}
+
+#endif // (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
+
+VOID
+FxDevice::FinishInitializing(
+ VOID
+ )
+
+/*++
+
+Routine Description:
+
+ This routine is called when the device is completely initialized.
+
+Arguments:
+
+ none.
+
+Returns:
+
+ none.
+
+--*/
+
+{
+
+ m_DeviceObject.SetFlags( m_DeviceObject.GetFlags() & ~DO_DEVICE_INITIALIZING);
+}
+
+VOID
+FxDevice::DeleteObject(
+ VOID
+ )
+/*++
+
+Routine Description:
+ Virtual override of an FxObject::DeleteObject. For PDOs which are created
+ statically and then deleted before being reported to WDF, we must simulate
+ a pnp remove event to trigger cleanup.
+
+Arguments:
+ None
+
+Return Value:
+ None
+
+ --*/
+{
+ if (IsPnp() && IsPdo()) {
+ FxPkgPdo* pPkgPdo;
+ KIRQL irql;
+ BOOLEAN remove;
+
+ remove = FALSE;
+
+ pPkgPdo = GetPdoPkg();
+
+ pPkgPdo->Lock(&irql);
+
+ if (pPkgPdo->m_Static && pPkgPdo->m_AddedToStaticList == FALSE) {
+ //
+ // Since no pnp action has been taken since the child was created, we
+ // should be in the initial state.
+ //
+ if (m_CurrentPnpState == WdfDevStatePnpInit) {
+ //
+ // A PDO in this state should be deletable
+ //
+ ASSERT(IsNoDeleteDDI() == FALSE);
+
+ remove = TRUE;
+ }
+ else {
+ //
+ // If we are not in the init state, we should be in the created
+ // state. This means we are failing from FxDevice::CreateDevice.
+ //
+ ASSERT(m_CurrentPnpState == WdfDevStatePnpObjectCreated);
+ }
+ }
+
+ pPkgPdo->Unlock(irql);
+
+ if (remove) {
+ //
+ // Cleanup the device and then let the super class delete the object.
+ //
+ (void) DeleteDeviceFromFailedCreateNoDelete(
+ STATUS_UNSUCCESSFUL, TRUE);
+ }
+ }
+ else if (IsLegacy() && m_PkgGeneral != NULL && m_DeviceObject.GetObject() != NULL) {
+ //
+ // We allow tracing devices to go through a normal DeleteObject() path
+ // where we do not prematurely delete the device object.
+ //
+ (void) FxVerifierCheckIrqlLevel(GetDriverGlobals(), PASSIVE_LEVEL);
+
+ m_DeviceObjectDeleted = TRUE;
+
+ //
+ // This reference will be released in Destroy().
+ //
+ Mx::MxReferenceObject(m_DeviceObject.GetObject());
+
+ if (m_PkgWmi != NULL) {
+ //
+ // Since a legacy NT4 driver does not have an explicit WMI
+ // deregistration DDI, we do it for them on deletion.
+ //
+ // This is done in DeleteObject because we need to deregister before
+ // we delete the device object, otherwise we can bugcheck when
+ // running under driver verifier.
+ //
+ m_PkgWmi->Deregister();
+ }
+
+ //
+ // By deleting the device object now, we prevent any new creates from
+ // being sent to the device (the io manager enforces this).
+ //
+ Mx::MxDeleteDevice(m_DeviceObject.GetObject());
+
+ if (m_PkgGeneral->CanDestroyControlDevice() == FALSE) {
+ //
+ // Delay the actual destruction of the device until the last open
+ // handle has been closed. ControlDeviceDelete() will perform the
+ // destruction later.
+ //
+ return;
+ }
+ }
+
+ __super::DeleteObject();
+}
+
+BOOLEAN
+FxDevice::Dispose(
+ VOID
+ )
+{
+ ASSERT(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
+
+ if (m_Legacy) {
+ if (m_PkgWmi != NULL) {
+ //
+ // We deregister in Dispose() (as well as DeleteObject()) for
+ // control devices which are implicitly destroyed when the driver
+ // unloads and FxDriver is being deleted.
+ //
+ // Since a legacy NT4 driver does not have an explicit WMI
+ // deregistration DDI, we do it for them on destruction.
+ //
+ // This is done in Dispose because we are guaranteed to be at
+ // passive level here. Even though m_PkgWmi was already
+ // Dispose()'ed (because it is a child of this object), it is still
+ // valid to reference the pointer because there is an explicit
+ // reference on the object that was taken when we created this object.
+ //
+ m_PkgWmi->Deregister();
+ }
+
+ //
+ // Important that the cleanup routine be called while the PDEVICE_OBJECT
+ // is valid!
+ //
+ CallCleanup();
+
+ //
+ // Manually destroy the children now so that by the time we wait on the
+ // dispose empty out, all of the children will have been added to it.
+ //
+ DestroyChildren();
+
+ if (m_DisposeList != NULL) {
+ m_DisposeList->WaitForEmpty();
+ }
+
+ //
+ // Now delete the device object
+ //
+ Destroy();
+
+ return FALSE;
+ }
+
+ return __super::Dispose();
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::_AcquireOptinRemoveLock(
+ __in MdDeviceObject DeviceObject,
+ __in MdIrp Irp
+ )
+{
+ NTSTATUS status;
+ FxIrp irp(Irp);
+
+ FxWdmDeviceExtension * wdmDeviceExtension =
+ FxDevice::_GetFxWdmExtension(DeviceObject);
+
+ if (wdmDeviceExtension->RemoveLockOptionFlags &
+ WDF_REMOVE_LOCK_OPTION_ACQUIRE_FOR_IO) {
+
+ status = Mx::MxAcquireRemoveLock(&(wdmDeviceExtension->IoRemoveLock), Irp);
+
+ if (!NT_SUCCESS(status)) {
+ return status;
+ }
+
+ irp.CopyCurrentIrpStackLocationToNext();
+
+ irp.SetCompletionRoutineEx(
+ DeviceObject,
+ _CompletionRoutineForRemlockMaintenance,
+ DeviceObject,
+ TRUE,
+ TRUE,
+ TRUE
+ );
+
+ irp.SetNextIrpStackLocation();
+ }
+
+ return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::DispatchWithLock(
+ __in MdDeviceObject DeviceObject,
+ __in MdIrp Irp
+ )
+{
+ NTSTATUS status;
+ FxIrp irp(Irp);
+
+ switch (_RequiresRemLock(irp.GetMajorFunction(),
+ irp.GetMinorFunction())) {
+
+ case FxDeviceRemLockRequired:
+ status = Mx::MxAcquireRemoveLock(
+ &_GetFxWdmExtension(DeviceObject)->IoRemoveLock,
+ Irp
+ );
+
+ if (!NT_SUCCESS(status)) {
+ irp.SetStatus(status);
+ irp.CompleteRequest(IO_NO_INCREMENT);
+
+ return status;
+ }
+
+ break;
+
+ case FxDeviceRemLockOptIn:
+ status = _AcquireOptinRemoveLock(
+ DeviceObject,
+ Irp
+ );
+
+ if (!NT_SUCCESS(status)) {
+ irp.SetStatus(status);
+ irp.CompleteRequest(IO_NO_INCREMENT);
+
+ return status;
+ }
+
+ break;
+
+ case FxDeviceRemLockTestValid:
+ //
+ // Try to Acquire and Release the RemLock. If acquiring the lock
+ // fails then it is not safe to process the IRP and the IRP should
+ // be completed immediately.
+ //
+ status = Mx::MxAcquireRemoveLock(
+ &_GetFxWdmExtension(DeviceObject)->IoRemoveLock,
+ Irp
+ );
+
+ if (!NT_SUCCESS(status)) {
+ irp.SetStatus(status);
+ irp.CompleteRequest(IO_NO_INCREMENT);
+
+ return status;
+ }
+
+ Mx::MxReleaseRemoveLock(
+ &_GetFxWdmExtension(DeviceObject)->IoRemoveLock,
+ Irp
+ );
+ break;
+ }
+
+ return Dispatch(DeviceObject, Irp);
+}
+
+_Must_inspect_result_
+__inline
+BOOLEAN
+IsPreprocessIrp(
+ __in MdIrp Irp,
+ __in FxIrpPreprocessInfo* Info
+ )
+{
+ UCHAR major, minor;
+ BOOLEAN preprocess;
+ FxIrp irp(Irp);
+
+ major = irp.GetMajorFunction();
+ minor = irp.GetMinorFunction();
+
+ preprocess = FALSE;
+
+ if (Info->Dispatch[major].EvtDevicePreprocess != NULL) {
+ if (Info->Dispatch[major].NumMinorFunctions == 0) {
+ //
+ // If the driver is not interested in particular minor codes,
+ // just give the irp to it.
+ //
+ preprocess = TRUE;
+ }
+ else {
+ ULONG i;
+
+ //
+ // Try to match up to a minor code.
+ //
+ for (i = 0; i < Info->Dispatch[major].NumMinorFunctions; i++) {
+ if (Info->Dispatch[major].MinorFunctions[i] == minor) {
+ preprocess = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+ return preprocess;
+}
+
+_Must_inspect_result_
+__inline
+NTSTATUS
+PreprocessIrp(
+ __in FxDevice* Device,
+ __in MdIrp Irp,
+ __in FxIrpPreprocessInfo* Info,
+ __in PVOID DispatchContext
+ )
+{
+ NTSTATUS status;
+ MdDeviceObject devObj;
+ UCHAR major, minor;
+ FxIrp irp(Irp);
+
+ major = irp.GetMajorFunction();
+ minor = irp.GetMinorFunction();
+
+ //
+ // If this is a pnp remove irp, this object could be deleted by the time
+ // EvtDevicePreprocess returns. To not touch freed pool, capture all
+ // values we will need before preprocessing.
+ //
+ devObj = Device->GetDeviceObject();
+
+ if (Info->ClassExtension == FALSE) {
+ status = Info->Dispatch[major].EvtDevicePreprocess( Device->GetHandle(),
+ Irp);
+ }
+ else {
+ status = Info->Dispatch[major].EvtCxDevicePreprocess(
+ Device->GetHandle(),
+ Irp,
+ DispatchContext);
+ }
+
+ //
+ // If we got this far, we handed the irp off to EvtDevicePreprocess, so we
+ // must now do our remlock maintainance if necessary.
+ //
+ if (FxDevice::_RequiresRemLock(major, minor) == FxDeviceRemLockRequired) {
+ //
+ // Keep the remove lock active until after we call into the driver.
+ // If the driver redispatches the irp to the framework, we will
+ // reacquire the remove lock at that point in time.
+ //
+ // Touching pDevObj after sending the pnp remove irp to the framework
+ // is OK b/c we have acquired the remlock previously and that will
+ // prevent this irp's processing racing with the pnp remove irp
+ // processing.
+ //
+ Mx::MxReleaseRemoveLock(Device->GetRemoveLock(),
+ Irp);
+ }
+
+ return status;
+}
+
+_Must_inspect_result_
+__inline
+NTSTATUS
+DispatchWorker(
+ __in FxDevice* Device,
+ __in MdIrp Irp,
+ __in WDFCONTEXT DispatchContext
+ )
+{
+ PLIST_ENTRY next;
+ FxIrp irp(Irp);
+
+ next = (PLIST_ENTRY)DispatchContext;
+
+ ASSERT(NULL != DispatchContext &&
+ ((UCHAR)DispatchContext & FX_IN_DISPATCH_CALLBACK) == 0);
+
+ //
+ // Check for any driver/class-extensions' preprocess requirements.
+ //
+ while (next != &Device->m_PreprocessInfoListHead) {
+ FxIrpPreprocessInfo* info;
+
+ info = CONTAINING_RECORD(next, FxIrpPreprocessInfo, ListEntry);
+
+ //
+ // Advance to next node.
+ //
+ next = next->Flink;
+
+ if (IsPreprocessIrp(Irp, info)) {
+ return PreprocessIrp(Device, Irp, info, next);
+ }
+ }
+
+ //
+ // No preprocess requirements, directly dispatch the IRP.
+ //
+ return Device->GetDispatchPackage(
+ irp.GetMajorFunction()
+ )->Dispatch(Irp);
+}
+
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::Dispatch(
+ __in MdDeviceObject DeviceObject,
+ __in MdIrp Irp
+ )
+{
+ FxDevice* device = FxDevice::GetFxDevice(DeviceObject);
+ return DispatchWorker(device,
+ Irp,
+ device->m_PreprocessInfoListHead.Flink);
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::DispatchPreprocessedIrp(
+ __in MdIrp Irp,
+ __in WDFCONTEXT DispatchContext
+ )
+{
+ NTSTATUS status;
+ UCHAR major, minor;
+ FxIrp irp(Irp);
+
+ //
+ // The contract for this DDI is just like IoCallDriver. The caller sets up
+ // their stack location and then the DDI advances to the next stack location.
+ // This means that the caller either has to call IoSkipCurrentIrpStackLocation
+ // or IoCopyCurrentIrpStackLocationToNext before calling this DDI.
+ //
+ irp.SetNextIrpStackLocation();
+
+ major = irp.GetMajorFunction();
+ minor = irp.GetMinorFunction();
+
+ //
+ // FxPkgPnp and FxWmiIrpHandler expect that there will be a remove lock
+ // acquired for all power irps. We release the remlock when we called
+ // Evt(Ext)DevicePreprocessIrp.
+ //
+ if (_RequiresRemLock(major, minor) == FxDeviceRemLockRequired) {
+ status = Mx::MxAcquireRemoveLock(
+ GetRemoveLock(),
+ Irp
+ );
+
+ if (!NT_SUCCESS(status)) {
+ goto Done;
+ }
+ }
+
+ return DispatchWorker(this, Irp, DispatchContext);
+
+Done:
+ irp.SetStatus(status);
+ irp.SetInformation(0);
+ irp.CompleteRequest(IO_NO_INCREMENT);
+
+ return status;
+}
+
+VOID
+FxDevice::InstallPackage(
+ __inout FxPackage *Package
+ )
+
+{
+ //
+ // Add this package as an association on FxDevice
+ // so its children get Dispose notifications.
+ //
+ // Note: This assumes a transfer of the controlling reference
+ // count which it will dereference on FxDevice teardown.
+ // We need to add an extra one here since packages have
+ // an existing reference count model.
+ //
+ Package->AddRef();
+ Package->AssignParentObject(this);
+}
+
+PVOID
+FxDevice::AllocateRequestMemory(
+ __in_opt PWDF_OBJECT_ATTRIBUTES RequestAttributes
+ )
+/*++
+
+Routine Description:
+ Allocates enough memory for an FxRequest* plus any additonal memory required
+ for the device's specific context memory.
+
+ If we are tracking memory, the allocation layout is
+ 0x0 - FX_POOL_TRACKER
+ 0x0 + sizeof(FX_POOL_TRACKER) - FX_POOL_HEADER
+ 0x0 + sizeof(FX_POOL_TRACKER) + FX_POOL_HEADER_SIZE - start of FxRequest
+
+ if no tracking is occuring, the allocation layout is
+ 0x0 - FX_POOL_HEADER
+ 0x0 + FX_POOL_HEADER_SIZE - start of FxRequest
+
+ the total size is precomputed in m_RequestLookasideListElementSize during
+ FxDevice::Initialize
+
+Arguments:
+ RequestAttributes - Attributes setting for the request.
+
+Return Value:
+ valid ptr or NULL
+
+ --*/
+
+{
+ PFX_DRIVER_GLOBALS pGlobals;
+ PFX_POOL_TRACKER pTracker;
+ PFX_POOL_HEADER pHeader;
+ PVOID ptr, pTrueBase;
+
+ pGlobals = GetDriverGlobals();
+ ptr = NULL;
+
+ if (IsPdo() && GetPdoPkg()->IsForwardRequestToParentEnabled()) {
+ pTrueBase = FxAllocateFromNPagedLookasideListNoTracking(&m_RequestLookasideList);
+ }
+ else {
+ pTrueBase = FxAllocateFromNPagedLookasideList(&m_RequestLookasideList,
+ m_RequestLookasideListElementSize);
+ }
+
+ if (pTrueBase != NULL) {
+ if (pGlobals->IsPoolTrackingOn()) {
+ pTracker = (PFX_POOL_TRACKER) pTrueBase;
+ pHeader = WDF_PTR_ADD_OFFSET_TYPE(pTrueBase,
+ sizeof(FX_POOL_TRACKER),
+ PFX_POOL_HEADER);
+
+ //
+ // Format and insert the Tracker in the NonPagedHeader list.
+ //
+ FxPoolInsertNonPagedAllocateTracker(&pGlobals->FxPoolFrameworks,
+ pTracker,
+ m_RequestLookasideListElementSize,
+ pGlobals->Tag,
+ _ReturnAddress());
+ }
+ else {
+ pHeader = (PFX_POOL_HEADER) pTrueBase;
+ }
+
+ //
+ // Common init
+ //
+ pHeader->Base = pTrueBase;
+ pHeader->FxDriverGlobals = pGlobals;
+
+ ptr = &pHeader->AllocationStart[0];
+
+ if (RequestAttributes == NULL) {
+ RequestAttributes = &m_RequestAttributes;
+ }
+
+ ptr = FxObjectAndHandleHeaderInit(
+ pGlobals,
+ ptr,
+ COMPUTE_OBJECT_SIZE(sizeof(FxRequest), 0),
+ RequestAttributes,
+ FxObjectTypeExternal
+ );
+
+#if FX_VERBOSE_TRACE
+ DoTraceLevelMessage(pGlobals, TRACE_LEVEL_VERBOSE, TRACINGREQUEST,
+ "Allocating FxRequest* %p, WDFREQUEST %p",
+ ptr, _ToHandle((FxObject*) ptr));
+#endif
+ return ptr;
+ }
+
+ return NULL;
+}
+
+VOID
+FxDevice::FreeRequestMemory(
+ __in FxRequest* Request
+ )
+{
+ PFX_POOL_HEADER pHeader;
+
+#if FX_VERBOSE_TRACE
+ DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGREQUEST,
+ "Free FxRequest* %p memory", Request);
+#endif
+
+ //
+ // Remove the request from the list of outstanding requests against this
+ // driver.
+ //
+ pHeader = FxObject::_CleanupPointer(GetDriverGlobals(), Request);
+ if (IsPdo() && GetPdoPkg()->IsForwardRequestToParentEnabled()) {
+ FxFreeToNPagedLookasideListNoTracking(&m_RequestLookasideList, pHeader->Base);
+ }
+ else {
+ FxFreeToNPagedLookasideList(&m_RequestLookasideList, pHeader->Base);
+ }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::QueryInterface(
+ __inout FxQueryInterfaceParams* Params
+ )
+{
+ switch (Params->Type) {
+ case FX_TYPE_DEVICE:
+ *Params->Object = (FxDevice*) this;
+ break;
+
+ default:
+ return __super::QueryInterface(Params);
+ }
+
+ return STATUS_SUCCESS;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::AddIoTarget(
+ __inout FxIoTarget* IoTarget
+ )
+{
+ NTSTATUS status;
+
+ status = m_IoTargetsList.Add(GetDriverGlobals(),
+ &IoTarget->m_TransactionedEntry);
+
+ if (NT_SUCCESS(status)) {
+ IoTarget->m_AddedToDeviceList = TRUE;
+ IoTarget->ADDREF(this);
+ }
+
+ return status;
+}
+
+VOID
+FxDevice::RemoveIoTarget(
+ __inout FxIoTarget* IoTarget
+ )
+{
+ m_IoTargetsList.Remove(GetDriverGlobals(),
+ &IoTarget->m_TransactionedEntry);
+
+ //
+ // Assumes that the caller has its own reference on the IoTarget
+ //
+ IoTarget->RELEASE(this);
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::AllocateEnumInfo(
+ VOID
+ )
+{
+ if (IsPnp()) {
+ return m_PkgPnp->AllocateEnumInfo();
+ }
+ else {
+ return STATUS_SUCCESS;
+ }
+}
+
+FxIoTarget*
+FxDevice::GetDefaultIoTarget(
+ VOID
+ )
+{
+ if (IsPnp() && IsFdo()) {
+ return GetFdoPkg()->m_DefaultTarget;
+ }
+ else {
+ return NULL;
+ }
+}
+
+FxIoTargetSelf*
+FxDevice::GetSelfIoTarget(
+ VOID
+ )
+/*++
+Routine Description:
+ Returns the Self IO target for this FxDevice.
+ Currently Self IO Target is supported only for a Pnp FDO.
+ If the Self IO Target has not been established, it returns NULL.
+--*/
+{
+ if (IsPnp() && IsFdo()) {
+ return GetFdoPkg()->m_SelfTarget;
+ }
+ else {
+ return NULL;
+ }
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::SetFilter(
+ __in BOOLEAN Value
+ )
+{
+ NTSTATUS status;
+
+ ASSERT(IsFdo());
+
+ status = m_PkgIo->SetFilter(Value);
+
+ if (NT_SUCCESS(status) && m_PkgPnp != NULL) {
+ status = GetFdoPkg()->SetFilter(Value);
+ }
+
+ if (NT_SUCCESS(status)) {
+ m_Filter = Value;
+ }
+
+ return status;
+}
+
+VOID
+FxDevice::SetFilterIoType(
+ VOID
+ )
+{
+ FxIoTarget * ioTarget;
+ FxTransactionedEntry * targetsList = NULL;
+
+ ASSERT(IsFilter());
+
+ m_DeviceObject.SetFlags( m_DeviceObject.GetFlags() & ~(DO_BUFFERED_IO | DO_DIRECT_IO));
+
+ //
+ // m_AttachedDevice can be NULL for UMDF, so check for NULL
+ //
+ if (m_AttachedDevice.GetObject() != NULL) {
+ m_DeviceObject.SetFlags(m_DeviceObject.GetFlags() |
+ (m_AttachedDevice.GetFlags() & (DO_BUFFERED_IO | DO_DIRECT_IO)));
+ }
+
+ if (m_DeviceObject.GetFlags() & DO_BUFFERED_IO) {
+ m_ReadWriteIoType = WdfDeviceIoBuffered;
+ }
+ else if (m_DeviceObject.GetFlags() & DO_DIRECT_IO) {
+ m_ReadWriteIoType = WdfDeviceIoDirect;
+ }
+ else {
+ m_ReadWriteIoType = WdfDeviceIoNeither;
+ }
+
+ //
+ // We also need to propagate these settings to any io targets that
+ // have already been created
+ //
+
+ m_IoTargetsList.LockForEnum(GetDriverGlobals());
+
+ targetsList = m_IoTargetsList.GetNextEntry(targetsList);
+
+ while (targetsList != NULL) {
+
+ ioTarget = (FxIoTarget *) targetsList->GetTransactionedObject();
+
+ if (ioTarget->GetTargetPDO() == GetPhysicalDevice()) {
+ ioTarget->UpdateTargetIoType();
+ }
+
+ targetsList = m_IoTargetsList.GetNextEntry(targetsList);
+ }
+
+ m_IoTargetsList.UnlockFromEnum(GetDriverGlobals());
+}
+
+BOOLEAN
+FxDevice::IsInterfaceRegistered(
+ _In_ const GUID* InterfaceClassGUID,
+ _In_opt_ PCUNICODE_STRING RefString
+ )
+{
+ PSINGLE_LIST_ENTRY ple;
+ BOOLEAN found = FALSE;
+
+ m_PkgPnp->m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
+
+ //
+ // Iterate over the interfaces and see if we have a match
+ //
+ for (ple = m_PkgPnp->m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
+ FxDeviceInterface *pDI;
+
+ pDI = FxDeviceInterface::_FromEntry(ple);
+
+ if (FxIsEqualGuid(&pDI->m_InterfaceClassGUID, InterfaceClassGUID)) {
+ if (RefString != NULL) {
+ if ((RefString->Length == pDI->m_ReferenceString.Length)
+ &&
+ (RtlCompareMemory(RefString->Buffer,
+ pDI->m_ReferenceString.Buffer,
+ RefString->Length) == RefString->Length)) {
+ //
+ // They match, carry on
+ //
+ DO_NOTHING();
+ }
+ else {
+ //
+ // The ref strings do not match, continue on in the search
+ // of the collection.
+ //
+ continue;
+ }
+ }
+ else if (pDI->m_ReferenceString.Length > 0) {
+ //
+ // Caller didn't specify a ref string but this interface has
+ // one, continue on in the search through the collection.
+ //
+ continue;
+ }
+
+ //
+ // Set the state and break out of the loop because we found our
+ // interface.
+ //
+ found = TRUE;
+ break;
+ }
+ }
+
+ m_PkgPnp->m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
+
+ return found;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::_AllocAndQueryProperty(
+ _In_ PFX_DRIVER_GLOBALS Globals,
+ _In_opt_ PWDFDEVICE_INIT DeviceInit,
+ _In_opt_ FxDevice* Device,
+ _In_opt_ MdDeviceObject RemotePdo,
+ _In_ DEVICE_REGISTRY_PROPERTY DeviceProperty,
+ _In_ POOL_TYPE PoolType,
+ _In_opt_ PWDF_OBJECT_ATTRIBUTES PropertyMemoryAttributes,
+ _Out_ WDFMEMORY* PropertyMemory
+ )
+{
+ FxMemoryObject* pMemory;
+ NTSTATUS status;
+ ULONG length = 0;
+
+ status = FxDevice::_QueryProperty(Globals,
+ DeviceInit,
+ Device,
+ RemotePdo,
+ DeviceProperty,
+ 0,
+ NULL,
+ &length);
+ if (status != STATUS_BUFFER_TOO_SMALL) {
+ DoTraceLevelMessage(Globals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
+ "Could not retrieve property %d length, %!STATUS!",
+ DeviceProperty, status);
+ _Analysis_assume_(!NT_SUCCESS(status));
+ return status;
+ }
+
+ status = FxMemoryObject::_Create(Globals,
+ PropertyMemoryAttributes,
+ PoolType,
+ Globals->Tag,
+ length,
+ &pMemory);
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(Globals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
+ "Could not allocate WDFMEMORY, %!STATUS!", status);
+ return status;
+ }
+
+ status = FxDevice::_QueryProperty(Globals,
+ DeviceInit,
+ Device,
+ RemotePdo,
+ DeviceProperty,
+ length,
+ pMemory->GetBuffer(),
+ &length);
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(Globals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
+ "Could not query for full buffer, size %d, for "
+ "property %d, %!STATUS!",
+ length, DeviceProperty, status);
+ pMemory->DeleteObject();
+ return status;
+ }
+
+ status = pMemory->Commit(PropertyMemoryAttributes,
+ (WDFOBJECT*)PropertyMemory);
+
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(Globals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
+ "Could not commit memory object, %!STATUS!",
+ status);
+ pMemory->DeleteObject();
+ }
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::_AllocAndQueryPropertyEx(
+ _In_ PFX_DRIVER_GLOBALS DriverGlobals,
+ _In_opt_ PWDFDEVICE_INIT DeviceInit,
+ _In_opt_ FxDevice* Device,
+ _In_ PVOID PropertyData,
+ _In_ FxPropertyType FxPropertyType,
+ _In_ POOL_TYPE PoolType,
+ _In_opt_ PWDF_OBJECT_ATTRIBUTES PropertyMemoryAttributes,
+ _Out_ WDFMEMORY* PropertyMemory,
+ _Out_ PDEVPROPTYPE PropertyType
+ )
+{
+ FxMemoryObject* pMemory;
+ NTSTATUS status;
+ ULONG length = 0;
+ DEVPROPTYPE propType;
+ ULONG requiredLength;
+
+ status = FxDevice::_QueryPropertyEx(DriverGlobals,
+ DeviceInit,
+ Device,
+ PropertyData,
+ FxPropertyType,
+ 0,
+ NULL,
+ &requiredLength,
+ &propType);
+ if (status != STATUS_BUFFER_TOO_SMALL) {
+ DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
+ "Could not retrieve property, %!STATUS!",
+ status);
+ _Analysis_assume_(!NT_SUCCESS(status));
+ return status;
+ }
+
+ *PropertyMemory = NULL;
+ *PropertyType = 0;
+
+ length = requiredLength;
+ status = FxMemoryObject::_Create(DriverGlobals,
+ PropertyMemoryAttributes,
+ PoolType,
+ DriverGlobals->Tag,
+ length,
+ &pMemory);
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
+ "Could not allocate WDFMEMORY, %!STATUS!", status);
+ return status;
+ }
+
+ status = FxDevice::_QueryPropertyEx(DriverGlobals,
+ DeviceInit,
+ Device,
+ PropertyData,
+ FxPropertyType,
+ length,
+ pMemory->GetBuffer(),
+ &requiredLength,
+ &propType);
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
+ "Could not query for full buffer, size %d, for "
+ "property, %!STATUS!",
+ length, status);
+ pMemory->DeleteObject();
+ return status;
+ }
+
+ status = pMemory->Commit(PropertyMemoryAttributes,
+ (WDFOBJECT*)PropertyMemory);
+
+ if (!NT_SUCCESS(status)) {
+ DoTraceLevelMessage(DriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
+ "Could not commit memory object, %!STATUS!",
+ status);
+ pMemory->DeleteObject();
+ }
+ else {
+ *PropertyMemory = pMemory->GetHandle();
+ *PropertyType = propType;
+ }
+
+ return status;
+}
+
+_Must_inspect_result_
+NTSTATUS
+FxDevice::_ValidateOpenKeyParams(
+ _In_ PFX_DRIVER_GLOBALS FxDriverGlobals,
+ _In_opt_ PWDFDEVICE_INIT DeviceInit,
+ _In_opt_ FxDevice* Device
+ )
+{
+ NTSTATUS status = STATUS_SUCCESS;
+
+ //
+ // This function should be called with exactly one valid WDFDEVICE_INIT
+ // or one valid FxDevice object. Supplying neither or both is an error.
+ //
+ if ((DeviceInit == NULL && Device == NULL) ||
+ (DeviceInit != NULL && Device != NULL)) {
+
+ status = STATUS_INVALID_PARAMETER;
+ DoTraceLevelMessage(
+ FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
+ "Device OpenKey/QueryProperty was called with invalid "
+ "DeviceInit and Device parameters, %!STATUS!", status);
+ FxVerifierDbgBreakPoint(FxDriverGlobals);
+ }
+
+ return status;
+}
+