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(¤tTickCount);
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 ¤tTickCount.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