1 /*++
2 
3 Copyright (c) Microsoft. All rights reserved.
4 
5 Module Name:
6 
7     PnpStateMachine.cpp
8 
9 Abstract:
10 
11     This module implements the PnP state machine for the driver framework.
12     This code was split out from FxPkgPnp.cpp.
13 
14 Author:
15 
16 
17 
18 
19 Environment:
20 
21     Both kernel and user mode
22 
23 Revision History:
24 
25 
26 
27 --*/
28 
29 #include "pnppriv.hpp"
30 #include <wdmguid.h>
31 
32 #include<ntstrsafe.h>
33 
34 extern "C" {
35 #if defined(EVENT_TRACING)
36 #include "PnpStateMachine.tmh"
37 #endif
38 }
39 
40 
41 //
42 // The PnP State Machine
43 //
44 // This state machine responds to several PnP events:
45 //
46 // AddDevice                    -- Always targets the same state
47 // IRP_MN_START_DEVICE
48 // IRP_MN_START_DEVICE Complete -- Handled on the way up the stack
49 // IRP_MN_QUERY_REMOVE_DEVICE
50 // IRP_MN_QUERY_STOP_DEVICE
51 // IRP_MN_CANCEL_REMOVE_DEVICE
52 // IRP_MN_CANCEL_STOP_DEVICE
53 // IRP_MN_STOP_DEVICE
54 // IRP_MN_REMOVE_DEVICE
55 // IRP_MN_SURPRISE_REMOVE_DEVICE -- Always targets the same state
56 // IRP_MN_EJECT
57 //
58 // Each state has an entry for each of these events, listing the
59 // target state for each of them.
60 //
61 
62 #if FX_STATE_MACHINE_VERIFY
63     #define VALIDATE_PNP_STATE(_CurrentState, _NewState)     \
64         ValidatePnpStateEntryFunctionReturnValue((_CurrentState), (_NewState))
65 #else
66     #define VALIDATE_PNP_STATE(_CurrentState, _NewState)   (0)
67 #endif  //FX_STATE_MACHINE_VERIFY
68 
69 // @@SMVERIFY_SPLIT_BEGIN
70 
71 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitOtherStates[] =
72 {
73     { PnpEventQueryRemove, WdfDevStatePnpInitQueryRemove DEBUGGED_EVENT },
74     { PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
75     { PnpEventParentRemoved, WdfDevStatePnpRemoved DEBUGGED_EVENT },
76     { PnpEventSurpriseRemove, WdfDevStatePnpInitSurpriseRemoved DEBUGGED_EVENT },
77     { PnpEventEject, WdfDevStatePnpEjectHardware DEBUGGED_EVENT },
78     { PnpEventNull, WdfDevStatePnpNull },
79 };
80 
81 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitStartingOtherStates[] =
82 {
83     { PnpEventStartDeviceFailed, WdfDevStatePnpInit DEBUGGED_EVENT },
84     { PnpEventNull, WdfDevStatePnpNull },
85 };
86 
87 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpHardwareAvailableOtherStates[] =
88 {
89     { PnpEventPwrPolStartFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
90     { PnpEventNull, WdfDevStatePnpNull },
91 };
92 
93 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueryStopPendingOtherStates[] =
94 {
95     { PnpEventCancelStop, WdfDevStatePnpQueryCanceled DEBUGGED_EVENT },
96     { PnpEventSurpriseRemove, WdfDevStatePnpQueriedSurpriseRemove TRAP_ON_EVENT },
97     { PnpEventNull, WdfDevStatePnpNull },
98 };
99 
100 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRemovedPdoWaitOtherStates[] =
101 {
102     { PnpEventStartDevice, WdfDevStatePnpPdoRestart DEBUGGED_EVENT },
103     { PnpEventRemove, WdfDevStatePnpCheckForDevicePresence DEBUGGED_EVENT },
104     { PnpEventParentRemoved, WdfDevStatePnpPdoRemoved DEBUGGED_EVENT },
105     { PnpEventSurpriseRemove, WdfDevStatePnpRemovedPdoSurpriseRemoved DEBUGGED_EVENT },
106     { PnpEventNull, WdfDevStatePnpNull },
107 };
108 
109 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartingOtherStates[] =
110 {
111     { PnpEventPwrPolStartFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
112     { PnpEventNull, WdfDevStatePnpNull },
113 };
114 
115 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedOtherStates[] =
116 {
117     { PnpEventQueryStop, WdfDevStatePnpQueryStopStaticCheck DEBUGGED_EVENT },
118     { PnpEventCancelStop, WdfDevStatePnpStartedCancelStop DEBUGGED_EVENT },
119     { PnpEventCancelRemove, WdfDevStatePnpStartedCancelRemove DEBUGGED_EVENT },
120     { PnpEventRemove, WdfDevStatePnpStartedRemoving DEBUGGED_EVENT },
121     { PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemoveIoStarted DEBUGGED_EVENT },
122     { PnpEventPowerUpFailed,   WdfDevStatePnpFailedIoStarting DEBUGGED_EVENT },
123     { PnpEventPowerDownFailed, WdfDevStatePnpFailedPowerDown DEBUGGED_EVENT },
124     { PnpEventStartDevice, WdfDevStatePnpRestart DEBUGGED_EVENT },
125     { PnpEventNull, WdfDevStatePnpNull },
126 };
127 
128 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueryRemovePendingOtherStates[] =
129 {
130     { PnpEventCancelRemove, WdfDevStatePnpQueryCanceled DEBUGGED_EVENT },
131     { PnpEventSurpriseRemove, WdfDevStatePnpQueriedSurpriseRemove TRAP_ON_EVENT },
132     { PnpEventNull, WdfDevStatePnpNull },
133 };
134 
135 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpQueriedRemovingOtherStates[] =
136 {
137     { PnpEventPwrPolStopFailed, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
138     { PnpEventNull, WdfDevStatePnpNull },
139 };
140 
141 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpInitQueryRemoveOtherStates[] =
142 {
143     { PnpEventCancelRemove, WdfDevStatePnpInitQueryRemoveCanceled DEBUGGED_EVENT },
144     { PnpEventNull, WdfDevStatePnpNull },
145 };
146 
147 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStoppedOtherStates[] =
148 {
149     { PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemove DEBUGGED_EVENT },
150     { PnpEventNull, WdfDevStatePnpNull },
151 };
152 
153 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStoppedWaitForStartCompletionOtherStates[] =
154 {
155     { PnpEventStartDeviceFailed, WdfDevStatePnpFailed TRAP_ON_EVENT },
156     { PnpEventNull, WdfDevStatePnpNull },
157 };
158 
159 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedStoppingOtherStates[] =
160 {
161     { PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
162     { PnpEventNull, WdfDevStatePnpNull },
163 };
164 
165 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedStoppingFailedOtherStates[] =
166 {
167     { PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware TRAP_ON_EVENT },
168     { PnpEventNull, WdfDevStatePnpNull },
169 };
170 
171 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpEjectFailedOtherStates[] =
172 {
173     { PnpEventSurpriseRemove, WdfDevStatePnpSurpriseRemove TRAP_ON_EVENT },
174     { PnpEventNull, WdfDevStatePnpNull },
175 };
176 
177 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpStartedRemovingOtherStates[] =
178 {
179     { PnpEventPwrPolStopFailed, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
180     { PnpEventNull, WdfDevStatePnpNull },
181 };
182 
183 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedPowerDownOtherStates[] =
184 {
185     { PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
186     { PnpEventNull, WdfDevStatePnpNull },
187 };
188 
189 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedIoStartingOtherStates[] =
190 {
191     { PnpEventPwrPolStopFailed, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
192     { PnpEventNull, WdfDevStatePnpNull },
193 };
194 
195 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpFailedWaitForRemoveOtherStates[] =
196 {
197     { PnpEventSurpriseRemove, WdfDevStatePnpFailedSurpriseRemoved DEBUGGED_EVENT },
198     { PnpEventStartDevice, WdfDevStatePnpFailedStarted TRAP_ON_EVENT },
199     { PnpEventNull, WdfDevStatePnpNull },
200 };
201 
202 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartOtherStates[] =
203 {
204     { PnpEventPwrPolStopFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
205     { PnpEventNull, WdfDevStatePnpNull },
206 };
207 
208 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartReleaseHardware[] =
209 {
210     { PnpEventStartDeviceFailed, WdfDevStatePnpFailed TRAP_ON_EVENT },
211     { PnpEventNull, WdfDevStatePnpNull },
212 };
213 
214 const PNP_EVENT_TARGET_STATE FxPkgPnp::m_PnpRestartHardwareAvailableOtherStates[] =
215 {
216     { PnpEventPwrPolStartFailed, WdfDevStatePnpHardwareAvailablePowerPolicyFailed DEBUGGED_EVENT },
217     { PnpEventNull, WdfDevStatePnpNull },
218 };
219 
220 const PNP_STATE_TABLE FxPkgPnp::m_WdfPnpStates[] = {
221     // State function
222     // First transition event & state
223     // Other transition events & states
224     // state info
225 
226     //      WdfDevStatePnpObjectCreated
227     {   NULL,
228         { PnpEventAddDevice, WdfDevStatePnpInit DEBUGGED_EVENT },
229         NULL,
230         { TRUE,
231           0 },
232     },
233 
234     //      WdfDevStatePnpCheckForDevicePresence
235     {   FxPkgPnp::PnpEventCheckForDevicePresence,
236         { PnpEventNull, WdfDevStatePnpNull },
237         NULL,
238         { FALSE,
239           0 },
240     },
241 
242     //      WdfDevStatePnpEjectFailed
243     {   NULL,
244         { PnpEventStartDevice, WdfDevStatePnpPdoRestart DEBUGGED_EVENT },
245         FxPkgPnp::m_PnpEjectFailedOtherStates,
246         { TRUE,
247           0 },
248     },
249 
250     //      WdfDevStatePnpEjectHardware
251     {   FxPkgPnp::PnpEventEjectHardware,
252         { PnpEventNull, WdfDevStatePnpNull },
253         NULL,
254         { FALSE,
255           0 },
256     },
257 
258     //      WdfDevStatePnpEjectedWaitingForRemove
259     {   NULL,
260         { PnpEventRemove, WdfDevStatePnpPdoRemoved DEBUGGED_EVENT },
261         NULL,
262         { TRUE,
263           PnpEventSurpriseRemove },  // can receive this if parent is surprise
264                                      // removed while the ejected pdo is waiting
265                                      // for remove.
266     },
267 
268     //      WdfDevStatePnpInit
269     {   NULL,
270         { PnpEventStartDevice, WdfDevStatePnpInitStarting DEBUGGED_EVENT },
271         FxPkgPnp::m_PnpInitOtherStates,
272         { TRUE,
273           PnpEventStartDevice },
274     },
275 
276     //      WdfDevStatePnpInitStarting
277     {   FxPkgPnp::PnpEventInitStarting,
278         { PnpEventStartDeviceComplete, WdfDevStatePnpHardwareAvailable DEBUGGED_EVENT },
279         FxPkgPnp::m_PnpInitStartingOtherStates,
280         { TRUE,
281           0 },
282     },
283 
284     //      WdfDevStatePnpInitSurpriseRemoved
285     {   FxPkgPnp::PnpEventInitSurpriseRemoved,
286         { PnpEventNull, WdfDevStatePnpNull },
287         NULL,
288         { FALSE,
289           0 },
290     },
291 
292     //      WdfDevStatePnpHardwareAvailable
293     {   FxPkgPnp::PnpEventHardwareAvailable,
294         { PnpEventPwrPolStarted, WdfDevStatePnpEnableInterfaces DEBUGGED_EVENT },
295         FxPkgPnp::m_PnpHardwareAvailableOtherStates,
296         { FALSE,
297           PnpEventPowerUpFailed
298         },
299     },
300 
301     //      WdfDevStatePnpEnableInterfaces
302     {   FxPkgPnp::PnpEventEnableInterfaces,
303         { PnpEventNull, WdfDevStatePnpNull },
304         NULL,
305         { FALSE,
306           0 },
307     },
308 
309     //      WdfDevStatePnpHardwareAvailablePowerPolicyFailed
310     {   FxPkgPnp::PnpEventHardwareAvailablePowerPolicyFailed,
311         { PnpEventNull, WdfDevStatePnpNull },
312         NULL,
313         { FALSE,
314           0 },
315     },
316 
317     //      WdfDevStatePnpQueryRemoveAskDriver
318     {   FxPkgPnp::PnpEventQueryRemoveAskDriver,
319         { PnpEventNull, WdfDevStatePnpNull },
320         NULL,
321         { FALSE,
322           0 },
323     },
324 
325     //      WdfDevStatePnpQueryRemovePending
326     {   FxPkgPnp::PnpEventQueryRemovePending,
327         { PnpEventRemove, WdfDevStatePnpQueriedRemoving DEBUGGED_EVENT },
328         FxPkgPnp::m_PnpQueryRemovePendingOtherStates,
329         { TRUE,
330           0,
331         },
332     },
333 
334     //      WdfDevStatePnpQueryRemoveStaticCheck
335     {   FxPkgPnp::PnpEventQueryRemoveStaticCheck,
336         { PnpEventNull, WdfDevStatePnpNull },
337         NULL,
338         { FALSE,
339           0 },
340     },
341 
342     //      WdfDevStatePnpQueriedRemoving,
343     {   FxPkgPnp::PnpEventQueriedRemoving,
344         { PnpEventPwrPolStopped, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
345         FxPkgPnp::m_PnpQueriedRemovingOtherStates,
346         { FALSE,
347           PnpEventPowerDownFailed | // We ignore these power failed events because
348           PnpEventPowerUpFailed     // they will be translated into failed power
349                                     // policy events.
350         },
351     },
352 
353     //      WdfDevStatePnpQueryStopAskDriver
354     {   FxPkgPnp::PnpEventQueryStopAskDriver,
355         { PnpEventNull, WdfDevStatePnpNull },
356         NULL,
357         { FALSE,
358           0 },
359     },
360 
361     //      WdfDevStatePnpQueryStopPending
362     {   FxPkgPnp::PnpEventQueryStopPending,
363         { PnpEventStop, WdfDevStatePnpStartedStopping DEBUGGED_EVENT },
364         FxPkgPnp::m_PnpQueryStopPendingOtherStates,
365         { TRUE,
366           0,
367         },
368     },
369 
370     //      WdfDevStatePnpQueryStopStaticCheck
371     {   FxPkgPnp::PnpEventQueryStopStaticCheck,
372         { PnpEventNull, WdfDevStatePnpNull },
373         NULL,
374         { FALSE,
375           0 },
376     },
377 
378     //      WdfDevStatePnpQueryCanceled,
379     {   FxPkgPnp::PnpEventQueryCanceled,
380         { PnpEventNull, WdfDevStatePnpNull },
381         NULL,
382         { FALSE,
383           0 },
384     },
385 
386     //      WdfDevStatePnpRemoved
387     {   FxPkgPnp::PnpEventRemoved,
388         { PnpEventNull, WdfDevStatePnpNull },
389         NULL,
390         { FALSE,
391           0 },
392     },
393 
394     //      WdfDevStatePnpPdoRemoved
395     {   FxPkgPnp::PnpEventPdoRemoved,
396         { PnpEventNull, WdfDevStatePnpNull },
397         NULL,
398         { FALSE,
399           0 },
400     },
401 
402     //      WdfDevStatePnpRemovedPdoWait
403     {   FxPkgPnp::PnpEventRemovedPdoWait,
404         { PnpEventEject, WdfDevStatePnpEjectHardware DEBUGGED_EVENT },
405         FxPkgPnp::m_PnpRemovedPdoWaitOtherStates,
406         { TRUE,
407           PnpEventCancelRemove | // Amazingly enough, you can get a cancel q.r.
408                                  // on a PDO without seeing the query remove if
409                                  // the stack is partially built
410           PnpEventQueryRemove  | // Can get a query remove from the removed state
411                                  // when installing a PDO that is disabled
412           PnpEventPowerDownFailed // We may get this for a PDO if implicit power
413                                   // down callbacks were failed. The failed power
414                                   // policy stop event took care of rundown.
415         },
416     },
417 
418     //      WdfDevStatePnpRemovedPdoSurpriseRemoved
419     {   FxPkgPnp::PnpEventRemovedPdoSurpriseRemoved,
420         { PnpEventNull, WdfDevStatePnpNull },
421         NULL,
422         { FALSE,
423           0 },
424     },
425 
426     //      WdfDevStatePnpRemovingDisableInterfaces
427     {   FxPkgPnp::PnpEventRemovingDisableInterfaces,
428         { PnpEventPwrPolRemoved, WdfDevStatePnpRemoved DEBUGGED_EVENT },
429         NULL,
430         { FALSE,
431           0 },
432     },
433 
434     //      WdfDevStatePnpRestarting
435     {   FxPkgPnp::PnpEventRestarting,
436         { PnpEventPwrPolStarted, WdfDevStatePnpStarted DEBUGGED_EVENT },
437         FxPkgPnp::m_PnpRestartingOtherStates,
438         { FALSE,
439           PnpEventPowerUpFailed
440         },
441     },
442 
443     //      WdfDevStatePnpStarted
444     {   FxPkgPnp::PnpEventStarted,
445         { PnpEventQueryRemove, WdfDevStatePnpQueryRemoveStaticCheck DEBUGGED_EVENT },
446         FxPkgPnp::m_PnpStartedOtherStates,
447         { TRUE,
448           0,
449         },
450     },
451 
452     //      WdfDevStatePnpStartedCancelStop
453     {   FxPkgPnp::PnpEventStartedCancelStop,
454         { PnpEventNull, WdfDevStatePnpNull },
455         NULL,
456         { FALSE,
457           0 },
458     },
459 
460     //      WdfDevStatePnpStartedCancelRemove
461     {   FxPkgPnp::PnpEventStartedCancelRemove,
462         { PnpEventNull, WdfDevStatePnpNull },
463         NULL,
464         { FALSE,
465           0 },
466     },
467 
468     //      WdfDevStatePnpStartedRemoving
469     {   FxPkgPnp::PnpEventStartedRemoving,
470         { PnpEventPwrPolStopped, WdfDevStatePnpRemovingDisableInterfaces DEBUGGED_EVENT },
471         FxPkgPnp::m_PnpStartedRemovingOtherStates,
472         { TRUE,
473           PnpEventPowerUpFailed | // device was idled out and in Dx when we got removed
474                                   // and this event is due to the power up that occured
475                                   // to move it into D0 so it could be disarmed
476           PnpEventPowerDownFailed
477         },
478     },
479 
480     //      WdfDevStatePnpStartingFromStopped
481     {   FxPkgPnp::PnpEventStartingFromStopped,
482         { PnpEventNull, WdfDevStatePnpNull },
483         NULL,
484         { FALSE,
485           0 },
486     },
487 
488     //      WdfDevStatePnpStopped
489     {   FxPkgPnp::PnpEventStopped,
490         { PnpEventStartDevice, WdfDevStatePnpStoppedWaitForStartCompletion DEBUGGED_EVENT },
491         FxPkgPnp::m_PnpStoppedOtherStates,
492         { TRUE,
493           0,
494         },
495     },
496 
497     //      WdfDevStatePnpStoppedWaitForStartCompletion
498     {   FxPkgPnp::PnpEventStoppedWaitForStartCompletion,
499         { PnpEventStartDeviceComplete, WdfDevStatePnpStartingFromStopped DEBUGGED_EVENT },
500         FxPkgPnp::m_PnpStoppedWaitForStartCompletionOtherStates,
501         { TRUE,
502           0 },
503     },
504 
505     //      WdfDevStatePnpStartedStopping
506     {   FxPkgPnp::PnpEventStartedStopping,
507         { PnpEventPwrPolStopped, WdfDevStatePnpStopped DEBUGGED_EVENT },
508         FxPkgPnp::m_PnpStartedStoppingOtherStates,
509         { TRUE,
510           PnpEventPowerUpFailed | // device was idled out and in Dx when we got stopped
511                                   // and this event is due to the power up that occured
512                                   // to move it into D0 so it could be disarmed
513           PnpEventPowerDownFailed
514         },
515     },
516 
517     // The function is named PnpEventSurpriseRemoved with a 'd' because
518     // PnpEventSurpriseRemove (no 'd') is an event name
519 
520     //      WdfDevStatePnpSurpriseRemove
521     {   FxPkgPnp::PnpEventSurpriseRemoved,
522         { PnpEventNull, WdfDevStatePnpNull },
523         NULL,
524         { FALSE,
525           0 },
526     },
527 
528     //      WdfDevStatePnpInitQueryRemove
529     {   FxPkgPnp::PnpEventInitQueryRemove,
530         { PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
531         FxPkgPnp::m_PnpInitQueryRemoveOtherStates,
532         { TRUE,
533           0 },
534     },
535 
536     //      WdfDevStatePnpInitQueryRemoveCanceled
537     {   FxPkgPnp::PnpEventInitQueryRemoveCanceled,
538         { PnpEventNull, WdfDevStatePnpNull },
539         NULL,
540         { TRUE,
541           0 },
542     },
543 
544     //      WdfDevStatePnpFdoRemoved
545     {   FxPkgPnp::PnpEventFdoRemoved,
546         { PnpEventNull, WdfDevStatePnpNull },
547         NULL,
548         { FALSE,
549           0 },
550     },
551 
552     //      WdfDevStatePnpRemovedWaitForChildren
553     {   NULL,
554         { PnpEventChildrenRemovalComplete, WdfDevStatePnpRemovedChildrenRemoved DEBUGGED_EVENT },
555         NULL,
556         { TRUE,
557           PnpEventPowerDownFailed  // device power down even from processing remove
558         },
559     },
560 
561     //      WdfDevStatePnpQueriedSurpriseRemove
562     {   FxPkgPnp::PnpEventQueriedSurpriseRemove,
563         { PnpEventNull, WdfDevStatePnpNull },
564         NULL,
565         { FALSE,
566           0 },
567     },
568 
569     //      WdfDevStatePnpSurpriseRemoveIoStarted
570     {   FxPkgPnp::PnpEventSurpriseRemoveIoStarted,
571         { PnpEventNull, WdfDevStatePnpNull },
572         NULL,
573         { FALSE,
574           0 },
575     },
576 
577     //      WdfDevStatePnpFailedPowerDown
578     {   FxPkgPnp::PnpEventFailedPowerDown,
579         { PnpEventPwrPolStopped, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
580         FxPkgPnp::m_PnpFailedPowerDownOtherStates,
581         { FALSE,
582           PnpEventPowerDownFailed ,
583         },
584     },
585 
586     //      WdfDevStatePnpFailedIoStarting
587     {   FxPkgPnp::PnpEventFailedIoStarting,
588         { PnpEventPwrPolStopped, WdfDevStatePnpFailedOwnHardware DEBUGGED_EVENT },
589         FxPkgPnp::m_PnpFailedIoStartingOtherStates,
590         { FALSE,
591           PnpEventPowerDownFailed |
592 
593           PnpEventPowerUpFailed   // if the device idled out and then failed
594                                   // d0 entry, the power up failed can be passed
595                                   // up by the IoInvalidateDeviceRelations and
596                                   // subsequence surprise remove event.
597         },
598     },
599 
600     //      WdfDevStatePnpFailedOwnHardware
601     {   FxPkgPnp::PnpEventFailedOwnHardware,
602         { PnpEventNull, WdfDevStatePnpNull },
603         NULL,
604         { FALSE,
605           0 },
606     },
607 
608     //      WdfDevStatePnpFailed
609     {   FxPkgPnp::PnpEventFailed,
610         { PnpEventPwrPolRemoved, WdfDevStatePnpFailedPowerPolicyRemoved DEBUGGED_EVENT },
611         NULL,
612         { FALSE,
613           0,
614         },
615     },
616 
617     //      WdfDevStatePnpFailedSurpriseRemoved
618     {   FxPkgPnp::PnpEventFailedSurpriseRemoved,
619         { PnpEventNull, WdfDevStatePnpNull },
620         NULL,
621         { FALSE,
622           0, },
623     },
624 
625     //      WdfDevStatePnpFailedStarted
626     {   FxPkgPnp::PnpEventFailedStarted,
627         { PnpEventNull, WdfDevStatePnpNull },
628         NULL,
629         { FALSE,
630           0, },
631     },
632 
633     //      WdfDevStatePnpFailedWaitForRemove,
634     {   NULL,
635         { PnpEventRemove, WdfDevStatePnpRemoved DEBUGGED_EVENT },
636         FxPkgPnp::m_PnpFailedWaitForRemoveOtherStates,
637         { TRUE,
638           PnpEventPowerUpFailed | // initial power up failed, power policy start
639                                   // failed event moved the state machine to the
640                                   // failed state first
641           PnpEventPowerDownFailed | // implicitD3 power down failed
642           PnpEventQueryRemove |  // start succeeded, but we still get a query in
643                                  // the removed case
644           PnpEventCancelStop |   // power down failure while processing query stop
645                                  // and q.s. irp completed with error
646           PnpEventCancelRemove   // power down failure while processing query remove
647                                  // and q.r. irp completed with error
648         },
649     },
650 
651     //      WdfDevStatePnpFailedInit
652     {   FxPkgPnp::PnpEventFailedInit,
653         { PnpEventNull, WdfDevStatePnpNull },
654         NULL,
655         { FALSE,
656           0 },
657     },
658 
659     //      WdfDevStatePnpPdoInitFailed
660     {   FxPkgPnp::PnpEventPdoInitFailed,
661         { PnpEventNull, WdfDevStatePnpNull },
662         NULL,
663         { FALSE,
664           0 },
665     },
666 
667     //      WdfDevStatePnpRestart
668     {   FxPkgPnp::PnpEventRestart,
669         { PnpEventPwrPolStopped, WdfDevStatePnpRestartReleaseHardware DEBUGGED_EVENT },
670         FxPkgPnp::m_PnpRestartOtherStates,
671         { FALSE,
672           PnpEventPowerUpFailed | // when stopping power policy, device was in
673                                   // Dx and bringing it to D0 succeeded or failed
674           PnpEventPowerDownFailed // same as power up
675         },
676     },
677 
678     //      WdfDevStatePnpRestartReleaseHardware
679     {   FxPkgPnp::PnpEventRestartReleaseHardware,
680         { PnpEventStartDeviceComplete, WdfDevStatePnpRestartHardwareAvailable DEBUGGED_EVENT },
681         FxPkgPnp::m_PnpRestartReleaseHardware,
682         { TRUE,
683           PnpEventPowerDownFailed // the previous pwr policy stop
684                                   // in WdfDevStaePnpRestart will
685                                   // cause these events to show up here
686         },
687     },
688 
689     //      WdfDevStatePnpRestartHardwareAvailable
690     {   FxPkgPnp::PnpEventRestartHardwareAvailable,
691         { PnpEventPwrPolStarted, WdfDevStatePnpStarted DEBUGGED_EVENT },
692         FxPkgPnp::m_PnpRestartHardwareAvailableOtherStates,
693         { TRUE,
694           PnpEventPowerUpFailed
695         },
696     },
697 
698     //      WdfDevStatePnpPdoRestart
699     {   FxPkgPnp::PnpEventPdoRestart,
700         { PnpEventNull, WdfDevStatePnpNull },
701         NULL,
702         { FALSE,
703           0 },
704     },
705 
706     //      WdfDevStatePnpFinal
707     {   FxPkgPnp::PnpEventFinal,
708         { PnpEventNull, WdfDevStatePnpNull },
709         NULL,
710         { TRUE,
711           PnpEventPowerDownFailed, // on the final implicit power down, a
712                                    // callback returned !NT_SUCCESS
713         },
714     },
715 
716     //      WdfDevStatePnpRemovedChildrenRemoved
717     {   FxPkgPnp::PnpEventRemovedChildrenRemoved,
718         { PnpEventNull, WdfDevStatePnpNull },
719         NULL,
720         { TRUE,
721           0 } ,
722     },
723 
724     //      WdfDevStatePnpQueryRemoveEnsureDeviceAwake
725     {   FxPkgPnp::PnpEventQueryRemoveEnsureDeviceAwake,
726         { PnpEventDeviceInD0, WdfDevStatePnpQueryRemovePending DEBUGGED_EVENT },
727         NULL,
728         { FALSE,
729           0 },
730     },
731 
732     //      WdfDevStatePnpQueryStopEnsureDeviceAwake
733     {   FxPkgPnp::PnpEventQueryStopEnsureDeviceAwake,
734         { PnpEventDeviceInD0, WdfDevStatePnpQueryStopPending DEBUGGED_EVENT },
735         NULL,
736         { FALSE,
737           0 },
738     },
739 
740     //      WdfDevStatePnpFailedPowerPolicyRemoved
741     {   FxPkgPnp::PnpEventFailedPowerPolicyRemoved,
742         { PnpEventNull, WdfDevStatePnpNull },
743         NULL,
744         { FALSE,
745           0 } ,
746     },
747 };
748 
749 // @@SMVERIFY_SPLIT_END
750 
751 VOID
PnpCheckAssumptions(VOID)752 FxPkgPnp::PnpCheckAssumptions(
753     VOID
754     )
755 /*++
756 
757 Routine Description:
758     This routine is never actually called by running code, it just has
759     WDFCASSERTs who upon failure, would not allow this file to be compiled.
760 
761     DO NOT REMOVE THIS FUNCTION just because it is not callec by any running
762     code.
763 
764 Arguments:
765     None
766 
767 Return Value:
768     None
769 
770   --*/
771 {
772     WDFCASSERT(sizeof(FxPnpStateInfo) == sizeof(ULONG));
773 
774     WDFCASSERT((sizeof(m_WdfPnpStates)/sizeof(m_WdfPnpStates[0]))
775                ==
776                (WdfDevStatePnpNull - WdfDevStatePnpObjectCreated));
777 
778     // we assume these are the same length when we update the history index
779     WDFCASSERT((sizeof(m_PnpMachine.m_Queue)/sizeof(m_PnpMachine.m_Queue[0]))
780                ==
781                (sizeof(m_PnpMachine.m_States.History)/
782                 sizeof(m_PnpMachine.m_States.History[0])));
783 }
784 
785 /*++
786 
787 The locking model for the PnP state machine requires that events be enqueued
788 possibly at DISPATCH_LEVEL.  It also requires that the PnP state machine be
789 runnable at PASSIVE_LEVEL.  Consequently, we have two locks, one DISPATCH_LEVEL
790 lock that guards the event queue and one PASSIVE_LEVEL lock that guards the
791 state machine itself.
792 
793 Algorithm:
794 
795 1)  Acquire the PnP queue lock.
796 2)  Enqueue the request.  Requests are put at the end of the queue, except if
797     they are PowerUp, or PowerDown, in which case they are put at the head of
798     the queue.
799 3)  Drop the PnP queue lock.
800 4)  If the thread is running at PASSIVE_LEVEL, skip to step 6.
801 5)  Queue a work item onto any work queue.
802 6)  Attempt to acquire the state machine lock, with a near-zero-length timeout.
803 7)  If successful, skip to step 10.
804 8)  Queue a work item onto any work queue.
805 9)  Acquire the state machine lock.
806 10) Acquire the PnP queue lock.
807 11) Attempt to dequeue an event.
808 12) Drop the PnP queue lock.
809 13) If there was no event to dequeue, drop the state machine lock and exit.
810 14) Execute the state handler.  This may involve taking one of the other state
811     machine queue locks, briefly, to deliver an event.
812 15) Go to Step 10.
813 
814 Implementing this algorithm requires three functions.
815 
816 PnpProcessEvent         -- Implements steps 1-8.
817 _PnpProcessEventInner   -- Implements step 9.
818 PnpProcessEventInner    -- Implements steps 10-15.
819 
820 --*/
821 
822 VOID
PnpProcessEvent(__in FxPnpEvent Event,__in BOOLEAN ProcessOnDifferentThread)823 FxPkgPnp::PnpProcessEvent(
824     __in FxPnpEvent   Event,
825     __in BOOLEAN      ProcessOnDifferentThread
826     )
827 /*++
828 
829 Routine Description:
830     This function implements steps 1-8 of the algorithm described above.
831 
832 Arguments:
833     Event - Current PnP event
834 
835 Return Value:
836 
837     NTSTATUS
838 
839 --*/
840 {
841     NTSTATUS status;
842     KIRQL oldIrql;
843 
844     //
845     // Take the lock, raising to DISPATCH_LEVEL.
846     //
847     m_PnpMachine.Lock(&oldIrql);
848 
849     if (m_PnpMachine.IsFull()) {
850         DoTraceLevelMessage(
851             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
852             "WDFDEVICE 0x%p !devobj 0x%p current pnp state %!WDF_DEVICE_PNP_STATE! "
853             "dropping event %!FxPnpEvent! because of a full queue",
854             m_Device->GetHandle(),
855             m_Device->GetDeviceObject(),
856             m_Device->GetDevicePnpState(),
857             Event);
858 
859         //
860         // The queue is full.  Bail.
861         //
862         m_PnpMachine.Unlock(oldIrql);
863 
864         ASSERT(!"The PnP queue is full.  This shouldn't be able to happen.");
865         return;
866     }
867 
868     if (m_PnpMachine.IsClosedLocked()) {
869         DoTraceLevelMessage(
870             GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
871             "WDFDEVICE 0x%p !devobj 0x%p current pnp state %!WDF_DEVICE_PNP_STATE! "
872             "dropping event %!FxPnpEvent! because of a closed queue",
873             m_Device->GetHandle(),
874             m_Device->GetDeviceObject(),
875             m_Device->GetDevicePnpState(),
876             Event);
877 
878         //
879         // The queue is closed.  Bail
880         //
881         m_PnpMachine.Unlock(oldIrql);
882 
883         return;
884     }
885 
886     //
887     // Enqueue the event.  Whether the event goes on the front
888     // or the end of the queue depends on which event it is.
889     //
890     if (Event & PnpPriorityEventsMask) {
891         //
892         // Stick it on the front of the queue, making it the next
893         // event that will be processed.
894         //
895         m_PnpMachine.m_Queue[m_PnpMachine.InsertAtHead()] = Event;
896     }
897     else {
898         //
899         // Stick it on the end of the queue.
900         //
901         m_PnpMachine.m_Queue[m_PnpMachine.InsertAtTail()] = Event;
902     }
903 
904     //
905     // Drop the lock.
906     //
907     m_PnpMachine.Unlock(oldIrql);
908 
909     //
910     // Now, if we are running at PASSIVE_LEVEL, attempt to run the state
911     // machine on this thread.  If we can't do that, then queue a work item.
912     //
913 
914     if (FALSE == ShouldProcessPnpEventOnDifferentThread(
915                     oldIrql,
916                     ProcessOnDifferentThread
917                     )) {
918 
919         LONGLONG timeout = 0;
920 
921         status = m_PnpMachine.m_StateMachineLock.AcquireLock(GetDriverGlobals(),
922                                                              &timeout);
923 
924         if (FxWaitLockInternal::IsLockAcquired(status)) {
925             FxPostProcessInfo info;
926 
927             //
928             // We now hold the state machine lock.  So call the function that
929             // dispatches the next state.
930             //
931             PnpProcessEventInner(&info);
932 
933             m_PnpMachine.m_StateMachineLock.ReleaseLock(GetDriverGlobals());
934 
935             info.Evaluate(this);
936             return;
937         }
938     }
939 
940     //
941     // For one reason or another, we couldn't run the state machine on this
942     // thread.  So queue a work item to do it.  If m_PnPWorkItemEnqueuing
943     // is non-zero, that means that the work item is already being enqueued
944     // on another thread.  This is significant, since it means that we can't do
945     // anything with the work item on this thread, but it's okay, since the
946     // work item will pick up our work and do it.
947     //
948     m_PnpMachine.QueueToThread();
949 }
950 
951 VOID
_PnpProcessEventInner(__inout FxPkgPnp * This,__inout FxPostProcessInfo * Info,__in PVOID WorkerContext)952 FxPkgPnp::_PnpProcessEventInner(
953     __inout FxPkgPnp* This,
954     __inout FxPostProcessInfo* Info,
955     __in PVOID WorkerContext
956     )
957 {
958     UNREFERENCED_PARAMETER(WorkerContext);
959 
960     //
961     // Take the state machine lock.
962     //
963     This->m_PnpMachine.m_StateMachineLock.AcquireLock(
964         This->GetDriverGlobals()
965         );
966 
967     //
968     // Call the function that will actually run the state machine.
969     //
970     This->PnpProcessEventInner(Info);
971 
972     //
973     // We are being called from the work item and m_WorkItemRunning is > 0, so
974     // we cannot be deleted yet.
975     //
976     ASSERT(Info->SomethingToDo() == FALSE);
977 
978     //
979     // Now release the lock
980     //
981     This->m_PnpMachine.m_StateMachineLock.ReleaseLock(
982         This->GetDriverGlobals()
983         );
984 }
985 
986 VOID
PnpProcessEventInner(__inout FxPostProcessInfo * Info)987 FxPkgPnp::PnpProcessEventInner(
988     __inout FxPostProcessInfo* Info
989     )
990 /*++
991 
992 Routine Description:
993     This routine runs the state machine.  It implements steps 10-15 of the
994     algorithm described above.
995 
996 --*/
997 {
998     WDF_DEVICE_PNP_STATE newState;
999     CPPNP_STATE_TABLE    entry;
1000     FxPnpEvent           event;
1001     KIRQL                oldIrql;
1002 
1003     //
1004     // Process as many events as we can.
1005     //
1006     for ( ; ; ) {
1007         entry = GetPnpTableEntry(m_Device->GetDevicePnpState());
1008 
1009         //
1010         // Get an event from the queue.
1011         //
1012         m_PnpMachine.Lock(&oldIrql);
1013 
1014         if (m_PnpMachine.IsEmpty()) {
1015             m_PnpMachine.GetFinishedState(Info);
1016 
1017             if (m_PnpMachine.m_FireAndForget) {
1018                 m_PnpMachine.m_FireAndForget = FALSE;
1019                 Info->m_FireAndForgetIrp = ClearPendingPnpIrp();
1020 
1021                 ASSERT(Info->m_FireAndForgetIrp != NULL);
1022             }
1023 
1024             Info->m_SetRemovedEvent = m_SetDeviceRemoveProcessed;
1025             m_SetDeviceRemoveProcessed = FALSE;
1026 
1027             //
1028             // The queue is empty.
1029             //
1030             m_PnpMachine.Unlock(oldIrql);
1031 
1032             return;
1033         }
1034 
1035         event = m_PnpMachine.m_Queue[m_PnpMachine.GetHead()];
1036 
1037         //
1038         // At this point, we need to determine whether we can process this
1039         // event.
1040         //
1041         if (event & PnpPriorityEventsMask) {
1042             //
1043             // These are always possible to handle.
1044             //
1045             DO_NOTHING();
1046         }
1047         else {
1048             //
1049             // Check to see if this state can handle new events.
1050             //
1051             if (entry->StateInfo.Bits.QueueOpen == FALSE) {
1052                 //
1053                 // This state can't handle new events.
1054                 //
1055                 m_PnpMachine.Unlock(oldIrql);
1056                 return;
1057             }
1058         }
1059 
1060         m_PnpMachine.IncrementHead();
1061         m_PnpMachine.Unlock(oldIrql);
1062 
1063         //
1064         // Find the entry in the PnP state table that corresponds
1065         // to this event.
1066         //
1067         newState = WdfDevStatePnpNull;
1068 
1069         if (entry->FirstTargetState.PnpEvent == event) {
1070             newState = entry->FirstTargetState.TargetState;
1071 
1072             DO_EVENT_TRAP(&entry->FirstTargetState);
1073         }
1074         else if (entry->OtherTargetStates != NULL) {
1075             ULONG i = 0;
1076 
1077             for (i = 0;
1078                  entry->OtherTargetStates[i].PnpEvent != PnpEventNull;
1079                  i++) {
1080                 if (entry->OtherTargetStates[i].PnpEvent == event) {
1081                     newState = entry->OtherTargetStates[i].TargetState;
1082                     DO_EVENT_TRAP(&entry->OtherTargetStates[i]);
1083                     break;
1084                 }
1085             }
1086         }
1087 
1088         if (newState == WdfDevStatePnpNull) {
1089             DoTraceLevelMessage(
1090                 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1091                 "WDFDEVICE 0x%p !devobj 0x%p current pnp state "
1092                 "%!WDF_DEVICE_PNP_STATE! dropping event %!FxPnpEvent!",
1093                 m_Device->GetHandle(),
1094                 m_Device->GetDeviceObject(),
1095                 m_Device->GetDevicePnpState(),
1096                 event);
1097 
1098             if ((entry->StateInfo.Bits.KnownDroppedEvents & event) == 0) {
1099                 COVERAGE_TRAP();
1100 
1101                 DoTraceLevelMessage(
1102                     GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
1103                     "WDFDEVICE 0x%p !devobj %p current state "
1104                     "%!WDF_DEVICE_PNP_STATE!, policy event %!FxPnpEvent! is not"
1105                     " a known dropped event, known dropped events are "
1106                     "%!FxPnpEvent!",
1107                     m_Device->GetHandle(),
1108                     m_Device->GetDeviceObject(),
1109                     m_Device->GetDevicePnpState(),
1110                     event,
1111                     entry->StateInfo.Bits.KnownDroppedEvents);
1112 
1113                 // DIAG:  add diag code here
1114             }
1115 
1116             //
1117             // This state doesn't respond to the Event.  Make sure we do not
1118             // drop an event which pends a pnp irp on the floor though.
1119             //
1120             if (event & PnpEventPending) {
1121                 //
1122                 // In the case of a previous power up/down failure, the following
1123                 // can happen
1124                 // 1  invalidate device relations
1125                 // 2  failure event is posted to pnp state machine
1126                 // 3  process power failure event first, but while processing,
1127                 //    query device state is completed, and failed /removed is reported
1128                 // 4  surprise remove comes, the irp is queued, the event is queued
1129                 // 5  processing of power failure event continues, gets to
1130                 //    Failed and completes the s.r irp
1131                 // 6  the surprise remove event is processed (the current value
1132                 //    of the local var event), but since we are already in the
1133                 //    Failed state, we ignore this event and end up
1134                 //    here.
1135                 //
1136                 // This means that if we are processing surprise remove, we cannot
1137                 // 100% expect that an irp has been pended.
1138                 //
1139                 PnpFinishProcessingIrp(
1140                     (event == PnpEventSurpriseRemove) ? FALSE : TRUE);
1141             }
1142             else {
1143                 DO_NOTHING();
1144             }
1145         }
1146         else {
1147             //
1148             // Now enter the new state.
1149             //
1150             PnpEnterNewState(newState);
1151         }
1152     }
1153 }
1154 
1155 VOID
PnpEnterNewState(__in WDF_DEVICE_PNP_STATE State)1156 FxPkgPnp::PnpEnterNewState(
1157     __in WDF_DEVICE_PNP_STATE State
1158     )
1159 /*++
1160 
1161 Routine Description:
1162     This function looks up the handler for a state and
1163     then calls it.
1164 
1165 Arguments:
1166     Event - Current PnP event
1167 
1168 Return Value:
1169     None.
1170 
1171 --*/
1172 {
1173     CPPNP_STATE_TABLE    entry;
1174     WDF_DEVICE_PNP_STATE currentState, newState;
1175     WDF_DEVICE_PNP_NOTIFICATION_DATA data;
1176 
1177     currentState = m_Device->GetDevicePnpState();
1178     newState = State;
1179 
1180     while (newState != WdfDevStatePnpNull) {
1181         DoTraceLevelMessage(
1182              GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNPPOWERSTATES,
1183              "WDFDEVICE 0x%p !devobj 0x%p entering PnP State "
1184              "%!WDF_DEVICE_PNP_STATE! from %!WDF_DEVICE_PNP_STATE!",
1185              m_Device->GetHandle(),
1186              m_Device->GetDeviceObject(),
1187              newState,
1188              currentState);
1189 
1190         if (m_PnpStateCallbacks != NULL) {
1191             //
1192             // Callback for leaving the old state
1193             //
1194             RtlZeroMemory(&data, sizeof(data));
1195 
1196             data.Type = StateNotificationLeaveState;
1197             data.Data.LeaveState.CurrentState = currentState;
1198             data.Data.LeaveState.NewState = newState;
1199 
1200             m_PnpStateCallbacks->Invoke(currentState,
1201                                         StateNotificationLeaveState,
1202                                         m_Device->GetHandle(),
1203                                         &data);
1204         }
1205 
1206         m_PnpMachine.m_States.History[m_PnpMachine.IncrementHistoryIndex()] =
1207             (USHORT) newState;
1208 
1209         if (m_PnpStateCallbacks != NULL) {
1210             //
1211             // Callback for entering the new state
1212             //
1213             RtlZeroMemory(&data, sizeof(data));
1214 
1215             data.Type = StateNotificationEnterState;
1216             data.Data.EnterState.CurrentState = currentState;
1217             data.Data.EnterState.NewState = newState;
1218 
1219             m_PnpStateCallbacks->Invoke(newState,
1220                                         StateNotificationEnterState,
1221                                         m_Device->GetHandle(),
1222                                         &data);
1223         }
1224 
1225         m_Device->SetDevicePnpState(newState);
1226         currentState = newState;
1227 
1228         entry = GetPnpTableEntry(currentState);
1229 
1230         //
1231         // Call the state handler if one is present and record our new state
1232         //
1233         if (entry->StateFunc != NULL) {
1234             newState = entry->StateFunc(this);
1235 
1236             //
1237             // Validate the return value if FX_STATE_MACHINE_VERIFY is enabled
1238             //
1239             VALIDATE_PNP_STATE(currentState, newState);
1240         }
1241         else {
1242             newState = WdfDevStatePnpNull;
1243         }
1244 
1245         if (m_PnpStateCallbacks != NULL) {
1246             //
1247             // Callback for post processing the new state
1248             //
1249             RtlZeroMemory(&data, sizeof(data));
1250 
1251             data.Type = StateNotificationPostProcessState;
1252             data.Data.PostProcessState.CurrentState = currentState;
1253 
1254             m_PnpStateCallbacks->Invoke(currentState,
1255                                         StateNotificationPostProcessState,
1256                                         m_Device->GetHandle(),
1257                                         &data);
1258         }
1259     }
1260 }
1261 
1262 WDF_DEVICE_PNP_STATE
PnpEventCheckForDevicePresence(__inout FxPkgPnp * This)1263 FxPkgPnp::PnpEventCheckForDevicePresence(
1264     __inout FxPkgPnp*   This
1265     )
1266 /*++
1267 
1268 Routine Description:
1269     This function implements the Check For Device
1270     Presence state.  This is a state that is specific
1271     to PDOs, so this function should be overloaded by
1272     the PDO class and never called.
1273 
1274 Arguments:
1275     none
1276 
1277 Return Value:
1278 
1279     VOID
1280 
1281 --*/
1282 {
1283     return This->PnpEventCheckForDevicePresenceOverload();
1284 }
1285 
1286 WDF_DEVICE_PNP_STATE
PnpEventEjectHardware(__inout FxPkgPnp * This)1287 FxPkgPnp::PnpEventEjectHardware(
1288     __inout FxPkgPnp*   This
1289     )
1290 /*++
1291 
1292 Routine Description:
1293     This function implements the Eject Hardware state.
1294     This is a state that is specific to PDOs, so this
1295     function should be overloaded by the PDO class
1296     and never called.
1297 
1298 Arguments:
1299     none
1300 
1301 Return Value:
1302 
1303     VOID
1304 
1305 --*/
1306 {
1307     return This->PnpEventEjectHardwareOverload();
1308 }
1309 
1310 WDF_DEVICE_PNP_STATE
PnpEventInitStarting(__inout FxPkgPnp * This)1311 FxPkgPnp::PnpEventInitStarting(
1312     __inout FxPkgPnp* This
1313     )
1314 /*++
1315 
1316 Routine Description:
1317     The device is recieving a start for the first time.  The start is on the way
1318     down the stack.
1319 
1320 Arguments:
1321     This - instance of the state machine
1322 
1323 Return Value:
1324     new machine state
1325 
1326   --*/
1327 {
1328     if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
1329         //
1330         // Start was sent asynchronously down the stack, the irp's completion
1331         // routine will move the state machine to the new state.
1332         //
1333         return WdfDevStatePnpNull;
1334     }
1335 
1336     return WdfDevStatePnpHardwareAvailable;
1337 }
1338 
1339 WDF_DEVICE_PNP_STATE
PnpEventInitSurpriseRemoved(__inout FxPkgPnp * This)1340 FxPkgPnp::PnpEventInitSurpriseRemoved(
1341     __inout FxPkgPnp*   This
1342     )
1343 /*++
1344 
1345 Routine Description:
1346     This transition should only occur for a PDO.
1347 
1348     The device was initialized, but then it's parent bus was surprise removed.
1349     Complete the surprise remove and wait for the remove.
1350 
1351 Arguments:
1352     This - instance of the state machine
1353 
1354 Return Value:
1355     WdfDevStatePnpInit
1356 
1357   --*/
1358 {
1359     This->PnpFinishProcessingIrp(TRUE);
1360 
1361     return WdfDevStatePnpInit;
1362 }
1363 
1364 WDF_DEVICE_PNP_STATE
PnpEventHardwareAvailable(__inout FxPkgPnp * This)1365 FxPkgPnp::PnpEventHardwareAvailable(
1366     __inout FxPkgPnp*   This
1367     )
1368 /*++
1369 
1370 Routine Description:
1371     This function implements the Hardware Available state.
1372 
1373 Arguments:
1374     none
1375 
1376 Return Value:
1377 
1378     VOID
1379 
1380 --*/
1381 {
1382     NTSTATUS status;
1383     BOOLEAN matched;
1384 
1385     status = STATUS_SUCCESS;
1386     matched = FALSE;
1387 
1388     This->QueryForReenumerationInterface();
1389 
1390     status = This->CreatePowerThreadIfNeeded();
1391 
1392     if (NT_SUCCESS(status)) {
1393         status = This->PnpPrepareHardware(&matched);
1394     }
1395 
1396     if (!NT_SUCCESS(status)) {
1397         if (matched == FALSE) {
1398             //
1399             // NOTE:  consider going to WdfDevStatePnpFailed instead of yet
1400             //        another failed state out of start device handling.
1401             //
1402 
1403             //
1404             // We can handle remove out of the init state, revert back to that
1405             // state.
1406             //
1407             return WdfDevStatePnpFailedInit;
1408         }
1409         else {
1410             //
1411             // EvtDevicePrepareHardware is what failed, goto a state where we
1412             // undo that call.
1413             //
1414             return WdfDevStatePnpFailedOwnHardware;
1415         }
1416     }
1417 
1418     //
1419     // We only query for the capabilities for the power policy owner because
1420     // we use the capabilities to determine the right Dx state when we want to
1421     // wake from S0 or Sx.  Since only the power policy owner can enable wake
1422     // behavior, only the owner needs to query for the information.
1423     //
1424     // ALSO, if we are a filter, there are issues in stacks wrt pnp reentrancy.
1425 
1426 
1427 
1428 
1429 
1430 
1431 
1432     //
1433     if (This->IsPowerPolicyOwner()) {
1434         //
1435         // Query the stack for capabilities before telling the stack hw is
1436         // available
1437         //
1438         status = This->QueryForCapabilities();
1439 
1440         if (!NT_SUCCESS(status)) {
1441             DoTraceLevelMessage(
1442                 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1443                 "could not query caps for stack, %!STATUS!", status);
1444 
1445             This->SetPendingPnpIrpStatus(status);
1446             return WdfDevStatePnpFailedOwnHardware;
1447         }
1448 
1449         This->m_CapsQueried = TRUE;
1450     }
1451 
1452     This->PnpPowerPolicyStart();
1453 
1454     return WdfDevStatePnpNull;
1455 }
1456 
1457 WDF_DEVICE_PNP_STATE
PnpEventEnableInterfaces(__inout FxPkgPnp * This)1458 FxPkgPnp::PnpEventEnableInterfaces(
1459     __inout FxPkgPnp* This
1460     )
1461 /*++
1462 
1463 Routine Description:
1464     The device has powered up and fully started, now enable the device
1465     interfaces and WMI.  We wait until the last possible moment because on
1466     win2k WMI registration is not synchronized with the completion of start
1467     device, so if we register WMI early in start device processing, we could get
1468     wmi requests while we are initializing, which is a race condition we want
1469     to eliminate.
1470 
1471 Arguments:
1472     This - instance of the state machine
1473 
1474 Return Value:
1475     new state
1476 
1477   --*/
1478 {
1479     NTSTATUS status;
1480 
1481     status = This->PnpEnableInterfacesAndRegisterWmi();
1482 
1483     if (!NT_SUCCESS(status)) {
1484         //
1485         // Upon failure, PnpEnableInterfacesAndRegisterWmi already marked the
1486         // irp as failed and recorded an internal error.
1487         //
1488         // FailedPowerDown will gracefully tear down the stack and bring it out
1489         // of D0.
1490         //
1491         return WdfDevStatePnpFailedPowerDown;
1492     }
1493 
1494     return WdfDevStatePnpStarted;
1495 }
1496 
1497 WDF_DEVICE_PNP_STATE
PnpEventHardwareAvailablePowerPolicyFailed(__inout FxPkgPnp * This)1498 FxPkgPnp::PnpEventHardwareAvailablePowerPolicyFailed(
1499     __inout FxPkgPnp* This
1500     )
1501 /*++
1502 
1503 Routine Description:
1504     Our previous state called PowerPolicyStart or PowerPolicyStopRemove and the
1505     power state machine could not perform the requested action.  We still have
1506     a start irp pending, so set its status and then proceed down the start failure
1507     path.
1508 
1509 Arguments:
1510     This - instance of the state machien
1511 
1512 Return Value:
1513     WdfDevStatePnpFailedOwnHardware
1514 
1515   --*/
1516 {
1517     This->SetPendingPnpIrpStatus(STATUS_DEVICE_POWER_FAILURE);
1518 
1519     return WdfDevStatePnpFailedOwnHardware;
1520 }
1521 
1522 WDF_DEVICE_PNP_STATE
PnpEventQueryRemoveAskDriver(__inout FxPkgPnp * This)1523 FxPkgPnp::PnpEventQueryRemoveAskDriver(
1524     __inout FxPkgPnp*   This
1525     )
1526 /*++
1527 
1528 Routine Description:
1529     This function implements the Query Remove Ask Driver
1530     state.  It's job is to invoke EvtDeviceQueryRemove and then complete the
1531     QueryRemove IRP if needed.
1532 
1533 Arguments:
1534     This - instance of the state machine
1535 
1536 Return Value:
1537     new state
1538 
1539 --*/
1540 {
1541     WDF_DEVICE_PNP_STATE state;
1542     NTSTATUS    status;
1543 
1544     //
1545     // First, call the driver.  If it succeeds, look at whether
1546     // it managed to stop its stuff.
1547     //
1548     status = This->m_DeviceQueryRemove.Invoke(This->m_Device->GetHandle());
1549 
1550     if (NT_SUCCESS(status)) {
1551         //
1552         // The driver has stopped all of its self managed io.  Proceed to
1553         // stop everything before passing the request down the stack.
1554         //
1555         state = WdfDevStatePnpQueryRemoveEnsureDeviceAwake;
1556     }
1557     else {
1558         //
1559         // The callback didn't manage to stop.  Go back to the Started state
1560         // where we will complete the pended pnp irp
1561         //
1562         DoTraceLevelMessage(
1563             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1564             "EvtDeviceQueryRemove failed, %!STATUS!", status);
1565 
1566         if (status == STATUS_NOT_SUPPORTED) {
1567             DoTraceLevelMessage(
1568                 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1569                 "EvtDeviceQueryRemove returned an invalid status "
1570                 "STATUS_NOT_SUPPORTED");
1571 
1572             if (This->GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
1573                 FxVerifierDbgBreakPoint(This->GetDriverGlobals());
1574             }
1575         }
1576 
1577         state = WdfDevStatePnpStarted;
1578     }
1579 
1580     This->SetPendingPnpIrpStatus(status);
1581 
1582     return state;
1583 }
1584 
1585 WDF_DEVICE_PNP_STATE
PnpEventQueryRemoveEnsureDeviceAwake(__inout FxPkgPnp * This)1586 FxPkgPnp::PnpEventQueryRemoveEnsureDeviceAwake(
1587    __inout FxPkgPnp *This
1588    )
1589 /*++
1590 Routine Description:
1591     This function brings the device to a working state if it is idle. If the
1592     device is already in working state, it ensures that it does not idle out.
1593 
1594 Arguments:
1595     This - instance of the state machine
1596 
1597 Return Value:
1598     new state
1599 --*/
1600 {
1601     NTSTATUS status;
1602     WDF_DEVICE_PNP_STATE state;
1603 
1604     //
1605     // Make sure that the device is powered on before we send the query remove
1606     // on its way.  If we do this after we send the query remove, we could race
1607     // with the remove which removes the reference and we want the device
1608     // powered on when processing remove.
1609     //
1610     status = This->PnpPowerReferenceDuringQueryPnp();
1611     if (STATUS_PENDING == status) {
1612         //
1613         // Device is transitioning to D0. The Pnp state machine will wait in
1614         // the current state until the transition is complete
1615         //
1616         state = WdfDevStatePnpNull;
1617     }
1618     else if (NT_SUCCESS(status)) {
1619         //
1620         // Already in D0
1621         //
1622         state = WdfDevStatePnpQueryRemovePending;
1623     }
1624     else {
1625         DoTraceLevelMessage(
1626             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1627             "StopIdle on WDFDEVICE %p failed, %!STATUS!, failing query remove",
1628             This->m_Device->GetHandle(), status);
1629 
1630         This->SetPendingPnpIrpStatus(status);
1631 
1632         //
1633         // The Started state will complete the irp when it sees the failure
1634         // status set on the irp.
1635         //
1636         state = WdfDevStatePnpStarted;
1637     }
1638 
1639     return state;
1640 }
1641 
1642 WDF_DEVICE_PNP_STATE
PnpEventQueryRemovePending(__inout FxPkgPnp * This)1643 FxPkgPnp::PnpEventQueryRemovePending(
1644     __inout FxPkgPnp*   This
1645     )
1646 /*++
1647 
1648 Routine Description:
1649     The device has fully stopped.  Let go of the query remove irp.
1650 
1651 Arguments:
1652     This - instance of the state machine for this device
1653 
1654 Return Value:
1655     WdfDevStatePnpNull
1656 
1657   --*/
1658 
1659 {
1660     FxIrp irp;
1661 
1662     irp.SetIrp(This->ClearPendingPnpIrp());
1663     (void) This->FireAndForgetIrp(&irp);
1664     return WdfDevStatePnpNull;
1665 }
1666 
1667 WDF_DEVICE_PNP_STATE
PnpEventQueryRemoveStaticCheck(__inout FxPkgPnp * This)1668 FxPkgPnp::PnpEventQueryRemoveStaticCheck(
1669     __inout FxPkgPnp*   This
1670     )
1671 /*++
1672 
1673 Routine Description:
1674     This function implements the Query Remove Static Check
1675     state.  It's job is to determine whether this device,
1676     in general, can stop.  If it can, then we proceed on
1677     to Query Remove Ask Driver.  If not, then we go to
1678     back to Started.
1679 
1680 Arguments:
1681     none
1682 
1683 Return Value:
1684 
1685     VOID
1686 
1687 --*/
1688 {
1689     NTSTATUS status;
1690     BOOLEAN completeQuery;
1691 
1692     completeQuery = TRUE;
1693 
1694     if (This->m_DeviceStopCount != 0) {
1695         DoTraceLevelMessage(
1696             This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1697             "Failing QueryRemoveDevice because the driver "
1698             "has indicated that it cannot be stopped, count %d",
1699             This->m_DeviceStopCount);
1700 
1701         status = STATUS_INVALID_DEVICE_STATE;
1702     }
1703     else if (This->IsInSpecialUse()) {
1704         DoTraceLevelMessage(
1705             This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1706             "Failing QueryRemoveDevice due to open special file counts "
1707             "(paging %d, hiber %d, dump %d, boot %d)",
1708             This->GetUsageCount(WdfSpecialFilePaging),
1709             This->GetUsageCount(WdfSpecialFileHibernation),
1710             This->GetUsageCount(WdfSpecialFileDump),
1711             This->GetUsageCount(WdfSpecialFileBoot));
1712 
1713         status = STATUS_DEVICE_NOT_READY;
1714     }
1715     else {
1716         //
1717         // Go on to next state in the "remove" progression.
1718         //
1719         completeQuery = FALSE;
1720         status = STATUS_SUCCESS;
1721     }
1722 
1723     if (completeQuery) {
1724         //
1725         // Store the status which Started will complete
1726         //
1727         This->SetPendingPnpIrpStatus(status);
1728 
1729         //
1730         // Revert to started
1731         //
1732         return WdfDevStatePnpStarted;
1733     }
1734     else {
1735         //
1736         // Wait for the other state machines to stop
1737         //
1738         return WdfDevStatePnpQueryRemoveAskDriver;
1739     }
1740 }
1741 
1742 WDF_DEVICE_PNP_STATE
PnpEventQueriedRemoving(__inout FxPkgPnp * This)1743 FxPkgPnp::PnpEventQueriedRemoving(
1744     __inout FxPkgPnp*   This
1745     )
1746 /*++
1747 
1748 Routine Description:
1749     The device was query removed and is now in the removed state.
1750 
1751 Arguments:
1752     This - instance of the state machine
1753 
1754 Return Value:
1755     WdfDevStatePnpNull
1756 
1757   --*/
1758 {
1759     //
1760     // It is important to stop power policy before releasing the reference.
1761     // If the reference was released first, we could get into a situation where
1762     // we immediately go idle and then we must send a D0 irp when in the remove.
1763     // If there are devices on top of this device and we send a D0 irp during
1764     // remove processing, the upper devices will be sent an irp after getting a
1765     // pnp remove (and either crash or fail the power irp upon receiving it).
1766     //
1767     This->PnpPowerPolicyStop();
1768     This->PnpPowerDereferenceSelf();
1769 
1770     return WdfDevStatePnpNull;
1771 }
1772 
1773 WDF_DEVICE_PNP_STATE
PnpEventQueryStopAskDriver(__inout FxPkgPnp * This)1774 FxPkgPnp::PnpEventQueryStopAskDriver(
1775     __inout FxPkgPnp*   This
1776     )
1777 /*++
1778 
1779 Routine Description:
1780     This function implements the Query Stop Ask Driver
1781     state.  It's job is to invoke the EvtDeviceQueryStop
1782     callback and then complete the QueryStop IRP if needed.
1783 
1784 Arguments:
1785     This - instance of the state machine
1786 
1787 Return Value:
1788     new state
1789 
1790 --*/
1791 {
1792     WDF_DEVICE_PNP_STATE state;
1793     NTSTATUS    status;
1794 
1795     //
1796     // First, call the driver.  If it succeeds, look at whether
1797     // it managed to stop its stuff.
1798     //
1799     status = This->m_DeviceQueryStop.Invoke(This->m_Device->GetHandle());
1800 
1801     if (NT_SUCCESS(status)) {
1802         //
1803         // Tell the other state machines to stop
1804         //
1805         state = WdfDevStatePnpQueryStopEnsureDeviceAwake;
1806     }
1807     else {
1808         DoTraceLevelMessage(
1809             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1810             "EvtDeviceQueryStop failed, %!STATUS!", status);
1811 
1812         if (status == STATUS_NOT_SUPPORTED) {
1813             DoTraceLevelMessage(
1814                 This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1815                 "EvtDeviceQueryStop returned an invalid status "
1816                 "STATUS_NOT_SUPPORTED");
1817 
1818             if (This->GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
1819                 FxVerifierDbgBreakPoint(This->GetDriverGlobals());
1820             }
1821         }
1822 
1823         //
1824         // The callback didn't manage to stop.  Go back to the Started state.
1825         //
1826         state = WdfDevStatePnpStarted;
1827     }
1828 
1829     This->SetPendingPnpIrpStatus(status);
1830 
1831     return state;
1832 }
1833 
1834 WDF_DEVICE_PNP_STATE
PnpEventQueryStopEnsureDeviceAwake(__inout FxPkgPnp * This)1835 FxPkgPnp::PnpEventQueryStopEnsureDeviceAwake(
1836    __inout FxPkgPnp *This
1837    )
1838 /*++
1839 Routine Description:
1840     This function brings the device to a working state if it is idle. If the
1841     device is already in working state, it ensures that it does not idle out.
1842 
1843 Arguments:
1844     This - instance of the state machine
1845 
1846 Return Value:
1847     new state
1848 --*/
1849 {
1850     NTSTATUS status;
1851     WDF_DEVICE_PNP_STATE state;
1852 
1853     //
1854     // Make sure that the device is powered on before we send the query stop
1855     // on its way.  If we do this after we send the query stop, we could race
1856     // with the stop and we want the device powered on when processing stop.
1857     //
1858     status = This->PnpPowerReferenceDuringQueryPnp();
1859     if (STATUS_PENDING == status) {
1860         //
1861         // Device is transitioning to D0. The Pnp state machine will wait in
1862         // the current state until the transition is complete
1863         //
1864         state = WdfDevStatePnpNull;
1865     }
1866     else if (NT_SUCCESS(status)) {
1867         //
1868         // Already in D0
1869         //
1870         state = WdfDevStatePnpQueryStopPending;
1871     }
1872     else {
1873         DoTraceLevelMessage(
1874             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1875             "StopIdle on WDFDEVICE %p failed, %!STATUS!, failing query stop",
1876             This->m_Device->GetHandle(), status);
1877 
1878         This->SetPendingPnpIrpStatus(status);
1879 
1880         //
1881         // The Started state will complete the irp when it sees the failure
1882         // status set on the irp.
1883         //
1884         state = WdfDevStatePnpStarted;
1885     }
1886 
1887     return state;
1888 }
1889 
1890 WDF_DEVICE_PNP_STATE
PnpEventQueryStopPending(__inout FxPkgPnp * This)1891 FxPkgPnp::PnpEventQueryStopPending(
1892     __inout FxPkgPnp* This
1893     )
1894 /*++
1895 
1896 Routine Description:
1897     Everything in the device has stopped due to the stop device irp.  Complete
1898     it now
1899 
1900 Arguments:
1901     This - instance of the state machine
1902 
1903 Return Value:
1904     WdfDevStatePnpNull
1905 
1906   --*/
1907 
1908 {
1909     FxIrp irp;
1910 
1911     irp.SetIrp(This->ClearPendingPnpIrp());
1912     (void) This->FireAndForgetIrp(&irp);
1913     return WdfDevStatePnpNull;
1914 }
1915 
1916 WDF_DEVICE_PNP_STATE
PnpEventQueryStopStaticCheck(__inout FxPkgPnp * This)1917 FxPkgPnp::PnpEventQueryStopStaticCheck(
1918     __inout FxPkgPnp*   This
1919     )
1920 /*++
1921 
1922 Routine Description:
1923     This function implements the Query Stop Static Check state.  It's job is to
1924     determine whether this device, in general, can stop.  If it can, then we
1925     proceed on to Query Stop Ask Driver.  Otherwise we will return to the
1926     Started state.  If the driver has set that the state machine should ignore
1927     query stop/remove, the query will succeed, otherwise it will fail
1928 
1929 Arguments:
1930     This - instance of the state machine
1931 
1932 Return Value:
1933    new machine state
1934 
1935 --*/
1936 {
1937     NTSTATUS status;
1938     BOOLEAN completeQuery = TRUE;
1939 
1940     if (This->m_DeviceStopCount != 0) {
1941         DoTraceLevelMessage(
1942             This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1943             "Failing QueryStopDevice because the driver "
1944             "has indicated that it cannot be stopped, count %d",
1945             This->m_DeviceStopCount);
1946 
1947         status = STATUS_INVALID_DEVICE_STATE;
1948     }
1949     else if (This->IsInSpecialUse()) {
1950         DoTraceLevelMessage(
1951             This->GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1952             "Failing QueryStopDevice due to open special file counts (paging %d,"
1953             " hiber %d, dump %d, boot %d)",
1954             This->GetUsageCount(WdfSpecialFilePaging),
1955             This->GetUsageCount(WdfSpecialFileHibernation),
1956             This->GetUsageCount(WdfSpecialFileDump),
1957             This->GetUsageCount(WdfSpecialFileBoot));
1958 
1959         status = STATUS_DEVICE_NOT_READY;
1960     }
1961     else {
1962         //
1963         // Go on to next state in the "stop" progression.
1964         //
1965         status = STATUS_SUCCESS;
1966         completeQuery = FALSE;
1967     }
1968 
1969     if (completeQuery) {
1970         //
1971         // Set the state which started will complete the request with
1972         //
1973         This->SetPendingPnpIrpStatus(status);
1974 
1975         //
1976         // Revert to started
1977         //
1978         return WdfDevStatePnpStarted;
1979     }
1980     else {
1981         //
1982         // Go ask power what the self managed io state is
1983         //
1984         return WdfDevStatePnpQueryStopAskDriver;
1985     }
1986 }
1987 
1988 VOID
PnpEventRemovedCommonCode(VOID)1989 FxPkgPnp::PnpEventRemovedCommonCode(
1990     VOID
1991     )
1992 /*++
1993 
1994 Routine Description:
1995     This function implements the Removed state.
1996 
1997 Arguments:
1998     none
1999 
2000 Return Value:
2001 
2002     VOID
2003 
2004 --*/
2005 {
2006     //
2007     // Purge non power managed queues now
2008     //
2009     m_Device->m_PkgIo->StopProcessingForPower(
2010         FxIoStopProcessingForPowerPurgeNonManaged
2011         );
2012 
2013     if (m_SelfManagedIoMachine != NULL) {
2014         m_SelfManagedIoMachine->Cleanup();
2015     }
2016 
2017     //
2018     // Cleanup WMI *after* EvtDeviceSelfManagedIoCleanup b/c we want to cleanup
2019     // after a well known and documented time.  The WMI docs state that you can
2020     // register providers and instances all the way through
2021     // EvtDeviceSelfManagedIoCleanup, so we mark WMI as cleaned up after that
2022     // call.
2023     //
2024     m_Device->WmiPkgCleanup();
2025 
2026     //
2027     // Mark the device as removed.
2028     //
2029     m_PnpStateAndCaps.Value &= ~FxPnpStateRemovedMask;
2030     m_PnpStateAndCaps.Value |= FxPnpStateRemovedTrue;
2031 
2032     //
2033     // Now call the driver and tell it to cleanup all its software state.
2034     //
2035     // We do the dispose early here before deleting the object
2036     // since the PNP remove event is the main trigger for
2037     // the Dispose chain in the framework.
2038     //
2039     // (Almost everything in the driver is rooted on the device object)
2040     //
2041 
2042     m_Device->EarlyDispose();
2043 
2044     //
2045     // All the children are in the disposed state, destroy them all.  m_Device
2046     // is not destroyed in this call.
2047     //
2048 
2049     m_Device->DestroyChildren();
2050 
2051     //
2052     // Wait for all children to drain out and cleanup.
2053     //
2054 
2055     m_Device->m_DisposeList->WaitForEmpty();
2056 
2057 }
2058 
2059 WDF_DEVICE_PNP_STATE
PnpEventQueryCanceled(__inout FxPkgPnp * This)2060 FxPkgPnp::PnpEventQueryCanceled(
2061     __inout FxPkgPnp*   This
2062     )
2063 /*++
2064 
2065 Routine Description:
2066     The device was in the queried state (remove or stop) and the query was
2067     canceled.  Remove the power reference taken and return to the started state.
2068 
2069 Arguments:
2070     This - instance of the state machine
2071 
2072 Return Value:
2073     WdfDevStatePnpStarted
2074 
2075   --*/
2076 {
2077     This->PnpPowerDereferenceSelf();
2078 
2079     return WdfDevStatePnpStarted;
2080 }
2081 
2082 WDF_DEVICE_PNP_STATE
PnpEventRemoved(__inout FxPkgPnp * This)2083 FxPkgPnp::PnpEventRemoved(
2084     __inout FxPkgPnp*   This
2085     )
2086 /*++
2087 
2088 Routine Description:
2089     This function implements the Removed state.  It tears down any remaining
2090     children and the moves into a role (FDO/PDO) specific state.
2091 
2092 Arguments:
2093     This - instance of the state machine
2094 
2095 Return Value:
2096     new machine state
2097 
2098 --*/
2099 {
2100     //
2101     // Remove any child PDOs which may still be lingering around.  We do
2102     // the cleanup here so that we do it only once for the PDO which is being
2103     // removed (but may stick around) b/c it was not reported as missing.
2104     //
2105     //
2106     // Iterate over all of the reported children
2107     //
2108     This->ChildListNotifyRemove(&This->m_PendingChildCount);
2109 
2110     //
2111     // Decrement our bias from when the device was (re)started.  If all of the
2112     // children removed themselves synchronously, we just move to the cleanup
2113     // state, otherwise wait for all the children to fully process the remove
2114     // before remove the parent so that ordering of removal between parent and
2115     // child is guaranteed (where the order is that all children are cleaned
2116     // up before the parent is cleaned up).
2117     //
2118     if (InterlockedDecrement(&This->m_PendingChildCount) > 0) {
2119         return WdfDevStatePnpRemovedWaitForChildren;
2120     }
2121     else {
2122         return WdfDevStatePnpRemovedChildrenRemoved;
2123     }
2124 }
2125 
2126 WDF_DEVICE_PNP_STATE
PnpEventPdoRemoved(__inout FxPkgPnp * This)2127 FxPkgPnp::PnpEventPdoRemoved(
2128     __inout FxPkgPnp*   This
2129     )
2130 /*++
2131 
2132 Routine Description:
2133     This function implements the PDO Removed state.  This function is called
2134     when the PDO is actually reported missing to the OS or the FDO is removed.
2135 
2136 Arguments:
2137     none
2138 
2139 Return Value:
2140     new state
2141 
2142 --*/
2143 {
2144     return This->PnpEventPdoRemovedOverload();
2145 }
2146 
2147 WDF_DEVICE_PNP_STATE
PnpEventRemovedPdoWait(__inout FxPkgPnp * This)2148 FxPkgPnp::PnpEventRemovedPdoWait(
2149     __inout FxPkgPnp* This
2150     )
2151 /*++
2152 
2153 Routine Description:
2154     Indicates to the remove path that remove processing is done and we will
2155     wait for additional PNP events to arrive at this state machine
2156 
2157 Arguments:
2158     This - Instance of the state machine
2159 
2160 Return Value:
2161     WdfDevStatePnpNull
2162 
2163   --*/
2164 {
2165     if (This->m_DeviceRemoveProcessed != NULL) {
2166         This->m_SetDeviceRemoveProcessed = TRUE;
2167     }
2168 
2169     return WdfDevStatePnpNull;
2170 }
2171 
2172 WDF_DEVICE_PNP_STATE
PnpEventRemovedPdoSurpriseRemoved(__inout FxPkgPnp * This)2173 FxPkgPnp::PnpEventRemovedPdoSurpriseRemoved(
2174     __inout FxPkgPnp* This
2175     )
2176 /*++
2177 
2178 Routine Description:
2179     PDO has been removed (could have been disabled in user mode) and is now
2180     surprise removed by the underlying bus.
2181 
2182 Arguments:
2183     This - instance of the state machine
2184 
2185 Return Value:
2186     WdfDevStatePnpRemovedPdoWait
2187 
2188   --*/
2189 {
2190     //
2191     // Invoke EvtDeviceSurpriseRemove
2192     //
2193     This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
2194 
2195     //
2196     // Call the overloaded surprise remove handler since
2197     // PnpEventSurpriseRemovePendingOverload will not get called
2198     //
2199     This->PnpEventSurpriseRemovePendingOverload();
2200 
2201     //
2202     // The surprise remove irp was pended, complete it now
2203     //
2204     This->PnpFinishProcessingIrp();
2205 
2206     return WdfDevStatePnpRemovedPdoWait;
2207 }
2208 
2209 VOID
PnpCleanupForRemove(__in BOOLEAN GracefulRemove)2210 FxPkgPnp::PnpCleanupForRemove(
2211     __in BOOLEAN GracefulRemove
2212     )
2213 /*++
2214 
2215 Routine Description:
2216     This is a common worker function between surprise remove and the graceful
2217     remove path to do common cleanup. This involves deregistering from WMI,
2218     device interfaces, symbolic links and stopping power managed i/o.
2219 
2220 Arguments:
2221     GracefulRemove - if TRUE, we are in the graceful remove path, otherwise we
2222                      are in the surprise remove path.
2223 
2224 Return Value:
2225     None
2226 
2227   --*/
2228 {
2229     //
2230     // Disable WMI.
2231     //
2232     m_Device->WmiPkgDeregister();
2233 
2234     //
2235     // Disable any device interfaces.
2236     //
2237     PnpDisableInterfaces();
2238 
2239     DeleteSymbolicLinkOverload(GracefulRemove);
2240 
2241 
2242 
2243 
2244 
2245 
2246 
2247 
2248     // Flush/purge top-edge queues
2249     m_Device->m_PkgIo->StopProcessingForPower(
2250         FxIoStopProcessingForPowerPurgeManaged
2251         );
2252 
2253     //
2254     // Invoke EvtDeviceSelfManagedIoFlush
2255     //
2256     if (m_SelfManagedIoMachine != NULL) {
2257         m_SelfManagedIoMachine->Flush();
2258     }
2259 
2260     //
2261     // Tell all the resource objects that they no longer own anything.
2262     //
2263     NotifyResourceobjectsToReleaseResources();
2264 
2265     //
2266     // Flush persistent state to permanent storage.  We do this in the failed
2267     // state for the surprise removed case.  By storing the state when we are
2268     // surprise removed, if the stack is reenumerated, it will pick up the saved
2269     // state that was just committed.  If the state was saved during remove
2270     // device it can be too late because the new instance of the same device
2271     // could already be up and running and not pick up the saved state.
2272     //
2273     // It is important to save the state before completing the (potentially)
2274     // pended pnp irp.  Completing the pnp irp will allow a new instance of the
2275     // device to be enumerated and we want to save state before that happens.
2276     //
2277     SaveState(FALSE);
2278 
2279     if (m_SharedPower.m_WaitWakeOwner) {
2280         //
2281         // Don't care about the return code, just blindly try to complete the
2282         // wake request.  The function can handle the case where there is no
2283         // irp to complete.
2284         //
2285         (void) PowerIndicateWaitWakeStatus(STATUS_NO_SUCH_DEVICE);
2286     }
2287 }
2288 
2289 WDF_DEVICE_PNP_STATE
PnpEventRemovingDisableInterfaces(__inout FxPkgPnp * This)2290 FxPkgPnp::PnpEventRemovingDisableInterfaces(
2291     __inout FxPkgPnp*   This
2292     )
2293 /*++
2294 
2295 Routine Description:
2296     This function implements the Removing Disable
2297     Interfaces state.  It disables any device interfaces
2298     and then tells the power policy state machine to prepare for device removal.
2299 
2300 Arguments:
2301     none
2302 
2303 Return Value:
2304 
2305     WdfDevStatePnpNull
2306 
2307 --*/
2308 {
2309     NTSTATUS status;
2310 
2311     //
2312     // Surprise remove path releases hardware first then disables the interfaces,
2313     // so do the same in the graceful remove path.
2314     //
2315 
2316     //
2317     // Call the driver and tell it to unmap resources.
2318     //
2319     status = This->PnpReleaseHardware();
2320     if (!NT_SUCCESS(status)) {
2321         //
2322         // The driver failed to unmap resources.  Presumably this means that
2323         // there are now some leaked PTEs.  Just log the failure.
2324         //
2325         DoTraceLevelMessage(
2326             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2327             "EvtDeviceReleaseHardware %p failed, %!STATUS!",
2328             This->m_Device->GetHandle(), status);
2329     }
2330 
2331     This->PnpCleanupForRemove(TRUE);
2332 
2333     //
2334     // Tell the power policy state machine to prepare for device removal
2335     //
2336     This->PnpPowerPolicyRemove();
2337 
2338     return WdfDevStatePnpNull;
2339 }
2340 
2341 WDF_DEVICE_PNP_STATE
PnpEventStarted(__inout FxPkgPnp * This)2342 FxPkgPnp::PnpEventStarted(
2343     __inout FxPkgPnp*   This
2344     )
2345 /*++
2346 
2347 Routine Description:
2348     Completes the pending request or sends it on its way.
2349 
2350 Arguments:
2351     This - instance of the state machine for the device
2352 
2353 Return Value:
2354     WdfDevStatePnpNull
2355 
2356   --*/
2357 {
2358 
2359     This->m_AchievedStart = TRUE;
2360 
2361     //
2362     // Log Telemetry event for the FDO
2363     //
2364     if (This->m_Device->IsPdo() == FALSE) {
2365         This->m_Device->FxLogDeviceStartTelemetryEvent();
2366     }
2367 
2368     This->PnpFinishProcessingIrp();
2369 
2370     return WdfDevStatePnpNull;
2371 }
2372 
2373 WDF_DEVICE_PNP_STATE
PnpEventStartedCancelStop(__inout FxPkgPnp * This)2374 FxPkgPnp::PnpEventStartedCancelStop(
2375     __inout FxPkgPnp* This
2376     )
2377 /*++
2378 
2379 Routine Description:
2380     Cancel stop received from the started state.  Just return to the started
2381     state where we will handle the pended irp.
2382 
2383 Arguments:
2384     This - Instance of the state machine
2385 
2386 Return Value:
2387     WdfDevStatePnpStarted
2388 
2389   --*/
2390 {
2391     UNREFERENCED_PARAMETER(This);
2392     return WdfDevStatePnpStarted;
2393 }
2394 
2395 WDF_DEVICE_PNP_STATE
PnpEventStartedCancelRemove(__inout FxPkgPnp * This)2396 FxPkgPnp::PnpEventStartedCancelRemove(
2397     __inout FxPkgPnp* This
2398     )
2399 /*++
2400 
2401 Routine Description:
2402     Cancel remove received from the started state.  Just return to the started
2403     state where we will handle the pended irp.
2404 
2405 Arguments:
2406     This - Instance of the state machine
2407 
2408 Return Value:
2409     WdfDevStatePnpStarted
2410 
2411   --*/
2412 {
2413     UNREFERENCED_PARAMETER(This);
2414 
2415     return WdfDevStatePnpStarted;
2416 }
2417 
2418 WDF_DEVICE_PNP_STATE
PnpEventStartedRemoving(__inout FxPkgPnp * This)2419 FxPkgPnp::PnpEventStartedRemoving(
2420     __inout FxPkgPnp* This
2421     )
2422 /*++
2423 
2424 Routine Description:
2425     Remove directly from started.  Power down the other state machines.
2426 
2427 Arguments:
2428     This - instance of the state machine
2429 
2430 Return Value:
2431     WdfDevStatePnpNull
2432 
2433   --*/
2434 {
2435     This->PnpPowerPolicyStop();
2436 
2437     return WdfDevStatePnpNull;
2438 }
2439 
2440 WDF_DEVICE_PNP_STATE
PnpEventRestarting(__inout FxPkgPnp * This)2441 FxPkgPnp::PnpEventRestarting(
2442     __inout FxPkgPnp*   This
2443     )
2444 /*++
2445 
2446 Routine Description:
2447     This function implements the Cancelling Stop state.
2448 
2449 Arguments:
2450     none
2451 
2452 Return Value:
2453 
2454     VOID
2455 
2456 --*/
2457 {
2458     //
2459     // Send an event to the Power Policy State Machine
2460     // telling it to "Start."
2461     //
2462     This->PnpPowerPolicyStart();
2463 
2464     return WdfDevStatePnpNull;
2465 }
2466 
2467 WDF_DEVICE_PNP_STATE
PnpEventStartingFromStopped(__inout FxPkgPnp * This)2468 FxPkgPnp::PnpEventStartingFromStopped(
2469     __inout FxPkgPnp*   This
2470     )
2471 /*++
2472 
2473 Routine Description:
2474     This function implements the Restarting From Stopped state.  It's job
2475     is to map new resources and then proceed to the Restarting state.
2476 
2477 Arguments:
2478     none
2479 
2480 Return Value:
2481 
2482     VOID
2483 
2484 --*/
2485 {
2486     NTSTATUS status;
2487     BOOLEAN matched;
2488 
2489     status = This->PnpPrepareHardware(&matched);
2490 
2491     if (!NT_SUCCESS(status)) {
2492         //
2493         // We can handle remove out of the init state, revert back to that state
2494         //
2495         if (matched == FALSE) {
2496             //
2497             // Wait for the remove irp to come in
2498             //
2499             COVERAGE_TRAP();
2500             return WdfDevStatePnpFailed;
2501         }
2502         else {
2503             //
2504             // EvtDevicePrepareHardware is what failed, goto a state where we
2505             // undo that call.
2506             //
2507             COVERAGE_TRAP();
2508             return WdfDevStatePnpFailedOwnHardware;
2509         }
2510     }
2511 
2512     return WdfDevStatePnpRestarting;
2513 }
2514 
2515 WDF_DEVICE_PNP_STATE
PnpEventStopped(__inout FxPkgPnp * This)2516 FxPkgPnp::PnpEventStopped(
2517     __inout FxPkgPnp*   This
2518     )
2519 /*++
2520 
2521 Routine Description:
2522     This function implements the Stopped state.  It's job is to invoke
2523     EvtDeviceReleaseHardware.
2524 
2525 Arguments:
2526     none
2527 
2528 Return Value:
2529 
2530     VOID
2531 
2532 --*/
2533 {
2534     WDF_DEVICE_PNP_STATE state;
2535     NTSTATUS status;
2536 
2537     status = This->PnpReleaseHardware();
2538     if (NT_SUCCESS(status)) {
2539         //
2540         // Tell all the resource objects that they no longer own anything.
2541         //
2542         This->NotifyResourceobjectsToReleaseResources();
2543 
2544         state = WdfDevStatePnpNull;
2545     }
2546     else {
2547         DoTraceLevelMessage(This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
2548                             "EvtDeviceReleaseHardware failed - %!STATUS!",
2549                             status);
2550         COVERAGE_TRAP();
2551 
2552         This->SetInternalFailure();
2553         state = WdfDevStatePnpFailed;
2554     }
2555 
2556     //
2557     // Send the irp on its merry way.  This irp (stop device) cannot be failed
2558     // and must always be sent down the stack.
2559     //
2560     This->PnpFinishProcessingIrp();
2561 
2562     return state;
2563 }
2564 
2565 WDF_DEVICE_PNP_STATE
PnpEventStoppedWaitForStartCompletion(__inout FxPkgPnp * This)2566 FxPkgPnp::PnpEventStoppedWaitForStartCompletion(
2567     __inout FxPkgPnp* This
2568     )
2569 /*++
2570 
2571 Routine Description:
2572     The start irp is coming down from the stopped state.  Send it down the stack
2573     and transition to the new state if needed.
2574 
2575 Arguments:
2576     This -  instance of the state machine
2577 
2578 Return Value:
2579     new machine state
2580 
2581   --*/
2582 {
2583     if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
2584         //
2585         // The start irp's completion routine will move the state machine into
2586         // the new state.
2587         //
2588         return WdfDevStatePnpNull;
2589     }
2590 
2591     return WdfDevStatePnpStartingFromStopped;
2592 }
2593 
2594 WDF_DEVICE_PNP_STATE
PnpEventStartedStopping(__inout FxPkgPnp * This)2595 FxPkgPnp::PnpEventStartedStopping(
2596     __inout FxPkgPnp*   This
2597     )
2598 /*++
2599 
2600 Routine Description:
2601     Received a stop irp.  Stop the power policy machine and then wait for it to
2602     complete.
2603 
2604 Arguments:
2605     This - instance of the state machine
2606 
2607 Return Value:
2608     WdfDevStatePnpNull
2609 
2610   --*/
2611 {
2612     //
2613     // It is important to stop power policy before releasing the reference.
2614     // If the reference was released first, we could get into a situation where
2615     // we immediately go idle and then we must send a D0 irp when in the remove.
2616     // If there are devices on top of this device and we send a D0 irp during
2617     // remove processing, the upper devices will be sent an irp after getting a
2618     // pnp remove (and either crash or fail the power irp upon receiving it).
2619     //
2620     This->PnpPowerPolicyStop();
2621     This->PnpPowerDereferenceSelf();
2622 
2623     return WdfDevStatePnpNull;
2624 }
2625 
2626 WDF_DEVICE_PNP_STATE
PnpEventSurpriseRemoved(__inout FxPkgPnp * This)2627 FxPkgPnp::PnpEventSurpriseRemoved(
2628     __inout FxPkgPnp*   This
2629     )
2630 /*++
2631 
2632 Routine Description:
2633     We got IRP_MN_SURPRISE_REMOVE_DEVICE while the system was pretty far down
2634     the removal path, or never started.  Call EvtDeviceSurpriseRemoval, call the
2635     surprise remove virtual and drop into the Failed path.
2636 
2637 Arguments:
2638     This - instance of the state machine
2639 
2640 Return Value:
2641     new device pnp state
2642 
2643   --*/
2644 {
2645     //
2646     // Invoke EvtDeviceSurpriseRemove
2647     //
2648     This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
2649 
2650     //
2651     // Notify the virtual override of the surprise remove.
2652     //
2653     This->PnpEventSurpriseRemovePendingOverload();
2654 
2655     return WdfDevStatePnpFailed;
2656 }
2657 
2658 WDF_DEVICE_PNP_STATE
PnpEventInitQueryRemove(__inout FxPkgPnp * This)2659 FxPkgPnp::PnpEventInitQueryRemove(
2660     __inout FxPkgPnp* This
2661     )
2662 /*++
2663 
2664 Routine Description:
2665     Query remove from the init state.  Complete the pended request.
2666 
2667 Arguments:
2668     This - instance of th state machine.
2669 
2670 Return Value:
2671     WdfDevStatePnpNull
2672 
2673   --*/
2674 {
2675     FxIrp irp(This->ClearPendingPnpIrp());
2676 
2677     irp.SetStatus(STATUS_SUCCESS);
2678     This->FireAndForgetIrp(&irp);
2679 
2680     return WdfDevStatePnpNull;
2681 }
2682 
2683 WDF_DEVICE_PNP_STATE
PnpEventInitQueryRemoveCanceled(__inout FxPkgPnp * This)2684 FxPkgPnp::PnpEventInitQueryRemoveCanceled(
2685     __inout FxPkgPnp* This
2686     )
2687 /*++
2688 
2689 Routine Description:
2690     Handle a query remove canceled from the init state.  Complete the pended
2691     request.
2692 
2693 Arguments:
2694     This - instance of the state machine
2695 
2696 Return Value:
2697     WdfDevStatePnpInit
2698 
2699   --*/
2700 {
2701     FxIrp irp(This->ClearPendingPnpIrp());
2702 
2703     This->FireAndForgetIrp(&irp);
2704 
2705     return WdfDevStatePnpInit;
2706 }
2707 
2708 WDF_DEVICE_PNP_STATE
PnpEventFdoRemoved(__inout FxPkgPnp * This)2709 FxPkgPnp::PnpEventFdoRemoved(
2710     __inout FxPkgPnp* This
2711     )
2712 /*++
2713 
2714 Routine Description:
2715     FDO is being removed, hand off to the derived pnp package
2716 
2717 Arguments:
2718     This - instance of the state machine
2719 
2720 Return Value:
2721     new device pnp state
2722 
2723   --*/
2724 {
2725     return This->PnpEventFdoRemovedOverload();
2726 }
2727 
2728 WDF_DEVICE_PNP_STATE
PnpEventQueriedSurpriseRemove(__inout FxPkgPnp * This)2729 FxPkgPnp::PnpEventQueriedSurpriseRemove(
2730     __inout FxPkgPnp* This
2731     )
2732 /*++
2733 
2734 Routine Description:
2735     The device was in a queried (either stop or cancel) state and was surprise
2736     removed from it.
2737 
2738 Arguments:
2739     This - instance of the state machine
2740 
2741 Return Value:
2742     new state, WdfDevStatePnpSurpriseRemoveIoStarted
2743 
2744   --*/
2745 {
2746     COVERAGE_TRAP();
2747 
2748     This->PnpPowerDereferenceSelf();
2749 
2750     return WdfDevStatePnpSurpriseRemoveIoStarted;
2751 }
2752 
2753 WDF_DEVICE_PNP_STATE
PnpEventSurpriseRemoveIoStarted(__inout FxPkgPnp * This)2754 FxPkgPnp::PnpEventSurpriseRemoveIoStarted(
2755     __inout FxPkgPnp* This
2756     )
2757 /*++
2758 
2759 Routine Description:
2760     We got IRP_MN_SURPRISE_REMOVE_DEVICE while the system was more or less
2761     running.  Start down the Surprise Remove/Device Failed path.  This state
2762     calls EvtDeviceSurpriseRemoval, calls the virtual surprise remove overload,
2763      and then drops into the Failed path.
2764 
2765 Arguments:
2766     This - instance of the state machine
2767 
2768 Return Value:
2769     new device pnp state
2770 
2771   --*/
2772 {
2773     //
2774     // Invoke EvtDeviceSurpriseRemove
2775     //
2776 
2777     This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
2778 
2779     //
2780     // Notify the virtual override of the surprise remove.
2781     //
2782     This->PnpEventSurpriseRemovePendingOverload();
2783 
2784     return WdfDevStatePnpFailedIoStarting;
2785 }
2786 
2787 WDF_DEVICE_PNP_STATE
PnpEventFailedPowerDown(__inout FxPkgPnp * This)2788 FxPkgPnp::PnpEventFailedPowerDown(
2789     __inout FxPkgPnp* This
2790     )
2791 /*++
2792 
2793 Routine Description:
2794     The device was in the started state and failed power down or could not
2795     enable its interfaces.  Gracefully power down the device and tear down
2796     the stack.
2797 
2798     The difference between this routine and PnpEventFailedIoStarting is that
2799     FailedIoStarting sends a surprise remove to the power state machine.  After
2800     surprise remove has been sent to the power state machine, it will not attempt
2801     to put the device into Dx because it assumes the device is no longer present.
2802     In this error case, we still want the device to be powered down, so we send
2803     a normal stop remove.
2804 
2805 Arguments:
2806     This - instance of the state machine
2807 
2808 Return Value:
2809     new state
2810 
2811   --*/
2812 {
2813     //
2814     // Normal stop so that the power state machine will go through the power off
2815     // path and not skip directly to off like it would if we sent it a surprise
2816     // remove notification.
2817     //
2818     This->PnpPowerPolicyStop();
2819 
2820     return WdfDevStatePnpNull;
2821 }
2822 
2823 WDF_DEVICE_PNP_STATE
PnpEventFailedIoStarting(__inout FxPkgPnp * This)2824 FxPkgPnp::PnpEventFailedIoStarting(
2825     __inout FxPkgPnp* This
2826     )
2827 /*++
2828 
2829 Routine Description:
2830     The device failed (or was yanked out of the machine) while it was more
2831     or less running.  Tell the driver to stop self-managed I/O and drop into
2832     the next state on the failure path.
2833 
2834 Arguments:                                    j
2835     This - instance of the state machine
2836 
2837 Return Value:
2838     new device pnp state
2839 
2840   --*/
2841 {
2842     This->PnpPowerPolicySurpriseRemove();
2843 
2844     return WdfDevStatePnpNull;
2845 }
2846 
2847 WDF_DEVICE_PNP_STATE
PnpEventFailedOwnHardware(__inout FxPkgPnp * This)2848 FxPkgPnp::PnpEventFailedOwnHardware(
2849     __inout FxPkgPnp* This
2850     )
2851 /*++
2852 
2853 Routine Description:
2854     The device failed (or was yanked out of the machine) while it owned access
2855     to the hardware.  Tell the driver to release resources and drop into
2856     the next state on the failure path.
2857 
2858 Arguments:
2859     This - instance of the state machine
2860 
2861 Return Value:
2862     new device pnp state
2863 
2864   --*/
2865 {
2866     //
2867     // Invoke EvtDeviceReleaseHardware
2868     //
2869     (void) This->PnpReleaseHardware();
2870 
2871     return WdfDevStatePnpFailed;
2872 }
2873 
2874 WDF_DEVICE_PNP_STATE
PnpEventFailed(__inout FxPkgPnp * This)2875 FxPkgPnp::PnpEventFailed(
2876     __inout FxPkgPnp* This
2877     )
2878 /*++
2879 
2880 Routine Description:
2881         The device failed (or was yanked out of the machine).  Disable interfaces,
2882         flush queues and tell the driver to clean up random stuff.
2883         Also ask the power policy state machine to prepare for device removal.
2884 
2885 Arguments:
2886     This - instance of the state machine
2887 
2888 Return Value:
2889     WdfDevStatePnpNull
2890 
2891   --*/
2892 {
2893     This->PnpCleanupForRemove(FALSE);
2894 
2895     //
2896     // Tell the power policy state machine to prepare for device removal
2897     //
2898     This->PnpPowerPolicyRemove();
2899 
2900     return WdfDevStatePnpNull;
2901 }
2902 
2903 WDF_DEVICE_PNP_STATE
PnpEventFailedPowerPolicyRemoved(__inout FxPkgPnp * This)2904 FxPkgPnp::PnpEventFailedPowerPolicyRemoved(
2905     __inout FxPkgPnp* This
2906     )
2907 /*++
2908 
2909 Routine Description:
2910     The power policy state machine has prepared for device removal. Invalidate
2911     the device state and wait for IRP_MN_REMOVE_DEVICE.
2912 
2913 Arguments:
2914     This - instance of the state machine
2915 
2916 Return Value:
2917     WdfDevStatePnpFailedWaitForRemove
2918 
2919   --*/
2920 {
2921     //
2922     // Finish processing any pended PnP IRP.  Since we can reach this state from
2923     // states where a pnp irp was *not* pended, we do not require a pnp irp to
2924     // have been pended when trying to complete it.
2925     //
2926     This->PnpFinishProcessingIrp(FALSE);
2927 
2928     //
2929     // Request reenumeration if the client driver asked for it or if there was
2930     // an internal failure *and* if the client driver didn't specify failure...
2931     // AND if we have not yet exceeded our restart count within a period of time.
2932     //
2933     if ((This->m_FailedAction == WdfDeviceFailedAttemptRestart ||
2934          (This->m_FailedAction == WdfDeviceFailedUndefined && This->m_InternalFailure))
2935         &&
2936         This->PnpCheckAndIncrementRestartCount()) {
2937         //
2938         // No need to invalidate state because we are in a state waiting for
2939         // a remove device anyways so failure is imminent.
2940         //
2941         This->AskParentToRemoveAndReenumerate();
2942     }
2943 
2944     if (This->m_FailedAction != WdfDeviceFailedUndefined || This->m_InternalFailure) {
2945         //
2946         // If the failure occurred in this device, then tear down the stack if
2947         // we are in a state in which pnp thinks we are started.  If we are
2948         // already in a stopped state, this invalidation will do no harm.
2949         //
2950         MxDeviceObject physicalDeviceObject(
2951                                 This->m_Device->GetPhysicalDevice()
2952                                 );
2953 
2954         //
2955         // We need to pass FDO as a parameter as UMDF currently doesn't have
2956         // PDOs and instead needs FDO to invalidate device state.
2957         //
2958         physicalDeviceObject.InvalidateDeviceState(
2959             This->m_Device->GetDeviceObject() //FDO
2960             );
2961     }
2962 
2963     return WdfDevStatePnpFailedWaitForRemove;
2964 }
2965 
2966 WDF_DEVICE_PNP_STATE
PnpEventFailedSurpriseRemoved(__inout FxPkgPnp * This)2967 FxPkgPnp::PnpEventFailedSurpriseRemoved(
2968     __inout FxPkgPnp* This
2969     )
2970 /*++
2971 
2972 Routine Description:
2973     The device has failed and then received a surprise remove.  This can easily
2974     happen on return from low power as following:
2975 
2976     1  device attempts to enter D0, D0Entry fails
2977     2  pnp state machine proceeds down failure path and stops at FailedWaitForRemove
2978     3  bus driver finds device missing, reports it as such and a s.r. irp
2979        arrives
2980 
2981 Arguments:
2982     This - instance of the state machine
2983 
2984 Return Value:
2985     WdfDevStatePnpFailedWaitForRemove
2986 
2987   --*/
2988 {
2989     //
2990     // Invoke EvtDeviceSurpriseRemove
2991     //
2992     This->m_DeviceSurpriseRemoval.Invoke(This->m_Device->GetHandle());
2993 
2994     //
2995     // Call the overloaded surprise remove handler
2996     //
2997     This->PnpEventSurpriseRemovePendingOverload();
2998 
2999     //
3000     // The surprise remove irp was pended, complete it now.  The irp need not
3001     // be present.  If we failed before the surprise irp was sent and the irp
3002     // arrived in the middle of processing the failure, we could have completed
3003     // the s.r. irp in FailedWaitForRemove, which is OK.
3004     //
3005     This->PnpFinishProcessingIrp(FALSE);
3006 
3007     //
3008     // Return back to the failed state where will wait for remove
3009     //
3010     return WdfDevStatePnpFailedWaitForRemove;
3011 }
3012 
3013 WDF_DEVICE_PNP_STATE
PnpEventFailedStarted(__inout FxPkgPnp * This)3014 FxPkgPnp::PnpEventFailedStarted(
3015     __inout FxPkgPnp* This
3016     )
3017 /*++
3018 
3019 Routine Description:
3020     Device failed (probably somewhere in the start path) and got another start
3021     request.  Fail the start and return to the state where we will wait for a
3022     remove irp.
3023 
3024 Arguments:
3025     This - instance of the state machine
3026 
3027 Return Value:
3028     WdfDevStatePnpFailedWaitForRemove
3029 
3030   --*/
3031 {
3032     //
3033     // Complete the pended start irp with error
3034     //
3035     This->SetPendingPnpIrpStatus(STATUS_INVALID_DEVICE_STATE);
3036     This->PnpFinishProcessingIrp();
3037 
3038     return WdfDevStatePnpFailedWaitForRemove;
3039 }
3040 
3041 WDF_DEVICE_PNP_STATE
PnpEventFailedInit(__inout FxPkgPnp * This)3042 FxPkgPnp::PnpEventFailedInit(
3043     __inout FxPkgPnp* This
3044     )
3045 /*++
3046 
3047 Routine Description:
3048     Processing of the start irp's resources failed.  Complete the start irp.
3049 
3050 Arguments:
3051     This - instance of the state machine
3052 
3053 Return Value:
3054     WdfDevStatePnpNull
3055 
3056   --*/
3057 {
3058     //
3059     // Release the power thread that we may have previously acquired in
3060     // HardwareAvailable.
3061     //
3062     This->ReleasePowerThread();
3063 
3064     //
3065     // Deref the reenumeration interface
3066     //
3067     This->ReleaseReenumerationInterface();
3068 
3069     This->PnpFinishProcessingIrp();
3070 
3071     return WdfDevStatePnpInit;
3072 }
3073 
3074 WDF_DEVICE_PNP_STATE
PnpEventPdoInitFailed(__inout FxPkgPnp * This)3075 FxPkgPnp::PnpEventPdoInitFailed(
3076     __inout FxPkgPnp* This
3077     )
3078 /*++
3079 
3080 Routine Description:
3081     The driver failed EvtDeviceSoftwareInit.  Run cleanup and die.
3082 
3083 Arguments:
3084     This - instance of the state machine
3085 
3086 Return Value:
3087     new device pnp state
3088 
3089   --*/
3090 {
3091     COVERAGE_TRAP();
3092 
3093 
3094     This->m_Device->EarlyDispose();
3095 
3096     //
3097     // All the children are in the disposed state, destroy them all.  m_Device
3098     // is not destroyed in this call.
3099     //
3100 
3101     This->m_Device->DestroyChildren();
3102 
3103     return WdfDevStatePnpFinal;
3104 }
3105 
3106 WDF_DEVICE_PNP_STATE
PnpEventRestart(__inout FxPkgPnp * This)3107 FxPkgPnp::PnpEventRestart(
3108     __inout FxPkgPnp* This
3109     )
3110 /*++
3111 
3112 Routine Description:
3113     A start to start transition has occurred.  Go through the normal stop path
3114     first and then restart things.
3115 
3116 Arguments:
3117     This - instance of the state machine
3118 
3119 Return Value:
3120     WdfDevStateNull or new machine state
3121 
3122   --*/
3123 {
3124     //
3125     // Stop the power policy machine so that we simulate stopping first
3126     //
3127     This->PnpPowerPolicyStop();
3128 
3129     return WdfDevStatePnpNull;
3130 }
3131 
3132 WDF_DEVICE_PNP_STATE
PnpEventRestartReleaseHardware(__inout FxPkgPnp * This)3133 FxPkgPnp::PnpEventRestartReleaseHardware(
3134     __inout FxPkgPnp* This
3135     )
3136 /*++
3137 
3138 Routine Description:
3139     Release the hardware resources and send the start irp down the stack
3140 
3141 Arguments:
3142     This - instance of the state machine
3143 
3144 Return Value:
3145     new machine state
3146 
3147   --*/
3148 {
3149     NTSTATUS status;
3150 
3151     status = This->PnpReleaseHardware();
3152     if (!NT_SUCCESS(status)) {
3153         DoTraceLevelMessage(
3154             This->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3155             "EvtDeviceReleaseHardware failed with %!STATUS!", status);
3156 
3157         COVERAGE_TRAP();
3158 
3159         This->SetInternalFailure();
3160         This->SetPendingPnpIrpStatus(status);
3161 
3162         return WdfDevStatePnpFailed;
3163     }
3164 
3165     if (This->PnpSendStartDeviceDownTheStackOverload() == FALSE) {
3166         //
3167         // The start irp's completion routine will move the state machine into
3168         // the new state.
3169         //
3170         return WdfDevStatePnpNull;
3171     }
3172 
3173     //
3174     // Start happened synchronously.  Transition to the new state now.
3175     //
3176     return WdfDevStatePnpRestartHardwareAvailable;
3177 }
3178 
3179 WDF_DEVICE_PNP_STATE
PnpEventRestartHardwareAvailable(__inout FxPkgPnp * This)3180 FxPkgPnp::PnpEventRestartHardwareAvailable(
3181     __inout FxPkgPnp* This
3182     )
3183 /*++
3184 
3185 Routine Description:
3186     Prepare the hardware and restart the power and power  policy state machines
3187     after a successful start -> start transition where the start irp is coming
3188     up the stack.
3189 
3190 Arguments:
3191     This - instance of the state machine
3192 
3193 Return Value:
3194     new machine state
3195 
3196   --*/
3197 {
3198     NTSTATUS status;
3199     BOOLEAN matched;
3200 
3201     status = This->PnpPrepareHardware(&matched);
3202 
3203     if (!NT_SUCCESS(status)) {
3204         if (matched == FALSE) {
3205             //
3206             // Wait for the remove irp to come in
3207             //
3208             COVERAGE_TRAP();
3209             return WdfDevStatePnpFailed;
3210         }
3211         else {
3212             //
3213             // EvtDevicePrepareHardware is what failed, goto a state where we
3214             // undo that call.
3215             //
3216             COVERAGE_TRAP();
3217             return WdfDevStatePnpFailedOwnHardware;
3218         }
3219     }
3220 
3221     This->PnpPowerPolicyStart();
3222 
3223     return WdfDevStatePnpNull;
3224 }
3225 
3226 WDF_DEVICE_PNP_STATE
PnpEventPdoRestart(__inout FxPkgPnp * This)3227 FxPkgPnp::PnpEventPdoRestart(
3228     __inout FxPkgPnp* This
3229     )
3230 /*++
3231 
3232 Routine Description:
3233     The PDO was in the removed state has received another start irp.  Reset state
3234     and then move into the start sequence.
3235 
3236 Arguments:
3237     This -  instance of the state machine
3238 
3239 Return Value:
3240     WdfDevStatePnpHardwareAvailable
3241 
3242   --*/
3243 {
3244     //
3245     // Since this is a PDO and it is being restarted, it could have had an internal
3246     // failure during its previous start.  Since we use m_InternalFailure to set
3247     // PNP_DEVICE_FAILED when handling IRP_MN_QUERY_PNP_DEVICE_STATE, it should
3248     // be set to FALSE so we don't immediately fail the device after the start
3249     // has been succeeded.
3250     //
3251     This->m_InternalFailure = FALSE;
3252     This->m_Failed = FALSE;
3253 
3254     //
3255     // The PDO is being restarted and could have previous had a power thread
3256     // running.  If so, the reference count goes to zero when removed from a
3257     // started state.  Reset back to 1.
3258     //
3259     This->m_PowerThreadInterfaceReferenceCount = 1;
3260 
3261     //
3262     // The count is decremented on the initial started->removed transition (and
3263     // not subsequent removed -> removed transitiosn).  On removed -> restarted,
3264     // we need to set the count back to a bias of 1 so when we process the remove
3265     // again we can know if there are any pending child (of this PDO) removals
3266     // that we must wait for.
3267     //
3268     This->m_PendingChildCount = 1;
3269 
3270     //
3271     // Reset WMI state
3272     //
3273     // This->m_Device->m_PkgWmi->ResetStateForPdoRestart(); __REACTOS__
3274 
3275 
3276     This->m_Device->m_PkgIo->ResetStateForRestart();
3277 
3278     if (This->IsPowerPolicyOwner()) {
3279         This->m_PowerPolicyMachine.m_Owner->m_PowerIdleMachine.Reset();
3280     }
3281 
3282     //
3283     // Set STATUS_SUCCESS in the irp so that the stack will start smoothly after
3284     // we have powered up.
3285     //
3286     This->SetPendingPnpIrpStatus(STATUS_SUCCESS);
3287 
3288     //
3289     // This flag is set on the wake-enabled device powering down path,
3290     // but if the device power down failed it may not have been cleared.
3291     //
3292     This->m_WakeInterruptsKeepConnected = FALSE;
3293 
3294 
3295     //
3296     // This flag is cleared so we can reacquire the start time and state
3297     //
3298     This->m_AchievedStart = FALSE;
3299 
3300     return WdfDevStatePnpHardwareAvailable;
3301 }
3302 
3303 WDF_DEVICE_PNP_STATE
PnpEventRemovedChildrenRemoved(__inout FxPkgPnp * This)3304 FxPkgPnp::PnpEventRemovedChildrenRemoved(
3305     __inout FxPkgPnp* This
3306     )
3307 /*++
3308 
3309 Routine Description:
3310     All of this device's previously enumerated children have been removed.  Move
3311     the state machine into its next state based on the device's role.
3312 
3313 Arguments:
3314     This - instance of the state machine
3315 
3316 Return Value:
3317     new state
3318 
3319   --*/
3320 {
3321     return This->PnpGetPostRemoveState();
3322 }
3323 
3324 WDF_DEVICE_PNP_STATE
PnpEventFinal(__inout FxPkgPnp * This)3325 FxPkgPnp::PnpEventFinal(
3326     __inout FxPkgPnp* This
3327     )
3328 /*++
3329 
3330 Routine Description:
3331     The final resting and dead state for the FxDevice that has been removed.  We
3332     release our final reference here and destroy the object.
3333 
3334 Arguments:
3335     This - This instance of the state machine
3336 
3337 Return Value:
3338     WdfDevStatePnpNull
3339 
3340   --*/
3341 {
3342     NTSTATUS status;
3343 
3344     //
3345     // We may not have a pnp irp at this stage (esp for PDO which are in the
3346     // removed state and whose parent is being removed) so we use the function
3347     // pointer as the unique tag.
3348     //
3349     // IoReleaseRemoveLockAndWait requires an outstanding reference to release,
3350     // so acquire it before calling it if we are in the case where the PDO is
3351     // being removed with no outstanding PNP remove irp b/c the parent is being
3352     // removed.
3353     //
3354     if (This->m_DeviceRemoveProcessed == NULL) {
3355         status = Mx::MxAcquireRemoveLock(
3356             This->m_Device->GetRemoveLock(),
3357             (PVOID)&FxPkgPnp::PnpEventFinal);
3358 
3359         ASSERT(NT_SUCCESS(status));
3360         UNREFERENCED_PARAMETER(status);
3361     }
3362 
3363     //
3364     // Indicate to the parent device that we are removed now (vs the destructor
3365     // of the object where we would never reach because in the case of the PDO
3366     // being removed b/c the parent is going away, the parent has a reference
3367     // on the PDO).
3368 
3369 
3370 
3371 
3372 
3373 
3374 
3375     if (This->m_Device->m_ParentWaitingOnChild) {
3376         (This->m_Device->m_ParentDevice->m_PkgPnp)->ChildRemoved();
3377     }
3378 
3379 
3380     if (This->m_DeviceRemoveProcessed == NULL) {
3381         //
3382         // We can get into this state w/out an event to set when a PDO (this
3383         // device) is in the removed state and then the parent is removed.
3384         //
3385 
3386         //
3387         // After this is called, any irp dispatched to FxDevice::DispatchWithLock
3388         // will fail with STATUS_INVALID_DEVICE_REQUEST.
3389         //
3390         Mx::MxReleaseRemoveLockAndWait(
3391             This->m_Device->GetRemoveLock(),
3392             (PVOID)&FxPkgPnp::PnpEventFinal);
3393 
3394         //
3395         // Delete the object when we exit the state machine.  Dispose was run
3396         // early in a previous state.
3397         //
3398         This->m_PnpMachine.SetDelayedDeletion();
3399     }
3400     else {
3401         //
3402         // The thread which received the pnp remove irp will delete the device
3403         //
3404         This->m_SetDeviceRemoveProcessed = TRUE;
3405     }
3406 
3407     return WdfDevStatePnpNull;
3408 }
3409 
3410 _Must_inspect_result_
3411 NTSTATUS
PnpEnableInterfacesAndRegisterWmi(VOID)3412 FxPkgPnp::PnpEnableInterfacesAndRegisterWmi(
3413     VOID
3414     )
3415 /*++
3416 
3417 Routine Description:
3418     Enables all of the device interfaces and then registers wmi.
3419 
3420 Arguments:
3421     None
3422 
3423 Return Value:
3424     NT_SUCCESS if all goes well, !NT_SUCCESS otherwise
3425 
3426   --*/
3427 {
3428     PSINGLE_LIST_ENTRY ple;
3429     NTSTATUS status;
3430 
3431     status = STATUS_SUCCESS;
3432 
3433     //
3434     // Enable any device interfaces.
3435     //
3436     m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
3437 
3438     m_DeviceInterfacesCanBeEnabled = TRUE;
3439 
3440     for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
3441         FxDeviceInterface *pDeviceInterface;
3442 
3443         pDeviceInterface = FxDeviceInterface::_FromEntry(ple);
3444 
3445 
3446 
3447 
3448 
3449 
3450 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
3451         //
3452         // By this time, the device interface, no matter what the WDFDEVICE role
3453         // will have been registered.
3454         //
3455         ASSERT(pDeviceInterface->m_SymbolicLinkName.Buffer != NULL);
3456 #endif
3457         pDeviceInterface->SetState(TRUE);
3458 
3459         status = STATUS_SUCCESS;
3460     }
3461 
3462     m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
3463 
3464     if (NT_SUCCESS(status)) {
3465         status = m_Device->WmiPkgRegister();
3466     }
3467 
3468     if (!NT_SUCCESS(status)) {
3469         SetInternalFailure();
3470         SetPendingPnpIrpStatus(status);
3471     }
3472 
3473     return status;
3474 }
3475 
NT_SUCCESS(return)3476 __drv_when(!NT_SUCCESS(return), __drv_arg(ResourcesMatched, _Must_inspect_result_))
3477 NTSTATUS
3478 FxPkgPnp::PnpPrepareHardware(
3479     __inout PBOOLEAN ResourcesMatched
3480     )
3481 /*++
3482 
3483 Routine Description:
3484     Matches the PNP resources with the WDFINTERRUPT objects registered and then
3485     calls EvtDevicePrepareHardware.  All start paths call this function
3486 
3487 Arguments:
3488     ResourcesMatched - indicates to the caller what stage failed if !NT_SUCCESS
3489                         is returned
3490 
3491 Return Value:
3492     NT_SUCCESS if all goes well, !NT_SUCCESS if failure occurrs
3493 
3494   --*/
3495 {
3496     NTSTATUS status;
3497     *ResourcesMatched = FALSE;
3498 
3499     //
3500     // FxPnpStateRemoved:
3501     // Mark the device a not removed.  This is just so that anybody sending
3502     // a PnP IRP_MN_QUERY_DEVICE_STATE gets a reasonable answer.
3503     //
3504     // FxPnpStateFailed, FxPnpStateResourcesChanged:
3505     // Both of these values can be set to true and cause another start to
3506     // be sent down the stack.  Reset these values back to false.  If there is
3507     // a need to set these values, the driver can set them in
3508     // EvtDevicePrepareHardware.
3509     //
3510     m_PnpStateAndCaps.Value &= ~(FxPnpStateRemovedMask |
3511                                  FxPnpStateFailedMask |
3512                                  FxPnpStateResourcesChangedMask);
3513     m_PnpStateAndCaps.Value |= (FxPnpStateRemovedUseDefault |
3514                                 FxPnpStateFailedUseDefault |
3515                                 FxPnpStateResourcesChangedUseDefault);
3516 
3517     //
3518     // This will parse the resources and setup all the WDFINTERRUPT handles
3519     //
3520     status = PnpMatchResources();
3521 
3522     if (!NT_SUCCESS(status)) {
3523         *ResourcesMatched = FALSE;
3524         SetInternalFailure();
3525         SetPendingPnpIrpStatus(status);
3526         return status;
3527     }
3528 
3529 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
3530     //
3531     // Build register resource table
3532     //
3533     status = m_Resources->BuildRegisterResourceTable();
3534     if (!NT_SUCCESS(status)) {
3535         SetInternalFailure();
3536         SetPendingPnpIrpStatus(status);
3537         goto exit;
3538     }
3539 
3540     //
3541     // Build Port resource table
3542     //
3543     status = m_Resources->BuildPortResourceTable();
3544     if (!NT_SUCCESS(status)) {
3545         SetInternalFailure();
3546         SetPendingPnpIrpStatus(status);
3547         goto exit;
3548     }
3549 
3550     //
3551     // We keep track if the device has any connection resources,
3552     // in which case we allow unrestricted access to interrupts
3553     // regardless of the UmdfDirectHardwareAccess directive.
3554     //
3555     status = m_Resources->CheckForConnectionResources();
3556     if (!NT_SUCCESS(status)) {
3557         SetInternalFailure();
3558         SetPendingPnpIrpStatus(status);
3559         goto exit;
3560     }
3561 #endif
3562 
3563     *ResourcesMatched = TRUE;
3564 
3565     m_Device->SetCallbackFlags(
3566                         FXDEVICE_CALLBACK_IN_PREPARE_HARDWARE
3567                         );
3568 
3569     status = m_DevicePrepareHardware.Invoke(m_Device->GetHandle(),
3570                                             m_ResourcesRaw->GetHandle(),
3571                                             m_Resources->GetHandle());
3572 
3573     m_Device->ClearCallbackFlags(
3574                         FXDEVICE_CALLBACK_IN_PREPARE_HARDWARE
3575                         );
3576 
3577     if (!NT_SUCCESS(status)) {
3578         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3579                             "EvtDevicePrepareHardware failed %!STATUS!", status);
3580 
3581         if (status == STATUS_NOT_SUPPORTED) {
3582             DoTraceLevelMessage(
3583                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3584                 "EvtDevicePrepareHardware returned an invalid status "
3585                 "STATUS_NOT_SUPPORTED");
3586 
3587             if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
3588                 FxVerifierDbgBreakPoint(GetDriverGlobals());
3589             }
3590         }
3591 
3592         SetInternalFailure();
3593         SetPendingPnpIrpStatus(status);
3594         goto exit;
3595     }
3596 
3597     //
3598     // Now that we have assigned the resources to all the interrupts, figure out
3599     // the highest synch irql for each interrupt set which shares a spinlock.
3600     //
3601     PnpAssignInterruptsSyncIrql();
3602 
3603     //
3604     // Do mode-specific work. For KMDF, there is nothing additional to do.
3605     //
3606     status = PnpPrepareHardwareInternal();
3607     if (!NT_SUCCESS(status)) {
3608         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3609                             "PrepareHardware failed %!STATUS!", status);
3610 
3611         SetInternalFailure();
3612         SetPendingPnpIrpStatus(status);
3613     }
3614 
3615 exit:
3616     return status;
3617 }
3618 
3619 _Must_inspect_result_
3620 NTSTATUS
PnpReleaseHardware(VOID)3621 FxPkgPnp::PnpReleaseHardware(
3622     VOID
3623     )
3624 /*++
3625 
3626 Routine Description:
3627     Invokes the driver's release hardware callback if present.
3628     Releases any interrupt resources allocated during the prepare hardware callback.
3629 
3630 Arguments:
3631     None
3632 
3633 Return Value:
3634     Driver's release hardware callback return status or
3635     STATUS_SUCCESS if callback is not present.
3636 
3637   --*/
3638 {
3639     NTSTATUS        status;
3640     FxInterrupt*    interrupt;
3641     PLIST_ENTRY     le;
3642 
3643     //
3644     // Invoke the device's release hardware callback.
3645     //
3646     status = m_DeviceReleaseHardware.Invoke(
3647                         m_Device->GetHandle(),
3648                         m_Resources->GetHandle());
3649 
3650     if (status == STATUS_NOT_SUPPORTED) {
3651         DoTraceLevelMessage(
3652             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3653             "EvtDeviceReleaseHardware returned an invalid status "
3654             "STATUS_NOT_SUPPORTED");
3655 
3656         if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
3657             FxVerifierDbgBreakPoint(GetDriverGlobals());
3658         }
3659     }
3660 
3661 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
3662     if (NT_SUCCESS(status)) {
3663         //
3664         // make sure driver has unmapped its resources
3665         //
3666         m_Resources->ValidateResourceUnmap();
3667     }
3668 
3669     //
3670     // delete the register and port resource tables
3671     //
3672     m_Resources->DeleteRegisterResourceTable();
3673     m_Resources->DeletePortResourceTable();
3674 #endif
3675 
3676     //
3677     // Delete all interrupt objects that were created in the prepare hardware
3678     // callback that the driver did not explicitly delete (in reverse order).
3679     //
3680     le = m_InterruptListHead.Blink;
3681 
3682     while(le != &m_InterruptListHead) {
3683 
3684         // Find the interrupt object pointer.
3685         interrupt = CONTAINING_RECORD(le, FxInterrupt, m_PnpList);
3686 
3687         // Advance the entry before it becomes invalid.
3688         le = le->Blink;
3689 
3690         // Let the interrupt know that 'release hardware' was called.
3691         interrupt->OnPostReleaseHardware();
3692     }
3693 
3694     return status;
3695 }
3696 
3697 VOID
PnpPowerPolicyStart(VOID)3698 FxPkgPnp::PnpPowerPolicyStart(
3699     VOID
3700     )
3701 /*++
3702 
3703 Routine Description:
3704     Informs the power policy state machine that it should start.
3705 
3706 Arguments:
3707     None
3708 
3709 Return Value:
3710     None
3711 
3712   --*/
3713 {
3714     PowerPolicyProcessEvent(PwrPolStart);
3715 }
3716 
3717 VOID
PnpPowerPolicyStop(VOID)3718 FxPkgPnp::PnpPowerPolicyStop(
3719     VOID
3720     )
3721 /*++
3722 
3723 Routine Description:
3724     Informs the power and power policy state machines that they should stop.
3725 
3726 Arguments:
3727     None
3728 
3729 Return Value:
3730     None
3731 
3732   --*/
3733 {
3734     PowerPolicyProcessEvent(PwrPolStop);
3735 }
3736 
3737 VOID
PnpPowerPolicySurpriseRemove(VOID)3738 FxPkgPnp::PnpPowerPolicySurpriseRemove(
3739     VOID
3740     )
3741 /*++
3742 
3743 Routine Description:
3744     Informs the policy state machines that it should stop due to the hardware
3745     being surprise removed.
3746 
3747 Arguments:
3748     None
3749 
3750 Return Value:
3751     None
3752 
3753   --*/
3754 {
3755     PowerPolicyProcessEvent(PwrPolSurpriseRemove);
3756 }
3757 
3758 VOID
PnpPowerPolicyRemove(VOID)3759 FxPkgPnp::PnpPowerPolicyRemove(
3760     VOID
3761     )
3762 /*++
3763 
3764 Routine Description:
3765     Informs the policy state machine that it should prepare for device removal.
3766 
3767 Arguments:
3768     None
3769 
3770 Return Value:
3771     None
3772 
3773   --*/
3774 {
3775     PowerPolicyProcessEvent(PwrPolRemove);
3776 }
3777 
3778 VOID
PnpFinishProcessingIrp(__in BOOLEAN IrpMustBePresent)3779 FxPkgPnp::PnpFinishProcessingIrp(
3780     __in BOOLEAN IrpMustBePresent
3781     )
3782 /*++
3783 
3784 Routine Description:
3785     Finishes handling a pended pnp irp
3786 
3787 Arguments:
3788     None
3789 
3790 Return Value:
3791     None
3792 
3793   --*/
3794 {
3795     FxIrp irp;
3796 
3797     UNREFERENCED_PARAMETER(IrpMustBePresent);
3798     ASSERT(IrpMustBePresent == FALSE || IsPresentPendingPnpIrp());
3799 
3800     //
3801     // Start device is the only request we handle on the way back up the stack.
3802     // Also, if we fail any pnp irps that we are allowed to fail, we just
3803     // complete them.
3804     //
3805     if (IsPresentPendingPnpIrp()) {
3806 
3807         irp.SetIrp(GetPendingPnpIrp());
3808         if (irp.GetMinorFunction() == IRP_MN_START_DEVICE
3809             ||
3810             !NT_SUCCESS(irp.GetStatus())) {
3811 
3812             irp.SetIrp(ClearPendingPnpIrp());
3813             CompletePnpRequest(&irp, irp.GetStatus());
3814         }
3815         else {
3816             m_PnpMachine.m_FireAndForget = TRUE;
3817         }
3818     }
3819 }
3820 
3821 VOID
PnpDisableInterfaces(VOID)3822 FxPkgPnp::PnpDisableInterfaces(
3823     VOID
3824     )
3825 /*++
3826 
3827 Routine Description:
3828     Disables all of the registerd interfaces on the device.
3829 
3830 Arguments:
3831     None
3832 
3833 Return Value:
3834     None
3835 
3836   --*/
3837 {
3838     PSINGLE_LIST_ENTRY ple;
3839 
3840     m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
3841 
3842     m_DeviceInterfacesCanBeEnabled = FALSE;
3843 
3844     for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
3845 
3846         FxDeviceInterface *pDeviceInterface;
3847         pDeviceInterface = FxDeviceInterface::_FromEntry(ple);
3848         pDeviceInterface->SetState(FALSE);
3849     }
3850 
3851     m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
3852 }
3853 
3854 VOID
PnpEventSurpriseRemovePendingOverload(VOID)3855 FxPkgPnp::PnpEventSurpriseRemovePendingOverload(
3856     VOID
3857     )
3858 {
3859 
3860     //
3861     // Mark all of the children as missing because the parent has just been
3862     // removed.   Note that this will happen after all of the children have
3863     // already received the surprise remove event.  This is OK because the
3864     // reported status is inspected during the remove device event which will
3865     // happen after the parent finishes processing the surprise event.
3866     //
3867     if (m_EnumInfo != NULL) {
3868         m_EnumInfo->m_ChildListList.LockForEnum(GetDriverGlobals());
3869         FxTransactionedEntry* ple;
3870 
3871         ple = NULL;
3872         while ((ple = m_EnumInfo->m_ChildListList.GetNextEntry(ple)) != NULL) {
3873             FxChildList::_FromEntry(ple)->NotifyDeviceSurpriseRemove();
3874         }
3875 
3876         m_EnumInfo->m_ChildListList.UnlockFromEnum(GetDriverGlobals());
3877     }
3878 }
3879 
3880 _Must_inspect_result_
3881 NTSTATUS
PnpMatchResources(VOID)3882 FxPkgPnp::PnpMatchResources(
3883     VOID
3884     )
3885 /*++
3886 
3887 Routine Description:
3888 
3889     This method is called in response to a PnP StartDevice IRP
3890     coming up the stack.  It:
3891 
3892     - Captures the device's resources
3893     - Calls out to interested resource objects
3894     - Sends an event to the PnP state machine
3895 
3896 Arguemnts:
3897 
3898     Irp - a pointer to the FxIrp
3899 
3900 Returns:
3901 
3902     NTSTATUS
3903 
3904 --*/
3905 {
3906     PCM_RESOURCE_LIST pResourcesRaw;
3907     PCM_RESOURCE_LIST pResourcesTranslated;
3908     FxResourceCm* resCmRaw;
3909     FxResourceCm* resCmTrans;
3910     FxInterrupt*  interrupt;
3911     PLIST_ENTRY ple;
3912     NTSTATUS status;
3913     FxCollectionEntry *curRaw, *curTrans, *endTrans;
3914     ULONG messageCount;
3915     FxIrp irp;
3916 
3917     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
3918                         "Entering PnpMatchResources");
3919 
3920     //
3921     // We must clear these flags before calling into the event handler because
3922     // it might set these states back (which is OK).  If we don't clear these
3923     // states and the start succeeds, we would endlessly report that our
3924     // resources have changed and be restarted over and over.
3925     //
3926     m_PnpStateAndCaps.Value &= ~(FxPnpStateFailedMask |
3927                                  FxPnpStateResourcesChangedMask);
3928     m_PnpStateAndCaps.Value |= (FxPnpStateFailedUseDefault |
3929                                 FxPnpStateResourcesChangedUseDefault);
3930 
3931     irp.SetIrp(m_PendingPnPIrp);
3932     pResourcesRaw = irp.GetParameterAllocatedResources();
3933     pResourcesTranslated = irp.GetParameterAllocatedResourcesTranslated();
3934 
3935     status = m_ResourcesRaw->BuildFromWdmList(pResourcesRaw, FxResourceNoAccess);
3936     if (!NT_SUCCESS(status)) {
3937         DoTraceLevelMessage(
3938             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3939             "Could not allocate raw resource list for WDFDEVICE 0x%p, %!STATUS!",
3940             m_Device->GetHandle(), status);
3941         goto Done;
3942     }
3943 
3944     status = m_Resources->BuildFromWdmList(pResourcesTranslated, FxResourceNoAccess);
3945     if (!NT_SUCCESS(status)) {
3946         DoTraceLevelMessage(
3947             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
3948             "Could not allocate translated resource list for WDFDEVICE 0x%p, %!STATUS!",
3949             m_Device->GetHandle(), status);
3950         goto Done;
3951     }
3952 
3953     //
3954     // reset the stored information in all interrupts in the rebalance case
3955     //
3956     for (ple = m_InterruptListHead.Flink;
3957          ple != &m_InterruptListHead;
3958          ple = ple->Flink) {
3959         interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
3960         interrupt->Reset();
3961     }
3962 
3963     //
3964     // Now iterate across the resources, looking for ones that correspond
3965     // to objects that we are managing.  Tell those objects about the resources
3966     // that were assigned.
3967     //
3968     ple = &m_InterruptListHead;
3969 
3970     endTrans = m_Resources->End();
3971 
3972     for (curTrans = m_Resources->Start(), curRaw = m_ResourcesRaw->Start();
3973          curTrans != endTrans;
3974          curTrans = curTrans->Next(), curRaw = curRaw->Next()) {
3975 
3976         ASSERT(curTrans->m_Object->GetType() == FX_TYPE_RESOURCE_CM);
3977         ASSERT(curRaw->m_Object->GetType() == FX_TYPE_RESOURCE_CM);
3978 
3979         resCmRaw = (FxResourceCm*) curRaw->m_Object;
3980 
3981         if (resCmRaw->m_Descriptor.Type == CmResourceTypeInterrupt) {
3982             //
3983             // We're looking at an interrupt resource.
3984             //
3985             if (ple->Flink == &m_InterruptListHead) {
3986                 //
3987                 // Oops, there are no more interrupt objects.
3988                 //
3989                 DoTraceLevelMessage(
3990                     GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP,
3991                     "Not enough interrupt objects created by WDFDEVICE 0x%p",
3992                     m_Device->GetHandle());
3993                 break;
3994             }
3995 
3996             resCmTrans = (FxResourceCm*) curTrans->m_Object;
3997             ASSERT(resCmTrans->m_Descriptor.Type == CmResourceTypeInterrupt);
3998 
3999             messageCount = resCmRaw->m_Descriptor.u.MessageInterrupt.Raw.MessageCount;
4000 
4001             if (FxInterrupt::_IsMessageInterrupt(resCmTrans->m_Descriptor.Flags)
4002                 &&
4003                 (messageCount > 1)) {
4004                 ULONG i;
4005                 //
4006                 // Multi-message MSI 2.2 needs to be handled differently
4007                 //
4008                 for (i = 0, ple = ple->Flink;
4009                      i < messageCount && ple != &m_InterruptListHead;
4010                      i++, ple = ple->Flink) {
4011 
4012                     //
4013                     // Get the next interrupt object.
4014                     //
4015                     interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
4016 
4017                     //
4018                     // Tell the interrupt object what its resources are.
4019                     //
4020                     interrupt->AssignResources(&resCmRaw->m_Descriptor,
4021                                                &resCmTrans->m_Descriptor);
4022                 }
4023             }
4024             else {
4025                 //
4026                 // This is either MSI2.2 with 1 message, MSI-X or Line based.
4027                 //
4028                 ple = ple->Flink;
4029                 interrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
4030 
4031                 //
4032                 // Tell the interrupt object what its resources are.
4033                 //
4034                 interrupt->AssignResources(&resCmRaw->m_Descriptor,
4035                                            &resCmTrans->m_Descriptor);
4036             }
4037         }
4038     }
4039 
4040 #if FX_IS_KERNEL_MODE
4041 
4042 
4043 
4044 
4045 
4046 
4047 
4048 
4049 
4050     //
4051     // If there are any pended I/Os that were sent to the target
4052     // that were pended in the transition to stop, then this will
4053     // resend them.
4054     //
4055     // ISSUE:  This has the potential of I/O completing
4056     // before the driver's start callback has been called...but,
4057     // this is the same as the PDO pending a sent irp and completing
4058     // it when the PDO is restarted before the FDO has a change to
4059     // process the start irp which was still pended below.
4060     //
4061     if (m_Device->IsFilter()) {
4062         //
4063         // If this is a filter device, then copy the FILE_REMOVABLE_MEDIA
4064         // characteristic from the lower device.
4065         //
4066         if (m_Device->GetAttachedDevice()->Characteristics & FILE_REMOVABLE_MEDIA) {
4067             ULONG characteristics;
4068 
4069             characteristics =
4070                 m_Device->GetDeviceObject()->Characteristics | FILE_REMOVABLE_MEDIA;
4071 
4072             m_Device->GetDeviceObject()->Characteristics = characteristics;
4073         }
4074 
4075 
4076 
4077 
4078 
4079 
4080 
4081 
4082 
4083 
4084         m_Device->SetFilterIoType();
4085     }
4086 
4087 #endif // FX_IS_KERNEL_MODE
4088 
4089 Done:
4090     DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
4091                         "Exiting PnpMatchResources %!STATUS!", status);
4092 
4093     return status;
4094 }
4095 
4096 VOID
PnpAssignInterruptsSyncIrql(VOID)4097 FxPkgPnp::PnpAssignInterruptsSyncIrql(
4098     VOID
4099     )
4100 /*++
4101 
4102 Routine Description:
4103     Figure out the highest synch irql for each interrupt set which shares a
4104     spinlock.
4105 
4106     foreach(interrupt assigned to this instance)
4107         determine the max sync irql in the set
4108         set all the associated interrupts to the sync irql
4109         set the sync irql on the first interrupt in the set
4110 
4111 Arguments:
4112     None
4113 
4114 Return Value:
4115     None
4116 
4117   --*/
4118 {
4119     PLIST_ENTRY ple;
4120     FxInterrupt* pInterrupt;
4121 
4122     for (ple = m_InterruptListHead.Flink;
4123          ple != &m_InterruptListHead;
4124          ple = ple->Flink) {
4125 
4126         KIRQL syncIrql;
4127 
4128         pInterrupt = CONTAINING_RECORD(ple, FxInterrupt, m_PnpList);
4129 
4130         syncIrql = pInterrupt->GetResourceIrql();
4131 
4132         if (syncIrql == PASSIVE_LEVEL) {
4133             //
4134             // The irql associated with the resources assigned is passive,
4135             // this can happen in the following scenarios:
4136             //
4137             // (1)  no resources were assigned.  Skip this interrupt b/c it has
4138             //      no associated resources. Note: setting the SynchronizeIrql
4139             //      to PASSIVE_LEVEL is a no-op.
4140             //
4141             // (2) this interrupt is handled at passive-level.
4142             //      Set SynchronizeIrql to passive-level and continue.
4143             //
4144             pInterrupt->SetSyncIrql(PASSIVE_LEVEL);
4145             continue;
4146         }
4147 
4148         if (pInterrupt->IsSharedSpinLock() == FALSE) {
4149             //
4150             // If the interrupt spinlock is not shared, it's sync irql is the
4151             // irql assigned to it in the resources.
4152             //
4153             pInterrupt->SetSyncIrql(syncIrql);
4154         }
4155         else if (pInterrupt->IsSyncIrqlSet() == FALSE) {
4156             FxInterrupt* pFwdInterrupt;
4157             PLIST_ENTRY pleFwd;
4158 
4159             //
4160             // Find all of the other interrupts which share the lock and compute
4161             // the max sync irql.
4162             //
4163             for (pleFwd = ple->Flink;
4164                  pleFwd != &m_InterruptListHead;
4165                  pleFwd = pleFwd->Flink) {
4166 
4167                 pFwdInterrupt = CONTAINING_RECORD(pleFwd, FxInterrupt, m_PnpList);
4168 
4169                 //
4170                 // If the 2 do not share the same lock, they are not in the same
4171                 // set.
4172                 //
4173                 if (pFwdInterrupt->SharesLock(pInterrupt) == FALSE) {
4174                     continue;
4175                 }
4176 
4177                 if (pFwdInterrupt->GetResourceIrql() > syncIrql) {
4178                     syncIrql = pFwdInterrupt->GetResourceIrql();
4179                 }
4180             }
4181 
4182             //
4183             // Now that we found the max sync irql, set it for all interrupts in
4184             // the set which share the lock
4185             //
4186             for (pleFwd = ple->Flink;
4187                  pleFwd != &m_InterruptListHead;
4188                  pleFwd = pleFwd->Flink) {
4189 
4190                 pFwdInterrupt = CONTAINING_RECORD(pleFwd, FxInterrupt, m_PnpList);
4191 
4192                 //
4193                 // If the 2 do not share the same lock, they are not in the same
4194                 // set.
4195                 //
4196                 if (pFwdInterrupt->SharesLock(pInterrupt) == FALSE) {
4197                     continue;
4198                 }
4199 
4200                 pFwdInterrupt->SetSyncIrql(syncIrql);
4201             }
4202 
4203             //
4204             // Set the sync irql for the first interrupt in the set.  We have set
4205             // the sync irql for all other interrupts in the set.
4206             //
4207             pInterrupt->SetSyncIrql(syncIrql);
4208         }
4209         else {
4210             //
4211             // If IsSyncIrqlSet is TRUE, we already covered this interrupt in a
4212             // previous pass of this loop when we computed the max sync irql for
4213             // an interrupt set.
4214             //
4215             ASSERT(pInterrupt->GetSyncIrql() > PASSIVE_LEVEL);
4216             DO_NOTHING();
4217         }
4218     }
4219 }
4220 
4221 _Must_inspect_result_
4222 NTSTATUS
ValidateCmResource(__inout PCM_PARTIAL_RESOURCE_DESCRIPTOR * CmResourceRaw,__inout PCM_PARTIAL_RESOURCE_DESCRIPTOR * CmResource)4223 FxPkgPnp::ValidateCmResource(
4224     __inout PCM_PARTIAL_RESOURCE_DESCRIPTOR* CmResourceRaw,
4225     __inout PCM_PARTIAL_RESOURCE_DESCRIPTOR* CmResource
4226     )
4227 /*++
4228 
4229 Routine Description:
4230     Makes sure the specified resource is valid.
4231 
4232 Arguments:
4233     CmResourceRaw - the raw resource to validate.
4234     CmResource - the translated resources to validate.
4235 
4236 Return Value:
4237     STATUS_SUCCESS if resource is valid or
4238     NTSTATUS error.
4239 
4240   --*/
4241 {
4242     NTSTATUS            status;
4243     FxCollectionEntry*  cur;
4244     FxCollectionEntry*  curRaw;
4245     FxResourceCm*       res;
4246     FxResourceCm*       resRaw;
4247     PFX_DRIVER_GLOBALS  fxDriverGlobals;
4248 
4249     ASSERT(m_ResourcesRaw != NULL);
4250     ASSERT(m_Resources != NULL);
4251 
4252     res = NULL;
4253     resRaw = NULL;
4254 
4255     fxDriverGlobals = GetDriverGlobals();
4256 
4257     //
4258     // Find the resource in our list.
4259     //
4260     for (cur = m_Resources->Start(), curRaw = m_ResourcesRaw->Start();
4261          cur != m_Resources->End();
4262          cur = cur->Next(), curRaw = curRaw->Next()) {
4263 
4264         res = (FxResourceCm*) cur->m_Object;
4265         resRaw = (FxResourceCm*) curRaw->m_Object;
4266 
4267         if (&res->m_DescriptorClone == *CmResource) {
4268             break;
4269         }
4270     }
4271 
4272     //
4273     // Error out if not found.
4274     //
4275     if (cur == m_Resources->End()) {
4276         status = STATUS_INVALID_PARAMETER;
4277         DoTraceLevelMessage(
4278             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4279             "The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
4280             "WDFDEVICE 0x%p, %!STATUS!",
4281             *CmResource, m_Device->GetHandle(), status);
4282         FxVerifierDbgBreakPoint(fxDriverGlobals);
4283         goto Done;
4284     }
4285 
4286     //
4287     // Error out if the associated raw resource is not the same.
4288     //
4289     if (&resRaw->m_DescriptorClone != *CmResourceRaw) {
4290         status = STATUS_INVALID_PARAMETER;
4291         DoTraceLevelMessage(
4292             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4293             "The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
4294             "WDFDEVICE 0x%p, %!STATUS!",
4295             *CmResourceRaw, m_Device->GetHandle(), status);
4296         FxVerifierDbgBreakPoint(fxDriverGlobals);
4297         goto Done;
4298     }
4299 
4300     //
4301     // Make sure driver didn't change any of the PnP settings.
4302     //
4303     if (sizeof(res->m_Descriptor) !=
4304             RtlCompareMemory(&res->m_DescriptorClone,
4305                              &res->m_Descriptor,
4306                              sizeof(res->m_Descriptor))) {
4307         status = STATUS_INVALID_PARAMETER;
4308         DoTraceLevelMessage(
4309             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4310             "The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
4311             "driver cannot change the assigned PnP resources, WDFDEVICE 0x%p, "
4312             "%!STATUS!",
4313             *CmResource, m_Device->GetHandle(), status);
4314         FxVerifierDbgBreakPoint(fxDriverGlobals);
4315         goto Done;
4316     }
4317 
4318     if (sizeof(resRaw->m_Descriptor) !=
4319             RtlCompareMemory(&resRaw->m_DescriptorClone,
4320                              &resRaw->m_Descriptor,
4321                              sizeof(resRaw->m_Descriptor))) {
4322         status = STATUS_INVALID_PARAMETER;
4323         DoTraceLevelMessage(
4324             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4325             "The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not valid, "
4326             "driver cannot change the assigned PnP resources, WDFDEVICE 0x%p, "
4327             "%!STATUS!",
4328             *CmResourceRaw, m_Device->GetHandle(), status);
4329         FxVerifierDbgBreakPoint(fxDriverGlobals);
4330         goto Done;
4331     }
4332 
4333     //
4334     // Return the real descriptor.
4335     //
4336     ASSERT(res != NULL && resRaw != NULL);
4337     *CmResource = &res->m_Descriptor;
4338     *CmResourceRaw = &resRaw->m_Descriptor;
4339 
4340     status = STATUS_SUCCESS;
4341 
4342 Done:
4343     return status;
4344 }
4345 
4346 _Must_inspect_result_
4347 NTSTATUS
ValidateInterruptResourceCm(__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResourceRaw,__in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResource,__in PWDF_INTERRUPT_CONFIG Configuration)4348 FxPkgPnp::ValidateInterruptResourceCm(
4349     __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResourceRaw,
4350     __in PCM_PARTIAL_RESOURCE_DESCRIPTOR CmIntResource,
4351     __in PWDF_INTERRUPT_CONFIG Configuration
4352     )
4353 /*++
4354 
4355 Routine Description:
4356     Makes sure the specified resource is valid for an interrupt resource.
4357 
4358 Arguments:
4359     CmIntResourceRaw - the raw interrupt resource to validate.
4360     CmIntResource - the translated interrupt resource to validate.
4361 
4362 Return Value:
4363     STATUS_SUCCESS if resource is valid or
4364     NTSTATUS error.
4365 
4366   --*/
4367 {
4368     NTSTATUS            status;
4369     PLIST_ENTRY         le;
4370     FxInterrupt*        interrupt;
4371     ULONG               messageCount;
4372     PFX_DRIVER_GLOBALS  fxDriverGlobals;
4373     PCM_PARTIAL_RESOURCE_DESCRIPTOR cmIntResourceRaw;
4374     PCM_PARTIAL_RESOURCE_DESCRIPTOR cmIntResource;
4375 
4376     cmIntResourceRaw = CmIntResourceRaw;
4377     cmIntResource = CmIntResource;
4378     fxDriverGlobals = GetDriverGlobals();
4379 
4380     //
4381     // Get the real descriptor not the copy.
4382     //
4383     status = ValidateCmResource(&cmIntResourceRaw, &cmIntResource);
4384     if (!NT_SUCCESS(status)) {
4385         goto Done;
4386     }
4387 
4388     //
4389     // Make sure this is an interrupt resource.
4390     //
4391     if (cmIntResourceRaw->Type != CmResourceTypeInterrupt) {
4392         status = STATUS_INVALID_PARAMETER;
4393         DoTraceLevelMessage(
4394             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4395             "The raw PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not an "
4396             "interrupt resource, WDFDEVICE 0x%p, %!STATUS!",
4397             CmIntResourceRaw, m_Device->GetHandle(), status);
4398         FxVerifierDbgBreakPoint(fxDriverGlobals);
4399         goto Done;
4400     }
4401 
4402     if (cmIntResource->Type != CmResourceTypeInterrupt) {
4403         status = STATUS_INVALID_PARAMETER;
4404         DoTraceLevelMessage(
4405             fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4406             "The translated PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p is not an "
4407             "interrupt resource, WDFDEVICE 0x%p, %!STATUS!",
4408             CmIntResource, m_Device->GetHandle(), status);
4409         FxVerifierDbgBreakPoint(fxDriverGlobals);
4410         goto Done;
4411     }
4412 
4413     //
4414     // Make sure resource was not claimed by another interrupt.
4415     // Multi-message MSI 2.2 interrupts: allowed to reuse claimed resources.
4416     //     Driver must create them sequentially.
4417     // Line-based interrupts: allowed to reuse claimed resources.
4418     //     Driver can create them out-of-order.
4419     // Other MSI: not allowed to reuse claimed resources.
4420     //
4421     messageCount = 0;
4422 
4423     for (le = m_InterruptListHead.Flink;
4424          le != &m_InterruptListHead;
4425          le = le->Flink) {
4426 
4427         interrupt = CONTAINING_RECORD(le, FxInterrupt, m_PnpList);
4428 
4429         if (cmIntResource != interrupt->GetResources()) {
4430             //
4431             // Multi-message MSI 2.2 interrupts must be sequential.
4432             //
4433             if (messageCount != 0) {
4434                 status = STATUS_INVALID_PARAMETER;
4435                 DoTraceLevelMessage(
4436                     fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4437                     "Multi-message MSI 2.2 interrupts must be created "
4438                     "sequentially, WDFDEVICE 0x%p, %!STATUS!",
4439                     m_Device->GetHandle(), status);
4440                 FxVerifierDbgBreakPoint(fxDriverGlobals);
4441                 goto Done;
4442             }
4443 
4444             continue;
4445         }
4446 
4447         if (interrupt->IsWakeCapable() &&
4448             Configuration->PassiveHandling) {
4449             DoTraceLevelMessage(
4450                 fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
4451                 "The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
4452                 "to create a wakable interrupt 0x%p, WDFDEVICE 0x%p and "
4453                 "any functional interrupt being shared with wakable interrupt "
4454                 "can not use passive level handling",
4455                 CmIntResource, interrupt->GetHandle(),
4456                 m_Device->GetHandle());
4457             status = STATUS_INVALID_PARAMETER;
4458             goto Done;
4459         }
4460 
4461         if (interrupt->IsPassiveHandling() &&
4462             Configuration->CanWakeDevice) {
4463             DoTraceLevelMessage(
4464                 fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
4465                 "The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
4466                 "to create a passive level interrupt 0x%p, WDFDEVICE 0x%p and "
4467                 "is now being used to create a wakable interrupt. A functional "
4468                 "passive level interrupt can not be shared with wakable interrupt",
4469                 CmIntResource, interrupt->GetHandle(),
4470                 m_Device->GetHandle());
4471             status = STATUS_INVALID_PARAMETER;
4472             goto Done;
4473         }
4474 
4475         //
4476         // Check for multi-message MSI 2.2 interrupts. These are allowed
4477         // to use the same resource.
4478         // We allow line based interrupts to reuse claimed resources.
4479         //
4480         if (FxInterrupt::_IsMessageInterrupt(cmIntResource->Flags) == FALSE) {
4481             DoTraceLevelMessage(
4482                 fxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
4483                 "The PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p was already used "
4484                 "to create interrupt 0x%p, WDFDEVICE 0x%p",
4485                 CmIntResource, interrupt->GetHandle(),
4486                 m_Device->GetHandle());
4487             continue;
4488         }
4489 
4490         //
4491         // Only allow the correct # of messages.
4492         //
4493         messageCount++;
4494         if (messageCount >
4495             cmIntResourceRaw->u.MessageInterrupt.Raw.MessageCount) {
4496 
4497             status = STATUS_INVALID_PARAMETER;
4498             DoTraceLevelMessage(
4499                 fxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
4500                 "All the MSI 2.2 interrupts for "
4501                 "PCM_PARTIAL_RESOURCE_DESCRIPTOR 0x%p are already created, "
4502                 "WDFDEVICE 0x%p, %!STATUS!",
4503                 CmIntResource, m_Device->GetHandle(), status);
4504             FxVerifierDbgBreakPoint(fxDriverGlobals);
4505             goto Done;
4506         }
4507     }
4508 
4509     status = STATUS_SUCCESS;
4510 
4511 Done:
4512     return status;
4513 }
4514 
4515 #define RESTART_START_ACHIEVED_NAME L"StartAchieved"
4516 #define RESTART_START_TIME_NAME     L"StartTime"
4517 #define RESTART_COUNT_NAME          L"Count"
4518 
4519 const PWCHAR FxPkgPnp::m_RestartStartAchievedName = RESTART_START_ACHIEVED_NAME;
4520 const PWCHAR FxPkgPnp::m_RestartStartTimeName = RESTART_START_TIME_NAME;
4521 const PWCHAR FxPkgPnp::m_RestartCountName = RESTART_COUNT_NAME;
4522 
4523 const ULONG FxPkgPnp::m_RestartTimePeriodMaximum = 60;
4524 const ULONG FxPkgPnp::m_RestartCountMaximum = 5;
4525 
4526 BOOLEAN
PnpIncrementRestartCountLogic(_In_ HANDLE RestartKey,_In_ BOOLEAN CreatedNewKey)4527 FxPkgPnp::PnpIncrementRestartCountLogic(
4528     _In_ HANDLE RestartKey,
4529     _In_ BOOLEAN CreatedNewKey
4530     )
4531 /*++
4532 
4533 Routine Description:
4534     This routine determines if this device should ask the bus driver to
4535     reenumerate the device.   This is determined by how many times the entire
4536     stack has asked for a restart within a given period.  This is stack wide
4537     because the settings are stored in a key in the device node itself (which all
4538     devices share).
4539 
4540     The period and number of times a restart are attempted are defined as constants
4541     (m_RestartTimePeriodMaximum, m_RestartCountMaximum)in this class.   They are
4542     current defined as a period of 60 seconds and a restart max count of 5.
4543 
4544     The settings are stored in a volatile key so that they do not persist across
4545     machine reboots.  Persisting across reboots makes no sense if we restrict the
4546     number of restarts w/in a period.
4547 
4548     The rules are as follows
4549     1)  if the key does not exist, treat this as the beginning of the period
4550         and ask for a reenumeration
4551     2)  if the key exists
4552         a)  if the beginning of the period and the restart count cannot be read
4553             do not ask for a reenumeration
4554         b)  if the beginning of the period is after the current time, either the
4555             current tick count has wrapped or the key has somehow survived a
4556             reboot.  Either way, treat this as a reset of the period and ask
4557             for a reenumeration
4558         c)  if the current time is after the period start time and within the
4559             restart period, increment the restart count.  if the count is <=
4560             the max restart count, ask for a reenumeration.  If it exceeds the
4561             max, do not ask for a reenumeration.
4562         d)  if the current time is after the period stat time and exceeds the
4563             maximum period, and if the device as reached the started state,
4564             reset the period, count, and started state, then ask for a
4565             reenumeration.
4566 
4567 Considerations:
4568     There is a reenumeration loop that a device can get caught in. If a device
4569     takes more than m_RestartTimePeriodMaximum to fail m_RestartCountMaximum
4570     times then the device will be caught in this loop. If it is failing on the
4571     way to PnpEventStarted then the device will likely cause a 9F bugcheck.
4572     This is because they hold a power lock while in this loop. If the device
4573     fails after PnpEventStarted then pnp can progress and the device can loop
4574     here indefinitely. We have shipped with this behavior for several releases,
4575     so we are hesitant to completely change this behavior. The concern is that
4576     a device out there relies on this behavior.
4577 
4578 Arguments:
4579     RestartKey - opened handle to the Restart registry key
4580     CreatedNewKey - TRUE if the Restart key was created just now
4581 
4582 Return Value:
4583     TRUE if a restart should be requested.
4584 
4585 --*/
4586 {
4587     NTSTATUS status = STATUS_SUCCESS;
4588     ULONG count;
4589     LARGE_INTEGER currentTickCount, startTickCount;
4590     BOOLEAN started, writeTick, writeCount, writeStarted;
4591 
4592     DECLARE_CONST_UNICODE_STRING(valueNameStartTime, RESTART_START_TIME_NAME);
4593     DECLARE_CONST_UNICODE_STRING(valueNameCount, RESTART_COUNT_NAME);
4594     DECLARE_CONST_UNICODE_STRING(valueNameStartAchieved, RESTART_START_ACHIEVED_NAME);
4595 
4596     count = 0;
4597     started = FALSE;
4598     writeTick = FALSE;
4599     writeCount = FALSE;
4600     writeStarted = FALSE;
4601 
4602     Mx::MxQueryTickCount(&currentTickCount);
4603 
4604     started = m_AchievedStart;
4605     if (started) {
4606         //
4607         // Save the fact the driver started without failing
4608         //
4609         writeStarted = TRUE;
4610     }
4611 
4612 
4613     //
4614     // If the key was created right now, there is nothing to check, just write out
4615     // the data.
4616     //
4617     if (CreatedNewKey) {
4618         writeTick = TRUE;
4619         writeCount = TRUE;
4620 
4621         //
4622         // First restart
4623         //
4624         count = 1;
4625     }
4626     else {
4627         ULONG length, type;
4628 
4629         //
4630         // First try to get the start time of when we first attempted a restart
4631         //
4632         status = FxRegKey::_QueryValue(GetDriverGlobals(),
4633                                        RestartKey,
4634                                        &valueNameStartTime,
4635                                        sizeof(startTickCount.QuadPart),
4636                                        &startTickCount.QuadPart,
4637                                        &length,
4638                                        &type);
4639 
4640         if (NT_SUCCESS(status) &&
4641             length == sizeof(startTickCount.QuadPart) && type == REG_BINARY) {
4642 
4643             //
4644             // Now try to get the last restart count
4645             //
4646             status = FxRegKey::_QueryULong(RestartKey,
4647                                            &valueNameCount,
4648                                            &count);
4649 
4650             if (status == STATUS_OBJECT_NAME_NOT_FOUND) {
4651                 //
4652                 // We read the start time, but not the count.  Assume there was
4653                 // at least one previous restart.
4654                 //
4655                 count = 1;
4656                 status = STATUS_SUCCESS;
4657             }
4658         }
4659 
4660         if (NT_SUCCESS(status)) {
4661             if (currentTickCount.QuadPart < startTickCount.QuadPart) {
4662                 //
4663                 // Somehow the key survived a reboot or the clock overflowed
4664                 // and the current time is less then the last time we started
4665                 // timing restarts.  Either way, just treat this as the first
4666                 // time we are restarting.
4667                 //
4668                 writeTick = TRUE;
4669                 writeCount = TRUE;
4670                 count = 1;
4671             }
4672             else {
4673                 LONGLONG delta;
4674 
4675                 //
4676                 // Compute the difference in time in 100 ns units
4677                 //
4678                 delta = (currentTickCount.QuadPart - startTickCount.QuadPart) *
4679                          Mx::MxQueryTimeIncrement();
4680 
4681                 if (delta <= WDF_ABS_TIMEOUT_IN_SEC(m_RestartTimePeriodMaximum)) {
4682                     //
4683                     // We are within the time limit, see if we are within the
4684                     // count limit
4685                     count++;
4686 
4687                     //
4688                     // The count starts at one, so include the maximum in the
4689                     // compare.
4690                     //
4691                     if (count <= m_RestartCountMaximum) {
4692                         writeCount = TRUE;
4693                     }
4694                     else {
4695                         //
4696                         // Exceeded the restart count, do not attempt to restart
4697                         // the device.
4698                         //
4699                         status = STATUS_UNSUCCESSFUL;
4700                     }
4701                 }
4702                 else {
4703                     if (started == FALSE) {
4704                         ULONG length, type, value;
4705                         status = FxRegKey::_QueryValue(GetDriverGlobals(),
4706                                                        RestartKey,
4707                                                        &valueNameStartAchieved,
4708                                                        sizeof(value),
4709                                                        &value,
4710                                                        &length,
4711                                                        &type);
4712                         if (!NT_SUCCESS(status) || length != sizeof(value) ||
4713                             type != REG_DWORD) {
4714                             value = 0;
4715                         }
4716                         started = value != 0;
4717                         status = STATUS_SUCCESS;
4718                     }
4719 
4720                     if (started) {
4721                         //
4722                         // Exceeded the time limit.  This is treated as a reset of
4723                         // the time limit, so we will try to restart and reset the
4724                         // start time and restart count.
4725                         //
4726                         writeTick = TRUE;
4727                         writeCount = TRUE;
4728                         count = 1;
4729 
4730                         //
4731                         // Erase the fact the driver once started and
4732                         // make it do it again to get another 5 attempts to
4733                         // restart.
4734                         //
4735                         writeStarted = TRUE;
4736                         started = FALSE;
4737                     }
4738                     else {
4739                         //
4740                         // Device never started
4741                         //
4742                         status = STATUS_UNSUCCESSFUL;
4743                     }
4744                 }
4745             }
4746         }
4747     }
4748 
4749     if (writeTick) {
4750         //
4751         // Write out the time and the count
4752         //
4753         NTSTATUS status2;
4754         status2 = FxRegKey::_SetValue(RestartKey,
4755                                      (PUNICODE_STRING)&valueNameStartTime,
4756                                      REG_BINARY,
4757                                      &currentTickCount.QuadPart,
4758                                      sizeof(currentTickCount.QuadPart));
4759         //
4760         // Don't let status report success if it was an error prior to _SetValue
4761         //
4762         if(NT_SUCCESS(status)) {
4763             status = status2;
4764         }
4765     }
4766 
4767     if (NT_SUCCESS(status) && writeCount) {
4768         status = FxRegKey::_SetValue(RestartKey,
4769                                      (PUNICODE_STRING)&valueNameCount,
4770                                      REG_DWORD,
4771                                      &count,
4772                                      sizeof(count));
4773     }
4774 
4775     if (writeStarted) {
4776         NTSTATUS status2;
4777         DWORD value = started;
4778         status2 = FxRegKey::_SetValue(RestartKey,
4779                                      (PUNICODE_STRING)&valueNameStartAchieved,
4780                                      REG_DWORD,
4781                                      &value,
4782                                      sizeof(value));
4783         //
4784         // Don't let status report success if it was an error prior to _SetValue
4785         //
4786         if(NT_SUCCESS(status)) {
4787             status = status2;
4788         }
4789     }
4790 
4791     return NT_SUCCESS(status) ? TRUE : FALSE;
4792 }
4793 
4794