1 /*++
2 
3 
4 Copyright (c) Microsoft. All rights reserved.
5 
6 Module Name:
7 
8     FxPkgPnp.cpp
9 
10 Abstract:
11 
12     This module implements the pnp IRP handlers for the driver framework.
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20     Both kernel and user mode
21 
22 Revision History:
23 
24 
25 
26 
27 
28 --*/
29 
30 #include "pnppriv.hpp"
31 
32 #include <initguid.h>
33 #include <wdmguid.h>
34 
35 extern "C" {
36 
37 #if defined(EVENT_TRACING)
38 #include "FxPkgPnp.tmh"
39 #endif
40 
41 }
42 
43 /* dc7a8e51-49b3-4a3a-9e81-625205e7d729 */
44 const GUID FxPkgPnp::GUID_POWER_THREAD_INTERFACE = {
45     0xdc7a8e51, 0x49b3, 0x4a3a, { 0x9e, 0x81, 0x62, 0x52, 0x05, 0xe7, 0xd7, 0x29 }
46 };
47 
FxPkgPnp(__in PFX_DRIVER_GLOBALS FxDriverGlobals,__in CfxDevice * Device,__in WDFTYPE Type)48 FxPkgPnp::FxPkgPnp(
49     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
50     __in CfxDevice* Device,
51     __in WDFTYPE Type
52     ) :
53     FxPackage(FxDriverGlobals, Device, Type)
54 {
55     ULONG i;
56 
57     m_DmaEnablerList = NULL;
58     m_RemovalDeviceList = NULL;
59     m_UsageDependentDeviceList = NULL;
60 
61     //
62     // Initialize the structures to the default state and then override the
63     // non WDF std default values to the unsupported / off values.
64     //
65     m_PnpStateAndCaps.Value =
66         FxPnpStateDisabledUseDefault         |
67         FxPnpStateDontDisplayInUIUseDefault  |
68         FxPnpStateFailedUseDefault           |
69         FxPnpStateNotDisableableUseDefault   |
70         FxPnpStateRemovedUseDefault          |
71         FxPnpStateResourcesChangedUseDefault |
72 
73         FxPnpCapLockSupportedUseDefault      |
74         FxPnpCapEjectSupportedUseDefault     |
75         FxPnpCapRemovableUseDefault          |
76         FxPnpCapDockDeviceUseDefault         |
77         FxPnpCapUniqueIDUseDefault           |
78         FxPnpCapSilentInstallUseDefault      |
79         FxPnpCapSurpriseRemovalOKUseDefault  |
80         FxPnpCapHardwareDisabledUseDefault   |
81         FxPnpCapNoDisplayInUIUseDefault
82         ;
83 
84     m_PnpCapsAddress = (ULONG) -1;
85     m_PnpCapsUINumber = (ULONG) -1;
86 
87     RtlZeroMemory(&m_PowerCaps, sizeof(m_PowerCaps));
88     m_PowerCaps.Caps =
89         FxPowerCapDeviceD1UseDefault   |
90         FxPowerCapDeviceD2UseDefault   |
91         FxPowerCapWakeFromD0UseDefault |
92         FxPowerCapWakeFromD1UseDefault |
93         FxPowerCapWakeFromD2UseDefault |
94         FxPowerCapWakeFromD3UseDefault
95         ;
96 
97     m_PowerCaps.DeviceWake = PowerDeviceMaximum;
98     m_PowerCaps.SystemWake = PowerSystemMaximum;
99 
100     m_PowerCaps.D1Latency = (ULONG) -1;
101     m_PowerCaps.D2Latency = (ULONG) -1;
102     m_PowerCaps.D3Latency = (ULONG) -1;
103 
104     m_PowerCaps.States = 0;
105     for (i = 0; i < PowerSystemMaximum; i++) {
106         _SetPowerCapState(i, PowerDeviceMaximum, &m_PowerCaps.States);
107     }
108 
109     RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface));
110     RtlZeroMemory(&m_SpecialSupport[0], sizeof(m_SpecialSupport));
111     RtlZeroMemory(&m_SpecialFileCount[0], sizeof(m_SpecialFileCount));
112 
113     m_PowerThreadInterface.Interface.Size = sizeof(m_PowerThreadInterface);
114     m_PowerThreadInterface.Interface.Version = 1;
115     m_PowerThreadInterface.Interface.Context = this;
116     m_PowerThreadInterface.Interface.InterfaceReference = &FxPkgPnp::_PowerThreadInterfaceReference;
117     m_PowerThreadInterface.Interface.InterfaceDereference = &FxPkgPnp::_PowerThreadInterfaceDereference;
118     m_PowerThreadInterface.PowerThreadEnqueue = &FxPkgPnp::_PowerThreadEnqueue;
119     m_PowerThread = NULL;
120     m_HasPowerThread = FALSE;
121     m_PowerThreadInterfaceReferenceCount = 1;
122     m_PowerThreadEvent = NULL;
123 
124     m_DeviceStopCount = 0;
125     m_CapsQueried = FALSE;
126     m_InternalFailure = FALSE;
127     m_FailedAction = WdfDeviceFailedUndefined;
128 
129     //
130     // We only set the pending child count to 1 once we know we have successfully
131     // created an FxDevice and initialized it fully.  If we delete an FxDevice
132     // which is half baked, it cannot have any FxChildLists which have any
133     // pending children on them.
134     //
135     m_PendingChildCount = 0;
136 
137     m_QueryInterfaceHead.Next = NULL;
138 
139     m_DeviceInterfaceHead.Next = NULL;
140     m_DeviceInterfacesCanBeEnabled = FALSE;
141 
142     m_Failed = FALSE;
143     m_SetDeviceRemoveProcessed = FALSE;
144 
145     m_SystemPowerState    = PowerSystemWorking;
146     m_DevicePowerState    = WdfPowerDeviceD3Final;
147     m_DevicePowerStateOld = WdfPowerDeviceD3Final;
148 
149     m_PendingPnPIrp = NULL;
150     m_PendingSystemPowerIrp = NULL;
151     m_PendingDevicePowerIrp = NULL;
152     m_SystemPowerAction = (UCHAR) PowerActionNone;
153 
154     m_PnpStateCallbacks = NULL;
155     m_PowerStateCallbacks = NULL;
156     m_PowerPolicyStateCallbacks = NULL;
157 
158     m_SelfManagedIoMachine = NULL;
159 
160     m_EnumInfo = NULL;
161 
162     m_Resources = NULL;
163     m_ResourcesRaw = NULL;
164 
165     InitializeListHead(&m_InterruptListHead);
166     m_InterruptObjectCount = 0;
167     m_WakeInterruptCount = 0;
168     m_WakeInterruptPendingAckCount = 0;
169     m_SystemWokenByWakeInterrupt = FALSE;
170     m_WakeInterruptsKeepConnected = FALSE;
171     m_AchievedStart = FALSE;
172 
173     m_SharedPower.m_WaitWakeIrp = NULL;
174     m_SharedPower.m_WaitWakeOwner = FALSE;
175     m_SharedPower.m_ExtendWatchDogTimer = FALSE;
176 
177     m_DeviceRemoveProcessed = NULL;
178 
179 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
180     //
181     // Interrupt APIs for Vista and forward
182     //
183     m_IoConnectInterruptEx      = FxLibraryGlobals.IoConnectInterruptEx;
184     m_IoDisconnectInterruptEx   = FxLibraryGlobals.IoDisconnectInterruptEx;
185 
186     //
187     // Interrupt APIs for Windows 8 and forward
188     //
189     m_IoReportInterruptActive   = FxLibraryGlobals.IoReportInterruptActive;
190     m_IoReportInterruptInactive = FxLibraryGlobals.IoReportInterruptInactive;
191 
192 #endif
193 
194     m_ReleaseHardwareAfterDescendantsOnFailure = FALSE;
195 
196     MarkDisposeOverride(ObjectDoNotLock);
197 }
198 
~FxPkgPnp()199 FxPkgPnp::~FxPkgPnp()
200 {
201     PSINGLE_LIST_ENTRY ple;
202 
203     Mx::MxAssert(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
204 
205     //
206     // We should either have zero pending children or we never made it out of
207     // the init state during a failed WDFDEVICE create or failed EvtDriverDeviceAdd
208     //
209     Mx::MxAssert(m_PendingChildCount == 0 ||
210            m_Device->GetDevicePnpState() == WdfDevStatePnpInit);
211 
212 
213     ple = m_DeviceInterfaceHead.Next;
214     while (ple != NULL) {
215         FxDeviceInterface* pDI;
216 
217         pDI = FxDeviceInterface::_FromEntry(ple);
218 
219         //
220         // Advance to the next before deleting the current
221         //
222         ple = ple->Next;
223 
224         //
225         // No longer in the list
226         //
227         pDI->m_Entry.Next = NULL;
228 
229         delete pDI;
230     }
231     m_DeviceInterfaceHead.Next = NULL;
232 
233     if (m_DmaEnablerList != NULL) {
234         delete m_DmaEnablerList;
235         m_DmaEnablerList = NULL;
236     }
237 
238     if (m_RemovalDeviceList != NULL) {
239         delete m_RemovalDeviceList;
240         m_RemovalDeviceList = NULL;
241     }
242 
243     if (m_UsageDependentDeviceList != NULL) {
244         delete m_UsageDependentDeviceList;
245         m_UsageDependentDeviceList = NULL;
246     }
247 
248     if (m_PnpStateCallbacks != NULL) {
249         delete m_PnpStateCallbacks;
250     }
251 
252     if (m_PowerStateCallbacks != NULL) {
253         delete m_PowerStateCallbacks;
254     }
255 
256     if (m_PowerPolicyStateCallbacks != NULL) {
257         delete m_PowerPolicyStateCallbacks;
258     }
259 
260     if (m_SelfManagedIoMachine != NULL) {
261         delete m_SelfManagedIoMachine;
262         m_SelfManagedIoMachine = NULL;
263     }
264 
265     if (m_EnumInfo != NULL) {
266         delete m_EnumInfo;
267         m_EnumInfo = NULL;
268     }
269 
270     if (m_Resources != NULL) {
271         m_Resources->RELEASE(this);
272         m_Resources = NULL;
273     }
274 
275     if (m_ResourcesRaw != NULL) {
276         m_ResourcesRaw->RELEASE(this);
277         m_ResourcesRaw = NULL;
278     }
279 
280     ASSERT(IsListEmpty(&m_InterruptListHead));
281 }
282 
283 BOOLEAN
Dispose(VOID)284 FxPkgPnp::Dispose(
285     VOID
286     )
287 {
288     PSINGLE_LIST_ENTRY ple;
289 
290     //
291     // All of the interrupts were freed during this object's dispose path.  This
292     // is because all of the interrupts were attached as children to this object.
293     //
294     // It is safe to just reinitialize the list head.
295     //
296     InitializeListHead(&m_InterruptListHead);
297 
298     m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
299 
300     //
301     // A derived class can insert an embedded FxQueryInterface into the QI list,
302     // so clear out the list before the destructor chain runs.  (The derived
303     // class will be destructed first, as such, the embedded FxQueryInterface
304     // will also destruct first and complain it is still in a list.
305     //
306     ple = m_QueryInterfaceHead.Next;
307 
308     //
309     // Clear out the head while holding the lock so that we synchronize against
310     // processing a QI while deleting the list.
311     //
312     m_QueryInterfaceHead.Next = NULL;
313 
314     m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
315 
316     while (ple != NULL) {
317         FxQueryInterface* pQI;
318 
319         pQI = FxQueryInterface::_FromEntry(ple);
320 
321         //
322         // Advance before we potentiall free the structure
323         //
324         ple = ple->Next;
325 
326         //
327         // FxQueryInterface's destructor requires Next be NULL
328         //
329         pQI->m_Entry.Next = NULL;
330 
331         if (pQI->m_EmbeddedInterface == FALSE) {
332             delete pQI;
333         }
334     }
335 
336 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
337     DropD3ColdInterface();
338 #endif
339 
340     //
341     // Call up the hierarchy
342     //
343     return FxPackage::Dispose(); // __super call
344 }
345 
346 
347 _Must_inspect_result_
348 NTSTATUS
Initialize(__in PWDFDEVICE_INIT DeviceInit)349 FxPkgPnp::Initialize(
350     __in PWDFDEVICE_INIT DeviceInit
351     )
352 
353 /*++
354 
355 Routine Description:
356 
357     This function initializes state associated with an instance of FxPkgPnp.
358     This differs from the constructor because it can do operations which
359     will fail, and can return failure.  (Constructors can't fail, they can
360     only throw exceptions, which we can't deal with in this kernel mode
361     environment.)
362 
363 Arguments:
364 
365     DeviceInit - Struct that the driver initialized that contains defaults.
366 
367 Returns:
368 
369     NTSTATUS
370 
371 --*/
372 
373 {
374     PFX_DRIVER_GLOBALS pFxDriverGlobals;
375     NTSTATUS status;
376 
377     pFxDriverGlobals = GetDriverGlobals();
378 
379     m_ReleaseHardwareAfterDescendantsOnFailure = (DeviceInit->ReleaseHardwareOrderOnFailure ==
380                 WdfReleaseHardwareOrderOnFailureAfterDescendants);
381 
382     status = m_QueryInterfaceLock.Initialize();
383     if (!NT_SUCCESS(status)) {
384         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
385                             TRACINGPNP,
386                             "Could not initialize QueryInterfaceLock for "
387                             "WDFDEVICE %p, %!STATUS!",
388                             m_Device->GetHandle(), status);
389         return status;
390     }
391 
392     status = m_DeviceInterfaceLock.Initialize();
393     if (!NT_SUCCESS(status)) {
394         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
395                             TRACINGPNP,
396                             "Could not initialize DeviceInterfaceLock for "
397                             "WDFDEVICE %p, %!STATUS!",
398                             m_Device->GetHandle(), status);
399         return status;
400     }
401 
402     //
403     // Initialize preallocated events for UM
404     // (For KM, events allocated on stack are used since event initialization
405     // doesn't fail in KM)
406     //
407 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
408 
409     status = m_CleanupEventUm.Initialize();
410     if (!NT_SUCCESS(status)) {
411         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
412                             TRACINGPNP,
413                             "Could not initialize cleanup event for "
414                             "WDFDEVICE %p, %!STATUS!",
415                             m_Device->GetHandle(), status);
416         return status;
417     }
418 
419     status = m_RemoveEventUm.Initialize(SynchronizationEvent, FALSE);
420     if (!NT_SUCCESS(status)) {
421         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
422                             TRACINGPNP,
423                             "Could not initialize remove event for "
424                             "WDFDEVICE %p, %!STATUS!",
425                             m_Device->GetHandle(), status);
426         return status;
427     }
428 #endif
429 
430     if (DeviceInit->IsPwrPolOwner()) {
431         m_PowerPolicyMachine.m_Owner = new (pFxDriverGlobals)
432             FxPowerPolicyOwnerSettings(this);
433 
434         if (m_PowerPolicyMachine.m_Owner == NULL) {
435             return STATUS_INSUFFICIENT_RESOURCES;
436         }
437 
438         status = m_PowerPolicyMachine.m_Owner->Init();
439         if (!NT_SUCCESS(status)) {
440             return status;
441         }
442 
443 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
444         QueryForD3ColdInterface();
445 #endif
446     }
447 
448     //
449     // we will change the access flags on the object later on when we build up
450     // the list from the wdm resources
451     //
452     status = FxCmResList::_CreateAndInit(&m_Resources,
453                                          pFxDriverGlobals,
454                                          m_Device,
455                                          WDF_NO_OBJECT_ATTRIBUTES,
456                                          FxResourceNoAccess);
457     if (!NT_SUCCESS(status)) {
458         return status;
459     }
460 
461     status = m_Resources->Commit(WDF_NO_OBJECT_ATTRIBUTES,
462                                  WDF_NO_HANDLE,
463                                  m_Device);
464 
465     //
466     // This should never fail
467     //
468     ASSERT(NT_SUCCESS(status));
469     if (!NT_SUCCESS(status)) {
470         m_Resources->DeleteFromFailedCreate();
471         m_Resources = NULL;
472         return status;
473     }
474 
475     m_Resources->ADDREF(this);
476 
477     //
478     // we will change the access flags on the object later on when we build up
479     // the list from the wdm resources
480     //
481     status = FxCmResList::_CreateAndInit(&m_ResourcesRaw,
482                                          pFxDriverGlobals,
483                                          m_Device,
484                                          WDF_NO_OBJECT_ATTRIBUTES,
485                                          FxResourceNoAccess);
486     if (!NT_SUCCESS(status)) {
487         return status;
488     }
489 
490     status = m_ResourcesRaw->Commit(WDF_NO_OBJECT_ATTRIBUTES,
491                                     WDF_NO_HANDLE,
492                                     m_Device);
493 
494     //
495     // This should never fail
496     //
497     ASSERT(NT_SUCCESS(status));
498     if (!NT_SUCCESS(status)) {
499         m_ResourcesRaw->DeleteFromFailedCreate();
500         m_ResourcesRaw = NULL;
501         return status;
502     }
503 
504     m_ResourcesRaw->ADDREF(this);
505 
506     status = RegisterCallbacks(&DeviceInit->PnpPower.PnpPowerEventCallbacks);
507     if (!NT_SUCCESS(status)) {
508         return status;
509     }
510 
511     if (IsPowerPolicyOwner()) {
512         RegisterPowerPolicyCallbacks(&DeviceInit->PnpPower.PolicyEventCallbacks);
513     }
514 
515     return status;
516 }
517 
518 _Must_inspect_result_
519 NTSTATUS
Dispatch(__in MdIrp Irp)520 FxPkgPnp::Dispatch(
521     __in MdIrp Irp
522     )
523 
524 /*++
525 
526 Routine Description:
527 
528     This is the main dispatch handler for the pnp package.  This method is
529     called by the framework manager when a PNP or Power IRP enters the driver.
530     This function will dispatch the IRP to a function designed to handle the
531     specific IRP.
532 
533 Arguments:
534 
535     Device - a pointer to the FxDevice
536 
537     Irp - a pointer to the FxIrp
538 
539 Returns:
540 
541     NTSTATUS
542 
543 --*/
544 
545 {
546     NTSTATUS status;
547     FxIrp irp(Irp);
548 
549 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
550     FX_TRACK_DRIVER(GetDriverGlobals());
551 #endif
552 
553     if (irp.GetMajorFunction() == IRP_MJ_PNP) {
554 
555         switch (irp.GetMinorFunction()) {
556         case IRP_MN_START_DEVICE:
557         case IRP_MN_QUERY_REMOVE_DEVICE:
558         case IRP_MN_REMOVE_DEVICE:
559         case IRP_MN_CANCEL_REMOVE_DEVICE:
560         case IRP_MN_STOP_DEVICE:
561         case IRP_MN_QUERY_STOP_DEVICE:
562         case IRP_MN_CANCEL_STOP_DEVICE:
563         case IRP_MN_SURPRISE_REMOVAL:
564         case IRP_MN_EJECT:
565         case IRP_MN_QUERY_PNP_DEVICE_STATE:
566             DoTraceLevelMessage(
567                 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
568                 "WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! IRP 0x%p",
569                 m_Device->GetHandle(),
570                 m_Device->GetDeviceObject(),
571                 irp.GetMinorFunction(), irp.GetIrp());
572             break;
573 
574         case IRP_MN_QUERY_DEVICE_RELATIONS:
575             DoTraceLevelMessage(
576                 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
577                 "WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! "
578                 "type %!DEVICE_RELATION_TYPE! IRP 0x%p",
579                 m_Device->GetHandle(),
580                 m_Device->GetDeviceObject(),
581                 irp.GetMinorFunction(),
582                 irp.GetParameterQDRType(), irp.GetIrp());
583             break;
584 
585         default:
586             DoTraceLevelMessage(
587                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
588                 "WDFDEVICE 0x%p !devobj 0x%p, IRP_MJ_PNP, %!pnpmn! IRP 0x%p",
589                 m_Device->GetHandle(),
590                 m_Device->GetDeviceObject(),
591                 irp.GetMinorFunction(), irp.GetIrp());
592             break;
593         }
594 
595         if (irp.GetMinorFunction() <= IRP_MN_SURPRISE_REMOVAL) {
596             status = (*GetDispatchPnp()[irp.GetMinorFunction()])(this, &irp);
597         }
598         else {
599             //
600             // For Pnp IRPs we don't understand, just forget about them
601             //
602             status = FireAndForgetIrp(&irp);
603         }
604     }
605     else {
606         //
607         // If this isn't a PnP Irp, it must be a power irp.
608         //
609         switch (irp.GetMinorFunction()) {
610         case IRP_MN_WAIT_WAKE:
611         case IRP_MN_SET_POWER:
612             if (irp.GetParameterPowerType() == SystemPowerState) {
613                 DoTraceLevelMessage(
614                     GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
615                     "WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! "
616                     "IRP 0x%p for %!SYSTEM_POWER_STATE! (S%d)",
617                     m_Device->GetHandle(),
618                     m_Device->GetDeviceObject(),
619                     irp.GetMinorFunction(), irp.GetIrp(),
620                     irp.GetParameterPowerStateSystemState(),
621                     irp.GetParameterPowerStateSystemState() - 1);
622             }
623             else {
624                 DoTraceLevelMessage(
625                     GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
626                     "WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! "
627                     "IRP 0x%p for %!DEVICE_POWER_STATE!",
628                     m_Device->GetHandle(),
629                     m_Device->GetDeviceObject(),
630                     irp.GetMinorFunction(), irp.GetIrp(),
631                     irp.GetParameterPowerStateDeviceState());
632             }
633             break;
634 
635         default:
636             DoTraceLevelMessage(
637                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
638                 "WDFDEVICE 0x%p !devobj 0x%p IRP_MJ_POWER, %!pwrmn! IRP 0x%p",
639                 m_Device->GetHandle(),
640                 m_Device->GetDeviceObject(),
641                 irp.GetMinorFunction(), irp.GetIrp());
642             break;
643         }
644 
645         Mx::MxAssert(irp.GetMajorFunction() == IRP_MJ_POWER);
646 
647         if (irp.GetMinorFunction() <= IRP_MN_QUERY_POWER) {
648             status = (*GetDispatchPower()[irp.GetMinorFunction()])(this, &irp);
649         }
650         else {
651             //
652             // For Power IRPs we don't understand, just forget about them
653             //
654             status = FireAndForgetIrp(&irp);
655         }
656     }
657 
658     return status;
659 }
660 
661 PNP_DEVICE_STATE
HandleQueryPnpDeviceState(__in PNP_DEVICE_STATE PnpDeviceState)662 FxPkgPnp::HandleQueryPnpDeviceState(
663     __in PNP_DEVICE_STATE PnpDeviceState
664     )
665 
666 /*++
667 
668 Routine Description:
669 
670     This function handled IRP_MN_QUERY_DEVICE_STATE.  Most of the bits are
671     just copied from internal Framework state.
672 
673 Arguments:
674 
675     PnpDeviceState - Bitfield that will be returned to the sender of the IRP.
676 
677 Returns:
678 
679     NTSTATUS
680 
681 --*/
682 
683 {
684     LONG state;
685 
686     state = GetPnpStateInternal();
687 
688     //
689     // Return device state set by driver.
690     //
691     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
692                              PNP_DEVICE_DISABLED,
693                              state,
694                              Disabled);
695     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
696                              PNP_DEVICE_DONT_DISPLAY_IN_UI,
697                              state,
698                              DontDisplayInUI);
699     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
700                              PNP_DEVICE_FAILED,
701                              state,
702                              Failed);
703     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
704                              PNP_DEVICE_NOT_DISABLEABLE,
705                              state,
706                              NotDisableable);
707     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
708                              PNP_DEVICE_REMOVED,
709                              state,
710                              Removed);
711     SET_PNP_DEVICE_STATE_BIT(&PnpDeviceState,
712                              PNP_DEVICE_RESOURCE_REQUIREMENTS_CHANGED,
713                              state,
714                              ResourcesChanged);
715 
716     if ((state & FxPnpStateDontDisplayInUIMask) == FxPnpStateDontDisplayInUIUseDefault) {
717         LONG caps;
718 
719         //
720         // Mask off all caps except for NoDispalyInUI
721         //
722         caps = GetPnpCapsInternal() & FxPnpCapNoDisplayInUIMask;
723 
724         //
725         // If the driver didn't specify pnp state, see if they specified no
726         // display as a capability.  For raw PDOs and just usability, it is not
727         // always clear to the driver writer that they must set both the pnp cap
728         // and the pnp state for the no display in UI property to stick after
729         // the device has been started.
730         //
731         if (caps == FxPnpCapNoDisplayInUITrue) {
732             PnpDeviceState |= PNP_DEVICE_DONT_DISPLAY_IN_UI;
733         }
734         else if (caps == FxPnpCapNoDisplayInUIFalse) {
735             PnpDeviceState &= ~PNP_DEVICE_DONT_DISPLAY_IN_UI;
736         }
737     }
738 
739     //
740     // Return device state maintained by frameworks.
741     //
742     if (IsInSpecialUse()) {
743         PnpDeviceState |= PNP_DEVICE_NOT_DISABLEABLE;
744     }
745 
746     //
747     // If there is an internal failure, then indicate that up to pnp.
748     //
749     if (m_InternalFailure || m_Failed) {
750         PnpDeviceState |= PNP_DEVICE_FAILED;
751     }
752 
753     return PnpDeviceState;
754 }
755 
756 _Must_inspect_result_
757 NTSTATUS
HandleQueryBusRelations(__inout FxIrp * Irp)758 FxPkgPnp::HandleQueryBusRelations(
759     __inout FxIrp* Irp
760     )
761 /*++
762 
763 Routine Description:
764     Handles a query device relations for the bus relations type (all other types
765     are handled by HandleQueryDeviceRelations).  This function will call into
766     each of the device's FxChildList objects to process the request.
767 
768 Arguments:
769     Irp - the request contain the query device relations
770 
771 Return Value:
772     NTSTATUS
773 
774   --*/
775 {
776     FxWaitLockTransactionedList* pList;
777     PDEVICE_RELATIONS pRelations;
778     FxTransactionedEntry* ple;
779     NTSTATUS status, listStatus;
780     BOOLEAN changed;
781 
782     //
783     // Before we do anything, callback into the driver
784     //
785     m_DeviceRelationsQuery.Invoke(m_Device->GetHandle(), BusRelations);
786 
787     //
788     // Default to success unless list processing fails
789     //
790     status = STATUS_SUCCESS;
791 
792     //
793     // Keep track of changes made by any list object.  If anything changes,
794     // remember it for post-processing.
795     //
796     changed = FALSE;
797 
798     pRelations = (PDEVICE_RELATIONS) Irp->GetInformation();
799 
800     if (m_EnumInfo != NULL) {
801         pList = &m_EnumInfo->m_ChildListList;
802 
803         pList->LockForEnum(GetDriverGlobals());
804     }
805     else {
806         pList = NULL;
807     }
808 
809     ple = NULL;
810     while (pList != NULL && (ple = pList->GetNextEntry(ple)) != NULL) {
811         FxChildList* pChildList;
812 
813         pChildList = FxChildList::_FromEntry(ple);
814 
815         //
816         // ProcessBusRelations will free and reallocate pRelations if necessary
817         //
818         listStatus = pChildList->ProcessBusRelations(&pRelations);
819 
820         //
821         // STATUS_NOT_SUPPORTED is a special value.  It indicates that the call
822         // to ProcessBusRelations did not modify pRelations in any way.
823         //
824         if (listStatus == STATUS_NOT_SUPPORTED) {
825             continue;
826         }
827 
828 
829         if (!NT_SUCCESS(listStatus)) {
830             DoTraceLevelMessage(
831                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
832                 "WDFDEVICE %p, WDFCHILDLIST %p returned %!STATUS! from "
833                 "processing bus relations",
834                 m_Device->GetHandle(), pChildList->GetHandle(), listStatus);
835             status = listStatus;
836             break;
837         }
838 
839         //
840         // We updated pRelations, change the status later
841         //
842         changed = TRUE;
843     }
844 
845     //
846     // By checking for NT_SUCCESS(status) below we account for
847     // both the cases - list changed, as well as list unchanged but possibly
848     // children being reported missing (that doesn't involve list change).
849     //
850     if (NT_SUCCESS(status)) {
851 
852         ple = NULL;
853         while (pList != NULL && (ple = pList->GetNextEntry(ple)) != NULL) {
854             FxChildList* pChildList;
855 
856             pChildList = FxChildList::_FromEntry(ple);
857 
858             //
859             // invoke the ReportedMissing callback for for children that are
860             // being reporte missing
861             //
862             pChildList->InvokeReportedMissingCallback();
863         }
864     }
865 
866     if (pList != NULL) {
867         pList->UnlockFromEnum(GetDriverGlobals());
868     }
869 
870     if (NT_SUCCESS(status) && changed == FALSE) {
871         //
872         // Went through the entire list of FxChildList objects, but no object
873         // modified the pRelations, so restore the caller's NTSTATUS.
874         //
875         status = Irp->GetStatus();
876     }
877 
878     //
879     // Re-set the relations into the structure so that any changes that any call
880     // to FxChildList::ProcessBusRelations takes effect and is reported.
881     //
882     Irp->SetInformation((ULONG_PTR) pRelations);
883     Irp->SetStatus(status);
884 
885     DoTraceLevelMessage(
886         GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
887         "WDFDEVICE %p, returning %!STATUS! from processing bus relations",
888         m_Device->GetHandle(), status
889         );
890 
891     if (NT_SUCCESS(status) && pRelations != NULL) {
892         ULONG i;
893 
894         DoTraceLevelMessage(
895             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
896             "WDFDEVICE %p returning %d devices in relations %p",
897             m_Device->GetHandle(), pRelations->Count, pRelations
898             );
899 
900         //
901         // Try to not consume an IFR entry per DO reported.  Instead, report up
902         // to 4 at a time.
903         //
904         for (i = 0; i < pRelations->Count && GetDriverGlobals()->FxVerboseOn; i += 4) {
905             if (i + 3 < pRelations->Count) {
906                 DoTraceLevelMessage(
907                     GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
908                     "PDO %p PDO %p PDO %p PDO %p",
909                     pRelations->Objects[i],
910                     pRelations->Objects[i+1],
911                     pRelations->Objects[i+2],
912                     pRelations->Objects[i+3]
913                     );
914             }
915             else if (i + 2 < pRelations->Count) {
916                 DoTraceLevelMessage(
917                     GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
918                     "PDO %p PDO %p PDO %p",
919                     pRelations->Objects[i],
920                     pRelations->Objects[i+1],
921                     pRelations->Objects[i+2]
922                     );
923             }
924             else if (i + 1 < pRelations->Count) {
925                 DoTraceLevelMessage(
926                     GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
927                     "PDO %p PDO %p",
928                     pRelations->Objects[i],
929                     pRelations->Objects[i+1]
930                     );
931             }
932             else {
933                 DoTraceLevelMessage(
934                     GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
935                     "PDO %p",
936                     pRelations->Objects[i]
937                     );
938             }
939         }
940     }
941 
942     return status;
943 }
944 
945 _Must_inspect_result_
946 NTSTATUS
HandleQueryBusInformation(__inout FxIrp * Irp)947 FxPkgPnp::HandleQueryBusInformation(
948     __inout FxIrp* Irp
949     )
950 {
951     NTSTATUS status;
952 
953     //
954     // Probably is a better check then this to see if the driver set the bus
955     // information
956     //
957     if (m_BusInformation.BusTypeGuid.Data1 != 0x0) {
958         PPNP_BUS_INFORMATION pBusInformation;
959         PFX_DRIVER_GLOBALS pFxDriverGlobals;
960 
961         pFxDriverGlobals = GetDriverGlobals();
962         pBusInformation = (PPNP_BUS_INFORMATION) MxMemory::MxAllocatePoolWithTag(
963                 PagedPool, sizeof(PNP_BUS_INFORMATION), pFxDriverGlobals->Tag);
964 
965         if (pBusInformation != NULL) {
966             //
967             // Initialize the PNP_BUS_INFORMATION structure with the data
968             // from PDO properties.
969             //
970             RtlCopyMemory(pBusInformation,
971                           &m_BusInformation,
972                           sizeof(PNP_BUS_INFORMATION));
973 
974             Irp->SetInformation((ULONG_PTR) pBusInformation);
975             status = STATUS_SUCCESS;
976         }
977         else {
978             Irp->SetInformation(NULL);
979             status = STATUS_INSUFFICIENT_RESOURCES;
980 
981             DoTraceLevelMessage(
982                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
983                 "WDFDEVICE %p could not allocate PNP_BUS_INFORMATION string, "
984                 " %!STATUS!", m_Device->GetHandle(), status);
985         }
986     }
987     else {
988         status = Irp->GetStatus();
989     }
990 
991     return status;
992 }
993 
994 _Must_inspect_result_
995 NTSTATUS
HandleQueryDeviceRelations(__inout FxIrp * Irp,__inout FxRelatedDeviceList * List)996 FxPkgPnp::HandleQueryDeviceRelations(
997     __inout FxIrp* Irp,
998     __inout FxRelatedDeviceList* List
999     )
1000 /*++
1001 
1002 Routine Description:
1003     Handles the query device relations request for all relation types *except*
1004     for bus relations (HandleQueryBusRelations handles that type exclusively).
1005 
1006     This function will allocate a PDEVICE_RELATIONS structure if the passed in
1007     FxRelatedDeviceList contains any devices to add to the relations list.
1008 
1009 Arguments:
1010     Irp - the request
1011 
1012     List - list containing devices to report in the relations
1013 
1014 Return Value:
1015     NTSTATUS
1016 
1017   --*/
1018 {
1019     PDEVICE_RELATIONS pPriorRelations, pNewRelations;
1020     FxRelatedDevice* entry;
1021     DEVICE_RELATION_TYPE type;
1022     ULONG count;
1023     size_t size;
1024     NTSTATUS status;
1025     BOOLEAN retry;
1026     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1027 
1028     if (List == NULL) {
1029         //
1030         // Indicate that we didn't modify the irp at all since we have no list
1031         //
1032         return STATUS_NOT_SUPPORTED;
1033     }
1034 
1035     pFxDriverGlobals = GetDriverGlobals();
1036     type = Irp->GetParameterQDRType();
1037     status = STATUS_SUCCESS;
1038 
1039     //
1040     // Notify driver that he should re-scan for device relations.
1041     //
1042     m_DeviceRelationsQuery.Invoke(m_Device->GetHandle(), type);
1043 
1044     pPriorRelations = (PDEVICE_RELATIONS) Irp->GetInformation();
1045     retry = FALSE;
1046 
1047     count = 0;
1048 
1049     List->LockForEnum(pFxDriverGlobals);
1050 
1051     //
1052     // Count how many entries there are in the list
1053     //
1054     for (entry = NULL; (entry = List->GetNextEntry(entry)) != NULL; count++) {
1055         DO_NOTHING();
1056     }
1057 
1058     //
1059     // If we have
1060     // 1)  no devices in the list AND
1061     //   a) we have nothing to report OR
1062     //   b) we have something to report and there are previous relations (which
1063     //      if left unchanged will be used to report our missing devices)
1064     //
1065     // THEN we have nothing else to do, just return
1066     //
1067     if (count == 0 &&
1068         (List->m_NeedReportMissing == 0 || pPriorRelations != NULL)) {
1069         List->UnlockFromEnum(pFxDriverGlobals);
1070         return STATUS_NOT_SUPPORTED;
1071     }
1072 
1073     if (pPriorRelations != NULL) {
1074         //
1075         // Looks like another driver in the stack has already added some
1076         // entries.  Make sure we allocate space for these additional entries.
1077         //
1078         count += pPriorRelations->Count;
1079     }
1080 
1081     //
1082     // Allocate space for the device relations structure (which includes
1083     // space for one PDEVICE_OBJECT, and then allocate enough additional
1084     // space for the extra PDEVICE_OBJECTS we need.
1085     //
1086     // (While no FxChildList objects are used in this function, this static
1087     // function from the class computes what we need.)
1088     //
1089     size = FxChildList::_ComputeRelationsSize(count);
1090 
1091     pNewRelations = (PDEVICE_RELATIONS) MxMemory::MxAllocatePoolWithTag(
1092         PagedPool, size, pFxDriverGlobals->Tag);
1093 
1094     if (pNewRelations == NULL) {
1095         //
1096         // Dereference any previously reported relations before exiting.  They
1097         // are dereferenced here because the PNP manager will see error and not
1098         // do anything while the driver which added these objects expects the
1099         // pnp manager to do the dereference.  Since this device is changing the
1100         // status, it must act like the pnp manager.
1101         //
1102         if (pPriorRelations != NULL) {
1103             ULONG i;
1104 
1105             for (i = 0; i < pPriorRelations->Count; i++) {
1106                 Mx::MxDereferenceObject(pPriorRelations->Objects[i]);
1107             }
1108         }
1109 
1110         if (List->IncrementRetries() < 3) {
1111             retry = TRUE;
1112         }
1113 
1114         status = STATUS_INSUFFICIENT_RESOURCES;
1115 
1116         DoTraceLevelMessage(
1117             pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1118             "WDFDEVICE %p could not allocate device relations for type %d string, "
1119             " %!STATUS!", m_Device->GetHandle(), type, status);
1120 
1121         goto Done;
1122     }
1123 
1124     RtlZeroMemory(pNewRelations, size);
1125 
1126     //
1127     // If there was an existing device relations structure, copy
1128     // the entries to the new structure.
1129     //
1130     if (pPriorRelations != NULL && pPriorRelations->Count > 0) {
1131         RtlCopyMemory(
1132             pNewRelations,
1133             pPriorRelations,
1134             FxChildList::_ComputeRelationsSize(pPriorRelations->Count)
1135             );
1136     }
1137 
1138     //
1139     // Walk the list and return the relations here
1140     //
1141     for (entry = NULL;
1142          (entry = List->GetNextEntry(entry)) != NULL;
1143          pNewRelations->Count++) {
1144         MdDeviceObject pdo;
1145 
1146         pdo = entry->GetDevice();
1147 
1148         if (entry->m_State == RelatedDeviceStateNeedsReportPresent) {
1149             entry->m_State = RelatedDeviceStateReportedPresent;
1150         }
1151 
1152         //
1153         // Add it to the DEVICE_RELATIONS structure.  Pnp dictates that each
1154         // PDO in the list be referenced.
1155         //
1156         pNewRelations->Objects[pNewRelations->Count] = reinterpret_cast<PDEVICE_OBJECT>(pdo);
1157         Mx::MxReferenceObject(pdo);
1158     }
1159 
1160 Done:
1161     if (NT_SUCCESS(status)) {
1162         List->ZeroRetries();
1163     }
1164 
1165     List->UnlockFromEnum(GetDriverGlobals());
1166 
1167     if (pPriorRelations != NULL) {
1168         MxMemory::MxFreePool(pPriorRelations);
1169     }
1170 
1171     if (retry) {
1172         MxDeviceObject physicalDeviceObject(
1173                                     m_Device->GetPhysicalDevice()
1174                                     );
1175 
1176         physicalDeviceObject.InvalidateDeviceRelations(type);
1177     }
1178 
1179     Irp->SetStatus(status);
1180     Irp->SetInformation((ULONG_PTR) pNewRelations);
1181 
1182     return status;
1183 }
1184 
1185 _Must_inspect_result_
1186 NTSTATUS
PostCreateDeviceInitialize(VOID)1187 FxPkgPnp::PostCreateDeviceInitialize(
1188     VOID
1189     )
1190 
1191 /*++
1192 
1193 Routine Description:
1194 
1195     This function does any initialization to this object which must be done
1196     after the underlying device object has been attached to the device stack,
1197     i.e. you can send IRPs down this stack now.
1198 
1199 Arguments:
1200 
1201     none
1202 
1203 Returns:
1204 
1205     NTSTATUS
1206 
1207 --*/
1208 
1209 {
1210     NTSTATUS status;
1211 
1212     status = m_PnpMachine.Init(this, &FxPkgPnp::_PnpProcessEventInner);
1213     if (!NT_SUCCESS(status)) {
1214         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1215                             "PnP State Machine init failed, %!STATUS!",
1216                             status);
1217         return status;
1218     }
1219 
1220     status = m_PowerMachine.Init(this, &FxPkgPnp::_PowerProcessEventInner);
1221     if (!NT_SUCCESS(status)) {
1222         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1223                             "Power State Machine init failed, %!STATUS!",
1224                             status);
1225         return status;
1226     }
1227 
1228     status = m_PowerPolicyMachine.Init(this, &FxPkgPnp::_PowerPolicyProcessEventInner);
1229     if (!NT_SUCCESS(status)) {
1230         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1231                             "Power Policy State Machine init failed, %!STATUS!",
1232                             status);
1233         return status;
1234     }
1235 
1236     return status;
1237 }
1238 
1239 VOID
FinishInitialize(__inout PWDFDEVICE_INIT DeviceInit)1240 FxPkgPnp::FinishInitialize(
1241     __inout PWDFDEVICE_INIT DeviceInit
1242     )
1243 /*++
1244 
1245 Routine Description:
1246     Finish initializing the object.  All initialization up until this point
1247     could fail.  This function cannot fail, so all it can do is assign field
1248     values and take allocations from DeviceInit.
1249 
1250 Arguments:
1251     DeviceInit - device initialization structure that the driver writer has
1252                  initialized
1253 
1254 Return Value:
1255     None
1256 
1257   --*/
1258 
1259 {
1260     //
1261     // Reassign the state callback arrays away from the init struct.
1262     // Set the init field to NULL so that it does not attempt to free the array
1263     // when it is destroyed.
1264     //
1265     m_PnpStateCallbacks = DeviceInit->PnpPower.PnpStateCallbacks;
1266     DeviceInit->PnpPower.PnpStateCallbacks = NULL;
1267 
1268     m_PowerStateCallbacks = DeviceInit->PnpPower.PowerStateCallbacks;
1269     DeviceInit->PnpPower.PowerStateCallbacks = NULL;
1270 
1271     m_PowerPolicyStateCallbacks = DeviceInit->PnpPower.PowerPolicyStateCallbacks;
1272     DeviceInit->PnpPower.PowerPolicyStateCallbacks = NULL;
1273 
1274     //
1275     // Bias the count towards one so that we can optimize the synchronous
1276     // cleanup case when the device is being destroyed.
1277     //
1278     m_PendingChildCount = 1;
1279 
1280     //
1281     // Now "Add" this device in the terms that the PnP state machine uses.  This
1282     // will be in the context of an actual AddDevice function for FDOs, and
1283     // something very much like it for PDOs.
1284     //
1285     // Important that the posting of the event is after setting of the state
1286     // callback arrays so that we can guarantee that any state transition
1287     // callback will be made.
1288     //
1289     PnpProcessEvent(PnpEventAddDevice);
1290 }
1291 
1292 VOID
ProcessDelayedDeletion(VOID)1293 FxPkgPnp::ProcessDelayedDeletion(
1294     VOID
1295     )
1296 {
1297     DoTraceLevelMessage(
1298         GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1299         "WDFDEVICE %p, !devobj %p processing delayed deletion from pnp state "
1300         "machine", m_Device->GetHandle(), m_Device->GetDeviceObject());
1301 
1302     CleanupStateMachines(FALSE);
1303     DeleteDevice();
1304 }
1305 
1306 VOID
SetSpecialFileSupport(__in WDF_SPECIAL_FILE_TYPE FileType,__in BOOLEAN Supported)1307 FxPkgPnp::SetSpecialFileSupport(
1308     __in WDF_SPECIAL_FILE_TYPE FileType,
1309     __in BOOLEAN Supported
1310     )
1311 
1312 /*++
1313 
1314 Routine Description:
1315 
1316     This function marks the device as capable of handling the paging path,
1317     hibernation or crashdumps.  Any device that is necessary for one of these
1318     three things will get notification.  It is then responsible for forwarding
1319     the notification to its parent. The Framework handles that.  This
1320     function just allows a driver to tell the Framework how to respond.
1321 
1322 
1323 Arguments:
1324 
1325     FileType - identifies which of the special paths the device is in
1326     Supported - Yes or No
1327 
1328 Returns:
1329 
1330     void
1331 
1332 --*/
1333 
1334 {
1335     switch (FileType) {
1336     case WdfSpecialFilePaging:
1337     case WdfSpecialFileHibernation:
1338     case WdfSpecialFileDump:
1339     case WdfSpecialFileBoot:
1340         SetUsageSupport(_SpecialTypeToUsage(FileType), Supported);
1341         break;
1342 
1343     default:
1344         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1345                             "Invalid special file type %x", FileType);
1346     }
1347 }
1348 
1349 _Must_inspect_result_
1350 NTSTATUS
PnpPassThroughQI(__in CfxDevice * Device,__inout FxIrp * Irp)1351 PnpPassThroughQI(
1352     __in    CfxDevice* Device,
1353     __inout FxIrp* Irp
1354     )
1355 
1356 /*++
1357 
1358 Routine Description:
1359 
1360     This driver may be sent IRP_MN_QUERY_INTERFACE, which is a way of getting
1361     a direct-call table from some driver in a device stack.  In some cases, the
1362     right response is to turn around and send a similar query to the device's
1363     parent.  This function does that.
1364 
1365 
1366 Arguments:
1367 
1368     Device - This WDFDEVICE.
1369     Irp - The IRP that was sent to us.
1370 
1371 Returns:
1372 
1373     NTSTATUS
1374 
1375 --*/
1376 
1377 {
1378     MdIrp pFwdIrp;
1379     NTSTATUS status;
1380     NTSTATUS prevStatus;
1381     MxDeviceObject pTopOfStack;
1382 
1383     prevStatus = Irp->GetStatus();
1384 
1385 
1386 
1387 
1388 
1389 
1390 
1391 
1392 
1393 
1394 
1395 
1396     pTopOfStack.SetObject(Device->m_ParentDevice->GetAttachedDeviceReference());
1397 
1398     pFwdIrp = FxIrp::AllocateIrp(pTopOfStack.GetStackSize());
1399 
1400     if (pFwdIrp != NULL) {
1401         FxAutoIrp fxFwdIrp(pFwdIrp);
1402 
1403         //
1404         // The worker routine copies stack parameters to forward-Irp, sends it
1405         // down the stack synchronously, then copies back the stack parameters
1406         // from forward-irp to original-irp
1407         //
1408         PnpPassThroughQIWorker(&pTopOfStack, Irp, &fxFwdIrp);
1409 
1410         if (fxFwdIrp.GetStatus() != STATUS_NOT_SUPPORTED) {
1411             status = fxFwdIrp.GetStatus();
1412         }
1413         else {
1414             status = prevStatus;
1415         }
1416 
1417         Irp->SetStatus(status);
1418         Irp->SetInformation(fxFwdIrp.GetInformation());
1419     }
1420     else {
1421         status = STATUS_INSUFFICIENT_RESOURCES;
1422 
1423         DoTraceLevelMessage(
1424             Device->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1425             "WDFDEVICE %p could not allocate IRP to send QI to parent !devobj "
1426             "%p, %!STATUS!", Device->GetHandle(), pTopOfStack.GetObject(),
1427             status);
1428     }
1429 
1430     pTopOfStack.DereferenceObject();
1431 
1432     return status;
1433 }
1434 
1435 _Must_inspect_result_
1436 NTSTATUS
HandleQueryInterfaceForPowerThread(__inout FxIrp * Irp,__out PBOOLEAN CompleteRequest)1437 FxPkgPnp::HandleQueryInterfaceForPowerThread(
1438     __inout FxIrp* Irp,
1439     __out   PBOOLEAN CompleteRequest
1440     )
1441 {
1442     NTSTATUS status;
1443 
1444     *CompleteRequest = TRUE;
1445 
1446     //
1447     // Send the request down the stack first.  If someone lower handles it
1448     // or failed trying to, just return their status
1449     //
1450     status = SendIrpSynchronously(Irp);
1451 
1452     if (NT_SUCCESS(status) || status != STATUS_NOT_SUPPORTED) {
1453         //
1454         // Success or failure trying to handle it
1455         //
1456         return status;
1457     }
1458 
1459     //
1460     // The semantic of this QI is that it sent down while processing start or
1461     // a device usage notification on the way *up* the stack.  That means that
1462     // by the time the QI gets to the lower part of the stack, the power thread
1463     // will have already been allocated and exported.
1464     //
1465     ASSERT(HasPowerThread());
1466 
1467     if (Irp->GetParameterQueryInterfaceVersion() == 1 &&
1468         Irp->GetParameterQueryInterfaceSize() >=
1469                                         m_PowerThreadInterface.Interface.Size) {
1470         //
1471         // Expose the interface to the requesting driver.
1472         //
1473         CopyQueryInterfaceToIrpStack(&m_PowerThreadInterface, Irp);
1474 
1475         status = STATUS_SUCCESS;
1476 
1477         //
1478         // Caller assumes a reference has been taken.
1479         //
1480         m_PowerThreadInterface.Interface.InterfaceReference(
1481             m_PowerThreadInterface.Interface.Context
1482             );
1483     }
1484     else {
1485         status = STATUS_INVALID_BUFFER_SIZE;
1486     }
1487 
1488     return status;
1489 }
1490 
1491 _Must_inspect_result_
1492 NTSTATUS
HandleQueryInterface(__inout FxIrp * Irp,__out PBOOLEAN CompleteRequest)1493 FxPkgPnp::HandleQueryInterface(
1494     __inout FxIrp* Irp,
1495     __out   PBOOLEAN CompleteRequest
1496     )
1497 
1498 /*++
1499 
1500 Routine Description:
1501 
1502     This driver may be sent IRP_MN_QUERY_INTERFACE, which is a way of getting
1503     a direct-call table from some driver in a device stack.  This function
1504     looks into a list of interfaces that the driver has registered and, if
1505     the interface that is being sought is present, it answers the IRP.
1506 
1507 Arguments:
1508 
1509     Irp - The IRP that was sent to us.
1510     CompleteRequest - tells the caller whether the IRP should be completed
1511 
1512 Returns:
1513 
1514     NTSTATUS
1515 
1516 --*/
1517 
1518 {
1519     FxDeviceProcessQueryInterfaceRequest callback;
1520     const GUID* pInterfaceType;
1521     PSINGLE_LIST_ENTRY ple;
1522     FxQueryInterface *pQI;
1523     PVOID pFound;
1524     PINTERFACE pExposedInterface;
1525     PVOID pExposedInterfaceSpecificData;
1526     BOOLEAN sendToParent;
1527 
1528     NTSTATUS status;
1529 
1530     *CompleteRequest = FALSE;
1531 
1532     pFound = NULL;
1533     pQI = NULL;
1534     pExposedInterface = NULL;
1535     pExposedInterfaceSpecificData = NULL;
1536     sendToParent = FALSE;
1537 
1538     pInterfaceType = Irp->GetParameterQueryInterfaceType();
1539     //
1540     // The power thread is special cased because it has a different semantic
1541     // then the usual QI irp that we expose to the driver writer.  In this case,
1542     // we want to fill in the interface if the lower stack does not support it.
1543     //
1544     if (FxIsEqualGuid(pInterfaceType, &GUID_POWER_THREAD_INTERFACE)) {
1545         return HandleQueryInterfaceForPowerThread(Irp, CompleteRequest);
1546     }
1547     else if (FxIsEqualGuid(pInterfaceType, &GUID_REENUMERATE_SELF_INTERFACE_STANDARD)) {
1548         if (m_Device->IsPdo()) {
1549             return ((FxPkgPdo*) this)->HandleQueryInterfaceForReenumerate(
1550                 Irp, CompleteRequest);
1551         }
1552     }
1553 
1554     status = Irp->GetStatus();
1555 
1556     //
1557     // Walk the interface collection and return the appropriate interface.
1558     //
1559     m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
1560 
1561     for (ple = m_QueryInterfaceHead.Next; ple != NULL; ple = ple->Next) {
1562         pQI = FxQueryInterface::_FromEntry(ple);
1563 
1564         if (FxIsEqualGuid(Irp->GetParameterQueryInterfaceType(),
1565                           &pQI->m_InterfaceType)) {
1566 
1567             pExposedInterface = Irp->GetParameterQueryInterfaceInterface();
1568             pExposedInterfaceSpecificData =
1569                 Irp->GetParameterQueryInterfaceInterfaceSpecificData();
1570 
1571             if (pQI->m_Interface != NULL) {
1572                 //
1573                 // NOTE:  If a driver has exposed the same interface GUID with
1574                 //        different sizes as a ways of versioning, then the driver
1575                 //        writer can specify the minimum size and version number
1576                 //        and then fill in the remaining fields in the callback
1577                 //        below.
1578                 //
1579                 if (pQI->m_Interface->Size <= Irp->GetParameterQueryInterfaceSize() &&
1580                     pQI->m_Interface->Version <= Irp->GetParameterQueryInterfaceVersion()) {
1581 
1582                     if (pQI->m_ImportInterface == FALSE) {
1583                         //
1584                         // Expose the interface to the requesting driver.
1585                         //
1586                         RtlCopyMemory(pExposedInterface,
1587                                       pQI->m_Interface,
1588                                       pQI->m_Interface->Size);
1589                     }
1590                     else {
1591                         //
1592                         // The interface contains data which the driver wants
1593                         // before copying over its information, so don't do a
1594                         // copy and let the event callback do the copy
1595                         //
1596                         DO_NOTHING();
1597                     }
1598                 }
1599                 else {
1600                     status = STATUS_INVALID_BUFFER_SIZE;
1601                     break;
1602                 }
1603             }
1604 
1605             callback.m_Method = pQI->m_ProcessRequest.m_Method;
1606             sendToParent = pQI->m_SendQueryToParentStack;
1607             pFound = pQI;
1608 
1609             status = STATUS_SUCCESS;
1610             break;
1611         }
1612     }
1613 
1614     m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
1615 
1616     if (!NT_SUCCESS(status) || pFound == NULL) {
1617         goto Done;
1618     }
1619 
1620     //
1621     // Let the driver see the interface before it is handed out.
1622     //
1623     status = callback.Invoke(m_Device->GetHandle(),
1624                              (LPGUID) Irp->GetParameterQueryInterfaceType(),
1625                              pExposedInterface,
1626                              pExposedInterfaceSpecificData);
1627 
1628     //
1629     // STATUS_NOT_SUPPORTED is a special cased error code which indicates that
1630     // the QI should travel down the rest of the stack.
1631     //
1632     if (!NT_SUCCESS(status) && status != STATUS_NOT_SUPPORTED) {
1633         goto Done;
1634     }
1635 
1636     //
1637     // If it is meant for the parent, send it down the parent stack
1638     //
1639     if (sendToParent) {
1640         status = PnpPassThroughQI(m_Device, Irp);
1641         goto Done;
1642     }
1643 
1644     //
1645     // Reference the interface before returning it to the requesting driver.
1646     // If this is an import interface, the event callback is free to not fill
1647     // in the InterfaceReference function pointer.
1648     //
1649     if (pExposedInterface->InterfaceReference != NULL) {
1650         pExposedInterface->InterfaceReference(pExposedInterface->Context);
1651     }
1652 
1653     //
1654     // If we are not a PDO in the stack, then send the fully formatted QI request
1655     // down the stack to allow others to filter the interface.
1656     //
1657     if (m_Device->IsPdo() == FALSE) {
1658         ASSERT(NT_SUCCESS(status) || status == STATUS_NOT_SUPPORTED);
1659 
1660         Irp->SetStatus(status);
1661         Irp->CopyCurrentIrpStackLocationToNext();
1662         status = Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
1663     }
1664 
1665 Done:
1666     if (pFound != NULL) {
1667         *CompleteRequest = TRUE;
1668     }
1669 
1670     return status;
1671 }
1672 
1673 _Must_inspect_result_
1674 NTSTATUS
QueryForCapabilities(VOID)1675 FxPkgPnp::QueryForCapabilities(
1676     VOID
1677     )
1678 {
1679     STACK_DEVICE_CAPABILITIES caps;
1680     NTSTATUS status;
1681 
1682     MxDeviceObject deviceObject;
1683 
1684     deviceObject.SetObject(m_Device->GetDeviceObject());
1685 
1686     status = GetStackCapabilities(GetDriverGlobals(),
1687                                   &deviceObject,
1688                                   &m_D3ColdInterface,
1689                                   &caps);
1690 
1691     if (NT_SUCCESS(status)) {
1692         ULONG states, i;
1693 
1694         ASSERT(caps.DeviceCaps.DeviceWake <= 0xFF && caps.DeviceCaps.SystemWake <= 0xFF);
1695 
1696         m_SystemWake = (BYTE) caps.DeviceCaps.SystemWake;
1697 
1698         //
1699         // Initialize the array of wakeable D-states to say that all system
1700         // states down to the one identified in the caps can generate wake.
1701         // This will be overridden below if the BIOS supplied more information.
1702         //
1703         // Compatibility Note: Non-ACPI bus drivers (root-enumerated drivers)
1704         // or other bus drivers that haven't set the power settings correctly
1705         // for their PDO may end up with a valid value for DeviceWake in the
1706         // device capabilities but a value of PowerSystemUnspecified for
1707         // SystemWake, in which case a call to WdfDeviceAssignS0IdleSettings or
1708         // WdfDeviceAssignSxWakeSettings DDIs will fail on 1.11+ resulting in
1709         // device compat issues. The failure is expected and right thing to do
1710         // but has compatibility implications - drivers that worked earlier now
1711         // fail on 1.11. Note that earlier versions of WDF did not have
1712         // m_DeviceWake as an array and stored just the capabilities->DeviceWake
1713         // value without regard to the SystemWake but the current implementation
1714         // introduces dependency on systemWake value). So for compat reasons,
1715         // for pre-1.11 compiled drivers we initilaize the array with DeviceWake
1716         // value ignoring SystemWake, removing any dependency of DeviceWake
1717         // on SystemWake value and thus preserving previous behavior for
1718         // pre-1.11 compiled drivers.
1719         //
1720         if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
1721 
1722             RtlFillMemory(m_DeviceWake, DeviceWakeStates, DeviceWakeDepthNotWakeable);
1723 
1724             for (i = PowerSystemWorking; i <= m_SystemWake; i++) {
1725 
1726                 //
1727                 // Note that this cast is hiding a conversion between two slightly
1728                 // incompatible types.  DeviceWake is in terms of DEVICE_POWER_STATE
1729                 // which is defined this way:
1730                 //
1731                 //  typedef enum _DEVICE_POWER_STATE {
1732                 //      PowerDeviceUnspecified = 0,
1733                 //      PowerDeviceD0,
1734                 //      PowerDeviceD1,
1735                 //      PowerDeviceD2,
1736                 //      PowerDeviceD3,
1737                 //      PowerDeviceMaximum
1738                 //  } DEVICE_POWER_STATE, *PDEVICE_POWER_STATE;
1739                 //
1740                 // m_DeviceWake is defined in terms of DEVICE_WAKE_DEPTH which is
1741                 // defined this way:
1742                 //
1743                 //  typedef enum _DEVICE_WAKE_DEPTH {
1744                 //      DeviceWakeDepthNotWakeable    = 0,
1745                 //      DeviceWakeDepthD0,
1746                 //      DeviceWakeDepthD1,
1747                 //      DeviceWakeDepthD2,
1748                 //      DeviceWakeDepthD3hot,
1749                 //      DeviceWakeDepthD3cold,
1750                 //      DeviceWakeDepthMaximum
1751                 //  } DEVICE_WAKE_DEPTH, *PDEVICE_WAKE_DEPTH;
1752                 //
1753                 // The result is that the conversion below will map D3 onto D3hot,
1754                 // which is a safe assumption to start with, one which may be
1755                 // overridden later.
1756                 //
1757                 C_ASSERT(PowerDeviceD0 == static_cast<DEVICE_POWER_STATE>(DeviceWakeDepthD0));
1758                 m_DeviceWake[i - PowerSystemWorking] = (BYTE) caps.DeviceCaps.DeviceWake;
1759             }
1760         }
1761         else {
1762             //
1763             // See comments above for information on mapping of device power
1764             // state to device wake depth.
1765             //
1766             RtlFillMemory(m_DeviceWake,
1767                           DeviceWakeStates,
1768                           (BYTE) caps.DeviceCaps.DeviceWake);
1769         }
1770 
1771         //
1772         // Capture the S -> D state mapping table as a ULONG for use in the
1773         // power policy owner state machine when the machine moves into Sx and
1774         // the device is not armed for wake and has set an IdealDxStateForSx
1775         // value
1776         //
1777         states = 0x0;
1778 
1779         for (i = 0; i < ARRAY_SIZE(caps.DeviceCaps.DeviceState); i++) {
1780             _SetPowerCapState(i,  caps.DeviceCaps.DeviceState[i], &states);
1781         }
1782 
1783         m_PowerPolicyMachine.m_Owner->m_SystemToDeviceStateMap = states;
1784 
1785         //
1786         // Query for the D3cold support interface.  If present, it will tell
1787         // us specifically which D-states will work for generating wake signals
1788         // from specific S-states.
1789         //
1790         // Earlier versions of WDF didn't make this query, so for compatibility,
1791         // we only make it now if the driver was built against WDF 1.11 or
1792         // later.  In truth, this just shifts the failure from initialization
1793         // time to run time, because the information that we're presumably
1794         // getting from the BIOS with this interrogation is saying that the
1795         // code in earlier verisions of WDF would have blindly enabled a device
1796         // for wake which simply wasn't capable of generating its wake signal
1797         // from the chosen D-state.  Thus the device would have been put into
1798         // a low power state and then failed to resume in response to its wake
1799         // signal.
1800         //
1801 
1802         if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
1803 
1804             //
1805             // Cycle through all the system states that this device can wake
1806             // from.  There's no need to look at deeper sleep states than
1807             // m_SystemWake because the driver will not arm for wake in
1808             // those states.
1809             //
1810             for (i = PowerSystemWorking; i <= m_SystemWake; i++) {
1811                 if (caps.DeepestWakeableDstate[i] != DeviceWakeDepthMaximum) {
1812                     m_DeviceWake[i - PowerSystemWorking] = (BYTE)caps.DeepestWakeableDstate[i];
1813                 }
1814             }
1815         }
1816     }
1817 
1818     return status;
1819 }
1820 
1821 _Must_inspect_result_
1822 NTSTATUS
_PnpStartDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)1823 FxPkgPnp::_PnpStartDevice(
1824     __inout FxPkgPnp* This,
1825     __inout FxIrp *Irp
1826     )
1827 /*++
1828 
1829 Routine Description:
1830     This method is called in response to a PnP StartDevice IRP coming down the
1831     stack.
1832 
1833 Arguments:
1834     This - device instance
1835     Irp - a pointer to the FxIrp
1836 
1837 Returns:
1838     STATUS_PENDING
1839 
1840 --*/
1841 {
1842     This->SetPendingPnpIrp(Irp);
1843     This->PnpProcessEvent(PnpEventStartDevice);
1844 
1845     return STATUS_PENDING;
1846 }
1847 
1848 _Must_inspect_result_
1849 NTSTATUS
_PnpQueryStopDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)1850 FxPkgPnp::_PnpQueryStopDevice(
1851     __inout FxPkgPnp* This,
1852     __inout FxIrp *Irp
1853     )
1854 
1855 /*++
1856 
1857 Routine Description:
1858 
1859     Pnp callback querying to see if the device can be stopped.
1860 
1861     The Framework philosophy surrounding Query Stop (and Query Remove) is that
1862     it's impossible to really know if you can stop unless you've tried to stop.
1863     This may not always be true, but it's hard to find a general strategy that
1864     works that is less conservative.  Furthermore, I couldn't find good examples
1865     of drivers that would really benefit from continuing to handle requests
1866     until the actual Stop IRP arrived, particularly when you consider that
1867     most QueryStops are followed immediately by Stops.
1868 
1869     So this function sends an event to the PnP State machine that begins the
1870     stopping process.  If it is successful, then ultimately the QueryStop IRP
1871     will be successfully completed.
1872 
1873 Arguments:
1874 
1875     This - a pointer to the PnP package
1876 
1877     Irp - a pointer to the FxIrp
1878 
1879 Return Value:
1880 
1881     STATUS_PENDING
1882 
1883   --*/
1884 
1885 {
1886     //
1887     // Keep this IRP around, since we're going to deal with it later.
1888     //
1889     This->SetPendingPnpIrp(Irp);
1890 
1891     //
1892     // Now run the state machine on this thread.
1893     //
1894     This->PnpProcessEvent(PnpEventQueryStop);
1895 
1896     return STATUS_PENDING;
1897 }
1898 
1899 _Must_inspect_result_
1900 NTSTATUS
_PnpCancelStopDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)1901 FxPkgPnp::_PnpCancelStopDevice(
1902     __inout FxPkgPnp* This,
1903     __inout FxIrp *Irp
1904     )
1905 /*++
1906 
1907 Routine Description:
1908 
1909     This routine is invoked in response to a query stop failing, somewhere in
1910     the stack.  Note that we can receive a cancel stop without being in the
1911     query stop state if a driver above us in the stack failed the query stop.
1912 
1913     Again, this function just exists to bridge the gap between the WDM IRPs
1914     and the PnP state machine.  This function does little more than send an
1915     event to the machine.
1916 
1917 Arguments:
1918 
1919     This - the package
1920 
1921     Irp - a pointer to the FxIrp
1922 
1923 Returns:
1924 
1925     STATUS_PENDING
1926 
1927 --*/
1928 {
1929     //
1930     // Seed the irp with success
1931     //
1932     Irp->SetStatus(STATUS_SUCCESS);
1933 
1934     //
1935     // Pend it and transition the state machine
1936     //
1937     This->SetPendingPnpIrp(Irp);
1938     This->PnpProcessEvent(PnpEventCancelStop);
1939 
1940     return STATUS_PENDING;
1941 }
1942 
1943 _Must_inspect_result_
1944 NTSTATUS
_PnpStopDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)1945 FxPkgPnp::_PnpStopDevice(
1946     __inout FxPkgPnp* This,
1947     __inout FxIrp *Irp
1948     )
1949 /*++
1950 
1951 Routine Description:
1952 
1953     This method is invoked in response to a Pnp StopDevice IRP.
1954 
1955 Arguments:
1956 
1957     Irp - a pointer to the FxIrp
1958 
1959 Returns:
1960 
1961     STATUS_PENDING
1962 
1963 --*/
1964 {
1965     //
1966     // Seed the irp with success
1967     //
1968     Irp->SetStatus(STATUS_SUCCESS);
1969 
1970     //
1971     // Pend and transition the state machine
1972     //
1973     This->SetPendingPnpIrp(Irp);
1974     This->PnpProcessEvent(PnpEventStop);
1975 
1976     return STATUS_PENDING;
1977 }
1978 
1979 _Must_inspect_result_
1980 NTSTATUS
_PnpQueryRemoveDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)1981 FxPkgPnp::_PnpQueryRemoveDevice(
1982     __inout FxPkgPnp* This,
1983     __inout FxIrp *Irp
1984     )
1985 /*++
1986 
1987 Routine Description:
1988 
1989     Again, the Framework handles QueryRemove by stopping everything going on
1990     related to the device and then asking the driver whether it can be
1991     removed.  This function just kicks the state machine.  Final completion
1992     of the IRP will come (much) later.
1993 
1994 Arguments:
1995 
1996     This - the package
1997 
1998     Irp - a pointer to the FxIrp
1999 
2000 Returns:
2001 
2002     STATUS_PENDING
2003 
2004 --*/
2005 {
2006     //
2007     // By default we handle this state.
2008     //
2009     Irp->SetStatus(STATUS_SUCCESS);
2010 
2011     //
2012     // Keep this IRP around, since we're going to deal with it later.
2013     //
2014     This->SetPendingPnpIrp(Irp);
2015 
2016     //
2017     // Now run the state machine on this thread.
2018     //
2019     This->PnpProcessEvent(PnpEventQueryRemove);
2020 
2021     return STATUS_PENDING;
2022 }
2023 
2024 _Must_inspect_result_
2025 NTSTATUS
_PnpCancelRemoveDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)2026 FxPkgPnp::_PnpCancelRemoveDevice(
2027     __inout FxPkgPnp* This,
2028     __inout FxIrp *Irp
2029     )
2030 
2031 /*++
2032 
2033 Routine Description:
2034 
2035     Notification of a previous remove being canceled.  Kick the state machine.
2036 
2037 Arguments:
2038 
2039     This - the package
2040 
2041     Irp - FxIrp representing the notification
2042 
2043 Return Value:
2044 
2045     STATUS_PENDING
2046 
2047   --*/
2048 
2049 {
2050     //
2051     // Seed the irp with success
2052     //
2053     Irp->SetStatus(STATUS_SUCCESS);
2054 
2055     //
2056     // Pend it and transition the state machine
2057     //
2058 
2059     This->SetPendingPnpIrp(Irp);
2060     This->PnpProcessEvent(PnpEventCancelRemove);
2061 
2062     return STATUS_PENDING;
2063 }
2064 
2065 VOID
CleanupStateMachines(__in BOOLEAN CleanupPnp)2066 FxPkgPnp::CleanupStateMachines(
2067     __in BOOLEAN CleanupPnp
2068     )
2069 {
2070 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
2071     FxCREvent * event = m_CleanupEventUm.GetSelfPointer();
2072 #else
2073     FxCREvent eventOnStack;
2074     eventOnStack.Initialize();
2075     FxCREvent * event = eventOnStack.GetSelfPointer();
2076 #endif
2077 
2078     //
2079     // Order of shutdown is important here.
2080     // o Pnp initiates events to power policy.
2081     // o Power policy initiates events to power and device-power-requirement
2082     // o Power does not initiate any events
2083     // o Device-power-requirement does not initiate any events
2084     //
2085     // By shutting them down in the order in which they send events, we can
2086     // guarantee that no new events will be posted into the subsidiary state
2087     // machines.
2088     //
2089 
2090     //
2091     // This will shut off the pnp state machine and synchronize any outstanding
2092     // threads of execution.
2093     //
2094     if (CleanupPnp && m_PnpMachine.SetFinished(
2095             event
2096             ) == FALSE) {
2097         DoTraceLevelMessage(
2098             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
2099             "WDFDEVICE %p, !devobj %p waiting for pnp state machine to finish",
2100             m_Device->GetHandle(), m_Device->GetDeviceObject());
2101 
2102         //
2103         // Process the event *before* completing the irp so that this event is in
2104         // the queue before the device remove event which will be processed
2105         // right after the start irp has been completed.
2106         //
2107         event->EnterCRAndWaitAndLeave();
2108     }
2109 
2110     //
2111     // Even though event is a SynchronizationEvent, so we need to reset it for
2112     // the next wait because SetFinished will set it if even if the transition
2113     // to the finished state is immediate
2114     //
2115     event->Clear();
2116 
2117     if (m_PowerPolicyMachine.SetFinished(event) == FALSE) {
2118         DoTraceLevelMessage(
2119             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
2120             "WDFDEVICE %p, !devobj %p waiting for pwr pol state machine to finish",
2121             m_Device->GetHandle(), m_Device->GetDeviceObject());
2122 
2123         event->EnterCRAndWaitAndLeave();
2124     }
2125 
2126     //
2127     // See previous comment about why we Clear()
2128     //
2129     event->Clear();
2130 
2131     if (m_PowerMachine.SetFinished(event) == FALSE) {
2132         DoTraceLevelMessage(
2133             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
2134             "WDFDEVICE %p, !devobj %p waiting for pwr state machine to finish",
2135             m_Device->GetHandle(), m_Device->GetDeviceObject());
2136 
2137         event->EnterCRAndWaitAndLeave();
2138     }
2139 
2140     if (IsPowerPolicyOwner()) {
2141         //
2142         // See previous comment about why we Clear()
2143         //
2144         event->Clear();
2145 
2146         if (NULL != m_PowerPolicyMachine.m_Owner->m_PoxInterface.
2147                                         m_DevicePowerRequirementMachine) {
2148 
2149             if (FALSE == m_PowerPolicyMachine.m_Owner->m_PoxInterface.
2150                           m_DevicePowerRequirementMachine->SetFinished(event)) {
2151 
2152                 DoTraceLevelMessage(
2153                     GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
2154                     "WDFDEVICE %p, !devobj %p waiting for device power "
2155                     "requirement state machine to finish",
2156                     m_Device->GetHandle(),
2157                     m_Device->GetDeviceObject());
2158 
2159                 event->EnterCRAndWaitAndLeave();
2160             }
2161         }
2162 
2163         m_PowerPolicyMachine.m_Owner->CleanupPowerCallback();
2164     }
2165 
2166     //
2167     // Release the power thread if we have one either through creation or query.
2168     // Since the power policy state machine is off, we should no longer need
2169     // a dedicated thread.
2170     //
2171     // *** NOTE ***
2172     // The power thread must be released *BEFORE* sending the irp down the stack
2173     // because this can happen
2174     // 1)  this driver is not the power thread owner, but the last client
2175     // 2)  we send the pnp irp first
2176     // 3)  the power thread owner waits on this thread for all the clients to go
2177     //     away, but this device still has a reference on it
2178     // 4)  this device will not release the reference b/c the owner is waiting
2179     //     in the same thread.
2180     //
2181     ReleasePowerThread();
2182 
2183     //
2184     // Deref the reenumeration interface
2185     //
2186     ReleaseReenumerationInterface();
2187 }
2188 
2189 VOID
CleanupDeviceFromFailedCreate(__in MxEvent * WaitEvent)2190 FxPkgPnp::CleanupDeviceFromFailedCreate(
2191     __in MxEvent * WaitEvent
2192     )
2193 /*++
2194 
2195 Routine Description:
2196     The device failed creation in some stage.  It is assumed that the device has
2197     enough state that it can survive a transition through the pnp state machine
2198     (which means that pointers like m_PkgIo are valid and != NULL).  When this
2199     function returns, it will have deleted the owning FxDevice.
2200 
2201 Arguments:
2202     WaitEvent - Event on which RemoveProcessed wait will be performed
2203 
2204                 We can't initialize this event on stack as the initialization
2205                 can fail in user-mode. We can't have Initialize method
2206                 preinitailize this event either as this function may get called
2207                 before initialize (or in case of initialization failure).
2208 
2209                 Hence the caller preallocates the event and passes to this
2210                 function.
2211 
2212                 Caller must initialize this event as SynchronizationEvent
2213                 and it must be unsignalled.
2214 Return Value:
2215     None
2216 
2217   --*/
2218 {
2219     Mx::MxAssert(Mx::MxGetCurrentIrql() == PASSIVE_LEVEL);
2220 
2221     //
2222     // Caller must initialize the event as Synchronization event and it should
2223     // be passed as non-signalled. But we Clear it just to be sure.
2224     //
2225     WaitEvent->Clear();
2226 
2227     ADDREF(WaitEvent);
2228 
2229     ASSERT(m_DeviceRemoveProcessed == NULL);
2230     m_DeviceRemoveProcessed = WaitEvent;
2231 
2232     //
2233     // Simulate a remove event coming to the device.  After this call returns
2234     // m_Device is still valid and must be deleted.
2235     //
2236     PnpProcessEvent(PnpEventRemove);
2237 
2238     //
2239     // No need to wait in a critical region because we are in the context of a
2240     // pnp request which is in the system context.
2241     //
2242     WaitEvent->WaitFor(Executive, KernelMode, FALSE, NULL);
2243     m_DeviceRemoveProcessed = NULL;
2244 
2245     RELEASE(WaitEvent);
2246 }
2247 
2248 VOID
DeleteDevice(VOID)2249 FxPkgPnp::DeleteDevice(
2250     VOID
2251     )
2252 /*++
2253 
2254 Routine Description:
2255     This routine will detach and delete the device object and free the memory
2256     for the device if there are no other references to it.  Before calling this
2257     routine, the state machines should have been cleaned up and the power thread
2258     released.
2259 
2260 --*/
2261 {
2262     //
2263     // This will detach and delete the device object
2264     //
2265     m_Device->Destroy();
2266 
2267     //
2268     // If this is the last reference, this will free the memory for the device
2269     //
2270     m_Device->DeleteObject();
2271 }
2272 
2273 _Must_inspect_result_
2274 NTSTATUS
_PnpRemoveDevice(__inout FxPkgPnp * This,__inout FxIrp * Irp)2275 FxPkgPnp::_PnpRemoveDevice(
2276     __inout FxPkgPnp* This,
2277     __inout FxIrp *Irp
2278     )
2279 
2280 /*++
2281 
2282 Routine Description:
2283 
2284     Notification of a remove.  Kick the state machine.
2285 
2286 Arguments:
2287 
2288     This - the package
2289 
2290     Irp - FxIrp representing the notification
2291 
2292 Return Value:
2293 
2294     status
2295 
2296   --*/
2297 
2298 {
2299 #if (FX_CORE_MODE==FX_CORE_USER_MODE)
2300     MxEvent * event = This->m_RemoveEventUm.GetSelfPointer();
2301 #else
2302     MxEvent eventOnStack;
2303     eventOnStack.Initialize(SynchronizationEvent, FALSE);
2304     MxEvent * event = eventOnStack.GetSelfPointer();
2305 #endif
2306 
2307     NTSTATUS status;
2308 
2309     status = Mx::MxAcquireRemoveLock(
2310         This->m_Device->GetRemoveLock(),
2311         Irp->GetIrp());
2312 
2313 #if DBG
2314     ASSERT(NT_SUCCESS(status));
2315 #else
2316     UNREFERENCED_PARAMETER(status);
2317 #endif
2318 
2319     //
2320     // Keep this object around after m_Device has RELEASE'ed its reference to
2321     // this package.
2322     //
2323     This->ADDREF(Irp);
2324 
2325     //
2326     // Removes are always success
2327     //
2328     Irp->SetStatus(STATUS_SUCCESS);
2329 
2330     ASSERT(This->m_DeviceRemoveProcessed == NULL);
2331     This->m_DeviceRemoveProcessed = event;
2332 
2333     //
2334     // Post the event and wait for the FxDevice to destroy itself or determine
2335     // it has not been reported missing yet (for PDOs and bus filters).
2336     //
2337     This->PnpProcessEvent(PnpEventRemove);
2338 
2339     DoTraceLevelMessage(
2340         This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
2341         "WDFDEVICE %p, !devobj %p waiting for remove event to finish processing",
2342         This->m_Device->GetHandle(), This->m_Device->GetDeviceObject());
2343 
2344     //
2345     // No need to wait in a critical region because we are in the context of a
2346     // pnp request which is in the system context.
2347     //
2348     event->WaitFor(Executive, KernelMode, FALSE, NULL);
2349 
2350     This->m_DeviceRemoveProcessed = NULL;
2351 
2352     status = This->ProcessRemoveDeviceOverload(Irp);
2353 
2354     //
2355     // Release the reference added at the top.  This is most likely going to be
2356     // the last reference on the package for KMDF. For UMDF, host manages the
2357     // lifetime of FxDevice so this may not be the last release for UMDF.
2358     //
2359     This->RELEASE(Irp);
2360 
2361     return status;
2362 }
2363 
2364 _Must_inspect_result_
2365 NTSTATUS
PnpSurpriseRemoval(__inout FxIrp * Irp)2366 FxPkgPnp::PnpSurpriseRemoval(
2367     __inout FxIrp* Irp
2368     )
2369 
2370 /*++
2371 
2372 Routine Description:
2373 
2374     Notification that the device has been surprise removed.  Kick the state
2375     machine.
2376 
2377 Arguments:
2378 
2379     Irp - pointer to FxIrp representing this notification
2380 
2381 Return Value:
2382 
2383     STATUS_PENDING
2384 
2385 --*/
2386 
2387 {
2388     //
2389     // Package specific handling
2390     //
2391     Irp->SetStatus(STATUS_SUCCESS);
2392     SetPendingPnpIrp(Irp);
2393     PnpProcessEvent(PnpEventSurpriseRemove);
2394 
2395     return STATUS_PENDING;
2396 }
2397 
2398 _Must_inspect_result_
2399 NTSTATUS
_DispatchWaitWake(__inout FxPkgPnp * This,__inout FxIrp * Irp)2400 FxPkgPnp::_DispatchWaitWake(
2401     __inout FxPkgPnp* This,
2402     __inout FxIrp *Irp
2403     )
2404 
2405 /*++
2406 
2407 Routine Description:
2408 
2409     This the first-level dispatch routine for IRP_MN_WAIT_WAKE.  What one
2410     does with a WaitWake IRP depends very much on whether one is an FDO, a PDO
2411     or a filter.  So dispatch immediately to a subclassable function.
2412 
2413 Arguments:
2414 
2415     This - the package
2416 
2417     Irp - pointer to FxIrp representing this notification
2418 
2419 Return Value:
2420 
2421     status
2422 
2423 --*/
2424 
2425 {
2426     return This->DispatchWaitWake(Irp);
2427 }
2428 
2429 _Must_inspect_result_
2430 NTSTATUS
DispatchWaitWake(__inout FxIrp * Irp)2431 FxPkgPnp::DispatchWaitWake(
2432     __inout FxIrp *Irp
2433     )
2434 /*++
2435 
2436 Routine Description:
2437 
2438     Handles wait wake requests in a generic fashion
2439 
2440 Arguments:
2441 
2442 
2443 Return Value:
2444 
2445   --*/
2446 {
2447     PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
2448     NTSTATUS status;
2449     PIRP oldIrp;
2450     KIRQL irql;
2451 
2452     if (IsPowerPolicyOwner()) {
2453         if (m_PowerPolicyMachine.m_Owner->m_RequestedWaitWakeIrp == FALSE) {
2454             //
2455             // A power irp arrived, but we did not request it.  log and bugcheck
2456             //
2457             DoTraceLevelMessage(
2458                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2459                 "Received wait wake power irp %p on device %p, but the irp was "
2460                 "not requested by the device (the power policy owner)",
2461                 Irp->GetIrp(), m_Device->GetDeviceObject());
2462 
2463             FxVerifierBugCheck(GetDriverGlobals(),  // globals
2464                    WDF_POWER_MULTIPLE_PPO, // specific type
2465                    (ULONG_PTR)m_Device->GetDeviceObject(), //parm 2
2466                    (ULONG_PTR)Irp->GetIrp());  // parm 3
2467 
2468             /* NOTREACHED */
2469         }
2470 
2471         //
2472         // We are no longer requesting a power irp because we received the one
2473         // we requested.
2474         //
2475         m_PowerPolicyMachine.m_Owner->m_RequestedWaitWakeIrp = FALSE;
2476     }
2477 
2478     //
2479     // The Framework has the concept of a "Wait/Wake Owner."  This is the layer
2480     // in the stack that is enabling and disabling wake at the bus level.  This
2481     // is probably the PDO or a bus filter like ACPI.sys.  This is distinct
2482     // from being the "Power Policy Owner," which would mean that this driver
2483     // is deciding what D-state is appropriate for the device, and also
2484     // sending Wait/Wake IRPs.
2485     //
2486 
2487     if (m_SharedPower.m_WaitWakeOwner) {
2488         m_PowerMachine.m_WaitWakeLock.Acquire(&irql);
2489 
2490         if (m_SharedPower.m_WaitWakeIrp != NULL) {
2491             //
2492             // We only allow one pended wait wake irp in the stack at a time.
2493             // Fail this secondary wait wake request.
2494             //
2495             status = STATUS_INVALID_DEVICE_STATE;
2496 
2497             DoTraceLevelMessage(
2498                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2499                 "Failing wait wake irp %p with %!STATUS! because wait wake irp "
2500                 "%p already pended",
2501                 Irp->GetIrp(), status, m_SharedPower.m_WaitWakeIrp);
2502         }
2503         else {
2504             MdCancelRoutine pRoutine;
2505 
2506             //
2507             // No wait wake irp is currently in the stack, so attempt to set
2508             // a cancel routine and transition the power state machine into a
2509             // a state where it can arm the device at the bus level for this
2510             // child.
2511             //
2512             // The power state machine expects the wait wake irp to have a cancel
2513             // routine set in all states.  For those states which require a
2514             // non cancelable irp, those states clear the cancel routine as
2515             // appropriate.
2516             //
2517             pRoutine = Irp->SetCancelRoutine(_PowerWaitWakeCancelRoutine);
2518 #if DBG
2519             ASSERT(pRoutine == NULL);
2520 #else
2521             UNREFERENCED_PARAMETER(pRoutine);
2522 #endif
2523             status = STATUS_PENDING;
2524 
2525             if (Irp->IsCanceled()) {
2526                 DoTraceLevelMessage(GetDriverGlobals(),
2527                                     TRACE_LEVEL_ERROR, TRACINGPNP,
2528                                     "wait wake irp %p already canceled", Irp->GetIrp());
2529 
2530                 //
2531                 // This IRP has already been cancelled, we must clear the cancel
2532                 // routine before completing the IRP.
2533                 //
2534                 pRoutine = Irp->SetCancelRoutine(NULL);
2535 
2536                 if (pRoutine != NULL) {
2537                     //
2538                     // Our cancel routine will not be called
2539                     //
2540 #if DBG
2541                     ASSERT(pRoutine == _PowerWaitWakeCancelRoutine);
2542 #else
2543                     UNREFERENCED_PARAMETER(pRoutine);
2544 #endif
2545                     Irp->SetStatus(STATUS_CANCELLED);
2546                     status = STATUS_CANCELLED;
2547                 }
2548             }
2549 
2550             if (status == STATUS_PENDING) {
2551                 //
2552                 // Either we successfully set the cancel routine or the irp
2553                 // was canceled and the cancel routine is about to run when
2554                 // we drop the lock.  If the routine is about to run, we still
2555                 // need to setup m_SharedPower to values that it expects.
2556                 //
2557                 Irp->MarkIrpPending();
2558                 m_SharedPower.m_WaitWakeIrp = Irp->GetIrp();
2559             }
2560         }
2561         m_PowerMachine.m_WaitWakeLock.Release(irql);
2562 
2563         if (NT_SUCCESS(status)) {
2564             //
2565             // Post to the appropriate matchines
2566             //
2567             PowerProcessEvent(PowerWakeArrival);
2568 
2569             if (IsPowerPolicyOwner()) {
2570                 PowerPolicyProcessEvent(PwrPolWakeArrived);
2571             }
2572         }
2573         else {
2574             CompletePowerRequest(Irp, status);
2575         }
2576 
2577         return status;
2578     }
2579     else if (IsPowerPolicyOwner()) {
2580         //
2581         // Try to set m_WaitWakeIrp to the new IRP value only if the current
2582         // value of m_WaitWakeIrp is NULL because there can only be one
2583         // active wait wake irp in the stack at a time.  Since the power policy
2584         // state machine never sends more then one, this is really a guard
2585         // against some other device in the stack sending a wait wake irp to
2586         // this device in the wrong state.
2587         //
2588         oldIrp = (PIRP) InterlockedCompareExchangePointer(
2589             (PVOID*) &m_SharedPower.m_WaitWakeIrp,
2590             Irp->GetIrp(),
2591             NULL
2592             );
2593 
2594         //
2595         // If oldIrp is NULL then there was no previous irp and we successfully
2596         // exchanged the new PIRP value into m_WaitWakeIrp
2597         //
2598         if (oldIrp == NULL) {
2599             m_PowerPolicyMachine.SetWaitWakeUnclaimed();
2600 
2601             //
2602             // NOTE:  There is a built in race condition here that WDF cannot
2603             //        solve with the given WDM primitives.  After arming the
2604             //        device for wake, there is a window where the wait wake irp
2605             //        has not yet been processed by the wait wake owner.  Until
2606             //        the wake request is processed, wake events could be generated
2607             //        and lost.  There is nothing we can do about this until we
2608             //        have synchronous "goto Dx and arm" command available.
2609             //
2610             Irp->CopyCurrentIrpStackLocationToNext();
2611             Irp->SetCompletionRoutineEx(m_Device->GetDeviceObject(),
2612                                         _PowerPolicyWaitWakeCompletionRoutine,
2613                                         this);
2614 
2615             //
2616             // Technically, there should be a PDO vs FDO overload here which
2617             // does the right thing w/regard to this request and if it is at the
2618             // bottom of the stack or not.  But, by design, we check for
2619             // m_WaitWakeOwner first which has the direct side affect of making
2620             // it impossible for a PDO to get to this point.
2621             //
2622             ASSERT(m_Device->IsFdo());
2623 
2624             status = Irp->PoCallDriver(m_Device->GetAttachedDevice());
2625 
2626             //
2627             // Send the wake arrived after sending the request as commented above.
2628             // This window between sending the request and sending the event to
2629             // the state machine allows the wait owner to complete the request
2630             // immediately.   When completed synchronously, it has an effect on
2631             //  both wake scenarios:
2632             //
2633             // 1) wake from S0:  the device never transitions to Dx and the idle
2634             //                   timer is resumed if no i/o is present
2635             //
2636             // 2) wake from sx:  the device is disarmed for wake from Sx and
2637             //                   put into Dx with being armed for wake.
2638             //
2639             PowerPolicyProcessEvent(PwrPolWakeArrived);
2640         }
2641         else {
2642             status = STATUS_POWER_STATE_INVALID;
2643 
2644             DoTraceLevelMessage(
2645                 FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
2646                 "already have a ww irp %p, failing new ww irp %p with %!STATUS!",
2647                 oldIrp, Irp->GetIrp(), status);
2648 
2649             CompletePowerRequest(Irp, status);
2650         }
2651     }
2652     else {
2653         //
2654         // Not the power policy owner, not the wait wake irp owner, ignore the
2655         // irp
2656         //
2657         // This will release the remove lock.
2658         //
2659         status = FireAndForgetIrp(Irp);
2660     }
2661 
2662     return status;
2663 }
2664 
2665 _Must_inspect_result_
2666 NTSTATUS
RegisterCallbacks(__in PWDF_PNPPOWER_EVENT_CALLBACKS DispatchTable)2667 FxPkgPnp::RegisterCallbacks(
2668     __in PWDF_PNPPOWER_EVENT_CALLBACKS DispatchTable
2669     )
2670 {
2671     NTSTATUS status;
2672 
2673     //
2674     // Update the callback table.
2675     //
2676     m_DeviceD0Entry.m_Method           = DispatchTable->EvtDeviceD0Entry;
2677     m_DeviceD0EntryPostInterruptsEnabled.m_Method =
2678                                          DispatchTable->EvtDeviceD0EntryPostInterruptsEnabled;
2679     m_DeviceD0ExitPreInterruptsDisabled.m_Method =
2680                                          DispatchTable->EvtDeviceD0ExitPreInterruptsDisabled;
2681     m_DeviceD0Exit.m_Method            = DispatchTable->EvtDeviceD0Exit;
2682 
2683     m_DevicePrepareHardware.m_Method   = DispatchTable->EvtDevicePrepareHardware;
2684     m_DeviceReleaseHardware.m_Method   = DispatchTable->EvtDeviceReleaseHardware;
2685 
2686     m_DeviceQueryStop.m_Method         = DispatchTable->EvtDeviceQueryStop;
2687     m_DeviceQueryRemove.m_Method       = DispatchTable->EvtDeviceQueryRemove;
2688 
2689     m_DeviceSurpriseRemoval.m_Method   = DispatchTable->EvtDeviceSurpriseRemoval;
2690 
2691     m_DeviceUsageNotification.m_Method = DispatchTable->EvtDeviceUsageNotification;
2692     m_DeviceUsageNotificationEx.m_Method = DispatchTable->EvtDeviceUsageNotificationEx;
2693     m_DeviceRelationsQuery.m_Method    = DispatchTable->EvtDeviceRelationsQuery;
2694 
2695     if (DispatchTable->EvtDeviceSelfManagedIoCleanup != NULL ||
2696         DispatchTable->EvtDeviceSelfManagedIoFlush != NULL ||
2697         DispatchTable->EvtDeviceSelfManagedIoInit != NULL ||
2698         DispatchTable->EvtDeviceSelfManagedIoSuspend != NULL ||
2699         DispatchTable->EvtDeviceSelfManagedIoRestart != NULL) {
2700 
2701         status = FxSelfManagedIoMachine::_CreateAndInit(&m_SelfManagedIoMachine,
2702                                                         this);
2703 
2704         if (!NT_SUCCESS(status)) {
2705             return status;
2706         }
2707 
2708         m_SelfManagedIoMachine->InitializeMachine(DispatchTable);
2709     }
2710 
2711     return STATUS_SUCCESS;
2712 }
2713 
2714 VOID
RegisterPowerPolicyCallbacks(__in PWDF_POWER_POLICY_EVENT_CALLBACKS Callbacks)2715 FxPkgPnp::RegisterPowerPolicyCallbacks(
2716     __in PWDF_POWER_POLICY_EVENT_CALLBACKS Callbacks
2717     )
2718 {
2719     m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromS0.m_Method =
2720         Callbacks->EvtDeviceArmWakeFromS0;
2721     m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromSx.m_Method =
2722         Callbacks->EvtDeviceArmWakeFromSx;
2723     m_PowerPolicyMachine.m_Owner->m_DeviceArmWakeFromSx.m_MethodWithReason =
2724         Callbacks->EvtDeviceArmWakeFromSxWithReason;
2725 
2726     m_PowerPolicyMachine.m_Owner->m_DeviceDisarmWakeFromS0.m_Method =
2727         Callbacks->EvtDeviceDisarmWakeFromS0;
2728     m_PowerPolicyMachine.m_Owner->m_DeviceDisarmWakeFromSx.m_Method =
2729         Callbacks->EvtDeviceDisarmWakeFromSx;
2730 
2731     m_PowerPolicyMachine.m_Owner->m_DeviceWakeFromS0Triggered.m_Method =
2732         Callbacks->EvtDeviceWakeFromS0Triggered;
2733     m_PowerPolicyMachine.m_Owner->m_DeviceWakeFromSxTriggered.m_Method =
2734         Callbacks->EvtDeviceWakeFromSxTriggered;
2735 }
2736 
2737 NTSTATUS
RegisterPowerPolicyWmiInstance(__in const GUID * Guid,__in FxWmiInstanceInternalCallbacks * Callbacks,__out FxWmiInstanceInternal ** Instance)2738 FxPkgPnp::RegisterPowerPolicyWmiInstance(
2739     __in  const GUID* Guid,
2740     __in  FxWmiInstanceInternalCallbacks* Callbacks,
2741     __out FxWmiInstanceInternal** Instance
2742     )
2743 {
2744     // WDF_WMI_PROVIDER_CONFIG config;
2745     // NTSTATUS status;
2746 
2747     // WDF_WMI_PROVIDER_CONFIG_INIT(&config, Guid);
2748 
2749     // //
2750     // // We are assuming we are registering either for the wait wake or device
2751     // // timeout GUIDs which both operate on BOOLEANs.  If we expand this API in
2752     // // the future, have the caller pass in a config structure for the provider
2753     // // GUID.
2754     // //
2755     // config.MinInstanceBufferSize = sizeof(BOOLEAN);
2756 
2757     // status = m_Device->m_PkgWmi->AddPowerPolicyProviderAndInstance(
2758     //     &config,
2759     //     Callbacks,
2760     //     Instance);
2761 
2762     // if (status == STATUS_OBJECT_NAME_COLLISION) {
2763     //     status = STATUS_SUCCESS;
2764     // }
2765 
2766     // if (!NT_SUCCESS(status)) {
2767     //     DoTraceLevelMessage(
2768     //         GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2769     //         "Failed to register WMI power GUID %!STATUS!", status);
2770     // }
2771     //
2772     // return status;
2773     ROSWDFNOTIMPLEMENTED;
2774     return STATUS_SUCCESS;
2775 }
2776 
2777 NTSTATUS
PowerPolicySetS0IdleSettings(__in PWDF_DEVICE_POWER_POLICY_IDLE_SETTINGS Settings)2778 FxPkgPnp::PowerPolicySetS0IdleSettings(
2779     __in PWDF_DEVICE_POWER_POLICY_IDLE_SETTINGS Settings
2780     )
2781 /*++
2782 
2783 Routine Description:
2784 
2785     Updates the S0 Idle settings for the device and then posts an update event
2786     to the power policy state machine.  The first this function is called, the
2787     ability to allow the user to control this setting is set.
2788 
2789 Arguments:
2790 
2791     Settings - The settings to apply.
2792 
2793 Return Value:
2794 
2795     NTSTATUS
2796 
2797   --*/
2798 {
2799     DEVICE_POWER_STATE dxState;
2800     ULONG idleTimeout;
2801     NTSTATUS status;
2802     BOOLEAN enabled, s0Capable, overridable, firstTime;
2803     WDF_TRI_STATE powerUpOnSystemWake;
2804     const LONGLONG negliblySmallIdleTimeout = -1; // 100 nanoseconds
2805 
2806     s0Capable = FALSE;
2807     dxState = PowerDeviceD3;
2808     overridable = FALSE;
2809     firstTime = TRUE;
2810 
2811     if (Settings->Enabled == WdfTrue) {
2812         enabled = TRUE;
2813 
2814     } else if (Settings->Enabled == WdfUseDefault) {
2815         enabled = TRUE;
2816 
2817         if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
2818             DECLARE_CONST_UNICODE_STRING(valueName, WDF_S0_IDLE_DEFAULT_VALUE_NAME);
2819 
2820             //
2821             // Read registry. If registry value is not found, the value of "enabled"
2822             // remains unchanged
2823             //
2824             ReadRegistryS0Idle(&valueName, &enabled);
2825         }
2826         else {
2827             DoTraceLevelMessage(
2828                 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
2829                 "If registry value WdfDefaultIdleInWorkingState was present, "
2830                 "it was not read because DDI WdfDeviceAssignS0IdleSettings "
2831                 "was not called at PASSIVE_LEVEL");
2832         }
2833     }
2834     else {
2835         enabled = FALSE;
2836     }
2837 
2838     if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Set) {
2839         firstTime = FALSE;
2840     }
2841 
2842     if (m_CapsQueried == FALSE && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
2843         status = QueryForCapabilities();
2844         if (!NT_SUCCESS(status)) {
2845             return status;
2846         }
2847 
2848         //
2849         // Do not set m_CapsQueried to TRUE yet because we will do that once we
2850         // know the entire stack has been built we will do the query again.
2851         //
2852     }
2853 
2854     switch (Settings->IdleCaps) {
2855     case IdleUsbSelectiveSuspend:
2856     case IdleCanWakeFromS0:
2857         s0Capable = TRUE;
2858 
2859         if (Settings->DxState == PowerDeviceMaximum) {
2860             dxState = PowerPolicyGetDeviceDeepestDeviceWakeState(PowerSystemWorking);
2861 
2862             //
2863             // Some bus drivers
2864 
2865             // incorrectly report DeviceWake=D0 to
2866             // indicate that it does not support wake instead of specifying
2867             // PowerDeviceUnspecified and KMDF ends up requesting
2868             // a D0 irp when going to Dx. The check prevents this bug.
2869             //
2870             if (dxState < PowerDeviceD1 ||
2871                 dxState > PowerDeviceD3 ||
2872                 (dxState > PowerDeviceD2 && Settings->IdleCaps == IdleUsbSelectiveSuspend)
2873                 ) {
2874                 status = STATUS_POWER_STATE_INVALID;
2875 
2876                 DoTraceLevelMessage(
2877                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2878                     "DeviceWake power state reported in device capabilities "
2879                     "%!DEVICE_POWER_STATE! indicates that device can not signal"
2880                     " a wake event, %!STATUS!",
2881                     dxState, status);
2882                 return status;
2883             }
2884         }
2885         else {
2886             DEVICE_POWER_STATE dxDeepest;
2887 
2888             dxState = Settings->DxState;
2889             dxDeepest = PowerPolicyGetDeviceDeepestDeviceWakeState(PowerSystemWorking);
2890 
2891             if (dxState > dxDeepest) {
2892                 status = STATUS_POWER_STATE_INVALID;
2893 
2894                 DoTraceLevelMessage(
2895                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2896                     "DxState specified by driver %!DEVICE_POWER_STATE! cannot "
2897                     "be lighter than lightest available device wake state"
2898                     " %!DEVICE_POWER_STATE!, %!STATUS!", dxState,
2899                     dxDeepest, status);
2900                 return status;
2901             }
2902 
2903             //
2904             // Can only perform wait wake from D2 on a USB device
2905             //
2906             if (dxState > PowerDeviceD2 &&
2907                 Settings->IdleCaps == IdleUsbSelectiveSuspend) {
2908                 status = STATUS_POWER_STATE_INVALID;
2909 
2910                 DoTraceLevelMessage(
2911                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2912                     "DxState specified by driver %!DEVICE_POWER_STATE! cannot "
2913                     "be lighter than PowerDeviceD2 for USB selective suspend "
2914                     "%!STATUS!",
2915                     dxState, status);
2916                 return status;
2917             }
2918         }
2919 
2920         if (Settings->IdleCaps == IdleUsbSelectiveSuspend) {
2921             status = m_PowerPolicyMachine.InitUsbSS();
2922 
2923             if (!NT_SUCCESS(status)) {
2924                 DoTraceLevelMessage(
2925                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2926                     "Failed to initialize USB selective suspend %!STATUS!",
2927                     status);
2928                 return status;
2929             }
2930         }
2931 
2932         break;
2933 
2934     case IdleCannotWakeFromS0:
2935         s0Capable = FALSE;
2936 
2937         if (Settings->DxState == PowerDeviceMaximum) {
2938             dxState = PowerDeviceD3;
2939         }
2940         else {
2941             dxState = Settings->DxState;
2942         }
2943 
2944         break;
2945 
2946     default:
2947         ASSERT(FALSE);
2948         break;
2949     }
2950 
2951     if (Settings->IdleTimeout == IdleTimeoutDefaultValue) {
2952         idleTimeout = FxPowerPolicyDefaultTimeout;
2953     }
2954     else {
2955         idleTimeout = Settings->IdleTimeout;
2956     }
2957 
2958     if (Settings->UserControlOfIdleSettings == IdleAllowUserControl) {
2959 
2960         // status = UpdateWmiInstanceForS0Idle(AddInstance);
2961         // if (!NT_SUCCESS(status)) {
2962         //     return status;
2963         // } __REACTOS__
2964 
2965         if (Settings->Enabled == WdfUseDefault) {
2966             //
2967             // Read the registry entry for idle enabled if it's the first time.
2968             //
2969             if (firstTime && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
2970                 DECLARE_CONST_UNICODE_STRING(valueName, WDF_S0_IDLE_ENABLED_VALUE_NAME);
2971 
2972                 //
2973                 // Read registry. If registry value is not found, the value of
2974                 // "enabled" remains unchanged
2975                 //
2976                 ReadRegistryS0Idle(&valueName, &enabled);
2977             }
2978             else {
2979                 //
2980                 // Use the saved value for idle enabled.
2981                 //
2982                 enabled = m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
2983             }
2984         }
2985 
2986         overridable = TRUE;
2987     }
2988     else if (Settings->UserControlOfIdleSettings == IdleDoNotAllowUserControl) {
2989         //
2990         // No user control
2991         //
2992         overridable = FALSE;
2993 
2994         // (void) UpdateWmiInstanceForS0Idle(RemoveInstance); __REACTOS__
2995     }
2996 
2997     //
2998     // !!!! DO NOT INTRODUCE FAILURES BEYOND THIS POINT !!!!
2999     //
3000     // We should not introduce any failures that are not due to driver errors
3001     // beyond this point. This is because we are going to commit the driver's
3002     // S0-idle settings now and any failure in the midst of that could leave us
3003     // in a bad state. Therefore, all failable code where the failure is beyond
3004     // the driver's control should be placed above this point.
3005     //
3006     // For example, a driver may want wake-from-S0 support, but the device may
3007     // not support it. We already checked for that failure above, before we
3008     // started committing any of the driver's S0-idle settings.
3009     //
3010     // Any failures below this point should only be due to driver errors, i.e.
3011     // the driver incorrectly calling the AssignS0IdleSettings DDI.
3012     //
3013 
3014     if (firstTime) {
3015         m_PowerPolicyMachine.m_Owner->m_IdleSettings.Set = TRUE;
3016         m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable = overridable;
3017     }
3018 
3019     //
3020     // IdleTimeoutType is available only on > 1.9
3021     //
3022 #ifndef __REACTOS__
3023     if (Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_9)) {
3024         if (firstTime) {
3025              if ((SystemManagedIdleTimeout == Settings->IdleTimeoutType) ||
3026                  (SystemManagedIdleTimeoutWithHint ==
3027                                         Settings->IdleTimeoutType)) {
3028                 //
3029                 // This is the first time S0-idle policy is being specified and
3030                 // the caller has asked for the idle timeout to be determined
3031                 // by the power manager.
3032                 //
3033                 status = m_PowerPolicyMachine.m_Owner->m_IdleSettings.
3034                             m_TimeoutMgmt.UseSystemManagedIdleTimeout(
3035                                                             GetDriverGlobals()
3036                                                             );
3037                 if (!NT_SUCCESS(status)) {
3038                     return status;
3039                 }
3040             }
3041         } else {
3042             //
3043             // This is not the first time S0-idle policy is being specified.
3044             // Verify that the caller is not trying to change their mind about
3045             // whether the idle timeout is determined by the power manager.
3046             //
3047             BOOLEAN currentlyUsingSystemManagedIdleTimeout;
3048             BOOLEAN callerWantsSystemManagedIdleTimeout;
3049 
3050             currentlyUsingSystemManagedIdleTimeout =
3051                 m_PowerPolicyMachine.m_Owner->m_IdleSettings.m_TimeoutMgmt.
3052                                                 UsingSystemManagedIdleTimeout();
3053             callerWantsSystemManagedIdleTimeout =
3054               ((SystemManagedIdleTimeout == Settings->IdleTimeoutType) ||
3055                (SystemManagedIdleTimeoutWithHint == Settings->IdleTimeoutType));
3056 
3057             //
3058             // UMDF currently does not implement
3059             // IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable. So
3060             // that method must be called only as part of the second check in
3061             // the "if" statement below. Since UMDF currently does not support
3062             // system managed idle timeout, the first check will always evaluate
3063             // to 'FALSE', so the second check never gets executed for UMDF.
3064             //
3065             if ((callerWantsSystemManagedIdleTimeout !=
3066                             currentlyUsingSystemManagedIdleTimeout)
3067                  &&
3068                 (IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable())
3069                 ) {
3070 
3071                 status = STATUS_INVALID_DEVICE_REQUEST;
3072                 DoTraceLevelMessage(
3073                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3074                     "A previous call to assign S0-idle policy specified that "
3075                     "the idle timeout %s be determined by the power manager. "
3076                     "This decision cannot be changed. %!STATUS!",
3077                     currentlyUsingSystemManagedIdleTimeout ?
3078                       "should" :
3079                       "should not",
3080                     status);
3081                 FxVerifierDbgBreakPoint(GetDriverGlobals());
3082                 return status;
3083             }
3084         }
3085     }
3086 #endif
3087 
3088     if (Settings->IdleCaps == IdleCannotWakeFromS0) {
3089         //
3090         // PowerUpIdleDeviceOnSystemWake field added after v1.7.
3091         // By default KMDF uses an optimization where the device is not powered
3092         // up when resuming from Sx if it is idle. The field
3093         // PowerUpIdleDeviceOnSystemWake is used to turn off this optimization and allow
3094         // device to power up when resuming from Sx. Note that this optimization
3095         // is applicable only for IdleCannotWakeFromS0. In other cases the
3096         // device is always powered up in order to arm for wake.
3097         //
3098         powerUpOnSystemWake =
3099             (Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_7)) ?
3100             Settings->PowerUpIdleDeviceOnSystemWake :
3101             WdfUseDefault;
3102 
3103         switch(powerUpOnSystemWake) {
3104         case WdfTrue:
3105             m_PowerPolicyMachine.m_Owner->m_IdleSettings.PowerUpIdleDeviceOnSystemWake = TRUE;
3106             DoTraceLevelMessage(
3107                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3108                 "Driver turned off S0Idle optimization. Device will be "
3109                 "powered up on resume from Sx even when it is idle");
3110             break;
3111         case WdfFalse:
3112             m_PowerPolicyMachine.m_Owner->m_IdleSettings.PowerUpIdleDeviceOnSystemWake = FALSE;
3113             DoTraceLevelMessage(
3114                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3115                 "Driver turned on S0Idle optimization. Device will remain "
3116                 "powered off if idle when resuming from Sx");
3117             break;
3118         case WdfUseDefault:
3119             DO_NOTHING();
3120             break;
3121         default:
3122             break;
3123         }
3124     }
3125 
3126     if (FALSE ==
3127             m_PowerPolicyMachine.m_Owner->m_IdleSettings.UsbSSCapabilityKnown)
3128     {
3129         if (Settings->IdleCaps == IdleUsbSelectiveSuspend) {
3130             m_PowerPolicyMachine.m_Owner->m_IdleSettings.UsbSSCapable = TRUE;
3131             m_PowerPolicyMachine.m_Owner->
3132                 m_IdleSettings.UsbSSCapabilityKnown = TRUE;
3133 
3134         } else if (Settings->IdleCaps == IdleCanWakeFromS0) {
3135 
3136             m_PowerPolicyMachine.m_Owner->
3137                 m_IdleSettings.UsbSSCapabilityKnown = TRUE;
3138         }
3139     }
3140 
3141     //
3142     // Wake FromS0Capable is set every time because we want to allow the driver
3143     // to swap between idle wake capable and idle not wake capable.  This should
3144     // be allowed so that a scenario similar to the following can be implemented:
3145     //
3146     // a) when the device has an outstanding open, the device should arm itself
3147     //    for wake when idle
3148     //
3149     // b)  when the device does not have an outstanding open, the device should
3150     //     be off and not armed.
3151     //
3152     // The only way to be off is to assign S0 wake settings, so the
3153     // WakeFromS0Capable field must change on each DDI call.  This is not a
3154     // problem for the power policy state machine because it evaluates
3155     // WakeFromS0Capable state before enabling of idle.  If we are not
3156     // WakeFromS0Capable and USB SS capable (ie a have a torn/unsynchronized
3157     // state) in m_IdleSettings, we will recover from it when processing
3158     // PwrPolS0IdlePolicyChanged in the state machine (b/c this event causes
3159     // both fields to be reevaluated).
3160     //
3161     m_PowerPolicyMachine.m_Owner->m_IdleSettings.WakeFromS0Capable = s0Capable;
3162 
3163     m_PowerPolicyMachine.m_Owner->m_IdleSettings.DxState = dxState;
3164 
3165     if (m_PowerPolicyMachine.m_Owner->
3166             m_IdleSettings.m_TimeoutMgmt.UsingSystemManagedIdleTimeout()) {
3167         //
3168         // With system managed idle timeout, we don't want to apply an idle
3169         // timeout of our own on top of that. Effectively, our idle timeout is
3170         // 0.
3171         // But we apply a negligibly small timeout value as this allows us to
3172         // keep the same logic in the idle state machine, regardless of whether
3173         // we're using system-managed idle timeout or driver-managed idle
3174         // timeout.
3175         //
3176         if (firstTime) {
3177             m_PowerPolicyMachine.m_Owner->
3178                 m_PowerIdleMachine.m_PowerTimeout.QuadPart =
3179                                             negliblySmallIdleTimeout;
3180         }
3181 
3182         if (SystemManagedIdleTimeoutWithHint == Settings->IdleTimeoutType) {
3183             //
3184             // We save the idle timeout hint, but we don't provide the hint to
3185             // the power framework immediately. This is because currently we may
3186             // or may not be registered with the power framework. Note that
3187             // WdfDeviceAssignS0IdleSettings might get called even when we are
3188             // not registered with the power framework.
3189             //
3190             // Therefore, we provide the hint to the power framework only when
3191             // we get to the WdfDevStatePwrPolStartingDecideS0Wake state. This
3192             // state is a good choice for providing the hint because:
3193             //   1. We know we would be registered with the power framework when
3194             //      we are in this state.
3195             //   2. Any change in S0-idle settings causes us to go through this
3196             //      state.
3197             //
3198             m_PowerPolicyMachine.m_Owner->m_PoxInterface.m_NextIdleTimeoutHint =
3199                                                                     idleTimeout;
3200         }
3201 
3202     } else {
3203         m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.m_PowerTimeout.QuadPart
3204             = WDF_REL_TIMEOUT_IN_MS(idleTimeout);
3205     }
3206 
3207     //
3208     // If the driver is 1.11 or later, update the bus drivers with the client's
3209     // choice on the topic of D3hot or D3cold.
3210     //
3211     if ((Settings->Size > sizeof(WDF_DEVICE_POWER_POLICY_IDLE_SETTINGS_V1_9)) &&
3212         (Settings->ExcludeD3Cold != WdfUseDefault)) {
3213         MxDeviceObject deviceObject;
3214         BOOLEAN enableD3Cold;
3215 
3216         deviceObject.SetObject(m_Device->GetDeviceObject());
3217 
3218         switch (Settings->ExcludeD3Cold) {
3219         case WdfFalse:
3220             enableD3Cold = TRUE;
3221             break;
3222         default:
3223             DoTraceLevelMessage(
3224                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3225                 "Invalid tri-state value for ExcludeD3Cold %d",
3226                 Settings->ExcludeD3Cold);
3227             __fallthrough;
3228         case WdfTrue:
3229             enableD3Cold = FALSE;
3230             break;
3231         }
3232 
3233         SetD3ColdSupport(GetDriverGlobals(),
3234                          &deviceObject,
3235                          &m_D3ColdInterface,
3236                          enableD3Cold);
3237     }
3238 
3239     PowerPolicySetS0IdleState(enabled);
3240 
3241     return STATUS_SUCCESS;
3242 }
3243 
3244 NTSTATUS
PowerPolicySetSxWakeSettings(__in PWDF_DEVICE_POWER_POLICY_WAKE_SETTINGS Settings,__in BOOLEAN ArmForWakeIfChildrenAreArmedForWake,__in BOOLEAN IndicateChildWakeOnParentWake)3245 FxPkgPnp::PowerPolicySetSxWakeSettings(
3246     __in PWDF_DEVICE_POWER_POLICY_WAKE_SETTINGS Settings,
3247     __in BOOLEAN ArmForWakeIfChildrenAreArmedForWake,
3248     __in BOOLEAN IndicateChildWakeOnParentWake
3249     )
3250 /*++
3251 
3252 Routine Description:
3253 
3254     Updates the Sx wake settings for the device.  No event is posted to the
3255     state machine because this setting is statically checked when the machine
3256     is entering an Sx state (unlike S0 idle which can be checked at any time).
3257 
3258     The first this function is called, the ability to allow the user to control
3259     this setting is set.
3260 
3261 Arguments:
3262 
3263     Settings - the new settings to apply
3264 
3265     ArmForWakeIfChildrenAreArmedForWake - Inidicates whether the device
3266         should arm for wake when one or more children are armed for wake
3267 
3268     IndicateChildWakeOnParentWake - Indicates whether the device should
3269         propagate the wake status to its children
3270 
3271 Return Value:
3272 
3273     NTSTATUS
3274 
3275   --*/
3276 {
3277     DEVICE_POWER_STATE dxState;
3278     NTSTATUS status;
3279     BOOLEAN overridable, firstTime, enabled;
3280 
3281     dxState = PowerDeviceD3;
3282     overridable = FALSE;
3283     firstTime = TRUE;
3284 
3285     if (Settings->Enabled == WdfTrue) {
3286         enabled = TRUE;
3287 
3288     }
3289     else if (Settings->Enabled == WdfUseDefault) {
3290         enabled = TRUE;
3291 
3292         if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
3293             DECLARE_CONST_UNICODE_STRING(valueName, WDF_SX_WAKE_DEFAULT_VALUE_NAME);
3294 
3295             //
3296             // Read registry. If registry value is not found, the value of "enabled"
3297             // remains unchanged
3298             //
3299             ReadRegistrySxWake(&valueName, &enabled);
3300         }
3301         else {
3302             DoTraceLevelMessage(
3303                 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
3304                 "If registry value WdfDefaultWakeFromSleepState was present, "
3305                 "it was not read because DDI WdfDeviceAssignSxWakeSettings "
3306                 "was not called at PASSIVE_LEVEL");
3307         }
3308     }
3309     else {
3310         enabled = FALSE;
3311     }
3312 
3313     if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Set) {
3314         firstTime = FALSE;
3315     }
3316 
3317     if (m_CapsQueried == FALSE && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
3318         status = QueryForCapabilities();
3319         if (!NT_SUCCESS(status)) {
3320             return status;
3321         }
3322 
3323         //
3324         // Do not set m_CapsQueried to TRUE yet because we will do that once we
3325         // know the entire stack has been built we will do the query again.
3326         //
3327     }
3328 
3329     if (Settings->DxState == PowerDeviceMaximum) {
3330         dxState = PowerPolicyGetDeviceDeepestDeviceWakeState((SYSTEM_POWER_STATE)m_SystemWake);
3331 
3332         //
3333         // Some bus drivers
3334 
3335         // incorrectly report DeviceWake=D0 to
3336         // indicate that it does not support wake instead of specifying
3337         // PowerDeviceUnspecified and KMDF ends up requesting
3338         // a D0 irp when going to Dx. The check prevents this bug.
3339         //
3340         if (dxState < PowerDeviceD1 || dxState > PowerDeviceD3) {
3341             status = STATUS_POWER_STATE_INVALID;
3342 
3343             DoTraceLevelMessage(
3344                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3345                 "DeviceWake power state reported in device capabilities "
3346                 "%!DEVICE_POWER_STATE! indicates that device can not signal a "
3347                 "wake event, %!STATUS!",
3348                 dxState, status);
3349             return status;
3350         }
3351     }
3352     else {
3353         DEVICE_POWER_STATE dxDeepest;
3354 
3355         dxState = Settings->DxState;
3356         dxDeepest = PowerPolicyGetDeviceDeepestDeviceWakeState((SYSTEM_POWER_STATE)m_SystemWake);
3357 
3358         if (dxState > dxDeepest) {
3359             status = STATUS_POWER_STATE_INVALID;
3360             DoTraceLevelMessage(
3361                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3362                 "DxState specified by driver %!DEVICE_POWER_STATE! cannot be"
3363                 " lighter than lightest available device wake state "
3364                 "%!DEVICE_POWER_STATE!, %!STATUS!", dxState,
3365                 dxDeepest, status);
3366             return status;
3367         }
3368     }
3369 
3370     if (Settings->UserControlOfWakeSettings == WakeAllowUserControl) {
3371 
3372         // status = UpdateWmiInstanceForSxWake(AddInstance); __REACTOS__
3373 
3374         // if (!NT_SUCCESS(status)) {
3375         //     return status;
3376         // }
3377 
3378         if (Settings->Enabled == WdfUseDefault) {
3379             //
3380             // Read the registry entry for wake enabled if it's the first time.
3381             //
3382             if (firstTime && Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
3383                 DECLARE_CONST_UNICODE_STRING(valueName, WDF_SX_WAKE_ENABLED_VALUE_NAME);
3384 
3385                 //
3386                 // Read registry. If registry value is not found, the value of
3387                 // "enabled" remains unchanged
3388                 //
3389                 ReadRegistrySxWake(&valueName, &enabled);
3390             }
3391             else {
3392                 //
3393                 // Use the saved value for wake enabled.
3394                 //
3395                 enabled = m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
3396             }
3397         }
3398 
3399         overridable = TRUE;
3400     }
3401     else if (Settings->UserControlOfWakeSettings == WakeDoNotAllowUserControl) {
3402         //
3403         // No user control, just set to enabled
3404         //
3405         overridable = FALSE;
3406 
3407         // (void) UpdateWmiInstanceForSxWake(RemoveInstance); __REACTOS__
3408     }
3409 
3410     if (firstTime) {
3411         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Set = TRUE;
3412         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable = overridable;
3413 
3414         //
3415         // If ArmForWakeIfChildrenAreArmedForWake setting is set to FALSE,
3416         // then we use the legacy framework behavior which did not depend
3417         // on the child device being capable of arming for wake or not.
3418         //
3419         m_PowerPolicyMachine.m_Owner->m_WakeSettings.ArmForWakeIfChildrenAreArmedForWake =
3420             ArmForWakeIfChildrenAreArmedForWake;
3421 
3422         //
3423         // If IndicateChildWakeOnParentWake setting is set to FALSE, then
3424         // we use the legacy framework behavior wherein the wake status
3425         // is not propagated from the parent device to the child device.
3426         //
3427         m_PowerPolicyMachine.m_Owner->m_WakeSettings.IndicateChildWakeOnParentWake =
3428             IndicateChildWakeOnParentWake;
3429     }
3430 
3431     m_PowerPolicyMachine.m_Owner->m_WakeSettings.DxState = dxState;
3432 
3433     PowerPolicySetSxWakeState(enabled);
3434 
3435     return STATUS_SUCCESS;
3436 }
3437 
3438 
3439 
3440 
3441 
3442 
3443 
3444 
3445 
3446 
3447 
3448 
3449 
3450 
3451 
3452 
3453 
3454 NTSTATUS
_S0IdleQueryInstance(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG,__out PVOID OutBuffer,__out PULONG BufferUsed)3455 FxPkgPnp::_S0IdleQueryInstance(
3456     __in  CfxDevice* Device,
3457     __in  FxWmiInstanceInternal* /* Instance */,
3458     __in  ULONG /* OutBufferSize */,
3459     __out PVOID OutBuffer,
3460     __out PULONG BufferUsed
3461     )
3462 {
3463     *((BOOLEAN*) OutBuffer) =
3464         (Device->m_PkgPnp)->m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
3465     *BufferUsed = sizeof(BOOLEAN);
3466 
3467     return STATUS_SUCCESS;
3468 }
3469 
3470 NTSTATUS
_S0IdleSetInstance(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG,__in PVOID InBuffer)3471 FxPkgPnp::_S0IdleSetInstance(
3472     __in CfxDevice* Device,
3473     __in FxWmiInstanceInternal* /* Instance */,
3474     __in ULONG /* InBufferSize */,
3475     __in PVOID InBuffer
3476     )
3477 {
3478     BOOLEAN value;
3479 
3480 
3481     //
3482     // FxWmiIrpHandler makes sure the buffer is at least one byte big, so we
3483     // don't check the buffer size.
3484     //
3485 
3486     value = *(PBOOLEAN) InBuffer;
3487 
3488     (Device->m_PkgPnp)->PowerPolicySetS0IdleState(value);
3489 
3490     return STATUS_SUCCESS;
3491 }
3492 
3493 NTSTATUS
_S0IdleSetItem(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG DataItemId,__in ULONG InBufferSize,__in PVOID InBuffer)3494 FxPkgPnp::_S0IdleSetItem(
3495     __in CfxDevice* Device,
3496     __in FxWmiInstanceInternal* /* Instance */,
3497     __in ULONG DataItemId,
3498     __in ULONG InBufferSize,
3499     __in PVOID InBuffer
3500     )
3501 {
3502     BOOLEAN value;
3503 
3504     if (DataItemId != 0) {
3505         return STATUS_INVALID_DEVICE_REQUEST;
3506     }
3507 
3508     if (InBufferSize < sizeof(BOOLEAN)) {
3509         return STATUS_BUFFER_TOO_SMALL;
3510     }
3511 
3512     value = *(BOOLEAN*) InBuffer;
3513     (Device->m_PkgPnp)->PowerPolicySetS0IdleState(value);
3514 
3515     return STATUS_SUCCESS;
3516 }
3517 
3518 NTSTATUS
_SxWakeQueryInstance(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG,__out PVOID OutBuffer,__out PULONG BufferUsed)3519 FxPkgPnp::_SxWakeQueryInstance(
3520     __in  CfxDevice* Device,
3521     __in  FxWmiInstanceInternal* /* Instance */,
3522     __in  ULONG /* OutBufferSize */,
3523     __out PVOID OutBuffer,
3524     __out PULONG BufferUsed
3525     )
3526 {
3527     *((BOOLEAN*) OutBuffer) =
3528         (Device->m_PkgPnp)->m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
3529     *BufferUsed = sizeof(BOOLEAN);
3530 
3531     return STATUS_SUCCESS;
3532 }
3533 
3534 NTSTATUS
_SxWakeSetInstance(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG,__in PVOID InBuffer)3535 FxPkgPnp::_SxWakeSetInstance(
3536     __in CfxDevice* Device,
3537     __in FxWmiInstanceInternal* /* Instance */,
3538     __in ULONG /* InBufferSize */,
3539     __in PVOID InBuffer
3540     )
3541 {
3542     BOOLEAN value;
3543 
3544     //
3545     // FxWmiIrpHandler makes sure that the buffer is at least one byte big, so
3546     // we don't check the buffer size
3547     //
3548     value = *(PBOOLEAN) InBuffer;
3549 
3550     (Device->m_PkgPnp)->PowerPolicySetSxWakeState(value);
3551 
3552     return STATUS_SUCCESS;
3553 }
3554 
3555 NTSTATUS
_SxWakeSetItem(__in CfxDevice * Device,__in FxWmiInstanceInternal *,__in ULONG DataItemId,__in ULONG InBufferSize,__in PVOID InBuffer)3556 FxPkgPnp::_SxWakeSetItem(
3557     __in CfxDevice* Device,
3558     __in FxWmiInstanceInternal* /* Instance */,
3559     __in ULONG DataItemId,
3560     __in ULONG InBufferSize,
3561     __in PVOID InBuffer
3562     )
3563 {
3564     BOOLEAN value;
3565 
3566     if (DataItemId != 0) {
3567         return STATUS_INVALID_DEVICE_REQUEST;
3568     }
3569 
3570     if (InBufferSize < sizeof(BOOLEAN)) {
3571         return STATUS_BUFFER_TOO_SMALL;
3572     }
3573 
3574     value = *(BOOLEAN*) InBuffer;
3575     (Device->m_PkgPnp)->PowerPolicySetSxWakeState(value);
3576 
3577     return STATUS_SUCCESS;
3578 }
3579 
3580 _Must_inspect_result_
3581 NTSTATUS
PowerPolicyHandleSystemQueryPower(__in SYSTEM_POWER_STATE QueryState)3582 FxPkgPnp::PowerPolicyHandleSystemQueryPower(
3583     __in SYSTEM_POWER_STATE QueryState
3584     )
3585 /*++
3586 
3587 Framework Philosophy Discussion:
3588 
3589     WDM sends IRP_MN_QUERY_POWER for system power states (where system power
3590     states are S0-working, S1-light-sleep, S2-deeper-sleep, S3-deepest-sleep,
3591     S4-hibernation, S5-soft-off.)  The idea is that, if a driver can't support
3592     a particular state, it fails the query.  The problem is that this idea is
3593     horribly broken.
3594 
3595     The first problem is that WDM doesn't always send these IRPs.  In some
3596     situations, (very low battery, system getting critically hot) WDM will
3597     attempt to preserve user data by sleeping or hibernating, rather than just
3598     crashing.  This is good, but it makes a driver writer's life very difficult,
3599     since it means that you have to deal with being told to go to a particular
3600     state even if you think that your device won't be able to deal well with
3601     that state.
3602 
3603     The second problem is that, by the time the system is going to sleep, the
3604     user probably isn't still looking at the screen.  This is especially true
3605     for laptop computers, as the system is very likely sleeping because the
3606     user closed the clamshell lid.  So any attempt to ask the user how to
3607     resolve a situation where a driver doesn't want to go to a low power state
3608     is futile.  Furthermore, even when the screen is still available, users
3609     dislike it when they push the sleep button or the power button on their
3610     machines and the machines don't do what they were told to do.
3611 
3612     The third problem is related to the second.  While there may be completely
3613     legitimate reasons for the driver to want to delay or even to veto a
3614     transition into a sleep state, (an example of a valid scenario would be one
3615     in which the driver was involved in burning a CD, an operation which can't
3616     be interrupted,) there isn't any good way for a driver to interact with a
3617     user anyhow.  (Which desktop is the right one to send messages to?  What
3618     should the UI for problem resolution look like?  How does a driver put up
3619     UI anyhow?)
3620 
3621     All the driver really knows is that it will or won't be able to maintain
3622     device state, and it will or won't be able to get enough power to arm
3623     any wake events that it might want to deliver (like PME#.)
3624 
3625     Consequently, the designers of the PnP/Power model in the Framework have
3626     decided that all QueryPower-Sx IRPs will be completed successfully
3627     except if the device cannot maintain enough power to trigger its wake
3628     signal *AND* if the system supports lighter sleep states than the
3629     one that is currently being queried.  (If it does, then the kernel's power
3630     manager will turn right around and query for those, next.)
3631 
3632     This story usually brings up a few objections:
3633 
3634     1)  My device is important!  When it's operating, I don't want
3635         the machine to just fall asleep.  I need to fail QueryPower-Sx to
3636         prevent that.
3637 
3638     This objection is an unfortunate consequence of the existing DDK.  There
3639     is a perfectly good API that allows a driver to say that the machine
3640     shouldn't just fall asleep.  (See PoSetSystemState.)  If a user presses
3641     a button telling the machine to go to sleep, then the driver has a
3642     responsibility to do that.
3643 
3644     2)  There are certain operations that just can't be interrupted!
3645 
3646     While that's true, those operations started somewhere, probably in user-
3647     mode.  Those same user-mode components would be much, much better suited
3648     toward negotiating with the user or with other components to figure out
3649     what to do when the uninterruptable must be interrupted.  User-mode
3650     components get notification that the system is going to sleep and they
3651     can delay or veto the transition.  Get over the idea that your driver
3652     needs to be involved, too.
3653 
3654 Routine Description:
3655 
3656     Determines if for the passed in System state, if we can wake the machine
3657     from it.  If the query state is the machine's minimum system state, then
3658     we always succeed it because we want the machine to go to at least some
3659     sleeping state.  We always succeed hibernate and system off requests as well.
3660 
3661 Arguments:
3662 
3663     QueryState - The proposed system state
3664 
3665 Return Value:
3666 
3667     NT_SUCCESS if the queried state should be allowed, !NT_SUCCESS otherwise
3668 
3669   --*/
3670 {
3671     PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
3672     NTSTATUS status;
3673 
3674     if (QueryState >= PowerSystemHibernate ||
3675         PowerPolicyCanWakeFromSystemState(QueryState)) {
3676         //
3677         // If the query is for the machine's minimum S state or we going into
3678         // hibernate or off, always succeed it.
3679         //
3680         status = STATUS_SUCCESS;
3681     }
3682     else {
3683 
3684         //
3685         // On Windows Vista and above, its OK to return a failure status code
3686         // if the system is going into an S state at which the device cannot
3687         // wake the system.
3688         //
3689         ASSERT(FxLibraryGlobals.OsVersionInfo.dwMajorVersion >= 6);
3690 
3691         //
3692         // The S state the machine is going into is one where we can't
3693         // wake it up because our D state is too low for this S state.
3694         // Since this isn't the minimum S state the machine is capable
3695         // of, reject the current query.
3696         //
3697         DoTraceLevelMessage(
3698             FxDriverGlobals, TRACE_LEVEL_WARNING, TRACINGPNP,
3699             "failing system query power because the device cannot wake the "
3700             "machine from S%d",
3701             QueryState - 1);
3702 
3703         status = STATUS_POWER_STATE_INVALID;
3704     }
3705 
3706     return status;
3707 }
3708 
3709 VOID
PowerPolicySetS0IdleState(__in BOOLEAN State)3710 FxPkgPnp::PowerPolicySetS0IdleState(
3711     __in BOOLEAN State
3712     )
3713 {
3714     m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled = State ? TRUE : FALSE;
3715     m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty = TRUE;
3716     PowerPolicyProcessEvent(PwrPolS0IdlePolicyChanged);
3717 }
3718 
3719 VOID
PowerPolicySetSxWakeState(__in BOOLEAN State)3720 FxPkgPnp::PowerPolicySetSxWakeState(
3721     __in BOOLEAN State
3722     )
3723 /*++
3724 
3725 Routine Description:
3726     Sets the wake from Sx state
3727 
3728     No need to post an event to the power policy state machine because we
3729     will not change any active due to a change in this setting.  We only
3730     evaluate this state when going into Sx and once in this state we will not
3731     change our behavior until the next Sx, which will then evaluate this state.
3732 
3733 Arguments:
3734     State - New state
3735 
3736 Return Value:
3737     VOID
3738 
3739   --*/
3740 {
3741     m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled = State ? TRUE : FALSE;
3742     m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty = TRUE;
3743 
3744     //
3745     // Since we are not posting an event to the power policy state machine, try
3746     // to write out the value now, otherwise it will be written when we
3747     // transition
3748     //
3749     if (Mx::MxGetCurrentIrql() == PASSIVE_LEVEL) {
3750         NTSTATUS status;
3751         LONGLONG timeout;
3752 
3753         timeout = 0;
3754 
3755         //
3756         // If the lock is already acquired on this thread, this will fail, which
3757         // is OK.
3758         //
3759         status = m_PowerPolicyMachine.m_StateMachineLock.AcquireLock(
3760             GetDriverGlobals(),
3761             &timeout
3762             );
3763 
3764         if (FxWaitLockInternal::IsLockAcquired(status)) {
3765             SaveState(TRUE);
3766 
3767             m_PowerPolicyMachine.m_StateMachineLock.ReleaseLock(
3768                 GetDriverGlobals()
3769                 );
3770         }
3771     }
3772 }
3773 
3774 VOID
SetDeviceFailed(__in WDF_DEVICE_FAILED_ACTION FailedAction)3775 FxPkgPnp::SetDeviceFailed(
3776     __in WDF_DEVICE_FAILED_ACTION FailedAction
3777     )
3778 /*++
3779 
3780 Routine Description:
3781     Marks the device as a victim of catastrophic failure, either in software
3782     or in hardware.
3783 
3784     If AttemptToRestart is TRUE, then we should try to get the stack re-built
3785     after it has been torn down.  This would typically be the case the failure
3786     was in the software, and possibly not be the case if the failure was in
3787     the hardware.
3788 
3789 Arguments:
3790     FailedAction - action to take once the stack has been removed
3791 
3792 Return Value:
3793     None
3794 
3795   --*/
3796 {
3797     NTSTATUS    status;
3798     MdDeviceObject pdo;
3799 
3800 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
3801     if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(2, 15) == FALSE &&
3802         FailedAction == WdfDeviceFailedAttemptRestart) {
3803 
3804         FailedAction = WdfDeviceFailedNoRestart;
3805         DoTraceLevelMessage(
3806             GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGDEVICE,
3807             "WdfDeviceFailedAttemptRestart is only available for UMDF 2.15 "
3808             "and later drivers. Reverting to WdfDeviceFailedNoRestart.");
3809     }
3810 #endif
3811 
3812     m_FailedAction = (BYTE) FailedAction;
3813 
3814     //
3815     // This will cause the PnP manager to tear down this stack, even if
3816     // the PDO can't be surprise-removed.
3817     //
3818     m_Failed = TRUE;
3819 
3820     if (FailedAction == WdfDeviceFailedAttemptRestart) {
3821         //
3822         // Attempt to get the PDO surprise-removed.
3823         //
3824         status = AskParentToRemoveAndReenumerate();
3825 
3826         if (NT_SUCCESS(status)) {
3827             return;
3828         }
3829     }
3830 
3831 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
3832     //
3833     // In between creating a PDO WDFDEVICE and it starting, if this DDI is called,
3834     // we will not have a valid PDO.  Make sure it is valid before we proceed.
3835     //
3836     pdo = m_Device->GetSafePhysicalDevice();
3837 
3838     if (pdo != NULL) {
3839         //
3840         // Now tell the PnP manager to re-query us for our state.
3841         //
3842         MxDeviceObject physicalDeviceObject(pdo);
3843 
3844         physicalDeviceObject.InvalidateDeviceState(
3845             m_Device->GetDeviceObject()
3846             );
3847     }
3848 #else // USER_MODE
3849     m_Device->GetMxDeviceObject()->InvalidateDeviceState(
3850         m_Device->GetDeviceObject());
3851     UNREFERENCED_PARAMETER(pdo);
3852 #endif
3853 }
3854 
3855 _Must_inspect_result_
3856 NTSTATUS
_PnpDeviceUsageNotification(__inout FxPkgPnp * This,__inout FxIrp * Irp)3857 FxPkgPnp::_PnpDeviceUsageNotification(
3858     __inout FxPkgPnp* This,
3859     __inout FxIrp *Irp
3860     )
3861 {
3862     return This->PnpDeviceUsageNotification(Irp);
3863 }
3864 
3865 _Must_inspect_result_
3866 NTSTATUS
PnpDeviceUsageNotification(__inout FxIrp * Irp)3867 FxPkgPnp::PnpDeviceUsageNotification(
3868     __inout FxIrp* Irp
3869     )
3870 {
3871     FxRelatedDeviceList* pList;
3872     FxRelatedDevice *pDependent;
3873     FxAutoIrp relatedIrp(NULL), parentIrp(NULL);
3874     MxDeviceObject topOfParentStack;
3875     DEVICE_USAGE_NOTIFICATION_TYPE type;
3876     NTSTATUS status;
3877     MxDeviceObject pAttached;
3878     MdIrp pNewIrp;
3879     CCHAR maxStack;
3880     BOOLEAN inPath, supported;
3881     ULONG oldFlags;
3882     MxAutoWorkItem workItem;
3883 
3884     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3885                         "Entering DeviceUsageNotification handler");
3886 
3887     status = STATUS_SUCCESS;
3888 
3889     type = Irp->GetParameterUsageNotificationType();
3890     inPath = Irp->GetParameterUsageNotificationInPath();
3891     supported = FALSE;
3892 
3893     DoTraceLevelMessage(
3894         GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3895         "type %x, in path %x, can support paging %x, dump file %x, "
3896         "hiber file %x, boot file %x",
3897         type, inPath,
3898         IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFilePaging)),
3899         IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileDump)),
3900         IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileHibernation)),
3901         IsUsageSupported(_SpecialTypeToUsage(WdfSpecialFileBoot)));
3902 
3903 
3904     if (type >= static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFilePaging)
3905         && type < static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileMax)) {
3906         if (inPath) {
3907             if (m_Device->IsFilter()) {
3908                 //
3909                 // Filters always support usage notifications
3910                 //
3911                 supported = TRUE;
3912             }
3913             else {
3914                 supported = IsUsageSupported(type);
3915             }
3916         }
3917         else {
3918             //
3919             // We always handle notifications where we are out of the path
3920             //
3921             supported = TRUE;
3922         }
3923     }
3924 
3925     if (supported == FALSE) {
3926         status = STATUS_NOT_IMPLEMENTED;
3927 
3928         DoTraceLevelMessage(
3929             GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3930             "Usage type %x not supported, %!STATUS!", type, status);
3931 
3932         return CompletePnpRequest(Irp, status);
3933     }
3934 
3935     //
3936     // Usage notification IRP gets forwarded to parent stack or to
3937     // dependent stack. Since in such cases (with deep device tree) the stack
3938     // may run out quickly, ensure there is enough stack, otherwise use a
3939     // workitem.
3940     //
3941     if (Mx::MxHasEnoughRemainingThreadStack() == FALSE &&
3942         (m_Device->IsPdo() ||
3943         m_UsageDependentDeviceList != NULL)) {
3944 
3945         status = workItem.Allocate(m_Device->GetDeviceObject());
3946         if (!NT_SUCCESS(status)) {
3947             DoTraceLevelMessage(
3948                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3949                 "WDFDEVICE %p !devobj %p could not allocate workitem "
3950                 "to send usage notification type %d, inpath %d, %!STATUS!",
3951                 m_Device->GetHandle(),
3952                 m_Device->GetDeviceObject(),
3953                 type, inPath, status);
3954 
3955             return CompletePnpRequest(Irp, status);
3956         }
3957     }
3958 
3959     //
3960     // Usage notification is supported.  Set the flags on the device object
3961     // before processing this any further and save the current flags on the
3962     // device object.
3963     //
3964     oldFlags = SetUsageNotificationFlags(type, inPath);
3965 
3966     if (m_Device->IsPdo()) {
3967 
3968 
3969 
3970 
3971 
3972 
3973 
3974 
3975 
3976 
3977 
3978 
3979         topOfParentStack.SetObject(
3980             m_Device->m_ParentDevice->GetAttachedDeviceReference());
3981 
3982         pNewIrp = FxIrp::AllocateIrp(topOfParentStack.GetStackSize());
3983         if (pNewIrp != NULL) {
3984             parentIrp.SetIrp(pNewIrp);
3985 
3986             //
3987             // parentIrp now owns the irp
3988             //
3989             pNewIrp = NULL;
3990 
3991             status = SendDeviceUsageNotification(&topOfParentStack,
3992                                                  &parentIrp,
3993                                                  &workItem,
3994                                                  Irp,
3995                                                  FALSE);
3996         }
3997         else {
3998             status = STATUS_INSUFFICIENT_RESOURCES;
3999 
4000             DoTraceLevelMessage(
4001                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4002                 "WDFDEVICE %p could not allocate PIRP for parent !devobj %p to "
4003                 "send usage notification type %d, inpath %d, %!STATUS!",
4004                 m_Device->GetHandle(), topOfParentStack.GetObject(),
4005                 type, inPath, status);
4006         }
4007         topOfParentStack.DereferenceObject();
4008         topOfParentStack.SetObject(NULL);
4009 
4010         if (!NT_SUCCESS(status)) {
4011             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
4012                                 "Exit %!STATUS!", status);
4013 
4014             RevertUsageNotificationFlags(type, inPath, oldFlags);
4015             return CompletePnpRequest(Irp, status);
4016         }
4017     }
4018 
4019     maxStack = 0;
4020     pDependent = NULL;
4021 
4022     //
4023     // If the driver supports the given special file, lets notify dependent
4024     // stacks.
4025     //
4026 
4027     //
4028     // LockForEnum will lock out new changes to the list until we unlock the list.
4029     // We remain at passive level once we are locked.
4030     //
4031     if (m_UsageDependentDeviceList != NULL) {
4032         //
4033         // We capture the m_UsageDependentDeviceList pointer value so that we
4034         // always use the same pointer value and that we have matched actions
4035         // (lock for enum / unlock from enum).  What we are trying to avoid is
4036         // this:
4037         // 1) we do not lock for enum because m_UsageDependentDeviceList == NULL
4038         // 2) in the middle of this function, m_UsageDependentDeviceList is
4039         //    assigned a pointer value
4040         // 3) we try to unlock from enum later (or iterate, thinking the enum
4041         //    lock is held) by checking m_UsageDependentDeviceList for NULL, and
4042         //    now that is != NULL, use it.
4043         //
4044         // By capturing the pointer now, we either have a list or not and we don't
4045         // hit situations 2 or 3.  So, the rule is every subseqeunt time we need
4046         // to check if there is valid m_UsageDependentDeviceList pointer, we
4047         // use pList, but when we use the list, we can use m_UsageDependentDeviceList
4048         // directly.
4049         //
4050         pList = m_UsageDependentDeviceList;
4051 
4052         m_UsageDependentDeviceList->LockForEnum(GetDriverGlobals());
4053 
4054         while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
4055 
4056             MxDeviceObject deviceObject(pDependent->GetDevice());
4057 
4058 
4059 
4060 
4061 
4062 
4063 
4064 
4065 
4066 
4067 
4068 
4069 
4070             pAttached.SetObject(deviceObject.GetAttachedDeviceReference());
4071 
4072             if (pAttached.GetStackSize() > maxStack) {
4073                 maxStack = pAttached.GetStackSize();
4074             }
4075 
4076             pAttached.DereferenceObject();
4077         }
4078     }
4079     else {
4080         pList = NULL;
4081     }
4082 
4083     if (maxStack > 0) {
4084         //
4085         // If we have a maxStack size, we have a list as well
4086         //
4087         ASSERT(m_UsageDependentDeviceList != NULL);
4088 
4089         //
4090         // Allocate one irp for all the stacks so that we don't have an
4091         // allocation later.  This way, once we have the irp, we can send the
4092         // usage notification to all stacks reliably, as well as the reverting
4093         // of the notification if we encounter failure.
4094         //
4095         pNewIrp = FxIrp::AllocateIrp(maxStack);
4096         if (pNewIrp == NULL) {
4097             status = STATUS_INSUFFICIENT_RESOURCES;
4098 
4099             DoTraceLevelMessage(
4100                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4101                 "WDFDEVICE %p could not allocate IRP to send usage notifications"
4102                 " to related stacks, type %d, inpath %d, status %!STATUS!",
4103                 m_Device->GetHandle(), type, inPath, status);
4104         }
4105         else {
4106             MxDeviceObject dependentDevice;
4107 
4108             //
4109             // relatedIrp will free the irp when it goes out of scope
4110             //
4111             relatedIrp.SetIrp(pNewIrp);
4112 
4113             //
4114             // Walk our collection of dependent device stacks, and notify
4115             // each stack of the device notification.  If any fail, notify
4116             // the stacks who already were told of the reverted behavior.
4117             //
4118             pDependent = NULL;
4119             while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
4120                 dependentDevice.SetObject(pDependent->GetDevice());
4121                 status = SendDeviceUsageNotification(&dependentDevice,
4122                                                      &relatedIrp,
4123                                                      &workItem,
4124                                                      Irp,
4125                                                      FALSE);
4126 
4127                 if (!NT_SUCCESS(status)) {
4128                     FxRelatedDevice* pDependent2;
4129 
4130                     pDependent2 = NULL;
4131 
4132                     //
4133                     // A device failed the device usage notification request.
4134                     // Notify the stacks that didn't fail, so they can unwind
4135                     // the operation.
4136                     //
4137                     while ((pDependent2 = m_UsageDependentDeviceList->GetNextEntry(pDependent2)) != NULL &&
4138                            pDependent2 != pDependent) {
4139                         dependentDevice.SetObject(pDependent2->GetDevice());
4140 
4141                         //
4142                         // We're already in a failure path. We can't do anything
4143                         // about yet another failure. So we ignore the return
4144                         // value.
4145                         //
4146                         (void) SendDeviceUsageNotification(&dependentDevice,
4147                                                            &relatedIrp,
4148                                                            &workItem,
4149                                                            Irp,
4150                                                            TRUE);
4151                     }
4152 
4153                     //
4154                     // Now break out of our outter loop.
4155                     //
4156                     break;
4157                 }
4158             }
4159         }
4160     }
4161 
4162     //
4163     // If we are successful to this point, then send the IRP down the
4164     // stack.
4165     //
4166     if (NT_SUCCESS(status)) {
4167         BOOLEAN referenceSucceeded, sendDown;
4168 
4169         referenceSucceeded = FALSE;
4170         sendDown = TRUE;
4171 
4172         //
4173         // Make sure the stack is in D0 before sending down the request.  This
4174         // will at least guarantee that all devices below this one are in D0
4175         // when the make the transition from power pageable to non or vice versa.
4176         //
4177         if (IsPowerPolicyOwner()) {
4178             status = PowerReference(TRUE);
4179 
4180             if (NT_SUCCESS(status)) {
4181                 referenceSucceeded = TRUE;
4182             }
4183             else {
4184                 Irp->SetStatus(status);
4185                 sendDown = FALSE;
4186             }
4187         }
4188 
4189         if (sendDown) {
4190             //
4191             // If we supported the usage, set the status to success, otherwise we
4192             // keep the status in the irp as it arrived to this device
4193             //
4194             if (supported) {
4195                 Irp->SetStatus(status);
4196             }
4197             status = SendIrpSynchronously(Irp);
4198         }
4199 
4200         //
4201         // Transitioning from a thread which was power pagable to non power
4202         // pagable.  We now need a power thread for the stack, ask for it.
4203         // Note that there is no need for power thread in case of "boot"
4204         // notification since boot notification doesn't require clearing device's
4205         // DO_POWER_PAGABLE flag (power thread is required when handling power
4206         // irp at dispatch level which can happen if the DO_POWER_PAGABLE flag
4207         // is cleared).
4208         //
4209         // NOTE:  Once we have a power thread, we never go back to using work
4210         //        items even though the stack may revert to power pagable.
4211         //        This is an acceptable tradeoff between resource usage and
4212         //        WDF complexity.
4213         //
4214         //
4215         if (NT_SUCCESS(status) &&
4216             inPath &&
4217             (HasPowerThread() == FALSE) &&
4218             type != static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileBoot)
4219             ) {
4220             status = QueryForPowerThread();
4221 
4222             if (!NT_SUCCESS(status)) {
4223                 //
4224                 // Keep status the same through out so we can set it back in
4225                 // the irp when we are done.
4226                 //
4227                 if (m_Device->IsPdo()) {
4228                     //
4229                     // need to revert our parent's stack
4230                     //
4231 
4232 
4233 
4234 
4235 
4236 
4237 
4238 
4239 
4240 
4241 
4242 
4243                     topOfParentStack.SetObject(
4244                         m_Device->m_ParentDevice->GetAttachedDeviceReference());
4245 
4246                     //
4247                     // Ignore the status because we can't do anything on failure
4248                     //
4249                     (void) SendDeviceUsageNotification(&topOfParentStack,
4250                                                        &parentIrp,
4251                                                        &workItem,
4252                                                        Irp,
4253                                                        TRUE);
4254 
4255                     topOfParentStack.DereferenceObject();
4256                 }
4257                 else {
4258                     //
4259                     // Notify the stack below us
4260                     //
4261                     Irp->CopyCurrentIrpStackLocationToNext();
4262                     Irp->SetParameterUsageNotificationInPath(FALSE);
4263 
4264                     //
4265                     // Required for pnp irps
4266                     //
4267                     Irp->SetStatus(STATUS_NOT_SUPPORTED);
4268 
4269                     //
4270                     // Ignore the status because we can't do anything on failure
4271                     //
4272                     (void) Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
4273                 }
4274 
4275                 Irp->SetStatus(status);
4276             }
4277         }
4278 
4279         //
4280         // Now check whether the lower devices succeeded or failed.  If they
4281         // failed, back out our changes and propogate the failure.
4282         //
4283         if (!NT_SUCCESS(status)) {
4284             //
4285             // Revert the flags set on the device object.
4286             //
4287             RevertUsageNotificationFlags(type, inPath, oldFlags);
4288 
4289             //
4290             // Notify dependent stacks of the failure.
4291             //
4292             pDependent = NULL;
4293 
4294             //
4295             // See pList initiatilazation as to why we compare pList for != NULL
4296             // and not m_UsageDependentDeviceList.
4297             //
4298             if (pList != NULL) {
4299                 MxDeviceObject dependentDevice;
4300 
4301                 while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
4302                     dependentDevice.SetObject(pDependent->GetDevice());
4303 
4304                     //
4305                     // We're already in a failure path. We can't do anything
4306                     // about yet another failure. So we ignore the return value.
4307                     //
4308                     (void) SendDeviceUsageNotification(&dependentDevice,
4309                                                        &relatedIrp,
4310                                                        &workItem,
4311                                                        Irp,
4312                                                        TRUE);
4313                 }
4314             }
4315         }
4316 
4317         //
4318         // By this point, we have propagated the notification to dependent devices
4319         // and lower stack, and if anyone failed during that time, we also
4320         // propagated failure to dependent stacks and lower stack.
4321         // If status is success at this point, invoke the driver's callback.
4322         //
4323         if (NT_SUCCESS(status)) {
4324             //
4325             // Invoke callback. Note that only one of the callbacks
4326             // DeviceUsageNotification or DeviceUsgeNotificationEx will get
4327             // invoked since only one of the callbacks at a time is supported.
4328             // We ensured that during registration of the callback.
4329             // Note that Ex callback will return success if driver did not
4330             // supply any callback.
4331             //
4332             m_DeviceUsageNotification.Invoke(m_Device->GetHandle(),
4333                                              _UsageToSpecialType(type),
4334                                              inPath);
4335 
4336             status = m_DeviceUsageNotificationEx.Invoke(
4337                                         m_Device->GetHandle(),
4338                                         _UsageToSpecialType(type),
4339                                         inPath
4340                                         );
4341 
4342             if (!NT_SUCCESS(status)) {
4343                 //
4344                 // Driver's callback returned failure. We need to propagate
4345                 // failure to lower stack and dependent stacks.
4346                 //
4347                 //
4348                 // Keep status the same through out so we can set it back in
4349                 // the irp when we are done.
4350                 //
4351                 if (m_Device->IsPdo()) {
4352                     //
4353                     // need to revert our parent's stack
4354                     //
4355 
4356 
4357 
4358 
4359 
4360 
4361 
4362 
4363 
4364 
4365 
4366 
4367                     topOfParentStack.SetObject(
4368                         m_Device->m_ParentDevice->GetAttachedDeviceReference());
4369 
4370                     //
4371                     // Ignore the status because we can't do anything on failure
4372                     //
4373                     (void) SendDeviceUsageNotification(&topOfParentStack,
4374                                                        &parentIrp,
4375                                                        &workItem,
4376                                                        Irp,
4377                                                        TRUE);
4378 
4379                     topOfParentStack.DereferenceObject();
4380                 }
4381                 else {
4382                     //
4383                     // Notify the stack below us
4384                     //
4385                     Irp->CopyCurrentIrpStackLocationToNext();
4386                     Irp->SetParameterUsageNotificationInPath(FALSE);
4387 
4388                     //
4389                     // Required for pnp irps
4390                     //
4391                     Irp->SetStatus(STATUS_NOT_SUPPORTED);
4392 
4393                     //
4394                     // Ignore the status because we can't do anything on failure
4395                     //
4396                     (void) Irp->SendIrpSynchronously(m_Device->GetAttachedDevice());
4397                 }
4398 
4399                 Irp->SetStatus(status);
4400 
4401                 //
4402                 // Revert the flags set on the device object.
4403                 //
4404                 RevertUsageNotificationFlags(type, inPath, oldFlags);
4405 
4406                 //
4407                 // Notify dependent stacks of the failure.
4408                 //
4409                 pDependent = NULL;
4410 
4411                 //
4412                 // See pList initiatilazation as to why we compare pList for != NULL
4413                 // and not m_UsageDependentDeviceList.
4414                 //
4415                 if (pList != NULL) {
4416                     MxDeviceObject dependentDevice;
4417 
4418                     while ((pDependent = m_UsageDependentDeviceList->GetNextEntry(pDependent)) != NULL) {
4419                         dependentDevice.SetObject(pDependent->GetDevice());
4420 
4421                         //
4422                         // We're already in a failure path. We can't do anything
4423                         // about yet another failure. So we ignore the return value.
4424                         //
4425                         (void) SendDeviceUsageNotification(&dependentDevice,
4426                                                            &relatedIrp,
4427                                                            &workItem,
4428                                                            Irp,
4429                                                            TRUE);
4430                     }
4431                 }
4432             }
4433 
4434             if (NT_SUCCESS(status)) {
4435 
4436                 CommitUsageNotification(type, oldFlags);
4437 
4438                 //
4439                 // If we are in the dump file path, we cannot idle out because we
4440                 // can experience a crash dump at any time.
4441                 //
4442                 if (IsPowerPolicyOwner() && type == DeviceUsageTypeDumpFile) {
4443                     //
4444                     // Add a reference everytime we are notified of being in the
4445                     // path, no need to match the first inPath notification and the
4446                     // last !inPath notification.
4447                     //
4448                     if (inPath) {
4449                         NTSTATUS refStatus;
4450 
4451                         ASSERT(GetUsageCount(type) > 0);
4452 
4453                         //
4454                         // Since our previous synchronous power reference succeeded,
4455                         // an addtional reference while we are powered up should
4456                         // never fail.
4457                         //
4458                         refStatus = PowerReference(FALSE);
4459 #if DBG
4460                         ASSERT(NT_SUCCESS(refStatus));
4461 #else
4462                         UNREFERENCED_PARAMETER(refStatus);
4463 #endif
4464                     }
4465                     else {
4466                         ASSERT(GetUsageCount(type) >= 0);
4467                         PowerDereference();
4468                     }
4469                 }
4470             }
4471         }
4472 
4473         //
4474         // We no longer need to be in D0 if we don't have to be.
4475         //
4476         if (referenceSucceeded) {
4477             ASSERT(IsPowerPolicyOwner());
4478             PowerDereference();
4479         }
4480     }
4481 
4482     //
4483     // See pList initiatilazation as to why we compare pList for != NULL
4484     // and not m_UsageDependentDeviceList.
4485     //
4486     if (pList != NULL) {
4487         m_UsageDependentDeviceList->UnlockFromEnum(GetDriverGlobals());
4488     }
4489 
4490     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
4491                         "Exit %!STATUS!", status);
4492 
4493     return CompletePnpRequest(Irp, status);
4494 }
4495 
4496 ULONG
SetUsageNotificationFlags(__in DEVICE_USAGE_NOTIFICATION_TYPE Type,__in BOOLEAN InPath)4497 FxPkgPnp::SetUsageNotificationFlags(
4498     __in DEVICE_USAGE_NOTIFICATION_TYPE Type,
4499     __in BOOLEAN InPath
4500     )
4501 /*++
4502 
4503 Routine Description:
4504 
4505     This routine sets the usage notification flags on the device object (for
4506     non-boot usages) and updates the special file usage count .
4507 
4508 Arguments:
4509 
4510     Type - the special file type - paging, hibernate, dump or boot file.
4511 
4512     InPath - indicates whether the system is creating or removing the special
4513         file on the device.
4514 
4515 Return Value:
4516 
4517     Returns the old flags on the device object.
4518 
4519 --*/
4520 {
4521     PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4522     ULONG oldFlags, newFlags;
4523 
4524     oldFlags = m_Device->GetDeviceObjectFlags();
4525 
4526     //
4527 
4528     //
4529     DoTraceLevelMessage(
4530         FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
4531         "Before:  type %d, in path %d, special count %d, flags 0x%x, "
4532         "device %p (WDFDEVICE %p), is pageable capable %d",
4533         Type, InPath, GetUsageCount(Type), oldFlags,
4534         m_Device->GetDeviceObject(), m_Device->GetHandle(),
4535         m_Device->IsPowerPageableCapable());
4536 
4537     //
4538     // Adjust our "special file count".
4539     //
4540     AdjustUsageCount(Type, InPath);
4541 
4542     //
4543     // Boot notification doesn't require updating device flags.
4544     //
4545     if (Type == static_cast<DEVICE_USAGE_NOTIFICATION_TYPE>(WdfSpecialFileBoot)) {
4546         return oldFlags;
4547     }
4548 
4549     if (m_Device->IsFilter()) {
4550         //
4551         // Clear the previous flags and reset them to the attached
4552         // device's flags
4553         //
4554         newFlags = oldFlags & ~(DO_POWER_PAGABLE | DO_POWER_INRUSH);
4555         newFlags |= m_Device->GetAttachedDeviceObjectFlags() &
4556                 (DO_POWER_PAGABLE | DO_POWER_INRUSH);
4557         m_Device->SetDeviceObjectFlags(newFlags);
4558     }
4559     else {
4560         if (InPath) {
4561             m_Device->SetDeviceObjectFlags(
4562                 m_Device->GetDeviceObjectFlags() & ~DO_POWER_PAGABLE
4563                 );
4564         }
4565         else {
4566             if (m_Device->IsPowerPageableCapable() && IsInSpecialUse() == FALSE) {
4567                 m_Device->SetDeviceObjectFlags(
4568                     m_Device->GetDeviceObjectFlags() | DO_POWER_PAGABLE
4569                     );
4570             }
4571         }
4572     }
4573 
4574     return oldFlags;
4575 }
4576 
4577 VOID
RevertUsageNotificationFlags(__in DEVICE_USAGE_NOTIFICATION_TYPE Type,__in BOOLEAN InPath,__in ULONG OldFlags)4578 FxPkgPnp::RevertUsageNotificationFlags(
4579     __in DEVICE_USAGE_NOTIFICATION_TYPE Type,
4580     __in BOOLEAN InPath,
4581     __in ULONG OldFlags
4582     )
4583 /*++
4584 
4585 Routine Description:
4586 
4587     This routine reverts the usage notification flags to the old flags on
4588     the device object and updates the special file usage count.
4589 
4590 Arguments:
4591 
4592     Type - the special file type - paging, hibernate or dump file.
4593 
4594     InPath - indicates whether the system is creating or removing the special
4595         file on the device.
4596 
4597     OldFlags - the previous flags on the device object.
4598 
4599 --*/
4600 {
4601     //
4602     // Re-adjust our "special file count".
4603     //
4604     InPath = !InPath;
4605     AdjustUsageCount(Type, InPath);
4606 
4607     //
4608     // Restore the flags on the device object.
4609     //
4610     m_Device->SetDeviceObjectFlags(OldFlags);
4611 }
4612 
4613 VOID
CommitUsageNotification(__in DEVICE_USAGE_NOTIFICATION_TYPE Type,__in ULONG OldFlags)4614 FxPkgPnp::CommitUsageNotification(
4615     __in DEVICE_USAGE_NOTIFICATION_TYPE Type,
4616     __in ULONG OldFlags
4617     )
4618 /*++
4619 
4620 Routine Description:
4621 
4622     This routine commits the usage notification flags on the device object
4623     and invokes the usage notification callbacks.  If the current flags on
4624     the device object indicates that there was a transition from power-pagable
4625     to non power-pagable, or vice-versa, then an event is posted to the power
4626     state machine to notify it of the change.  After this routine is called
4627     the PNP manager will no longer be able to disable the device.
4628 
4629 Arguments:
4630 
4631     Type - the special file type - paging, hibernation or crash dump file.
4632 
4633     InPath - indicates whether the system is creating or removing the special
4634         file on the device.
4635 
4636     OldFlags - the previous flags on the device object.
4637 
4638 --*/
4639 {
4640     PFX_DRIVER_GLOBALS FxDriverGlobals = GetDriverGlobals();
4641     ULONG newFlags;
4642 
4643     newFlags = m_Device->GetDeviceObjectFlags();
4644 
4645     if ((OldFlags & DO_POWER_PAGABLE) == DO_POWER_PAGABLE &&
4646         (newFlags & DO_POWER_PAGABLE) == 0) {
4647         //
4648         // We transitioned from a power pageable to a non power pageable
4649         // device.  Move the power state machine to the appropriate
4650         // state.
4651         //
4652         PowerProcessEvent(PowerMarkNonpageable);
4653     }
4654 
4655     if ((OldFlags & DO_POWER_PAGABLE) == 0 &&
4656         (newFlags & DO_POWER_PAGABLE) == DO_POWER_PAGABLE) {
4657         //
4658         // We transitioned from a non power pageable to a power pageable
4659         // device.  Move the power state machine to the appropriate
4660         // state.
4661         //
4662         PowerProcessEvent(PowerMarkPageable);
4663     }
4664 
4665     //
4666     // Notify PNP that it should no longer be able
4667     // to disable this device.
4668     //
4669     MxDeviceObject physicalDeviceObject(
4670                                 m_Device->GetPhysicalDevice()
4671                                 );
4672     physicalDeviceObject.InvalidateDeviceState(
4673         m_Device->GetDeviceObject()
4674         );
4675 
4676     DoTraceLevelMessage(
4677         FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
4678         "After:  special count %d, flags 0x%x, device %p (WDFDEVICE %p)",
4679         GetUsageCount(Type), newFlags,
4680         m_Device->GetDeviceObject(), m_Device->GetHandle());
4681 }
4682 
4683 _Must_inspect_result_
4684 NTSTATUS
AddUsageDevice(__in MdDeviceObject DependentDevice)4685 FxPkgPnp::AddUsageDevice(
4686     __in MdDeviceObject DependentDevice
4687     )
4688 {
4689     FxRelatedDevice* pRelated;
4690     NTSTATUS status;
4691 
4692     if (m_UsageDependentDeviceList == NULL) {
4693         KIRQL irql;
4694 
4695         Lock(&irql);
4696         if (m_UsageDependentDeviceList == NULL) {
4697             m_UsageDependentDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
4698 
4699             if (m_UsageDependentDeviceList != NULL) {
4700                 status = STATUS_SUCCESS;
4701             }
4702             else {
4703                 status = STATUS_INSUFFICIENT_RESOURCES;
4704 
4705                 DoTraceLevelMessage(
4706                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4707                     "Could not allocate usage device list for WDFDEVICE %p, "
4708                     "%!STATUS!", m_Device->GetHandle(), status);
4709             }
4710 
4711         }
4712         else {
4713             //
4714             // another thread allocated the list already
4715             //
4716             status = STATUS_SUCCESS;
4717         }
4718         Unlock(irql);
4719 
4720         if (!NT_SUCCESS(status)) {
4721             return status;
4722         }
4723     }
4724 
4725     pRelated = new(GetDriverGlobals())
4726         FxRelatedDevice(DependentDevice, GetDriverGlobals());
4727 
4728     if (pRelated == NULL) {
4729         return STATUS_INSUFFICIENT_RESOURCES;
4730     }
4731 
4732     status = m_UsageDependentDeviceList->Add(GetDriverGlobals(), pRelated);
4733 
4734     if (!NT_SUCCESS(status)) {
4735         pRelated->DeleteFromFailedCreate();
4736     }
4737 
4738     return status;
4739 }
4740 
4741 VOID
RemoveUsageDevice(__in MdDeviceObject DependentDevice)4742 FxPkgPnp::RemoveUsageDevice(
4743     __in MdDeviceObject DependentDevice
4744     )
4745 {
4746     if (m_UsageDependentDeviceList != NULL) {
4747         m_UsageDependentDeviceList->Remove(GetDriverGlobals(), DependentDevice);
4748     }
4749 }
4750 
4751 _Must_inspect_result_
4752 NTSTATUS
AddRemovalDevice(__in MdDeviceObject DependentDevice)4753 FxPkgPnp::AddRemovalDevice(
4754     __in MdDeviceObject DependentDevice
4755     )
4756 {
4757     FxRelatedDevice* pRelated;
4758     NTSTATUS status;
4759 
4760     if (m_RemovalDeviceList == NULL) {
4761         KIRQL irql;
4762 
4763         Lock(&irql);
4764         if (m_RemovalDeviceList == NULL) {
4765             m_RemovalDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
4766 
4767             if (m_RemovalDeviceList != NULL) {
4768                 status = STATUS_SUCCESS;
4769             }
4770             else {
4771                 status = STATUS_INSUFFICIENT_RESOURCES;
4772 
4773                 DoTraceLevelMessage(
4774                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4775                     "Could not allocate removal device list for WDFDEVICE %p, "
4776                     "%!STATUS!", m_Device->GetHandle(), status);
4777             }
4778         }
4779         else {
4780             //
4781             // another thread allocated the list already
4782             //
4783             status = STATUS_SUCCESS;
4784         }
4785         Unlock(irql);
4786 
4787         if (!NT_SUCCESS(status)) {
4788             return status;
4789         }
4790     }
4791 
4792     pRelated = new(GetDriverGlobals())
4793         FxRelatedDevice(DependentDevice, GetDriverGlobals());
4794     if (pRelated == NULL) {
4795         return STATUS_INSUFFICIENT_RESOURCES;
4796     }
4797 
4798     status = m_RemovalDeviceList->Add(GetDriverGlobals(), pRelated);
4799 
4800     if (NT_SUCCESS(status)) {
4801         //
4802         // RemovalRelations are queried automatically by PnP when the device is
4803         // going to be query removed.  No need to tell pnp that the list changed
4804         // until it needs to query for it.
4805         //
4806         DO_NOTHING();
4807     }
4808     else {
4809         pRelated->DeleteFromFailedCreate();
4810     }
4811 
4812     return status;
4813 }
4814 
4815 VOID
RemoveRemovalDevice(__in MdDeviceObject DependentDevice)4816 FxPkgPnp::RemoveRemovalDevice(
4817     __in MdDeviceObject DependentDevice
4818     )
4819 {
4820     if (m_RemovalDeviceList != NULL) {
4821         m_RemovalDeviceList->Remove(GetDriverGlobals(), DependentDevice);
4822     }
4823 
4824     //
4825     // RemovalRelations are queried automatically by PnP when the device is
4826     // going to be query removed.  No need to tell pnp that the list changed
4827     // until it needs to query for it.
4828     //
4829 }
4830 
4831 VOID
ClearRemovalDevicesList(VOID)4832 FxPkgPnp::ClearRemovalDevicesList(
4833     VOID
4834     )
4835 {
4836     FxRelatedDevice* pEntry;
4837 
4838     if (m_RemovalDeviceList == NULL) {
4839         return;
4840     }
4841 
4842     m_RemovalDeviceList->LockForEnum(GetDriverGlobals());
4843     while ((pEntry = m_RemovalDeviceList->GetNextEntry(NULL)) != NULL) {
4844         m_RemovalDeviceList->Remove(GetDriverGlobals(), pEntry->GetDevice());
4845     }
4846     m_RemovalDeviceList->UnlockFromEnum(GetDriverGlobals());
4847 
4848     //
4849     // RemovalRelations are queried automatically by PnP when the device is
4850     // going to be query removed.  No need to tell pnp that the list changed
4851     // until it needs to query for it.
4852     //
4853 }
4854 
4855 VOID
SetInternalFailure(VOID)4856 FxPkgPnp::SetInternalFailure(
4857     VOID
4858     )
4859 /*++
4860 
4861 Routine Description:
4862     Sets the failure field and then optionally invalidates the device state.
4863 
4864 Arguments:
4865     InvalidateState - If TRUE, the state is invalidated
4866 
4867 Return Value:
4868     None
4869 
4870   --*/
4871 {
4872     m_InternalFailure = TRUE;
4873 
4874     MxDeviceObject physicalDeviceObject(
4875                                 m_Device->GetPhysicalDevice()
4876                                 );
4877     physicalDeviceObject.InvalidateDeviceState(
4878         m_Device->GetDeviceObject()
4879         );
4880 }
4881 
4882 VOID
SetPendingPnpIrp(__inout FxIrp * Irp,__in BOOLEAN MarkIrpPending)4883 FxPkgPnp::SetPendingPnpIrp(
4884     __inout FxIrp* Irp,
4885     __in    BOOLEAN MarkIrpPending
4886     )
4887 {
4888     if (m_PendingPnPIrp != NULL ) {
4889         FxIrp pendingIrp(m_PendingPnPIrp);
4890 
4891         //
4892         // A state changing pnp irp is already pended. If we don't bugcheck
4893         // the pended pnp irp will be overwritten with new pnp irp and the old
4894         // one may never get completed, which may have drastic implications (
4895         // unresponsive system, power manager not sending Sx Irp etc.)
4896         //
4897         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4898             "A new state changing pnp irp %!pnpmn! IRP %p arrived while another "
4899             "pnp irp %!pnpmn! IRP %p is still pending WDFDEVICE %p\n",
4900             Irp->GetMinorFunction(), Irp->GetIrp(),
4901             pendingIrp.GetMinorFunction(),pendingIrp.GetIrp(),
4902             m_Device->GetHandle());
4903 
4904         FxVerifierBugCheck(GetDriverGlobals(),  // globals
4905                            WDF_PNP_FATAL_ERROR, // specific type
4906                            (ULONG_PTR)m_Device->GetHandle(), //parm 2
4907                            (ULONG_PTR)Irp->GetIrp());  // parm 3
4908 
4909         /* NOTREACHED */
4910         return;
4911     }
4912     if (MarkIrpPending) {
4913         Irp->MarkIrpPending();
4914     }
4915     m_PendingPnPIrp = Irp->GetIrp();
4916 }
4917 
4918 _Must_inspect_result_
4919 NTSTATUS
AllocateEnumInfo(VOID)4920 FxPkgPnp::AllocateEnumInfo(
4921     VOID
4922     )
4923 {
4924     KIRQL irql;
4925     NTSTATUS status;
4926 
4927     if (m_EnumInfo != NULL) {
4928         return STATUS_SUCCESS;
4929     }
4930 
4931     Lock(&irql);
4932     if (m_EnumInfo == NULL) {
4933         m_EnumInfo = new (GetDriverGlobals()) FxEnumerationInfo(GetDriverGlobals());
4934 
4935         if (m_EnumInfo != NULL) {
4936             status = m_EnumInfo->Initialize();
4937 
4938             if (!NT_SUCCESS(status)) {
4939                 delete m_EnumInfo;
4940                 m_EnumInfo = NULL;
4941 
4942                 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR,
4943                                     TRACINGPNP,
4944                                     "Could not initialize enum info for "
4945                                     "WDFDEVICE %p, %!STATUS!",
4946                                     m_Device->GetHandle(), status);
4947             }
4948 
4949         }
4950         else {
4951             status = STATUS_INSUFFICIENT_RESOURCES;
4952 
4953             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
4954                                 "Could not allocate enum info for WDFDEVICE %p, "
4955                                 "%!STATUS!", m_Device->GetHandle(), status);
4956         }
4957     }
4958     else {
4959         //
4960         // another thread allocated the list already
4961         //
4962         status = STATUS_SUCCESS;
4963     }
4964     Unlock(irql);
4965 
4966     return status;
4967 }
4968 
4969 VOID
AddChildList(__in FxChildList * List)4970 FxPkgPnp::AddChildList(
4971     __in FxChildList* List
4972     )
4973 {
4974     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
4975                         "Adding FxChildList %p, WDFCHILDLIST %p", List,
4976                         List->GetHandle());
4977 
4978     m_EnumInfo->m_ChildListList.Add(GetDriverGlobals(),
4979                                     &List->m_TransactionLink);
4980 }
4981 
4982 VOID
RemoveChildList(__in FxChildList * List)4983 FxPkgPnp::RemoveChildList(
4984     __in FxChildList* List
4985     )
4986 {
4987     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
4988                         "Removing FxChildList %p, WDFCHILDLIST %p", List,
4989                         List->GetHandle());
4990 
4991     m_EnumInfo->m_ChildListList.Remove(GetDriverGlobals(), &List->m_TransactionLink);
4992 
4993     //
4994 
4995     //
4996 }
4997 
4998 VOID
ChildListNotifyRemove(__inout PLONG PendingCount)4999 FxPkgPnp::ChildListNotifyRemove(
5000     __inout PLONG PendingCount
5001     )
5002 {
5003     FxTransactionedEntry* ple;
5004 
5005     ple = NULL;
5006 
5007     if (m_EnumInfo != NULL) {
5008         m_EnumInfo->m_ChildListList.LockForEnum(GetDriverGlobals());
5009         while ((ple = m_EnumInfo->m_ChildListList.GetNextEntry(ple)) != NULL) {
5010             FxChildList::_FromEntry(ple)->NotifyDeviceRemove(PendingCount);
5011         }
5012         m_EnumInfo->m_ChildListList.UnlockFromEnum(GetDriverGlobals());
5013     }
5014 }
5015 
5016 VOID
AddQueryInterface(__in FxQueryInterface * QI,__in BOOLEAN Lock)5017 FxPkgPnp::AddQueryInterface(
5018     __in FxQueryInterface* QI,
5019     __in BOOLEAN Lock
5020     )
5021 /*++
5022 
5023 Routine Description:
5024     Add a query interface structure to the list of interfaces supported
5025 
5026 Arguments:
5027     QI - the interface to add
5028 
5029     Lock - indication of the list lock should be acquired or not
5030 
5031 Return Value:
5032     None
5033 
5034   --*/
5035 {
5036     SINGLE_LIST_ENTRY **ppPrev, *pCur;
5037 
5038     if (Lock) {
5039         m_QueryInterfaceLock.AcquireLock(GetDriverGlobals());
5040     }
5041 
5042     ASSERT(QI->m_Entry.Next == NULL);
5043 
5044     //
5045     // Iterate until we find the end of the list and then append the new
5046     // structure.  ppPrev is the pointer to the Next pointer value.  When we
5047     // get to the end, ppPrev will be equal to the Next field which points to NULL.
5048     //
5049     ppPrev = &m_QueryInterfaceHead.Next;
5050     pCur = m_QueryInterfaceHead.Next;
5051 
5052     while (pCur != NULL) {
5053         ppPrev = &pCur->Next;
5054         pCur = pCur->Next;
5055     }
5056 
5057     *ppPrev = &QI->m_Entry;
5058 
5059     if (Lock) {
5060         m_QueryInterfaceLock.ReleaseLock(GetDriverGlobals());
5061     }
5062 }
5063 
5064 __drv_maxIRQL(DISPATCH_LEVEL)
__drv_minIRQL(DISPATCH_LEVEL)5065 __drv_minIRQL(DISPATCH_LEVEL)
5066 __drv_requiresIRQL(DISPATCH_LEVEL)
5067 __drv_sameIRQL
5068 VOID
5069 FxWatchdog::_WatchdogDpc(
5070     __in     PKDPC Dpc,
5071     __in_opt PVOID Context,
5072     __in_opt PVOID SystemArgument1,
5073     __in_opt PVOID SystemArgument2
5074     )
5075 /*++
5076 
5077 Routine Description:
5078 
5079     This routine's job is to crash the machine, attempting to get some data
5080     into the crashdump file (or minidump) about why the machine stopped
5081     responding during an attempt to put the machine to sleep.
5082 
5083 Arguments:
5084 
5085     This - the instance of FxPkgPnp
5086 
5087 Return Value:
5088 
5089     this routine never returns
5090 
5091 --*/
5092 {
5093     WDF_POWER_ROUTINE_TIMED_OUT_DATA data;
5094     FxWatchdog* pThis;
5095     CfxDevice* pDevice;
5096 
5097     UNREFERENCED_PARAMETER(Dpc);
5098     UNREFERENCED_PARAMETER(SystemArgument1);
5099     UNREFERENCED_PARAMETER(SystemArgument2);
5100 
5101     pThis = (FxWatchdog*) Context;
5102     pDevice = pThis->m_PkgPnp->GetDevice();
5103 
5104     DoTraceLevelMessage(pDevice->GetDriverGlobals(),
5105                         TRACE_LEVEL_ERROR, TRACINGPNP,
5106                         "The driver failed to return from a callback routine "
5107                         "in a reasonable period of time.  This prevented the "
5108                         "machine from going to sleep or from hibernating.  The "
5109                         "machine crashed because that was the best way to get "
5110                         "data about the cause of the crash into a minidump file.");
5111 
5112     data.PowerState = pDevice->GetDevicePowerState();
5113     data.PowerPolicyState = pDevice->GetDevicePowerPolicyState();
5114     data.DeviceObject = reinterpret_cast<PDEVICE_OBJECT>(pDevice->GetDeviceObject());
5115     data.Device = pDevice->GetHandle();
5116     data.TimedOutThread = reinterpret_cast<PKTHREAD>(pThis->m_CallingThread);
5117 
5118     FxVerifierBugCheck(pDevice->GetDriverGlobals(),
5119                        WDF_POWER_ROUTINE_TIMED_OUT,
5120                        (ULONG_PTR) &data);
5121 }
5122 
5123 _Must_inspect_result_
5124 NTSTATUS
CreatePowerThread(VOID)5125 FxPkgPnp::CreatePowerThread(
5126     VOID
5127     )
5128 /*++
5129 
5130 Routine Description:
5131     Creates a power thread for the device node.  This thread is share among all
5132     the devices in the stack through the POWER_THREAD_INTERFACE structure and
5133     PnP query interface.
5134 
5135 Arguments:
5136     None
5137 
5138 Return Value:
5139     NTSTATUS
5140 
5141   --*/
5142 {
5143     FxSystemThread *pThread, *pOld;
5144     NTSTATUS status;
5145 
5146     status = FxSystemThread::_CreateAndInit(
5147                 &pThread,
5148                 GetDriverGlobals(),
5149                 m_Device->GetHandle(),
5150                 m_Device->GetDeviceObject());
5151 
5152     if (!NT_SUCCESS(status)) {
5153         return status;
5154     }
5155 
5156     //
5157     // Simple locking logic in case N requests are conncurrent.  (The requests
5158     // never should be concurrent, but in the case that there are 2 usage
5159     // notifications coming in from two different sources, it could
5160     // theoritically happen.)
5161     //
5162     pOld = (FxSystemThread*) InterlockedCompareExchangePointer(
5163         (PVOID*) &m_PowerThread, pThread, NULL);
5164 
5165     if (pOld != NULL) {
5166         //
5167         // Someone also set the thread pointer value at the same time, free
5168         // our new one here.
5169         //
5170         pThread->ExitThread();
5171         pThread->DeleteObject();
5172     }
5173 
5174     m_HasPowerThread = TRUE;
5175 
5176     return STATUS_SUCCESS;
5177 }
5178 
5179 VOID
ReleasePowerThread(VOID)5180 FxPkgPnp::ReleasePowerThread(
5181     VOID
5182     )
5183 /*++
5184 
5185 Routine Description:
5186     If this device is the owner of the power thread, it kills the thread.
5187     Otherwise, if this device has acquired the thread from a lower device,
5188     release the reference now.
5189 
5190 Arguments:
5191     None
5192 
5193 Return Value:
5194     None
5195 
5196   --*/
5197 {
5198     BOOLEAN hadThread;
5199 
5200     hadThread = m_HasPowerThread;
5201 
5202     //
5203     // Set to FALSE before cleaning up the reference or thread itself in case
5204     // there is some other context trying to enqueue.  The only way that could
5205     // be happening is if the power policy owner is not WDF and sends power irps
5206     // after query remove or surprise remove.
5207     //
5208     m_HasPowerThread = FALSE;
5209 
5210     //
5211     // Check for ownership
5212     //
5213     if (m_PowerThread != NULL) {
5214 
5215         FxCREvent event;
5216 
5217         //
5218         // Event on stack is used, which is fine since this code is invoked
5219         // only in KM. Verify this assumption.
5220         //
5221         // If this code is ever needed for UM, m_PowerThreadEvent should be
5222         // pre-initialized (simlar to the way m_RemoveEventUm is used)
5223         //
5224         WDF_VERIFY_KM_ONLY_CODE();
5225 
5226         ASSERT(m_PowerThreadEvent == NULL);
5227         m_PowerThreadEvent = event.GetSelfPointer();
5228 
5229         if (InterlockedDecrement(&m_PowerThreadInterfaceReferenceCount) > 0) {
5230             //
5231             // Wait for all references to go away before exitting the thread.
5232             // A reference will be taken for every device in the stack above this
5233             // one which queried for the interface.
5234             //
5235             event.EnterCRAndWaitAndLeave();
5236         }
5237 
5238         m_PowerThreadEvent = NULL;
5239 
5240         //
5241         // Wait for the thread to exit and then delete it.  Since we have
5242         // turned off the power policy state machine, we can safely do this here.
5243         // Any upper level clients will have also turned off their power policy
5244         // state machines.
5245         //
5246         m_PowerThread->ExitThread();
5247         m_PowerThread->DeleteObject();
5248 
5249         m_PowerThread = NULL;
5250     }
5251     else if (hadThread) {
5252         //
5253         // Release our reference
5254         //
5255         m_PowerThreadInterface.Interface.InterfaceDereference(
5256             m_PowerThreadInterface.Interface.Context
5257             );
5258     }
5259 }
5260 
5261 VOID
_PowerThreadInterfaceReference(__inout PVOID Context)5262 FxPkgPnp::_PowerThreadInterfaceReference(
5263     __inout PVOID Context
5264     )
5265 /*++
5266 
5267 Routine Description:
5268     Increments the ref count on the thread interface.
5269 
5270 Arguments:
5271     Context - FxPkgPnp*
5272 
5273 Return Value:
5274     None
5275 
5276   --*/
5277 {
5278     LONG count;
5279 
5280     count = InterlockedIncrement(
5281         &((FxPkgPnp*) Context)->m_PowerThreadInterfaceReferenceCount
5282         );
5283 
5284 #if DBG
5285     ASSERT(count >= 2);
5286 #else
5287     UNREFERENCED_PARAMETER(count);
5288 #endif
5289 }
5290 
5291 VOID
_PowerThreadInterfaceDereference(__inout PVOID Context)5292 FxPkgPnp::_PowerThreadInterfaceDereference(
5293     __inout PVOID Context
5294     )
5295 /*++
5296 
5297 Routine Description:
5298     Interface deref for the thread interface.  If this is the last reference
5299     released, an event is set so that the thread which waiting for the last ref
5300     to go away can unblock.
5301 
5302 Arguments:
5303     Context - FxPkgPnp*
5304 
5305 Return Value:
5306     None
5307 
5308   --*/
5309 
5310 {
5311     FxPkgPnp* pThis;
5312 
5313     pThis = (FxPkgPnp*) Context;
5314 
5315     if (InterlockedDecrement(&pThis->m_PowerThreadInterfaceReferenceCount) == 0) {
5316         pThis->m_PowerThreadEvent->Set();
5317     }
5318 }
5319 
5320 _Must_inspect_result_
5321 NTSTATUS
PnpPowerReferenceSelf(VOID)5322 FxPkgPnp::PnpPowerReferenceSelf(
5323     VOID
5324     )
5325 /*++
5326 
5327 Routine Description:
5328     Take a power reference during a query pnp transition.
5329 
5330 Arguments:
5331     None
5332 
5333 Return Value:
5334     None
5335 
5336   --*/
5337 {
5338     if (IsPowerPolicyOwner()) {
5339         //
5340         // We want to synchronously wait to move into D0
5341         //
5342         return PowerReference(TRUE);
5343     }
5344     else {
5345         return STATUS_SUCCESS;
5346     }
5347 }
5348 
5349 _Must_inspect_result_
5350 NTSTATUS
PnpPowerReferenceDuringQueryPnp(VOID)5351 FxPkgPnp::PnpPowerReferenceDuringQueryPnp(
5352     VOID
5353     )
5354 /*++
5355 
5356 Routine Description:
5357     Take a power reference during a query pnp transition.
5358 
5359 Arguments:
5360     None
5361 
5362 Return Value:
5363     None
5364 
5365   --*/
5366 {
5367     if (IsPowerPolicyOwner()) {
5368         //
5369         // We want to synchronously wait to move into D0
5370         //
5371         return m_PowerPolicyMachine.m_Owner->
5372             m_PowerIdleMachine.PowerReferenceWithFlags(
5373                 FxPowerReferenceSendPnpPowerUpEvent
5374                 );
5375     }
5376     else {
5377         return STATUS_SUCCESS;
5378     }
5379 }
5380 
5381 VOID
PnpPowerDereferenceSelf(VOID)5382 FxPkgPnp::PnpPowerDereferenceSelf(
5383     VOID
5384     )
5385 /*++
5386 
5387 Routine Description:
5388     Release the power reference taken during a query pnp transition
5389 
5390 Arguments:
5391     None
5392 
5393 Return Value:
5394     None
5395 
5396   --*/
5397 {
5398     if (IsPowerPolicyOwner()) {
5399         PowerDereference();
5400     }
5401 }
5402 
5403 NTSTATUS
CompletePowerRequest(__inout FxIrp * Irp,__in NTSTATUS Status)5404 FxPkgPnp::CompletePowerRequest(
5405     __inout FxIrp* Irp,
5406     __in    NTSTATUS Status
5407     )
5408 {
5409     MdIrp irp;
5410 
5411     //
5412     // Once we call CompleteRequest, 2 things happen
5413     // 1) this object may go away
5414     // 2) Irp->m_Irp will be cleared
5415     //
5416     // As such, we capture the underlying WDM objects so that we can use them
5417     // to release the remove lock and use the PIRP *value* as a tag to release
5418     // the remlock.
5419     //
5420     irp = Irp->GetIrp();
5421 
5422     Irp->SetStatus(Status);
5423     Irp->StartNextPowerIrp();
5424     Irp->CompleteRequest(IO_NO_INCREMENT);
5425 
5426     Mx::MxReleaseRemoveLock(m_Device->GetRemoveLock(),
5427                             irp);
5428 
5429     return Status;
5430 }
5431 
5432 LONG
GetPnpStateInternal(VOID)5433 FxPkgPnp::GetPnpStateInternal(
5434     VOID
5435     )
5436 /*++
5437 
5438 Routine Description:
5439     Returns the pnp device state encoded into a ULONG.  This state is the state
5440     that is reported to PNp via IRP_MN_QUERY_PNP_DEVICE_STATE after it has been
5441     decoded into the bits pnp expects
5442 
5443 Arguments:
5444     None
5445 
5446 Return Value:
5447     the current state bits
5448 
5449   --*/
5450 {
5451     LONG state;
5452     KIRQL irql;
5453 
5454     //
5455     // State is shared with the caps bits.  Use a lock to guard against
5456     // corruption of the value between these 2 values
5457     //
5458     Lock(&irql);
5459     state = m_PnpStateAndCaps.Value & FxPnpStateMask;
5460     Unlock(irql);
5461 
5462     return state;
5463 }
5464 
5465 LONG
GetPnpCapsInternal(VOID)5466 FxPkgPnp::GetPnpCapsInternal(
5467     VOID
5468     )
5469 /*++
5470 
5471 Routine Description:
5472     Returns the pnp device capabilities encoded into a LONG.  This state is used
5473     in reporting device capabilities via IRP_MN_QUERY_CAPABILITIES and filling
5474     in the PDEVICE_CAPABILITIES structure.
5475 
5476 Arguments:
5477     None
5478 
5479 Return Value:
5480     the current pnp cap bits
5481 
5482   --*/
5483 {
5484     LONG caps;
5485     KIRQL irql;
5486 
5487     Lock(&irql);
5488     caps = m_PnpStateAndCaps.Value & FxPnpCapMask;
5489     Unlock(irql);
5490 
5491     return caps;
5492 }
5493 
5494 
5495 VOID
SetPnpCaps(__in PWDF_DEVICE_PNP_CAPABILITIES PnpCapabilities)5496 FxPkgPnp::SetPnpCaps(
5497     __in PWDF_DEVICE_PNP_CAPABILITIES PnpCapabilities
5498     )
5499 /*++
5500 
5501 Routine Description:
5502     Encode the driver provided pnp capabilities into our internal capabilities
5503     bit field and store the result.
5504 
5505 Arguments:
5506     PnpCapabilities - capabilities as reported by the driver writer
5507 
5508 Return Value:
5509     None
5510 
5511   --*/
5512 {
5513     LONG pnpCaps;
5514     KIRQL irql;
5515 
5516     pnpCaps = 0;
5517     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, LockSupported);
5518     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, EjectSupported);
5519     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, Removable);
5520     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, DockDevice);
5521     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, UniqueID);
5522     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, SilentInstall);
5523     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, SurpriseRemovalOK);
5524     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, HardwareDisabled);
5525     pnpCaps |= GET_PNP_CAP_BITS_FROM_STRUCT(PnpCapabilities, NoDisplayInUI);
5526 
5527     //
5528     // Since the caller of IRP_MN_QUERY_CAPABILITIES sets these 2 values to -1,
5529     // we can reuse the -1 as the default/no override value since the associated
5530     // PDEVICE_CAPAPBILITIES structure will have been predisposed to these values
5531     //
5532     if (PnpCapabilities->Address != (ULONG) -1) {
5533         m_PnpCapsAddress = PnpCapabilities->Address;
5534     }
5535     if (PnpCapabilities->UINumber != (ULONG) -1) {
5536         m_PnpCapsUINumber = PnpCapabilities->UINumber;
5537     }
5538 
5539     //
5540     // Use the FxPnpStateMask to keep the state mask while applying the new
5541     // pnp capabilities.
5542     //
5543     Lock(&irql);
5544     m_PnpStateAndCaps.Value = (m_PnpStateAndCaps.Value & FxPnpStateMask) | pnpCaps;
5545     Unlock(irql);
5546 }
5547 
5548 VOID
GetPnpState(__out PWDF_DEVICE_STATE State)5549 FxPkgPnp::GetPnpState(
5550     __out PWDF_DEVICE_STATE State
5551     )
5552 /*++
5553 
5554 Routine Description:
5555     Decodes our internal pnp state bitfield into the external WDF_DEVICE_STATE
5556     structure
5557 
5558 Arguments:
5559     State - the structure to decode into
5560 
5561 Return Value:
5562     None
5563 
5564   --*/
5565 {
5566     LONG state;
5567 
5568     state = GetPnpStateInternal();
5569 
5570     SET_TRI_STATE_FROM_STATE_BITS(state, State, Disabled);
5571     SET_TRI_STATE_FROM_STATE_BITS(state, State, DontDisplayInUI);
5572     SET_TRI_STATE_FROM_STATE_BITS(state, State, Failed);
5573     SET_TRI_STATE_FROM_STATE_BITS(state, State, NotDisableable);
5574     SET_TRI_STATE_FROM_STATE_BITS(state, State, Removed);
5575     SET_TRI_STATE_FROM_STATE_BITS(state, State, ResourcesChanged);
5576 }
5577 
5578 VOID
SetPnpState(__in PWDF_DEVICE_STATE State)5579 FxPkgPnp::SetPnpState(
5580     __in PWDF_DEVICE_STATE State
5581     )
5582 /*++
5583 
5584 Routine Description:
5585     Encodes the driver writer provided state into our internal bit field.
5586 
5587 Arguments:
5588     State - the states to encode
5589 
5590 Return Value:
5591     None
5592 
5593   --*/
5594 {
5595     LONG pnpState;
5596     KIRQL irql;
5597 
5598     pnpState = 0x0;
5599     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Disabled);
5600     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, DontDisplayInUI);
5601     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Failed);
5602     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, NotDisableable);
5603     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, Removed);
5604     pnpState |= GET_PNP_STATE_BITS_FROM_STRUCT(State, ResourcesChanged);
5605 
5606     //
5607     // Mask off FxPnpCapMask to keep the capabilities part of the bitfield
5608     // the same while change the pnp state.
5609     //
5610     Lock(&irql);
5611     m_PnpStateAndCaps.Value = (m_PnpStateAndCaps.Value & FxPnpCapMask) | pnpState;
5612     Unlock(irql);
5613 }
5614 
5615 VOID
_SetPowerCapState(__in ULONG Index,__in DEVICE_POWER_STATE State,__out PULONG Result)5616 FxPkgPnp::_SetPowerCapState(
5617     __in  ULONG Index,
5618     __in  DEVICE_POWER_STATE State,
5619     __out PULONG Result
5620     )
5621 /*++
5622 
5623 Routine Description:
5624     Encodes the given device power state (State) into Result at the given Index.
5625     States are encoded in nibbles (4 bit chunks), starting at the bottom of the
5626     result and  moving upward
5627 
5628 Arguments:
5629     Index - zero based index into the number of nibbles to encode the value
5630 
5631     State - State to encode
5632 
5633     Result - pointer to where the encoding will take place
5634 
5635 Return Value:
5636     None
5637 
5638   --*/
5639 {
5640     //
5641     // We store off state in 4 bits, starting at the lowest byte
5642     //
5643     ASSERT(Index < 8);
5644 
5645     //
5646     // Erase the old value
5647     //
5648     *Result &= ~(0xF << (Index * 4));
5649 
5650     //
5651     // Write in the new one
5652     //
5653     *Result |= (0xF & State) << (Index * 4);
5654 }
5655 
5656 DEVICE_POWER_STATE
_GetPowerCapState(__in ULONG Index,__in ULONG State)5657 FxPkgPnp::_GetPowerCapState(
5658     __in ULONG Index,
5659     __in ULONG State
5660     )
5661 /*++
5662 
5663 Routine Description:
5664     Decodes our internal device state encoding and returns a normalized device
5665     power state for the given index.
5666 
5667 Arguments:
5668     Index - nibble (4 bit chunk) index into the State
5669 
5670     State - value which has the device states encoded into it
5671 
5672 Return Value:
5673     device power state for the given Index
5674 
5675   --*/
5676 {
5677     ASSERT(Index < 8);
5678                                 // isolate the value            and normalize it
5679     return (DEVICE_POWER_STATE) ((State & (0xF << (Index * 4))) >> (Index * 4));
5680 }
5681 
5682 VOID
SetPowerCaps(__in PWDF_DEVICE_POWER_CAPABILITIES PowerCapabilities)5683 FxPkgPnp::SetPowerCaps(
5684     __in PWDF_DEVICE_POWER_CAPABILITIES PowerCapabilities
5685     )
5686 /*++
5687 
5688 Routine Description:
5689     Encodes the driver provided power capabilities into the object.  The device
5690     power states are encoded into one ULONG while the other power caps are
5691     encoded into their own distinct fields.
5692 
5693 Arguments:
5694     PowerCapabilities - the power caps reported by the driver writer
5695 
5696 Return Value:
5697     None
5698 
5699   --*/
5700 {
5701     ULONG states, i;
5702     USHORT powerCaps;
5703 
5704     states = 0x0;
5705 
5706     //
5707     // Build up the device power state encoding into a temp var so that if we are
5708     // retrieving the encoding in another thread, we don't get a partial view
5709     //
5710     for (i = 0; i < ARRAY_SIZE(PowerCapabilities->DeviceState); i++) {
5711         _SetPowerCapState(i,  PowerCapabilities->DeviceState[i], &states);
5712     }
5713 
5714     m_PowerCaps.States = states;
5715 
5716     //
5717     // Same idea.  Build up the caps locally first so that when we assign them
5718     // into the object, it is assigned as a whole.
5719     //
5720     powerCaps = 0x0;
5721     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, DeviceD1);
5722     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, DeviceD2);
5723     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD0);
5724     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD1);
5725     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD2);
5726     powerCaps |= GET_POWER_CAP_BITS_FROM_STRUCT(PowerCapabilities, WakeFromD3);
5727 
5728     m_PowerCaps.Caps = powerCaps;
5729 
5730     if (PowerCapabilities->DeviceWake != PowerDeviceMaximum) {
5731         m_PowerCaps.DeviceWake = (BYTE) PowerCapabilities->DeviceWake;
5732     }
5733     if (PowerCapabilities->SystemWake != PowerSystemMaximum) {
5734         m_PowerCaps.SystemWake = (BYTE) PowerCapabilities->SystemWake;
5735     }
5736 
5737     m_PowerCaps.D1Latency = PowerCapabilities->D1Latency;
5738     m_PowerCaps.D2Latency = PowerCapabilities->D2Latency;
5739     m_PowerCaps.D3Latency = PowerCapabilities->D3Latency;
5740 
5741     if (PowerCapabilities->IdealDxStateForSx != PowerDeviceMaximum) {
5742         //
5743         // Caller has already validated that IdealDxStateForSx is only set if
5744         // they device is the power policy owner.
5745         //
5746         m_PowerPolicyMachine.m_Owner->m_IdealDxStateForSx = (BYTE)
5747             PowerCapabilities->IdealDxStateForSx;
5748     }
5749 }
5750 
5751 NTSTATUS
CompletePnpRequest(__inout FxIrp * Irp,__in NTSTATUS Status)5752 FxPkgPnp::CompletePnpRequest(
5753     __inout FxIrp* Irp,
5754     __in    NTSTATUS Status
5755     )
5756 {
5757     MdIrp pIrp = Irp->GetIrp();
5758 
5759     Irp->SetStatus(Status);
5760     Irp->CompleteRequest(IO_NO_INCREMENT);
5761 
5762     Mx::MxReleaseRemoveLock(m_Device->GetRemoveLock(),
5763                             pIrp);
5764 
5765     return Status;
5766 }
5767 
5768 BOOLEAN
PowerPolicyIsWakeEnabled(VOID)5769 FxPkgPnp::PowerPolicyIsWakeEnabled(
5770     VOID
5771     )
5772 {
5773     if (IsPowerPolicyOwner() && PowerPolicyGetCurrentWakeReason() != 0x0) {
5774         return TRUE;
5775     }
5776     else {
5777         return FALSE;
5778     }
5779 }
5780 
5781 ULONG
PowerPolicyGetCurrentWakeReason(VOID)5782 FxPkgPnp::PowerPolicyGetCurrentWakeReason(
5783     VOID
5784     )
5785 /*++
5786 
5787 Routine Description:
5788     This routine determines the reasons for whether wake should be enabled or
5789     not.  Wake could be enabled because it is explicitly enabled for the device
5790     in the wake policy settings or, because the device opted to depend on its
5791     children being armed for wake.
5792 
5793 Arguments:
5794     None
5795 
5796 Return Value:
5797     Returns a combination of FxPowerPolicySxWakeChildrenArmedFlag, to indicate
5798     that wake can be enabled because of more than one children being armed for
5799     wake, and FxPowerPolicySxWakeDeviceEnabledFlag, to indicate that wake can
5800     be enabled because the device was explicitly enabled in the wake policy
5801     settings.
5802 
5803     Returns Zero to indicate that wake is currently disabled for the device.
5804 
5805 --*/
5806 {
5807     ULONG wakeReason;
5808 
5809     wakeReason = 0x0;
5810 
5811     if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.ArmForWakeIfChildrenAreArmedForWake &&
5812         m_PowerPolicyMachine.m_Owner->m_ChildrenArmedCount > 0) {
5813         //
5814         // Wake settings depends on children and one or more children are
5815         // armed for wake.
5816         //
5817         wakeReason |= FxPowerPolicySxWakeChildrenArmedFlag;
5818     }
5819 
5820     if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled) {
5821         //
5822         // Wake settings is explicitly enabled.
5823         //
5824         wakeReason |= FxPowerPolicySxWakeDeviceEnabledFlag;
5825     }
5826 
5827     return wakeReason;
5828 }
5829 
5830 VOID
SaveState(__in BOOLEAN UseCanSaveState)5831 FxPkgPnp::SaveState(
5832     __in BOOLEAN UseCanSaveState
5833     )
5834 /*++
5835 
5836 Routine Description:
5837     Saves any permanent state of the device out to the registry
5838 
5839 Arguments:
5840     None
5841 
5842 Return Value:
5843     None
5844 
5845   --*/
5846 
5847 {
5848     UNICODE_STRING name;
5849     FxAutoRegKey hKey;
5850     NTSTATUS status;
5851     ULONG value;
5852 
5853     //
5854     // We only have settings to save if we are the power policy owner
5855     //
5856     if (IsPowerPolicyOwner() == FALSE) {
5857         return;
5858     }
5859 
5860     if (UseCanSaveState &&
5861         m_PowerPolicyMachine.m_Owner->m_CanSaveState == FALSE) {
5862         DoTraceLevelMessage(
5863             GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
5864             "Not saving wake settings for WDFDEVICE %p due to system power "
5865             "transition", m_Device->GetHandle());
5866         return;
5867     }
5868 
5869     //
5870     // Check to see if there is anything to write out
5871     //
5872     if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty == FALSE &&
5873         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty == FALSE) {
5874         return;
5875     }
5876 
5877     //
5878     // No need to write out if user control is not enabled
5879     //
5880     if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable == FALSE &&
5881         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable == FALSE) {
5882         return;
5883     }
5884 
5885     //
5886     // If the device is in paging path we should not be touching registry during
5887     // power up because it may incur page fault which won't be satisfied if the
5888     // device is still not powered up, blocking power Irp. User control state
5889     // change will not get written if any at this time but will be flushed out
5890     // to registry during device disable/remove in the remove path.
5891     //
5892     if (IsUsageSupported(DeviceUsageTypePaging) && IsDevicePowerUpIrpPending()) {
5893         return;
5894     }
5895 
5896 #if ((FX_CORE_MODE)==(FX_CORE_KERNEL_MODE))
5897 
5898     status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_WRITE);
5899     if (!NT_SUCCESS(status)) {
5900         return;
5901     }
5902 #else
5903     status = STATUS_SUCCESS;
5904 #endif
5905 
5906     if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.Overridable &&
5907         m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty) {
5908         RtlInitUnicodeString(&name, WDF_S0_IDLE_ENABLED_VALUE_NAME);
5909         value = m_PowerPolicyMachine.m_Owner->m_IdleSettings.Enabled;
5910 
5911         WriteStateToRegistry(hKey.m_Key, &name, value);
5912 
5913         m_PowerPolicyMachine.m_Owner->m_IdleSettings.Dirty = FALSE;
5914     }
5915 
5916     if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.Overridable &&
5917         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty)  {
5918         RtlInitUnicodeString(&name, WDF_SX_WAKE_ENABLED_VALUE_NAME);
5919         value = m_PowerPolicyMachine.m_Owner->m_WakeSettings.Enabled;
5920 
5921         WriteStateToRegistry(hKey.m_Key, &name, value);
5922 
5923         m_PowerPolicyMachine.m_Owner->m_WakeSettings.Dirty = FALSE;
5924     }
5925 }
5926 
5927 VOID
AddInterruptObject(__in FxInterrupt * Interrupt)5928 FxPkgPnp::AddInterruptObject(
5929     __in FxInterrupt* Interrupt
5930     )
5931 /*++
5932 
5933 Routine Description:
5934 
5935     This routine adds a WDFINTERRUPT object onto the list of interrupts
5936     which are attached to this device.  This list is used in response to
5937     several IRPs.
5938 
5939 Note:
5940 
5941     It shouldn't be necessary to lock this list, since the driver will add or
5942     remove interrupt objects only in callbacks that are effectively serialized
5943     by the PnP manager.
5944 
5945 Further note:
5946 
5947     This list must remain sorted by order of interrupt object creation.  E.g.
5948     the first interrupt object created must be first in this list.
5949 
5950 Arguments:
5951 
5952     Interrupt - a Framework interrupt object
5953 
5954 Return Value:
5955 
5956     VOID
5957 
5958 --*/
5959 {
5960     m_InterruptObjectCount++;
5961     InsertTailList(&m_InterruptListHead, &Interrupt->m_PnpList);
5962 }
5963 
5964 VOID
RemoveInterruptObject(__in FxInterrupt * Interrupt)5965 FxPkgPnp::RemoveInterruptObject(
5966     __in FxInterrupt* Interrupt
5967     )
5968 /*++
5969 
5970 Routine Description:
5971     This routine removes a WDFINTERRUPT object onto the list of interrupts
5972     which are attached to this device.  This list is used in response to
5973     several IRPs.
5974 
5975 Arguments:
5976 
5977     Interrupt - a Framework interrupt object
5978 
5979 Return Value:
5980 
5981     VOID
5982 
5983 --*/
5984 {
5985     m_InterruptObjectCount--;
5986     RemoveEntryList(&Interrupt->m_PnpList);
5987 }
5988 
5989 VOID
NotifyResourceobjectsToReleaseResources(VOID)5990 FxPkgPnp::NotifyResourceobjectsToReleaseResources(
5991     VOID
5992     )
5993 /*++
5994 
5995 Routine Description:
5996 
5997     This routine traverses all resource objects and tells them that their
5998     resources are no longer valid.
5999 
6000 Arguments:
6001 
6002     none
6003 
6004 Return Value:
6005 
6006     VOID
6007 
6008 --*/
6009 {
6010     FxInterrupt* interruptInstance;
6011     PLIST_ENTRY intListEntry;
6012 
6013     //
6014     // Revoke each of the interrupts.
6015     //
6016 
6017     intListEntry = m_InterruptListHead.Flink;
6018 
6019     while (intListEntry != &m_InterruptListHead) {
6020 
6021         //
6022         // Disconnect interrupts and then tell them that they no longer
6023         // own their resources.
6024         //
6025 
6026         interruptInstance = CONTAINING_RECORD(intListEntry, FxInterrupt, m_PnpList);
6027         interruptInstance->RevokeResources();
6028         intListEntry = intListEntry->Flink;
6029     }
6030 
6031     //
6032     // Now revoke each of the DMA enablers (only system-mode enablers
6033     // will be affected by this)
6034     //
6035 
6036     if (m_DmaEnablerList != NULL) {
6037         FxTransactionedEntry* listEntry;
6038 
6039         m_DmaEnablerList->LockForEnum(GetDriverGlobals());
6040 
6041         for (listEntry = m_DmaEnablerList->GetNextEntry(NULL);
6042              listEntry != NULL;
6043              listEntry = m_DmaEnablerList->GetNextEntry(listEntry)) {
6044             RevokeDmaEnablerResources(
6045                 (FxDmaEnabler *) listEntry->GetTransactionedObject()
6046                 );
6047         }
6048 
6049         m_DmaEnablerList->UnlockFromEnum(GetDriverGlobals());
6050     }
6051 }
6052 
6053 _Must_inspect_result_
6054 NTSTATUS
NotifyResourceObjectsD0(__in ULONG NotifyFlags)6055 FxPkgPnp::NotifyResourceObjectsD0(
6056     __in ULONG NotifyFlags
6057     )
6058 /*++
6059 
6060 Routine Description:
6061 
6062     This routine traverses all resource objects and tells them that the device
6063     is entering D0.  If an error is encountered, the walking of the list is
6064     halted.
6065 
6066 Arguments:
6067 
6068     none
6069 
6070 Return Value:
6071 
6072     VOID
6073 
6074 --*/
6075 {
6076     FxInterrupt* pInterrupt;
6077     PLIST_ENTRY ple;
6078     NTSTATUS status;
6079 
6080     for (ple = m_InterruptListHead.Flink;
6081          ple != &m_InterruptListHead;
6082          ple = ple->Flink) {
6083 
6084         //
6085         // Connect the interrupts
6086         //
6087         pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
6088 
6089         status = pInterrupt->Connect(NotifyFlags);
6090 
6091         if (!NT_SUCCESS(status)) {
6092             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6093                                 "WDFINTERRUPT %p failed to connect, %!STATUS!",
6094                                 pInterrupt->GetHandle(), status);
6095 
6096             return status;
6097         }
6098     }
6099 
6100     return STATUS_SUCCESS;
6101 }
6102 
6103 NTSTATUS
NotifyResourceObjectsDx(__in ULONG NotifyFlags)6104 FxPkgPnp::NotifyResourceObjectsDx(
6105     __in ULONG NotifyFlags
6106     )
6107 /*++
6108 
6109 Routine Description:
6110     This routine traverses all resource objects and tells them that the device
6111     is leaving D0.  If there is an error, the remaining resources in the list
6112     are still notified of exitting D0.
6113 
6114 Arguments:
6115     NotifyFlags - combination of values from the enum NotifyResourcesFlags
6116 
6117 Return Value:
6118     None
6119 
6120 --*/
6121 {
6122     FxInterrupt* pInterrupt;
6123     PLIST_ENTRY ple;
6124     NTSTATUS status, finalStatus;
6125 
6126     finalStatus = STATUS_SUCCESS;
6127 
6128     //
6129     // Disconect in the reverse order in which we connected the interrupts
6130     //
6131     for (ple = m_InterruptListHead.Blink;
6132          ple != &m_InterruptListHead;
6133          ple = ple->Blink) {
6134 
6135         //
6136         // Disconnect interrupts and then tell them that they no longer
6137         // own their resources.
6138         //
6139         pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
6140 
6141         status = pInterrupt->Disconnect(NotifyFlags);
6142 
6143         if (!NT_SUCCESS(status)) {
6144             //
6145             // When we encounter an error we still disconnect the remaining
6146             // interrupts b/c we would just do it later during device tear down
6147             // (might as well do it now).
6148             //
6149             DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6150                                 "WDFINTERRUPT %p failed to disconnect, %!STATUS!",
6151                                 pInterrupt->GetHandle(), status);
6152             //
6153             // Overwriting a previous finalStatus is OK, we'll just report the
6154             // last one
6155             //
6156             finalStatus = status;
6157         }
6158     }
6159 
6160     return finalStatus;
6161 }
6162 
6163 VOID
SendEventToAllWakeInterrupts(__in FxWakeInterruptEvents WakeInterruptEvent)6164 FxPkgPnp::SendEventToAllWakeInterrupts(
6165     __in FxWakeInterruptEvents WakeInterruptEvent
6166     )
6167 /*++
6168 
6169 Routine Description:
6170     This routine traverses all interrupt objects and queues the
6171     given event into those interrupts that are marked as wake
6172     capable and have a state machine
6173 
6174 Arguments:
6175     WakeInterruptEvent - Event to be queued in the wake interrupt
6176                          state machines
6177 
6178 Return Value:
6179     None
6180 
6181 --*/
6182 {
6183     FxInterrupt* pInterrupt;
6184     PLIST_ENTRY ple;
6185 
6186     if (m_WakeInterruptCount == 0) {
6187         return;
6188     }
6189 
6190     //
6191     // Initialize the pending count to the wake interrupt count
6192     // If the event needs an acknowledgement, this variable will
6193     // be decremented as the interrupt machines ack.
6194     //
6195     m_WakeInterruptPendingAckCount = m_WakeInterruptCount;
6196 
6197     for (ple = m_InterruptListHead.Flink;
6198          ple != &m_InterruptListHead;
6199          ple = ple->Flink) {
6200 
6201         pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
6202 
6203         if (pInterrupt->IsWakeCapable()) {
6204             pInterrupt->ProcessWakeInterruptEvent(WakeInterruptEvent);
6205         }
6206 
6207     }
6208 }
6209 
6210 VOID
AckPendingWakeInterruptOperation(__in BOOLEAN ProcessPowerEventOnDifferentThread)6211 FxPkgPnp::AckPendingWakeInterruptOperation(
6212     __in BOOLEAN ProcessPowerEventOnDifferentThread
6213     )
6214 /*++
6215 
6216 Routine Description:
6217     This routine is invoked by the interrupt wake machine to acknowledge
6218     back an event that was queued into it. When the last interrupt machine
6219     acknowledges, an event is queued back into the Pnp/power state machine
6220 
6221 Arguments:
6222 
6223     ProcessPowerEventOnDifferentThread - Once all wake interrupts for the device
6224         have acknowledged the operation, if this is TRUE, the power state
6225         machine will process the PowerWakeInterruptCompleteTransition event on a
6226         seperate thread.
6227 
6228 Return Value:
6229     None
6230 
6231 --*/
6232 {
6233     if (InterlockedDecrement((LONG *)&m_WakeInterruptPendingAckCount) == 0) {
6234         PowerProcessEvent(
6235             PowerWakeInterruptCompleteTransition,
6236             ProcessPowerEventOnDifferentThread
6237             );
6238     }
6239 }
6240 
6241 
6242 NTSTATUS
AssignPowerFrameworkSettings(__in PWDF_POWER_FRAMEWORK_SETTINGS PowerFrameworkSettings)6243 FxPkgPnp::AssignPowerFrameworkSettings(
6244     __in PWDF_POWER_FRAMEWORK_SETTINGS PowerFrameworkSettings
6245     )
6246 {
6247 //     NTSTATUS status;
6248 //     PPO_FX_COMPONENT_IDLE_STATE idleStates = NULL;
6249 //     ULONG idleStatesSize = 0;
6250 //     PPO_FX_COMPONENT component = NULL;
6251 //     ULONG componentSize = 0;
6252 //     PPOX_SETTINGS poxSettings = NULL;
6253 //     ULONG poxSettingsSize = 0;
6254 //     BYTE * buffer = NULL;
6255 
6256 //     if (FALSE==(IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable())) {
6257 //         //
6258 //         // If system-managed idle timeout is not available on this OS, then
6259 //         // there is nothing to do.
6260 //         //
6261 //         DoTraceLevelMessage(
6262 //             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
6263 //             "WDFDEVICE %p !devobj %p Power framework is not supported on the "
6264 //             "current OS. Therefore, the power framework settings will not take "
6265 //             "effect.",
6266 //             m_Device->GetHandle(),
6267 //             m_Device->GetDeviceObject()
6268 //             );
6269 //         return STATUS_SUCCESS;
6270 //     }
6271 
6272 //     if (NULL != PowerFrameworkSettings->Component) {
6273 //         //
6274 //         // Caller should ensure that IdleStateCount is not zero
6275 //         //
6276 //         ASSERT(0 != PowerFrameworkSettings->Component->IdleStateCount);
6277 
6278 //         //
6279 //         // Compute buffer size needed for storing F-states
6280 //         //
6281 //         status = RtlULongMult(
6282 //                     PowerFrameworkSettings->Component->IdleStateCount,
6283 //                     sizeof(*(PowerFrameworkSettings->Component->IdleStates)),
6284 //                     &idleStatesSize
6285 //                     );
6286 //         if (FALSE == NT_SUCCESS(status)) {
6287 //             DoTraceLevelMessage(
6288 //                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6289 //                 "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
6290 //                 "required to store F-states. RtlULongMult failed with "
6291 //                 "%!STATUS!",
6292 //                 m_Device->GetHandle(),
6293 //                 m_Device->GetDeviceObject(),
6294 //                 status
6295 //                 );
6296 //             goto exit;
6297 //         }
6298 
6299 //         //
6300 //         // Compute buffer size needed for storing component information
6301 //         // (including F-states)
6302 //         //
6303 //         status = RtlULongAdd(idleStatesSize,
6304 //                              sizeof(*component),
6305 //                              &componentSize);
6306 //         if (FALSE == NT_SUCCESS(status)) {
6307 //             DoTraceLevelMessage(
6308 //                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6309 //                 "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
6310 //                 "required to store driver's component information. RtlULongAdd "
6311 //                 "failed with %!STATUS!",
6312 //                 m_Device->GetHandle(),
6313 //                 m_Device->GetDeviceObject(),
6314 //                 status
6315 //                 );
6316 //             goto exit;
6317 //         }
6318 //     }
6319 
6320 //     //
6321 //     // Compute total buffer size needed for power framework settings
6322 //     //
6323 //     status = RtlULongAdd(componentSize,
6324 //                          sizeof(*poxSettings),
6325 //                          &poxSettingsSize);
6326 //     if (FALSE == NT_SUCCESS(status)) {
6327 //         DoTraceLevelMessage(
6328 //             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6329 //             "WDFDEVICE %p !devobj %p Unable to compute length of buffer "
6330 //             "required to store driver's power framework settings. RtlULongAdd "
6331 //             "failed with %!STATUS!",
6332 //             m_Device->GetHandle(),
6333 //             m_Device->GetDeviceObject(),
6334 //             status
6335 //             );
6336 //         goto exit;
6337 //     }
6338 
6339 //     //
6340 //     // Allocate memory to copy the settings
6341 //     //
6342 //     buffer = (BYTE *) MxMemory::MxAllocatePoolWithTag(NonPagedPool,
6343 //                                                       poxSettingsSize,
6344 //                                                       GetDriverGlobals()->Tag);
6345 //     if (NULL == buffer) {
6346 //         status = STATUS_INSUFFICIENT_RESOURCES;
6347 //         DoTraceLevelMessage(
6348 //             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
6349 //             "WDFDEVICE %p !devobj %p Unable to allocate buffer required to "
6350 //             "store F-states. %!STATUS!",
6351 //             m_Device->GetHandle(),
6352 //             m_Device->GetDeviceObject(),
6353 //             status
6354 //             );
6355 //         goto exit;
6356 //     }
6357 
6358 //     //
6359 //     // Set our pointers to point to appropriate locations in the buffer.
6360 //     //
6361 //     // NOTES:
6362 //     //   - The array of F-states comes first because it has ULONGLONG members
6363 //     //     because of which it has the biggest alignment requirement.
6364 //     //   - The logic below works even if the client driver did not specify any
6365 //     //     component information. In that case idleStatesSize and componentSize
6366 //     //     are both 0 and 'poxSettings' points to the beginning of the allocated
6367 //     //     buffer
6368 //     //
6369 //     idleStates = (PPO_FX_COMPONENT_IDLE_STATE) buffer;
6370 //     component = (PPO_FX_COMPONENT) (buffer + idleStatesSize);
6371 //     poxSettings = (PPOX_SETTINGS) (buffer + componentSize);
6372 
6373 //     //
6374 //     // Copy the relevant parts of the settings buffer
6375 //     //
6376 //     poxSettings->EvtDeviceWdmPostPoFxRegisterDevice =
6377 //         PowerFrameworkSettings->EvtDeviceWdmPostPoFxRegisterDevice;
6378 //     poxSettings->EvtDeviceWdmPrePoFxUnregisterDevice =
6379 //         PowerFrameworkSettings->EvtDeviceWdmPrePoFxUnregisterDevice;
6380 //     poxSettings->Component = PowerFrameworkSettings->Component;
6381 //     poxSettings->ComponentActiveConditionCallback =
6382 //         PowerFrameworkSettings->ComponentActiveConditionCallback;
6383 //     poxSettings->ComponentIdleConditionCallback =
6384 //         PowerFrameworkSettings->ComponentIdleConditionCallback;
6385 //     poxSettings->ComponentIdleStateCallback =
6386 //         PowerFrameworkSettings->ComponentIdleStateCallback;
6387 //     poxSettings->PowerControlCallback =
6388 //         PowerFrameworkSettings->PowerControlCallback;
6389 //     poxSettings->PoFxDeviceContext = PowerFrameworkSettings->PoFxDeviceContext;
6390 
6391 //     if (NULL != PowerFrameworkSettings->Component) {
6392 //         //
6393 //         // Copy the component information
6394 //         //
6395 //         poxSettings->Component = component;
6396 //         RtlCopyMemory(poxSettings->Component,
6397 //                       PowerFrameworkSettings->Component,
6398 //                       sizeof(*component));
6399 
6400 //         //
6401 //         // Caller should ensure that IdleStates is not NULL
6402 //         //
6403 //         ASSERT(NULL != PowerFrameworkSettings->Component->IdleStates);
6404 
6405 //         //
6406 //         // Copy the F-states
6407 //         //
6408 //         poxSettings->Component->IdleStates = idleStates;
6409 //         RtlCopyMemory(poxSettings->Component->IdleStates,
6410 //                       PowerFrameworkSettings->Component->IdleStates,
6411 //                       idleStatesSize);
6412 //     }
6413 
6414 //     //
6415 //     // Commit these settings
6416 //     //
6417 //     status = m_PowerPolicyMachine.m_Owner->
6418 //                 m_IdleSettings.m_TimeoutMgmt.CommitPowerFrameworkSettings(
6419 //                                                             GetDriverGlobals(),
6420 //                                                             poxSettings
6421 //                                                             );
6422 //     if (FALSE == NT_SUCCESS(status)) {
6423 //         goto exit;
6424 //     }
6425 
6426 //     status = STATUS_SUCCESS;
6427 
6428 // exit:
6429 //     if (FALSE == NT_SUCCESS(status)) {
6430 //         if (NULL != buffer) {
6431 //             MxMemory::MxFreePool(buffer);
6432 //         }
6433 //     }
6434 //     return status;
6435     ROSWDFNOTIMPLEMENTED;
6436     return STATUS_SUCCESS;
6437 }
6438 
6439 DEVICE_POWER_STATE
PowerPolicyGetDeviceDeepestDeviceWakeState(__in SYSTEM_POWER_STATE SystemState)6440 FxPkgPnp::PowerPolicyGetDeviceDeepestDeviceWakeState(
6441     __in SYSTEM_POWER_STATE SystemState
6442     )
6443 {
6444     DEVICE_POWER_STATE dxState;
6445 
6446     //
6447     // Earlier versions of WDF (pre-1.11) did not take into account SystemState
6448     // in figuring out the deepest wake state. So for compatibility we restrict
6449     // this to drivers compiled for 1.11 or newer. See comments in the
6450     // FxPkgPnp::QueryForCapabilities function for more information on
6451     // compatibility risk.
6452     //
6453     if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1,11)) {
6454         if ((SystemState < PowerSystemWorking) ||
6455             (SystemState > PowerSystemHibernate)) {
6456             dxState = PowerDeviceD0;
6457         } else {
6458             dxState = MapWakeDepthToDstate(
6459                 (DEVICE_WAKE_DEPTH)m_DeviceWake[SystemState - PowerSystemWorking]
6460                 );
6461         }
6462     }
6463     else {
6464         //
6465         // For pre-1.11 drivers, all of m_DeviceWake array is populated with
6466         // the same DeviceWake value obtained from device capabilities so we
6467         // just use the first one (index 0).
6468         //
6469         dxState = MapWakeDepthToDstate((DEVICE_WAKE_DEPTH)m_DeviceWake[0]);
6470     }
6471 
6472     //
6473     // Per WDM docs DeviceWake and SystemWake must be non-zero to support
6474     // wake. We warn about the violation. Ideally, a verifier check would have
6475     // been better but we want to avoid that because some drivers may
6476     // intentionally call this DDI and do not treat the DDI failure as fatal (
6477     // because they are aware that DDI may succeed in some cases), and a verifier
6478     // breakpoint would force them to avoid the verifier failure by not enabling
6479     // verifier.
6480     //
6481     if (dxState == PowerDeviceUnspecified ||
6482         m_SystemWake == PowerSystemUnspecified) {
6483         DoTraceLevelMessage(
6484             GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
6485             "SystemWake %!SYSTEM_POWER_STATE! and DeviceWake "
6486             "%!DEVICE_POWER_STATE! power state reported in device "
6487             "capabilities do not support wake. Both the SystemWake and "
6488             "DeviceWake members should be nonzero to support wake-up on "
6489             "this system.", m_SystemWake, dxState);
6490     }
6491 
6492     return dxState;
6493 }
6494 
6495