1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxPkgPdo.cpp
8 
9 Abstract:
10 
11     This module implements the Pnp package for Pdo devices.
12 
13 Author:
14 
15 
16 
17 Environment:
18 
19     Both kernel and user mode
20 
21 Revision History:
22 
23 
24 
25 
26 --*/
27 
28 #include "pnppriv.hpp"
29 #include <wdmguid.h>
30 
31 // Tracing support
32 #if defined(EVENT_TRACING)
33 extern "C" {
34 #include "FxPkgPdo.tmh"
35 }
36 #endif
37 
38 const PFN_PNP_POWER_CALLBACK FxPkgPdo::m_PdoPnpFunctionTable[IRP_MN_SURPRISE_REMOVAL + 1] =
39 {
40     _PnpStartDevice,                // IRP_MN_START_DEVICE
41     _PnpQueryRemoveDevice,          // IRP_MN_QUERY_REMOVE_DEVICE
42     _PnpRemoveDevice,               // IRP_MN_REMOVE_DEVICE
43     _PnpCancelRemoveDevice,         // IRP_MN_CANCEL_REMOVE_DEVICE
44     _PnpStopDevice,                 // IRP_MN_STOP_DEVICE
45     _PnpQueryStopDevice,            // IRP_MN_QUERY_STOP_DEVICE
46     _PnpCancelStopDevice,           // IRP_MN_CANCEL_STOP_DEVICE
47 
48     _PnpQueryDeviceRelations,       // IRP_MN_QUERY_DEVICE_RELATIONS
49     _PnpQueryInterface,             // IRP_MN_QUERY_INTERFACE
50     _PnpQueryCapabilities,          // IRP_MN_QUERY_CAPABILITIES
51     _PnpQueryResources,             // IRP_MN_QUERY_RESOURCES
52     _PnpQueryResourceRequirements,  // IRP_MN_QUERY_RESOURCE_REQUIREMENTS
53     _PnpQueryDeviceText,            // IRP_MN_QUERY_DEVICE_TEXT
54     _PnpFilterResourceRequirements, // IRP_MN_FILTER_RESOURCE_REQUIREMENTS
55 
56     _PnpCompleteIrp,                // 0x0E
57 
58     _PnpCompleteIrp,                // IRP_MN_READ_CONFIG
59     _PnpCompleteIrp,                // IRP_MN_WRITE_CONFIG
60     _PnpEject,                      // IRP_MN_EJECT
61     _PnpSetLock,                    // IRP_MN_SET_LOCK
62     _PnpQueryId,                    // IRP_MN_QUERY_ID
63     _PnpQueryPnpDeviceState,        // IRP_MN_QUERY_PNP_DEVICE_STATE
64     _PnpQueryBusInformation,        // IRP_MN_QUERY_BUS_INFORMATION
65     _PnpDeviceUsageNotification,    // IRP_MN_DEVICE_USAGE_NOTIFICATION
66     _PnpSurpriseRemoval,            // IRP_MN_SURPRISE_REMOVAL
67 };
68 
69 const PFN_PNP_POWER_CALLBACK  FxPkgPdo::m_PdoPowerFunctionTable[IRP_MN_QUERY_POWER + 1] =
70 {
71     _DispatchWaitWake,      // IRP_MN_WAIT_WAKE
72     _DispatchPowerSequence, // IRP_MN_POWER_SEQUENCE
73     _DispatchSetPower,      // IRP_MN_SET_POWER
74     _DispatchQueryPower,    // IRP_MN_QUERY_POWER
75 };
76 
77 //#if defined(ALLOC_PRAGMA)
78 //#pragma code_seg("PAGE")
79 //#endif
80 
81 FxPkgPdo::FxPkgPdo(
82     __in PFX_DRIVER_GLOBALS FxDriverGlobals,
83     __in CfxDevice *Device
84     ) :
85     FxPkgPnp(FxDriverGlobals, Device, FX_TYPE_PACKAGE_PDO)
86 /*++
87 
88 Routine Description:
89 
90     This is the constructor for the FxPkgPdo.  Don't do any initialization
91     that might fail here.
92 
93 Arguments:
94 
95     none
96 
97 Returns:
98 
99     none
100 
101 --*/
102 
103 {
104     m_DeviceTextHead.Next = NULL;
105 
106     m_DeviceID   = NULL;
107     m_InstanceID = NULL;
108     m_HardwareIDs = NULL;
109     m_CompatibleIDs = NULL;
110     m_ContainerID = NULL;
111     m_IDsAllocation = NULL;
112 
113     m_RawOK = FALSE;
114     m_Static = FALSE;
115     m_AddedToStaticList = FALSE;
116 
117     //
118     // By default the PDO is the owner of wait wake irps (only case where this
119     // wouldn't be the case is for a bus filter to be sitting above us).
120     //
121     m_SharedPower.m_WaitWakeOwner = TRUE;
122 
123     m_Description = NULL;
124     m_OwningChildList = NULL;
125 
126     m_EjectionDeviceList = NULL;
127     m_DeviceEjectProcessed = NULL;
128 
129     m_CanBeDeleted = FALSE;
130     m_EnableWakeAtBusInvoked = FALSE;
131 }
132 
133 FxPkgPdo::~FxPkgPdo(
134     VOID
135     )
136 /*++
137 
138 Routine Description:
139 
140     This is the destructor for the FxPkgPdo
141 
142 Arguments:
143 
144     none
145 
146 Returns:
147 
148     none
149 
150 --*/
151 {
152     //
153     // If IoCreateDevice fails on a dynamically created PDO, m_Description will
154     // be != NULL b/c we will not go through the pnp state machine in its entirety
155     // for cleanup.  FxChildList will need a valid m_Description to cleanup upon
156     // failure from EvtChildListDeviceCrate, so we just leave m_Description alone
157     // here if != NULL.
158     //
159     // ASSERT(m_Description == NULL);
160 
161     FxDeviceText::_CleanupList(&m_DeviceTextHead);
162 
163     //
164     // This will free the underlying memory for m_DeviceID, m_InstanceID,
165     // m_HardwareIDs, m_CompatibleIDs and m_ContainerID
166     //
167     if (m_IDsAllocation != NULL) {
168         FxPoolFree(m_IDsAllocation);
169         m_IDsAllocation = NULL;
170     }
171 
172     if (m_OwningChildList != NULL) {
173         m_OwningChildList->RELEASE(this);
174     }
175 
176     if (m_EjectionDeviceList != NULL) {
177         delete m_EjectionDeviceList;
178         m_EjectionDeviceList = NULL;
179     }
180 }
181 
182 _Must_inspect_result_
183 NTSTATUS
184 FxPkgPdo::Initialize(
185     __in PWDFDEVICE_INIT DeviceInit
186     )
187 /*++
188 
189 Routine Description:
190 
191     Do initialization that might fail here.
192 
193 Arguemnts:
194 
195     DeviceInit - the structure the driver passed in with initialization data
196 
197 Returns:
198 
199     NTSTATUS
200 
201 --*/
202 {
203     NTSTATUS status;
204     size_t cbLength, cbStrLength;
205     PWSTR pCur;
206     PdoInit* pPdo;
207 
208     status = FxPkgPnp::Initialize(DeviceInit);
209     if (!NT_SUCCESS(status)) {
210         return status;
211     }
212 
213     cbLength = 0;
214 
215     //
216     // Compute the total number of bytes required for all strings and then
217     // make one allocation and remember pointers w/in the string so we can
218     // retrieve them individually later.
219     //
220     pPdo = &DeviceInit->Pdo;
221     cbLength += FxCalculateTotalStringSize(&pPdo->HardwareIDs);
222     cbLength += FxCalculateTotalStringSize(&pPdo->CompatibleIDs);
223 
224     if (pPdo->DeviceID != NULL) {
225         cbLength += pPdo->DeviceID->ByteLength(TRUE);
226     }
227     if (pPdo->InstanceID != NULL) {
228         cbLength += pPdo->InstanceID->ByteLength(TRUE);
229     }
230     if (pPdo->ContainerID != NULL) {
231         cbLength += pPdo->ContainerID->ByteLength(TRUE);
232     }
233 
234     m_IDsAllocation = (PWSTR) FxPoolAllocate(GetDriverGlobals(),
235                                              PagedPool,
236                                              cbLength);
237 
238     if (m_IDsAllocation == NULL) {
239         status = STATUS_INSUFFICIENT_RESOURCES;
240 
241         DoTraceLevelMessage(
242             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
243             "DeviceInit %p could not allocate string for device IDs "
244             "(length %I64d), %!STATUS!", DeviceInit, cbLength, status);
245 
246         return status;
247     }
248 
249     pCur = m_IDsAllocation;
250 
251     m_HardwareIDs = pCur;
252     pCur = FxCopyMultiSz(m_HardwareIDs, &pPdo->HardwareIDs);
253 
254     m_CompatibleIDs = pCur;
255     pCur = FxCopyMultiSz(m_CompatibleIDs, &pPdo->CompatibleIDs);
256 
257     if (pPdo->DeviceID != NULL) {
258         m_DeviceID = pCur;
259 
260         //
261         // Copy the bytes and then null terminate the buffer
262         //
263         cbStrLength = pPdo->DeviceID->ByteLength(FALSE);
264 
265         RtlCopyMemory(m_DeviceID,
266                       pPdo->DeviceID->Buffer(),
267                       cbStrLength);
268 
269         m_DeviceID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
270 
271         pCur = (PWSTR) WDF_PTR_ADD_OFFSET(m_DeviceID,
272                                           cbStrLength + sizeof(UNICODE_NULL));
273     }
274 
275     if (pPdo->InstanceID != NULL) {
276         m_InstanceID = pCur;
277 
278         //
279         // Copy the bytes and then null terminate the buffer
280         //
281         cbStrLength = pPdo->InstanceID->ByteLength(FALSE);
282 
283         RtlCopyMemory(m_InstanceID,
284                       pPdo->InstanceID->Buffer(),
285                       cbStrLength);
286 
287         m_InstanceID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
288 
289         pCur = (PWSTR) WDF_PTR_ADD_OFFSET(m_InstanceID,
290                                           cbStrLength + sizeof(UNICODE_NULL));
291     }
292 
293     if (pPdo->ContainerID != NULL) {
294         m_ContainerID = pCur;
295 
296         //
297         // Copy the bytes and then null terminate the buffer
298         //
299         cbStrLength = pPdo->ContainerID->ByteLength(FALSE);
300 
301         RtlCopyMemory(m_ContainerID,
302                       pPdo->ContainerID->Buffer(),
303                       cbStrLength);
304 
305         m_ContainerID[cbStrLength / sizeof(WCHAR)] = UNICODE_NULL;
306     }
307 
308     m_Static = pPdo->Static;
309 
310     if (m_Static) {
311         //
312         // Statically enumerated children do not support reenumeration requests
313         // from the stack on top of them.
314         //
315 
316         //
317         // The only way we can have static children is if an FDO enumerates them.
318         //
319 
320 
321 
322 
323 
324         Mx::MxAssert(m_Device->m_ParentDevice->IsFdo());
325         m_OwningChildList = m_Device->m_ParentDevice->GetFdoPkg()->m_StaticDeviceList;
326 
327         m_OwningChildList->ADDREF(this);
328     }
329     else {
330         m_Description = pPdo->DescriptionEntry;
331 
332         m_OwningChildList = m_Description->GetParentList();
333         m_OwningChildList->ADDREF(this);
334     }
335 
336     return STATUS_SUCCESS;
337 }
338 
339 VOID
340 FxPkgPdo::FinishInitialize(
341     __in PWDFDEVICE_INIT DeviceInit
342     )
343 {
344     PdoInit* pdoInit;
345 
346     pdoInit = &DeviceInit->Pdo;
347 
348     m_DefaultLocale = pdoInit->DefaultLocale;
349     m_DeviceTextHead.Next = pdoInit->DeviceText.Next;
350     pdoInit->DeviceText.Next = NULL;
351 
352     //
353     // Important to do this last since this will cause a pnp state machine
354     // transition
355     //
356     FxPkgPnp::FinishInitialize(DeviceInit); // __super call
357 }
358 
359 _Must_inspect_result_
360 NTSTATUS
361 FxPkgPdo::SendIrpSynchronously(
362     __in FxIrp* Irp
363     )
364 /*++
365 
366 Routine Description:
367     Virtual override for synchronously sending a request down the stack and
368     catching it on the way back up.  For PDOs, we are the bottom, so this is a
369     no-op.
370 
371 Arguments:
372     Irp - The request
373 
374 Return Value:
375     Status in the Irp
376 
377   --*/
378 {
379     return Irp->GetStatus();
380 }
381 
382 _Must_inspect_result_
383 NTSTATUS
384 FxPkgPdo::FireAndForgetIrp(
385     __inout FxIrp *Irp
386     )
387 /*++
388 
389 Routine Description:
390 
391     Virtual override for sending a request down the stack and forgetting about
392     it.  Since we are the bottom of the stack, just complete the request.
393 
394 Arguments:
395 
396 Return Value:
397 
398 --*/
399 {
400     NTSTATUS status;
401 
402     status = Irp->GetStatus();
403 
404     if (Irp->GetMajorFunction() == IRP_MJ_POWER) {
405         return CompletePowerRequest(Irp, status);
406     }
407     else {
408         return CompletePnpRequest(Irp, status);
409     }
410 }
411 
412 _Must_inspect_result_
413 NTSTATUS
414 FxPkgPdo::_PnpCompleteIrp(
415     __in    FxPkgPnp* This,
416     __inout FxIrp *Irp
417     )
418 {
419     return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, Irp->GetStatus());
420 }
421 
422 _Must_inspect_result_
423 NTSTATUS
424 FxPkgPdo::_PnpQueryDeviceRelations(
425     __in    FxPkgPnp* This,
426     __inout FxIrp *Irp
427     )
428 {
429     return ((FxPkgPdo*) This)->PnpQueryDeviceRelations(Irp);
430 }
431 
432 _Must_inspect_result_
433 NTSTATUS
434 FxPkgPdo::PnpQueryDeviceRelations(
435     __inout FxIrp *Irp
436     )
437 /*++
438 
439 Routine Description:
440 
441     This method is called in response to a PnP QDR. PDOs handle Ejection
442     Relations and Target Relations.
443 
444 Arguments:
445 
446     Irp - a pointer to the FxIrp
447 
448 Returns:
449 
450     NTSTATUS
451 
452 --*/
453 {
454     PDEVICE_RELATIONS pDeviceRelations;
455     NTSTATUS status;
456     DEVICE_RELATION_TYPE type;
457     PFX_DRIVER_GLOBALS pFxDriverGlobals;
458 
459     status = Irp->GetStatus();
460     pFxDriverGlobals = GetDriverGlobals();
461 
462     type = Irp->GetParameterQDRType();
463     switch (type) {
464     case BusRelations:
465         status = HandleQueryBusRelations(Irp);
466         break;
467 
468     case EjectionRelations:
469     case RemovalRelations:
470         status = HandleQueryDeviceRelations(
471             Irp,
472             (type == RemovalRelations) ? m_RemovalDeviceList
473                                        : m_EjectionDeviceList);
474 
475         //
476         // STATUS_NOT_SUPPORTED is a special value. It means that
477         // HandleQueryDeviceRelations did not modify the irp at all and it
478         // should be sent off as is.
479         //
480         if (status == STATUS_NOT_SUPPORTED) {
481             //
482             // Complete the request with the status it was received with
483             //
484             status = Irp->GetStatus();
485         }
486         break;
487 
488     case TargetDeviceRelation:
489         pDeviceRelations = (PDEVICE_RELATIONS) MxMemory::MxAllocatePoolWithTag(
490                 PagedPool, sizeof(DEVICE_RELATIONS), pFxDriverGlobals->Tag);
491 
492         if (pDeviceRelations != NULL) {
493             PDEVICE_OBJECT pDeviceObject;
494 
495             pDeviceObject = reinterpret_cast<PDEVICE_OBJECT> (m_Device->GetDeviceObject());
496 
497             Mx::MxReferenceObject(pDeviceObject);
498 
499             pDeviceRelations->Count = 1;
500             pDeviceRelations->Objects[0] = pDeviceObject;
501 
502             Irp->SetInformation((ULONG_PTR) pDeviceRelations);
503             status = STATUS_SUCCESS;
504         }
505         else {
506             Irp->SetInformation(NULL);
507             status = STATUS_INSUFFICIENT_RESOURCES;
508 
509             DoTraceLevelMessage(
510                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
511                 "WDFDEVICE %p failing TargetDeviceRelations, %!STATUS!",
512                 m_Device->GetHandle(), status);
513         }
514         break;
515     }
516 
517     return CompletePnpRequest(Irp, status);
518 }
519 
520 _Must_inspect_result_
521 NTSTATUS
522 FxPkgPdo::_PnpQueryInterface(
523     IN FxPkgPnp* This,
524     IN FxIrp *Irp
525     )
526 /*++
527 
528 Routine Description:
529     Query interface handler for the PDO
530 
531 Arguments:
532     This - the package
533 
534     Irp - the QI request
535 
536 Return Value:
537     NTSTATUS
538 
539   --*/
540 {
541     NTSTATUS status;
542     BOOLEAN completeIrp;
543 
544     status = ((FxPkgPdo*) This)->HandleQueryInterface(Irp, &completeIrp);
545 
546     return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status);
547 }
548 
549 _Must_inspect_result_
550 NTSTATUS
551 FxPkgPdo::_PnpQueryCapabilities(
552     __inout FxPkgPnp* This,
553     __inout FxIrp *Irp
554     )
555 {
556     return ((FxPkgPdo*) This)->PnpQueryCapabilities(Irp);
557 }
558 
559 _Must_inspect_result_
560 NTSTATUS
561 FxPkgPdo::PnpQueryCapabilities(
562     __inout FxIrp *Irp
563     )
564 
565 /*++
566 
567 Routine Description:
568 
569     This method is invoked in response to a Pnp QueryCapabilities IRP.
570 
571 Arguments:
572 
573     Irp - a pointer to the FxIrp
574 
575 Returns:
576 
577     NTSTATUS
578 
579 --*/
580 
581 {
582     PDEVICE_CAPABILITIES pDeviceCapabilities;
583     STACK_DEVICE_CAPABILITIES parentStackCapabilities = {0};
584     NTSTATUS status;
585 
586     status = STATUS_UNSUCCESSFUL;
587 
588     pDeviceCapabilities = Irp->GetParameterDeviceCapabilities();
589 
590     //
591     // Confirm this is a valid DeviceCapabilities structure.
592     //
593     ASSERT(pDeviceCapabilities->Size >= sizeof(DEVICE_CAPABILITIES));
594     ASSERT(pDeviceCapabilities->Version == 1);
595 
596     if ((pDeviceCapabilities->Version == 1) &&
597         (pDeviceCapabilities->Size >= sizeof(DEVICE_CAPABILITIES))) {
598 
599         //
600         // Since query caps must be sent to the parent stack until it reaches
601         // the root, we can quickly run out of stack space.  If that happens,
602         // then move to a work item to get a fresh stack with plenty of stack
603         // space.
604         //
605         if (Mx::MxHasEnoughRemainingThreadStack() == FALSE) {
606             MxWorkItem workItem;
607 
608             status = workItem.Allocate(m_Device->GetDeviceObject());
609 
610             if (NT_SUCCESS(status)) {
611                 //
612                 // Store off the work item so we can free it in the worker routine
613                 //
614                 Irp->SetContext(0, (PVOID)workItem.GetWorkItem());
615 
616                 //
617                 // Mark the irp as pending because it will be completed in
618                 // another thread
619                 //
620                 Irp->MarkIrpPending();
621 
622                 //
623                 // Kick off to another thread
624                 //
625                 workItem.Enqueue(_QueryCapsWorkItem, Irp->GetIrp());
626 
627                 return STATUS_PENDING;
628             }
629             else {
630                 //
631                 // Not enough for a work item, return error
632                 //
633                 status = STATUS_INSUFFICIENT_RESOURCES;
634             }
635         }
636         else {
637             MxDeviceObject parentDeviceObject;
638 
639             parentDeviceObject.SetObject(
640                 m_Device->m_ParentDevice->GetDeviceObject());
641             status = GetStackCapabilities(
642                 GetDriverGlobals(),
643                 &parentDeviceObject,
644                 NULL,   // D3ColdInterface
645                 &parentStackCapabilities);
646 
647             if (NT_SUCCESS(status)) {
648 #pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "prefast is confused")
649                 HandleQueryCapabilities(pDeviceCapabilities,
650                                         &parentStackCapabilities.DeviceCaps);
651 
652                 //
653                 // The check above does not guarantee STATUS_SUCCESS explicitly
654                 // (ie the verifier can change the value to something other then
655                 // STATUS_SUCCESS) so set it here
656                 //
657                 status = STATUS_SUCCESS;
658             }
659         }
660     }
661 
662     return CompletePnpRequest(Irp, status);
663 }
664 
665 VOID
666 FxPkgPdo::HandleQueryCapabilities(
667     __inout PDEVICE_CAPABILITIES ReportedCaps,
668     __in_bcount(ParentCaps->size) PDEVICE_CAPABILITIES ParentCaps
669     )
670 {
671     LONG pnpCaps;
672     ULONG i;
673 
674     //
675     // PowerSystemUnspecified is reserved for system use as per the DDK
676     //
677     for (i = PowerSystemWorking; i < PowerSystemMaximum; i++) {
678         DEVICE_POWER_STATE state;
679 
680         state = _GetPowerCapState(i, m_PowerCaps.States);
681 
682         if (state == PowerDeviceMaximum) {
683             //
684             // PDO did not specify any value, use parent's cap
685             //
686 #pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "Esp:675")
687             ReportedCaps->DeviceState[i] = ParentCaps->DeviceState[i];
688         }
689         else {
690             //
691             // Use PDO's reported value
692             //
693             ReportedCaps->DeviceState[i] = state;
694         }
695     }
696 
697     pnpCaps = GetPnpCapsInternal();
698 
699     //
700     // Appropriately fill the DeviceCapabilities structure.
701     //
702     SET_PNP_CAP(pnpCaps, ReportedCaps, LockSupported);
703     SET_PNP_CAP(pnpCaps, ReportedCaps, EjectSupported);
704     SET_PNP_CAP(pnpCaps, ReportedCaps, Removable);
705     SET_PNP_CAP(pnpCaps, ReportedCaps, DockDevice);
706     SET_PNP_CAP(pnpCaps, ReportedCaps, UniqueID);
707 
708     if ((pnpCaps & FxPnpCapSilentInstallMask) != FxPnpCapSilentInstallUseDefault) {
709         SET_PNP_CAP(pnpCaps, ReportedCaps, SilentInstall);
710     }
711     else if (m_RawOK) {
712         //
713         // By default, we report raw devices as silent install devices
714         // because if they are raw, they don't need any further
715         // installation.
716         //
717         ReportedCaps->SilentInstall = TRUE;
718     }
719 
720     SET_PNP_CAP(pnpCaps, ReportedCaps, SurpriseRemovalOK);
721     SET_PNP_CAP(pnpCaps, ReportedCaps, HardwareDisabled);
722     SET_PNP_CAP(pnpCaps, ReportedCaps, NoDisplayInUI);
723 
724     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD0);
725     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD1);
726     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD2);
727     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, WakeFromD3);
728     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, DeviceD1);
729     SET_POWER_CAP(m_PowerCaps.Caps , ReportedCaps, DeviceD2);
730 
731     if (m_RawOK) {
732         ReportedCaps->RawDeviceOK = TRUE;
733     }
734 
735     ReportedCaps->UINumber = m_PnpCapsUINumber;
736     ReportedCaps->Address  = m_PnpCapsAddress;
737 
738     if (m_PowerCaps.SystemWake != PowerSystemMaximum) {
739         ReportedCaps->SystemWake = (SYSTEM_POWER_STATE) m_PowerCaps.SystemWake;
740     }
741     else {
742         ReportedCaps->SystemWake = ParentCaps->SystemWake;
743     }
744 
745     //
746     // Set the least-powered device state from which the device can
747     // wake the system.
748     //
749     if (m_PowerCaps.DeviceWake != PowerDeviceMaximum) {
750         ReportedCaps->DeviceWake = (DEVICE_POWER_STATE) m_PowerCaps.DeviceWake;
751     }
752     else {
753         ReportedCaps->DeviceWake = ParentCaps->DeviceWake;
754     }
755 
756     //
757     // Set the Device wake up latencies.
758     //
759     if (m_PowerCaps.D1Latency != (ULONG) -1) {
760         ReportedCaps->D1Latency = m_PowerCaps.D1Latency;
761     }
762     else {
763         ReportedCaps->D1Latency = 0;
764     }
765 
766     if (m_PowerCaps.D2Latency != (ULONG) -1) {
767         ReportedCaps->D2Latency = m_PowerCaps.D2Latency;
768     }
769     else {
770         ReportedCaps->D2Latency = 0;
771     }
772 
773     if (m_PowerCaps.D3Latency != (ULONG) -1) {
774         ReportedCaps->D3Latency = m_PowerCaps.D3Latency;
775     }
776 }
777 
778 VOID
779 FxPkgPdo::_QueryCapsWorkItem(
780     __in MdDeviceObject DeviceObject,
781     __in PVOID Context
782     )
783 {
784     STACK_DEVICE_CAPABILITIES parentCapabilities;
785     MdWorkItem pItem;
786     FxPkgPdo* pPkgPdo;
787     FxIrp irp;
788     NTSTATUS status;
789     MxDeviceObject parentDeviceObject;
790 
791     irp.SetIrp((MdIrp)Context);
792     pItem = (MdWorkItem) irp.GetContext(0);
793 
794     pPkgPdo = FxDevice::GetFxDevice(DeviceObject)->GetPdoPkg();
795 
796     parentDeviceObject.SetObject(
797         pPkgPdo->m_Device->m_ParentDevice->GetDeviceObject());
798 
799     status = GetStackCapabilities(
800         pPkgPdo->m_Device->GetDriverGlobals(),
801         &parentDeviceObject,
802         NULL, // D3ColdInterface
803         &parentCapabilities);
804 
805     if (NT_SUCCESS(status)) {
806 #pragma prefast(suppress:__WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "prefast is confused")
807         pPkgPdo->HandleQueryCapabilities(
808             irp.GetParameterDeviceCapabilities(),
809             &parentCapabilities.DeviceCaps
810             );
811         status = STATUS_SUCCESS;
812     }
813 
814     pPkgPdo->CompletePnpRequest(&irp, status);
815 
816     MxWorkItem::_Free(pItem);
817 }
818 
819 FxDeviceText *
820 FindObjectForGivenLocale(
821     __in PSINGLE_LIST_ENTRY Head,
822     __in LCID LocaleId
823     )
824 
825 /*++
826 
827 Routine Description:
828 
829 
830 
831 Arguments:
832 
833 Returns:
834 
835 --*/
836 
837 {
838     PSINGLE_LIST_ENTRY ple;
839 
840     for (ple = Head->Next; ple != NULL; ple = ple->Next) {
841         FxDeviceText *pDeviceText;
842 
843         pDeviceText= FxDeviceText::_FromEntry(ple);
844 
845         if (pDeviceText->m_LocaleId == LocaleId) {
846             //
847             // We found our object!
848             //
849             return pDeviceText;
850         }
851     }
852 
853     return NULL;
854 }
855 
856 _Must_inspect_result_
857 NTSTATUS
858 FxPkgPdo::_PnpQueryDeviceText(
859     __inout FxPkgPnp* This,
860     __inout FxIrp *Irp
861     )
862 
863 /*++
864 
865 Routine Description:
866 
867     This method is invoked in response to a Pnp QueryDeviceText IRP.
868     We return the decription or the location.
869 
870 Arguments:
871 
872     Irp - a pointer to the FxIrp
873 
874 Returns:
875 
876     NTSTATUS
877 
878 --*/
879 
880 {
881     FxPkgPdo* pThis;
882     LCID localeId;
883     FxDeviceText *pDeviceTextObject;
884     NTSTATUS status;
885     PFX_DRIVER_GLOBALS pFxDriverGlobals;
886 
887     pThis = (FxPkgPdo*) This;
888     pFxDriverGlobals = pThis->GetDriverGlobals();
889 
890     localeId = Irp->GetParameterQueryDeviceTextLocaleId();
891     status = Irp->GetStatus();
892 
893     //
894     // The PDO package maintains a collection of "DeviceText" objects.  We
895     // will look up the item in the collection with the "appropriate" locale.
896     //
897     // If no entries are found in the collection for the given locale, then
898     // we will use the "default locale" property of the PDO and use the
899     // entry for the "default locale".
900     //
901 
902     //
903     // Try to find the FxDeviceText object for the given locale.
904     //
905     pDeviceTextObject = FindObjectForGivenLocale(
906         &pThis->m_DeviceTextHead, localeId);
907 
908     if (pDeviceTextObject == NULL) {
909         pDeviceTextObject = FindObjectForGivenLocale(
910             &pThis->m_DeviceTextHead, pThis->m_DefaultLocale);
911     }
912 
913     if (pDeviceTextObject != NULL) {
914         PWCHAR pInformation;
915 
916         pInformation = NULL;
917 
918         switch (Irp->GetParameterQueryDeviceTextType()) {
919         case DeviceTextDescription:
920             pInformation = pDeviceTextObject->m_Description;
921             break;
922 
923         case DeviceTextLocationInformation:
924             pInformation = pDeviceTextObject->m_LocationInformation;
925             break;
926         }
927 
928         //
929         // Information should now point to a valid unicode string.
930         //
931         if (pInformation != NULL) {
932             PWCHAR pBuffer;
933             size_t length;
934 
935             length = (wcslen(pInformation) + 1) * sizeof(WCHAR);
936 
937             //
938             // Make sure the information field of the IRP isn't already set.
939             //
940             ASSERT(Irp->GetInformation() == NULL);
941 
942             pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
943                 PagedPool, length, pFxDriverGlobals->Tag);
944 
945             if (pBuffer != NULL) {
946                 RtlCopyMemory(pBuffer, pInformation, length);
947                 Irp->SetInformation((ULONG_PTR) pBuffer);
948 
949                 status = STATUS_SUCCESS;
950             }
951             else {
952                 status = STATUS_INSUFFICIENT_RESOURCES;
953 
954                 DoTraceLevelMessage(
955                     pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
956                     "WDFDEVICE %p failing Query Device Text, type %d, %!STATUS!",
957                     pThis->m_Device->GetHandle(),
958                     Irp->GetParameterQueryDeviceTextType(), status);
959             }
960         }
961     }
962 
963     return pThis->CompletePnpRequest(Irp, status);
964 }
965 
966 _Must_inspect_result_
967 NTSTATUS
968 FxPkgPdo::_PnpEject(
969     __inout FxPkgPnp* This,
970     __inout FxIrp *Irp
971     )
972 /*++
973 
974 Routine Description:
975 
976     Ejection is handled by the PnP state machine. Handle it synchronously.
977     Don't pend it since PnP manager does not serilaize it with other state
978     changing pnp irps if handled asynchronously.
979 
980 Arguments:
981 
982     This - the package
983 
984     Irp - the request
985 
986 Return Value:
987 
988     NTSTATUS
989 
990   --*/
991 {
992     MxEvent event;
993     FxPkgPdo* pdoPkg;
994     NTSTATUS status;
995 
996     pdoPkg = (FxPkgPdo*)This;
997 
998     //
999     // This will make sure no new state changing pnp irps arrive while
1000     // we are still processing this one. Also, note that irp is not being
1001     // marked pending.
1002     //
1003     pdoPkg->SetPendingPnpIrp(Irp, FALSE);
1004 
1005     status = event.Initialize(SynchronizationEvent, FALSE);
1006     if (!NT_SUCCESS(status)) {
1007 
1008         DoTraceLevelMessage(
1009             pdoPkg->GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1010             "Event allocation failed while processing eject for WDFDEVICE %p,"
1011             " %!STATUS!",
1012             pdoPkg->m_Device->GetHandle(), status);
1013     }
1014     else {
1015         ASSERT(pdoPkg->m_DeviceEjectProcessed == NULL);
1016         pdoPkg->m_DeviceEjectProcessed = event.GetSelfPointer();
1017 
1018         //
1019         // let state machine process eject
1020         //
1021         pdoPkg->PnpProcessEvent(PnpEventEject);
1022 
1023         //
1024         // No need to wait in a critical region because we are in the context of a
1025         // pnp request which is in the system context.
1026         //
1027         event.WaitFor(Executive, KernelMode, FALSE, NULL);
1028 
1029         pdoPkg->m_DeviceEjectProcessed = NULL;
1030 
1031         status = Irp->GetStatus();
1032     }
1033 
1034     //
1035     // complete request
1036     //
1037 
1038     pdoPkg->ClearPendingPnpIrp();
1039     pdoPkg->CompletePnpRequest(Irp, status);
1040 
1041     return status;
1042 }
1043 
1044 WDF_DEVICE_PNP_STATE
1045 FxPkgPdo::PnpEventEjectHardwareOverload(
1046     VOID
1047     )
1048 /*++
1049 
1050 Routine Description:
1051 
1052     This function implements the EjectHardware state.  This
1053     function overloads the base PnP State machine handler.  Its
1054     job is to call EvtDeviceEject.  If that succeeds, then we
1055     transition immediately to EjectedWaitingForRemove.  If not,
1056     then to EjectFailed.
1057 
1058 Arguments:
1059 
1060     none
1061 
1062 Return Value:
1063 
1064     NTSTATUS
1065 
1066 --*/
1067 {
1068     NTSTATUS status;
1069     WDF_DEVICE_PNP_STATE state;
1070 
1071     status = m_DeviceEject.Invoke(m_Device->GetHandle());
1072 
1073     if (NT_SUCCESS(status)) {
1074 
1075         //
1076         // Upon a successful eject, mark the child as missing so that when we
1077         // get another QDR, it is not re-reported.
1078         //
1079         FxChildList* pList;
1080         MxEvent* event;
1081 
1082         pList = m_Description->GetParentList();
1083 
1084         status = pList->UpdateAsMissing(m_Description->GetId());
1085         if (NT_SUCCESS(status)) {
1086             DoTraceLevelMessage(
1087                 GetDriverGlobals(), TRACE_LEVEL_INFORMATION, TRACINGPNP,
1088                 "PDO WDFDEVICE %p !devobj %p marked missing as a result of eject",
1089                 m_Device->GetHandle(), m_Device->GetDeviceObject());
1090         }
1091         else {
1092             DoTraceLevelMessage(
1093                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1094                 "Failed to mark PDO WDFDEVICE %p !devobj %p missing after eject %!STATUS!",
1095                 m_Device->GetHandle(), m_Device->GetDeviceObject(),
1096                 status);
1097         }
1098 
1099         //
1100         // We must wait for any pending scans to finish so that the previous
1101         // update as missing is enacted into the list and reported to the
1102         // OS.  Otherwise, if we don't wait we could be in the middle of a
1103         // scan, complete the eject, report the child again and it will be
1104         // reenumerated.
1105         //
1106         event = pList->GetScanEvent();
1107 
1108         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1109                             "waiting on event %p for device to finish scanning",
1110                             &event);
1111 
1112         //
1113         // No need to wait in a crtical region because we are in the context of a
1114         // pnp request which is in the system context.
1115         //
1116         event->WaitFor(Executive, KernelMode, FALSE, NULL);
1117 
1118         //
1119         // Change the state.
1120         //
1121         state = WdfDevStatePnpEjectedWaitingForRemove;
1122     }
1123     else {
1124         state = WdfDevStatePnpEjectFailed;
1125         DoTraceLevelMessage(
1126             GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1127             "Eject failed since driver's EvtDeviceEject returned %!STATUS!", status);
1128 
1129         if (status == STATUS_NOT_SUPPORTED) {
1130             DoTraceLevelMessage(
1131                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDEVICE,
1132                 "EvtDeviceEject returned an invalid status STATUS_NOT_SUPPORTED");
1133 
1134             if (GetDriverGlobals()->IsVerificationEnabled(1, 11, OkForDownLevel)) {
1135                 FxVerifierDbgBreakPoint(GetDriverGlobals());
1136             }
1137         }
1138     }
1139 
1140     //
1141     // set irp status
1142     //
1143     SetPendingPnpIrpStatus(status);
1144 
1145     //
1146     // Pnp dispatch routine is waiting on this event, and it will complete
1147     // the Eject irp
1148     //
1149     m_DeviceEjectProcessed->Set();
1150 
1151     return state;
1152 }
1153 
1154 WDF_DEVICE_PNP_STATE
1155 FxPkgPdo::PnpEventCheckForDevicePresenceOverload(
1156     VOID
1157     )
1158 /*++
1159 
1160 Routine Description:
1161 
1162     This function implements the CheckForDevicePresence state.  This
1163     function overloads the base PnP State machine handler.  It's
1164     job is to figure out whether the removed device is actually
1165     still attached.  It then changes state based on that result.
1166 
1167 Arguments:
1168 
1169     none
1170 
1171 Return Value:
1172 
1173     NTSTATUS
1174 
1175 --*/
1176 
1177 {
1178     if (m_Description != NULL) {
1179         if (m_Description->IsDeviceRemoved()) {
1180             //
1181             // The description freed itself now that the device has been reported
1182             // missing.
1183             //
1184             return WdfDevStatePnpPdoRemoved;
1185         }
1186         else {
1187             //
1188             // Device was not reported as missing, keep it alive
1189             //
1190             return WdfDevStatePnpRemovedPdoWait;
1191         }
1192     }
1193     else {
1194         //
1195         // Only static children can get this far without having an m_Description
1196         //
1197         ASSERT(m_Static);
1198 
1199         //
1200         // The description freed itself now that the device has been reported
1201         // missing.
1202         //
1203         return WdfDevStatePnpPdoRemoved;
1204     }
1205 }
1206 
1207 WDF_DEVICE_PNP_STATE
1208 FxPkgPdo::PnpEventPdoRemovedOverload(
1209     VOID
1210     )
1211 /*++
1212 
1213 Routine Description:
1214 
1215     This function implements the Removed state.  This
1216     function overloads the base PnP State machine handler.
1217 
1218 Arguments:
1219 
1220     none
1221 
1222 Return Value:
1223 
1224     NTSTATUS
1225 
1226 --*/
1227 {
1228     m_CanBeDeleted = TRUE;
1229 
1230     //
1231     // Unconditionally delete the symbolic link now (vs calling
1232     // DeleteSymbolicLinkOverload()) because we know that the device has been
1233     // reported as missing.
1234     //
1235     m_Device->DeleteSymbolicLink();
1236 
1237     //
1238     // Do that which all device stacks need to do upon removal.
1239     //
1240     PnpEventRemovedCommonCode();
1241 
1242     //
1243     // m_Device is Release()'ed in FxPkgPnp::PnpEventFinal
1244     //
1245     if (m_Description != NULL) {
1246         DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP,
1247                             "Removing entry reference %p on FxPkgPnp %p",
1248                             m_Description, this);
1249 
1250         m_Description->ProcessDeviceRemoved();
1251         m_Description = NULL;
1252     }
1253 
1254     return WdfDevStatePnpFinal;
1255 }
1256 
1257 WDF_DEVICE_PNP_STATE
1258 FxPkgPdo::PnpGetPostRemoveState(
1259     VOID
1260     )
1261 {
1262     //
1263     // Transition to the check for device presence state.
1264     //
1265     return WdfDevStatePnpCheckForDevicePresence;
1266 }
1267 
1268 WDF_DEVICE_PNP_STATE
1269 FxPkgPdo::PnpEventFdoRemovedOverload(
1270     VOID
1271     )
1272 {
1273     ASSERT(!"This should only be implemented for FDOs.");
1274 
1275     //
1276     // Do something safe.  Act like the device is not present.
1277     //
1278     return WdfDevStatePnpFinal;
1279 }
1280 
1281 VOID
1282 FxPkgPdo::PnpEventSurpriseRemovePendingOverload(
1283     VOID
1284     )
1285 {
1286     if (m_Description != NULL) {
1287         m_Description->DeviceSurpriseRemoved();
1288     }
1289 
1290     FxPkgPnp::PnpEventSurpriseRemovePendingOverload();
1291 }
1292 
1293 BOOLEAN
1294 FxPkgPdo::PnpSendStartDeviceDownTheStackOverload(
1295     VOID
1296     )
1297 /*++
1298 
1299 Routine Description:
1300     Process the start irp on the way down the stack during a start.
1301 
1302     Since the PDO is the bottom, just move to the new state where we
1303     are processing the irp up the stack.
1304 
1305 Arguments:
1306     None
1307 
1308 Return Value:
1309     TRUE, the completion of the start irp down the stack was synchronous
1310 
1311   --*/
1312 {
1313     //
1314     // We are successful so far, indicate this in the irp.
1315     //
1316     SetPendingPnpIrpStatus(STATUS_SUCCESS);
1317 
1318     return TRUE;
1319 }
1320 
1321 _Must_inspect_result_
1322 NTSTATUS
1323 FxPkgPdo::_PnpSetLock(
1324     __inout FxPkgPnp* This,
1325     __inout FxIrp *Irp
1326     )
1327 /*++
1328 
1329 Routine Description:
1330     Set lock
1331 
1332 Arguments:
1333     This - the package
1334 
1335     Irp - the irp
1336 
1337 Return Value:
1338     NTSTATUS
1339 
1340   --*/
1341 {
1342     NTSTATUS status;
1343     BOOLEAN lock;
1344 
1345     lock = Irp->GetParameterSetLockLock();
1346 
1347     status = ((FxPkgPdo*) This)->m_DeviceSetLock.Invoke(
1348         ((FxPkgPdo*) This)->m_Device->GetHandle(), lock);
1349 
1350     if (NT_SUCCESS(status)) {
1351         Irp->SetInformation(NULL);
1352     }
1353 
1354     return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status);
1355 }
1356 
1357 _Must_inspect_result_
1358 NTSTATUS
1359 FxPkgPdo::_PnpQueryId(
1360     __inout FxPkgPnp* This,
1361     __inout FxIrp *Irp
1362     )
1363 {
1364     FxPkgPdo* pThis;
1365     NTSTATUS status;
1366     PWCHAR pBuffer;
1367     PCWSTR pSrc;
1368     size_t cbLength;
1369     PFX_DRIVER_GLOBALS pFxDriverGlobals;
1370     BUS_QUERY_ID_TYPE queryIdType;
1371 
1372     pThis = (FxPkgPdo*) This;
1373     pFxDriverGlobals  = pThis->GetDriverGlobals();
1374     status = Irp->GetStatus();
1375     cbLength = 0;
1376 
1377     queryIdType = Irp->GetParameterQueryIdType();
1378 
1379     switch (queryIdType) {
1380     case BusQueryDeviceID:
1381     case BusQueryInstanceID:
1382     case BusQueryContainerID:
1383         if (queryIdType == BusQueryDeviceID) {
1384             pSrc = pThis->m_DeviceID;
1385         }
1386         else if (queryIdType == BusQueryInstanceID) {
1387             pSrc = pThis->m_InstanceID;
1388         }
1389         else {
1390             pSrc = pThis->m_ContainerID;
1391         }
1392 
1393         if (pSrc != NULL) {
1394             cbLength = (wcslen(pSrc) + 1) * sizeof(WCHAR);
1395 
1396             pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
1397                 PagedPool, cbLength, pFxDriverGlobals->Tag);
1398         }
1399         else {
1400             status = Irp->GetStatus();
1401             break;
1402         }
1403 
1404         if (pBuffer != NULL) {
1405 
1406             //
1407             // This will copy the NULL terminator too
1408             //
1409             RtlCopyMemory(pBuffer, pSrc, cbLength);
1410             Irp->SetInformation((ULONG_PTR) pBuffer);
1411             status = STATUS_SUCCESS;
1412         }
1413         else {
1414             status = STATUS_INSUFFICIENT_RESOURCES;
1415         }
1416         break;
1417 
1418     case BusQueryHardwareIDs:
1419     case BusQueryCompatibleIDs:
1420         if (queryIdType == BusQueryHardwareIDs) {
1421             pSrc = pThis->m_HardwareIDs;
1422         }
1423         else {
1424             pSrc = pThis->m_CompatibleIDs;
1425         }
1426 
1427         if (pSrc != NULL) {
1428             cbLength = FxCalculateTotalMultiSzStringSize(pSrc);
1429         }
1430         else {
1431             //
1432             // Must return an empty list
1433             //
1434             cbLength = 2 * sizeof(UNICODE_NULL);
1435         }
1436 
1437         pBuffer = (PWCHAR) MxMemory::MxAllocatePoolWithTag(
1438             PagedPool, cbLength, pFxDriverGlobals->Tag);
1439 
1440         if (pBuffer != NULL) {
1441             if (pSrc != NULL) {
1442                 RtlCopyMemory(pBuffer, pSrc, cbLength);
1443             }
1444             else {
1445                 RtlZeroMemory(pBuffer, cbLength);
1446             }
1447 
1448             Irp->SetInformation((ULONG_PTR) pBuffer);
1449             status = STATUS_SUCCESS;
1450         }
1451         else {
1452             status = STATUS_INSUFFICIENT_RESOURCES;
1453         }
1454         break;
1455     }
1456 
1457     if (!NT_SUCCESS(status)) {
1458         Irp->SetInformation(NULL);
1459 
1460         if (status == STATUS_NOT_SUPPORTED) {
1461             DoTraceLevelMessage(
1462                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1463                 "WDFDEVICE %p does not have a string for PnP query IdType "
1464                 "%!BUS_QUERY_ID_TYPE!, %!STATUS!",
1465                 pThis->m_Device->GetHandle(),
1466                 queryIdType, status);
1467         }
1468         else {
1469             DoTraceLevelMessage(
1470                 pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
1471                 "WDFDEVICE %p could not alloc string for PnP query IdType "
1472                 "%!BUS_QUERY_ID_TYPE!, %!STATUS!",
1473                 pThis->m_Device->GetHandle(),
1474                 queryIdType, status);
1475         }
1476     }
1477 
1478     return ((FxPkgPdo*) pThis)->CompletePnpRequest(Irp, status);
1479 }
1480 
1481 _Must_inspect_result_
1482 NTSTATUS
1483 FxPkgPdo::_PnpQueryPnpDeviceState(
1484     __inout FxPkgPnp* This,
1485     __inout FxIrp *Irp
1486     )
1487 /*++
1488 
1489 Routine Description:
1490     indicates the current device state
1491 
1492 Arguments:
1493     This - the package
1494 
1495     Irp - the request
1496 
1497 Return Value:
1498     NTSTATUS
1499 
1500   --*/
1501 {
1502     PNP_DEVICE_STATE pnpDeviceState;
1503     PFX_DRIVER_GLOBALS FxDriverGlobals = This->GetDriverGlobals();
1504 
1505     DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
1506                         "Entering QueryPnpDeviceState handler");
1507 
1508     pnpDeviceState = ((FxPkgPdo*) This)->HandleQueryPnpDeviceState(
1509         (PNP_DEVICE_STATE) Irp->GetInformation());
1510 
1511     Irp->SetInformation((ULONG_PTR) pnpDeviceState);
1512 
1513     DoTraceLevelMessage(
1514         FxDriverGlobals, TRACE_LEVEL_INFORMATION, TRACINGPNP,
1515         "WDFDEVICE 0x%p !devobj 0x%p returning PNP_DEVICE_STATE 0x%d IRP 0x%p",
1516         This->GetDevice()->GetHandle(),
1517         This->GetDevice()->GetDeviceObject(),
1518         pnpDeviceState,
1519         Irp->GetIrp());
1520 
1521     DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP,
1522                         "Exiting QueryPnpDeviceState handler");
1523 
1524     return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, STATUS_SUCCESS);
1525 }
1526 
1527 _Must_inspect_result_
1528 NTSTATUS
1529 FxPkgPdo::_PnpQueryBusInformation(
1530     __inout FxPkgPnp* This,
1531     __inout FxIrp *Irp
1532     )
1533 /*++
1534 
1535 Routine Description:
1536     Returns the bus information for this child
1537 
1538 Arguments:
1539     This - the package
1540 
1541     Irp - the request
1542 
1543 Return Value:
1544     NTSTATUS
1545 
1546   --*/
1547 {
1548     FxPkgPdo* pThis;
1549     NTSTATUS status;
1550 
1551     pThis = (FxPkgPdo*) This;
1552 
1553     status = pThis->m_Device->m_ParentDevice->m_PkgPnp->
1554         HandleQueryBusInformation(Irp);
1555 
1556     return pThis->CompletePnpRequest(Irp, status);
1557 }
1558 
1559 _Must_inspect_result_
1560 NTSTATUS
1561 FxPkgPdo::_PnpSurpriseRemoval(
1562     __inout FxPkgPnp* This,
1563     __inout FxIrp *Irp
1564     )
1565 {
1566     FxPkgPdo* pThis;
1567 
1568     pThis = (FxPkgPdo*) This;
1569 
1570     pThis->m_Description->DeviceSurpriseRemoved();
1571 
1572     return pThis->PnpSurpriseRemoval(Irp);
1573 }
1574 
1575 VOID
1576 FxPkgPdo::RegisterCallbacks(
1577     __in PWDF_PDO_EVENT_CALLBACKS DispatchTable
1578     )
1579 {
1580     m_DeviceResourcesQuery.m_Method            = DispatchTable->EvtDeviceResourcesQuery;
1581     m_DeviceResourceRequirementsQuery.m_Method = DispatchTable->EvtDeviceResourceRequirementsQuery;
1582     m_DeviceEject.m_Method                     = DispatchTable->EvtDeviceEject;
1583     m_DeviceSetLock.m_Method                   = DispatchTable->EvtDeviceSetLock;
1584 
1585     m_DeviceEnableWakeAtBus.m_Method           = DispatchTable->EvtDeviceEnableWakeAtBus;
1586     m_DeviceDisableWakeAtBus.m_Method          = DispatchTable->EvtDeviceDisableWakeAtBus;
1587 
1588     //
1589     // this callback was added in V1.11
1590     //
1591     if (DispatchTable->Size > sizeof(WDF_PDO_EVENT_CALLBACKS_V1_9)) {
1592         m_DeviceReportedMissing.m_Method       = DispatchTable->EvtDeviceReportedMissing;
1593     }
1594 }
1595 
1596 _Must_inspect_result_
1597 NTSTATUS
1598 FxPkgPdo::AskParentToRemoveAndReenumerate(
1599     VOID
1600     )
1601 /*++
1602 
1603 Routine Description:
1604     This routine asks the PDO to ask its parent bus driver to Surprise-Remove
1605     and re-enumerate the PDO.  This will be done only at the point of
1606     catastrophic software failure, and occasionally after catastrophic hardware
1607     failure.
1608 
1609 Arguments:
1610     None
1611 
1612 Return Value:
1613     status
1614 
1615   --*/
1616 {
1617     //
1618     // Static children do not support reenumeration.
1619     //
1620     if (m_Description != NULL && m_Static == FALSE) {
1621         m_Description->GetParentList()->ReenumerateEntry(m_Description);
1622         return STATUS_SUCCESS;
1623     }
1624 
1625     return STATUS_NOT_FOUND;
1626 }
1627 
1628 
1629 VOID
1630 FxPkgPdo::DeleteSymbolicLinkOverload(
1631     __in BOOLEAN GracefulRemove
1632     )
1633 /*++
1634 
1635 Routine Description:
1636     Role specific virtual function which determines if the symbolic link for a
1637     device should be deleted.
1638 
1639 Arguments:
1640     None
1641 
1642 Return Value:
1643     None
1644 
1645   --*/
1646 {
1647     if (GracefulRemove) {
1648         //
1649         // We will remove the symbolic link when we determine if the PDO was
1650         // reported missing or not.
1651         //
1652         return;
1653     }
1654     else if (m_Description->IsDeviceReportedMissing()) {
1655         //
1656         // Surprise removed and we have reported the PDO as missing
1657         //
1658 
1659         m_Device->DeleteSymbolicLink();
1660     }
1661 }
1662 
1663 
1664 VOID
1665 FxPkgPdo::_RemoveAndReenumerateSelf(
1666     __in PVOID Context
1667     )
1668 /*++
1669 
1670 Routine Description:
1671     This routine is passed out to higher-level drivers as part of the
1672     re-enumeration interface.
1673 
1674 Arguments:
1675     This
1676 
1677 Return Value:
1678     void
1679 
1680   --*/
1681 {
1682     ((FxPkgPdo*) Context)->AskParentToRemoveAndReenumerate();
1683 }
1684 
1685 _Must_inspect_result_
1686 NTSTATUS
1687 FxPkgPdo::HandleQueryInterfaceForReenumerate(
1688     __in  FxIrp* Irp,
1689     __out PBOOLEAN CompleteRequest
1690     )
1691 /*++
1692 
1693 Routine Description:
1694     Handles a query interface on the PDO for the self reenumeration interface.
1695 
1696 Arguments:
1697     Irp - the request containing the QI
1698 
1699     CompleteRequest - whether the caller should complete the request when this
1700                       call returns
1701 
1702 Return Value:
1703     status to complete the irp with
1704 
1705   --*/
1706 {
1707     PREENUMERATE_SELF_INTERFACE_STANDARD pInterface;
1708     NTSTATUS status;
1709 
1710     *CompleteRequest = TRUE;
1711 
1712     if (m_Static) {
1713         //
1714         // Return the embedded status in the irp since this is a statically
1715         // enumerated child.  Only dynamically enuemrated child support self
1716         // reenumeration.
1717         //
1718         return Irp->GetStatus();
1719     }
1720 
1721     if (Irp->GetParameterQueryInterfaceVersion() == 1 &&
1722         Irp->GetParameterQueryInterfaceSize() >= sizeof(*pInterface)) {
1723 
1724         pInterface = (PREENUMERATE_SELF_INTERFACE_STANDARD)
1725             Irp->GetParameterQueryInterfaceInterface();
1726 
1727         //
1728         // Expose the interface to the requesting driver.
1729         //
1730         pInterface->Version = 1;
1731         pInterface->Size = sizeof(*pInterface);
1732         pInterface->Context = this;
1733         pInterface->InterfaceReference = FxDevice::_InterfaceReferenceNoOp;
1734         pInterface->InterfaceDereference = FxDevice::_InterfaceDereferenceNoOp;
1735         pInterface->SurpriseRemoveAndReenumerateSelf = &FxPkgPdo::_RemoveAndReenumerateSelf;
1736 
1737         status = STATUS_SUCCESS;
1738 
1739         //
1740         // Caller assumes a reference has been taken.
1741         //
1742         pInterface->InterfaceReference(pInterface->Context);
1743     }
1744     else {
1745         status = STATUS_INVALID_BUFFER_SIZE;
1746     }
1747 
1748     return status;
1749 }
1750 
1751 _Must_inspect_result_
1752 NTSTATUS
1753 FxPkgPdo::ProcessRemoveDeviceOverload(
1754     __inout FxIrp* Irp
1755     )
1756 {
1757     if (m_CanBeDeleted) {
1758         //
1759         // After this is called, any irp dispatched to FxDevice::DispatchWithLock
1760         // will fail with STATUS_INVALID_DEVICE_REQUEST.
1761         //
1762 
1763 
1764 
1765 
1766 
1767 
1768 
1769         Mx::MxReleaseRemoveLockAndWait(
1770             m_Device->GetRemoveLock(),
1771             Irp->GetIrp()
1772             );
1773 
1774         //
1775         // Cleanup the state machines and release the power thread.
1776         //
1777         CleanupStateMachines(TRUE);
1778 
1779         //
1780         // Detach and delete the device object.
1781         //
1782         DeleteDevice();
1783 
1784         //
1785         // Can't call CompletePnpRequest because we just released the tag in
1786         // IoReleaseRemoveLockAndWait above.
1787         //
1788         Irp->CompleteRequest(IO_NO_INCREMENT);
1789 
1790         return STATUS_SUCCESS;
1791     }
1792     else {
1793         //
1794         // This was a PDO which was not reported missing, so do not free the
1795         // memory and clear out our stack local address.
1796         //
1797         m_DeviceRemoveProcessed = NULL;
1798         return CompletePnpRequest(Irp, Irp->GetStatus());
1799     }
1800 }
1801 
1802 _Must_inspect_result_
1803 NTSTATUS
1804 FxPkgPdo::QueryForPowerThread(
1805     VOID
1806     )
1807 /*++
1808 
1809 Routine Description:
1810     Since the PDO is the lowest device in the stack, it does not have to send
1811     a query down the stack.  Rather, it just creates the thread and returns.
1812 
1813 Arguments:
1814     None
1815 
1816 Return Value:
1817     NTSTATUS
1818 
1819   --*/
1820 {
1821     return CreatePowerThread();
1822 }
1823 
1824 _Must_inspect_result_
1825 NTSTATUS
1826 FxPkgPdo::AddEjectionDevice(
1827     __in MdDeviceObject DependentDevice
1828     )
1829 {
1830     FxRelatedDevice* pRelated;
1831     NTSTATUS status;
1832 
1833     if (m_EjectionDeviceList == NULL) {
1834         KIRQL irql;
1835 
1836         Lock(&irql);
1837         if (m_EjectionDeviceList == NULL) {
1838             m_EjectionDeviceList = new (GetDriverGlobals()) FxRelatedDeviceList();
1839 
1840             if (m_EjectionDeviceList != NULL) {
1841                 status = STATUS_SUCCESS;
1842             }
1843             else {
1844                 status = STATUS_INSUFFICIENT_RESOURCES;
1845                 DoTraceLevelMessage(
1846                     GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
1847                     "Could not allocate ejection device list for PDO WDFDEVICE %p",
1848 
1849                     m_Device->GetHandle());
1850             }
1851         }
1852         else {
1853             //
1854             // another thread allocated the list already
1855             //
1856             status = STATUS_SUCCESS;
1857         }
1858         Unlock(irql);
1859 
1860         if (!NT_SUCCESS(status)) {
1861             return status;
1862         }
1863     }
1864 
1865     pRelated = new(GetDriverGlobals())
1866         FxRelatedDevice(DependentDevice, GetDriverGlobals());
1867 
1868     if (pRelated == NULL) {
1869         return STATUS_INSUFFICIENT_RESOURCES;
1870     }
1871 
1872     status = m_EjectionDeviceList->Add(GetDriverGlobals(), pRelated);
1873 
1874     if (NT_SUCCESS(status)) {
1875         //
1876         // EjectRelations are queried automatically by PnP when the device is
1877         // going to be ejected.  No need to tell pnp that the list changed
1878         // until it needs to query for it.
1879         //
1880         DO_NOTHING();
1881     }
1882     else {
1883         pRelated->DeleteFromFailedCreate();
1884     }
1885 
1886     return status;
1887 }
1888 
1889 VOID
1890 FxPkgPdo::RemoveEjectionDevice(
1891     __in MdDeviceObject DependentDevice
1892     )
1893 {
1894     if (m_EjectionDeviceList != NULL) {
1895         m_EjectionDeviceList->Remove(GetDriverGlobals(), DependentDevice);
1896     }
1897 
1898     //
1899     // EjectRelations are queried automatically by PnP when the device is
1900     // going to be ejected.  No need to tell pnp that the list changed
1901     // until it needs to query for it.
1902     //
1903 }
1904 
1905 VOID
1906 FxPkgPdo::ClearEjectionDevicesList(
1907     VOID
1908     )
1909 {
1910     FxRelatedDevice* pEntry;
1911 
1912     if (m_EjectionDeviceList != NULL) {
1913         m_EjectionDeviceList->LockForEnum(GetDriverGlobals());
1914         while ((pEntry = m_EjectionDeviceList->GetNextEntry(NULL)) != NULL) {
1915             m_EjectionDeviceList->Remove(GetDriverGlobals(),
1916                                          pEntry->GetDevice());
1917         }
1918         m_EjectionDeviceList->UnlockFromEnum(GetDriverGlobals());
1919     }
1920 
1921     //
1922     // EjectRelations are queried automatically by PnP when the device is
1923     // going to be ejected.  No need to tell pnp that the list changed
1924     // until it needs to query for it.
1925     //
1926 }
1927