1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     supportKM.cpp
8 
9 Abstract:
10 
11     This module implements the pnp support routines.
12 
13 Author:
14 
15 
16 Environment:
17 
18     kernel mode only
19 
20 Revision History:
21 
22 --*/
23 
24 #include "../pnppriv.hpp"
25 #include <wdmguid.h>
26 
27 #if defined(EVENT_TRACING)
28 #include "supportKM.tmh"
29 #endif
30 
31 VOID
32 CopyQueryInterfaceToIrpStack(
33     __in PPOWER_THREAD_INTERFACE PowerThreadInterface,
34     __in FxIrp* Irp
35     )
36 {
37     PIO_STACK_LOCATION stack;
38 
39     stack = Irp->GetCurrentIrpStackLocation();
40 
41     RtlCopyMemory(stack->Parameters.QueryInterface.Interface,
42         PowerThreadInterface,
43         PowerThreadInterface->Interface.Size);
44 }
45 
46 _Must_inspect_result_
47 NTSTATUS
48 GetStackCapabilities(
49     __in PFX_DRIVER_GLOBALS DriverGlobals,
50     __in MxDeviceObject* DeviceInStack,
51     __in_opt PD3COLD_SUPPORT_INTERFACE D3ColdInterface,
52     __out PSTACK_DEVICE_CAPABILITIES Capabilities
53     )
54 {
55     ULONG i;
56     FxAutoIrp irp;
57     NTSTATUS status;
58 
59     ASSERT(Capabilities != NULL);
60 
61     status = STATUS_INSUFFICIENT_RESOURCES;
62 
63     //
64     // Normally this would be assigned to a local variable on the stack.  Since
65     // query caps iteratively moves down the device's tree until it hits the
66     // root.  As such, stack usage is consumed quickly.  While this is only one
67     // pointer value we are saving, it does eventually add up on very deep stacks.
68     //
69     DeviceInStack->SetObject(DeviceInStack->GetAttachedDeviceReference());
70     if (DeviceInStack->GetObject() == NULL) {
71         goto Done;
72     }
73 
74     irp.SetIrp(FxIrp::AllocateIrp(DeviceInStack->GetStackSize()));
75     if (irp.GetIrp() == NULL) {
76         goto Done;
77     }
78 
79     //
80     // Initialize device capabilities.
81     //
82     RtlZeroMemory(Capabilities, sizeof(STACK_DEVICE_CAPABILITIES));
83     Capabilities->DeviceCaps.Size = sizeof(DEVICE_CAPABILITIES);
84     Capabilities->DeviceCaps.Version  =  1;
85     Capabilities->DeviceCaps.Address  = (ULONG) -1;
86     Capabilities->DeviceCaps.UINumber = (ULONG) -1;
87 
88     //
89     // Initialize the Irp.
90     //
91     irp.SetStatus(STATUS_NOT_SUPPORTED);
92 
93     irp.ClearNextStack();
94     irp.SetMajorFunction(IRP_MJ_PNP);
95     irp.SetMinorFunction(IRP_MN_QUERY_CAPABILITIES);
96     irp.SetParameterDeviceCapabilities(&Capabilities->DeviceCaps);
97 
98     status = irp.SendIrpSynchronously(DeviceInStack->GetObject());
99 
100     if (!NT_SUCCESS(status)) {
101         DoTraceLevelMessage(
102             DriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
103             "Failed to get DEVICE_CAPABILITIES from !devobj %p, %!STATUS!",
104             DeviceInStack->GetObject(), status);
105         goto Done;
106     }
107 
108     //
109     // Invoke the D3cold support interface.  If present, it will tell
110     // us specifically which D-states will work for generating wake signals
111     // from specific S-states.
112     //
113     // Earlier versions of WDF didn't make this query, so for compatibility,
114     // we only make it now if the driver was built against WDF 1.11 or
115     // later.  In truth, this just shifts the failure from initialization
116     // time to run time, because the information that we're presumably
117     // getting from the BIOS with this interrogation is saying that the
118     // code in earlier verisions of WDF would have blindly enabled a device
119     // for wake which simply wasn't capable of generating its wake signal
120     // from the chosen D-state.  Thus the device would have been put into
121     // a low power state and then failed to resume in response to its wake
122     // signal.
123     //
124 
125     for (i = 0; i <= PowerSystemHibernate; i++) {
126         Capabilities->DeepestWakeableDstate[i] = DeviceWakeDepthMaximum;
127     }
128 
129     if (ARGUMENT_PRESENT(D3ColdInterface) &&
130         (D3ColdInterface->GetIdleWakeInfo != NULL) &&
131         DriverGlobals->IsVersionGreaterThanOrEqualTo(1,11)) {
132 
133         DEVICE_WAKE_DEPTH deepestWakeableDstate;
134 
135         for (i = PowerSystemWorking; i <= PowerSystemHibernate; i++) {
136 
137             //
138             // Failure from D3ColdInterface->GetIdleWakeInfo just means that
139             // the bus drivers didn't have any information beyond what can
140             // gleaned from the older Query-Capabilities code path.
141             //
142             // In specific ACPI terms, ACPI will respond to this function with
143             // success whenever there is an _SxW object (where x is the sleep
144             // state, a value from 0 to 4.)
145             //
146             // PCI will respond to this interface if ACPI doesn't override its
147             // answer and if a parent bus is capable of leaving D0 in S0, and
148             // if the PCI device has
149             status = D3ColdInterface->GetIdleWakeInfo(
150                 D3ColdInterface->Context,
151                 (SYSTEM_POWER_STATE)i,
152                 &deepestWakeableDstate
153                 );
154 
155             if (NT_SUCCESS(status)) {
156                 Capabilities->DeepestWakeableDstate[i] = deepestWakeableDstate;
157             }
158         }
159     }
160 
161     status = STATUS_SUCCESS;
162 
163 Done:
164     if (DeviceInStack->GetObject() != NULL) {
165         DeviceInStack->DereferenceObject();
166     }
167 
168     return status;
169 }
170 
171 VOID
172 SetD3ColdSupport(
173     __in PFX_DRIVER_GLOBALS DriverGlobals,
174     __in MxDeviceObject* DeviceInStack,
175     __in PD3COLD_SUPPORT_INTERFACE D3ColdInterface,
176     __in BOOLEAN UseD3Cold
177     )
178 {
179     UNREFERENCED_PARAMETER(DriverGlobals);
180     UNREFERENCED_PARAMETER(DeviceInStack);
181 
182     if (D3ColdInterface->SetD3ColdSupport != NULL) {
183         D3ColdInterface->SetD3ColdSupport(D3ColdInterface->Context, UseD3Cold);
184     }
185 }
186 
187 _Must_inspect_result_
188 PVOID
189 GetIoMgrObjectForWorkItemAllocation(
190     VOID
191     )
192 /*++
193 Routine description:
194     Returns an IO manager object that can be passed in to IoAllocateWorkItem
195 
196 Arguments:
197     None
198 
199 Return value:
200     Pointer to the object that can be passed in to IoAllocateWorkItem
201 --*/
202 {
203     // return (PVOID) FxLibraryGlobals.DriverObject;
204     // __REACTOS__ : we don't have a WDF driver object here, use a child one
205 
206     PFX_DRIVER_GLOBALS fxDriverGlobals = GetFxDriverGlobals(WdfDriverGlobals);
207     return fxDriverGlobals->Driver->GetDriverObject();
208 }
209 
210 BOOLEAN
211 IdleTimeoutManagement::_SystemManagedIdleTimeoutAvailable(
212     VOID
213     )
214 {
215     return (NULL != FxLibraryGlobals.PoxRegisterDevice);
216 }
217 
218 _Must_inspect_result_
219 NTSTATUS
220 SendDeviceUsageNotification(
221     __in MxDeviceObject* RelatedDevice,
222     __inout FxIrp* RelatedIrp,
223     __in MxWorkItem* Workitem,
224     __in FxIrp* OriginalIrp,
225     __in BOOLEAN Revert
226     )
227 {
228     NTSTATUS status;
229 
230     //
231     // use workitem if available
232     //
233     if (Workitem->GetWorkItem() != NULL) {
234         FxUsageWorkitemParameters param;
235 
236         param.RelatedDevice = RelatedDevice;
237         param.RelatedIrp = RelatedIrp;
238         param.OriginalIrp = OriginalIrp;
239         param.Revert = Revert;
240 
241         //
242         // Kick off to another thread
243         //
244         Workitem->Enqueue(_DeviceUsageNotificationWorkItem, &param);
245 
246         //
247         // wait for the workitem to finish
248         //
249         param.Event.EnterCRAndWaitAndLeave();
250 
251         status = param.Status;
252     }
253     else {
254         status = SendDeviceUsageNotificationWorker(RelatedDevice,
255                                                    RelatedIrp,
256                                                    OriginalIrp,
257                                                    Revert);
258     }
259 
260     return status;
261 }
262 
263 NTSTATUS
264 SendDeviceUsageNotificationWorker(
265     __in MxDeviceObject* RelatedDevice,
266     __inout FxIrp* RelatedIrp,
267     __in FxIrp* OriginalIrp,
268     __in BOOLEAN Revert
269     )
270 {
271     MxDeviceObject relatedTopOfStack;
272     NTSTATUS status;
273 
274     relatedTopOfStack.SetObject(RelatedDevice->GetAttachedDeviceReference());
275     ASSERT(relatedTopOfStack.GetObject() != NULL);
276 
277     //
278     // Initialize the new IRP with the stack data from the current IRP and
279     // and send it to the parent stack.
280     //
281     RelatedIrp->InitNextStackUsingStack(OriginalIrp);
282 
283     if (Revert) {
284         RelatedIrp->SetParameterUsageNotificationInPath(
285             !RelatedIrp->GetNextStackParameterUsageNotificationInPath());
286     }
287 
288     RelatedIrp->SetStatus(STATUS_NOT_SUPPORTED);
289 
290     status = RelatedIrp->SendIrpSynchronously(relatedTopOfStack.GetObject());
291 
292     relatedTopOfStack.DereferenceObject();
293 
294     return status;
295 }
296 
297 VOID
298 STDCALL
299 _DeviceUsageNotificationWorkItem(
300     __in MdDeviceObject DeviceObject,
301     __in PVOID Context
302     )
303 {
304     FxUsageWorkitemParameters* param;
305     NTSTATUS status;
306 
307     UNREFERENCED_PARAMETER(DeviceObject);
308 
309     param = (FxUsageWorkitemParameters*) Context;
310 
311     status = SendDeviceUsageNotificationWorker(param->RelatedDevice,
312                                                param->RelatedIrp,
313                                                param->OriginalIrp,
314                                                param->Revert);
315 
316     //
317     // capture status in notification object
318     //
319     param->Status = status;
320 
321     //
322     // set event to allow the origial notifcation thread to proceed
323     //
324     param->Event.Set();
325 }
326 
327