1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxPkgPdoKM.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     Kernel mode only
20 
21 Revision History:
22 
23 
24 
25 --*/
26 
27 #include "..\pnppriv.hpp"
28 #include <wdmguid.h>
29 
30 // Tracing support
31 #if defined(EVENT_TRACING)
32 extern "C" {
33 #include "FxPkgPdoKM.tmh"
34 }
35 #endif
36 
37 _Must_inspect_result_
38 NTSTATUS
39 FxPkgPdo::_PnpQueryResources(
40     __inout FxPkgPnp* This,
41     __inout FxIrp *Irp
42     )
43 {
44     return ((FxPkgPdo*) This)->PnpQueryResources(Irp);
45 }
46 
47 _Must_inspect_result_
48 NTSTATUS
49 FxPkgPdo::PnpQueryResources(
50     __inout FxIrp *Irp
51     )
52 
53 /*++
54 
55 Routine Description:
56 
57     This method is invoked in response to a Pnp QueryResources IRP.  We return
58     the resources that the device is currently consuming.
59 
60 Arguments:
61 
62     Irp - a pointer to the FxIrp
63 
64 Returns:
65 
66     NTSTATUS
67 
68 --*/
69 
70 {
71     FxCmResList *pResList = NULL;
72     PCM_RESOURCE_LIST pWdmResourceList;
73     WDFCMRESLIST list;
74     NTSTATUS status;
75 
76     //
77     // It is only necessary to create a collection if the caller is interested
78     // in this callback.
79     //
80     if (m_DeviceResourcesQuery.m_Method == NULL) {
81         return CompletePnpRequest(Irp, Irp->GetStatus());
82     }
83 
84     pWdmResourceList = NULL;
85 
86     status = FxCmResList::_CreateAndInit(&pResList,
87                                          GetDriverGlobals(),
88                                          m_Device,
89                                          WDF_NO_OBJECT_ATTRIBUTES,
90                                          FxResourceAllAccessAllowed);
91     if (!NT_SUCCESS(status)) {
92         goto exit;
93     }
94 
95     status = pResList->Commit(NULL, (PWDFOBJECT) &list);
96 
97     if (NT_SUCCESS(status)) {
98         status = m_DeviceResourcesQuery.Invoke(
99             m_Device->GetHandle(), list);
100 
101         if (NT_SUCCESS(status)) {
102             //
103             // Walk the resource collection and create the appropriate
104             // CM_RESOURCE_LIST.
105             //
106             if (pResList->Count()) {
107                 pWdmResourceList = pResList->CreateWdmList();
108             }
109             else {
110                 //
111                 // The driver didn't add any resources, so we'll just
112                 // ignore this Irp.
113                 //
114                 // Return that status that was passed in.
115                 //
116                 status = Irp->GetStatus();
117                 pWdmResourceList = (PCM_RESOURCE_LIST) Irp->GetInformation();
118             }
119         }
120     }
121 
122     pResList->DeleteObject();
123 
124     exit:
125         Irp->SetInformation((ULONG_PTR) pWdmResourceList);
126         return CompletePnpRequest(Irp, status);
127 }
128 
129 _Must_inspect_result_
130 NTSTATUS
131 FxPkgPdo::_PnpQueryResourceRequirements(
132     __inout FxPkgPnp* This,
133     __inout FxIrp *Irp
134     )
135 {
136     return ((FxPkgPdo*) This)->PnpQueryResourceRequirements(Irp);
137 }
138 
139 _Must_inspect_result_
140 NTSTATUS
141 FxPkgPdo::PnpQueryResourceRequirements(
142     __inout FxIrp *Irp
143     )
144 
145 /*++
146 
147 Routine Description:
148 
149     This method is invoked in response to a Pnp QueryResourceRequirements IRP.
150     We return the set (of sets) of possible resources that we could accept
151     which would allow our device to work.
152 
153 Arguments:
154 
155     Irp - a pointer to the FxIrp
156 
157 Returns:
158 
159     NTSTATUS
160 
161 --*/
162 
163 {
164     PIO_RESOURCE_REQUIREMENTS_LIST pWdmRequirementsList;
165     FxIoResReqList *pIoResReqList = NULL;
166     PSINGLE_LIST_ENTRY ple;
167     NTSTATUS status;
168 
169     status = STATUS_SUCCESS;
170 
171     m_DeviceInterfaceLock.AcquireLock(GetDriverGlobals());
172 
173 
174 
175 
176     // We know for sure that the PDO is known to pnp now because it has received
177     // this query resource requirements request.
178     m_Device->m_PdoKnown = TRUE;
179 
180     //
181     // Now that it is a known PDO, we can register interfaces on it whose
182     // registration was delayed because the PDO was unknown at the time of the
183     // call to WdfDeviceCreateDeviceInterface. If it is not the first time
184     // then we re-register (see comments below for reason).
185     //
186     for (ple = m_DeviceInterfaceHead.Next; ple != NULL; ple = ple->Next) {
187         FxDeviceInterface *pDeviceInterface;
188 
189         pDeviceInterface = FxDeviceInterface::_FromEntry(ple);
190 
191         //
192         // At this time the interface may be in registered or unregistered state.
193         // The reason being that pnp may unregister the interface from underneath
194         // during the life of PDO (note that drivers never unregister explicitly
195         // as there is no unregistration API, and the scenarios in which it can
196         // happen are given below). Therefore WDF needs to re-register the interface,
197         // otherwise driver may end up with an unregistered interface and fail to
198         // enable interface.
199         //
200         // Pnp can unregister the interface in following cases:
201         // 1. The driver is uninstalled, and re-installed
202         //    In this case, the stack is torn down but the PDO is not
203         //    deleted. Pnp deletes the interface registration, however WDF
204         //    doesn't delete the interface structure because it is deleted as
205         //    part of PDO deletion in destructor. When the driver is re-installed
206         //    Pnp sends another query resource requirements irp and WDF needs to
207         //    re-register at this time.
208         //
209         // 2. Pnp couldn't find a driver and loaded a NULL driver while
210         //    waiting for reinstall to happen from WU. This is similar to case above
211         //    except that the pnp activities are transparent to user.
212         //    In this case, PDO was never started. However it did get
213         //    the query resource requirements irp and therefore its interface
214         //    was registered by WDF. Pnp deleted the interface registration
215         //    when installing NULL driver. When pnp finally finds a driver from
216         //    WU, it sends another query resource requirement irp. At this time,
217         //    WDF needs to re-register.
218         //
219         // In both the above cases, WDF has to re-register (and free previous
220         // sym link) when query resource requirements irp arrives again. Note
221         // that Pnp doesn't delete the interface registration during disable,
222         // s/w surprise-removal, or resource rebalance. In case of resource
223         // rebalance, query resource requirement irp is sent after stop, and
224         // WDF will end up registering again even though pnp did not unregister
225         // the interface. This is fine because kernel API for registration
226         // allows multiple calls to register, and in case registration already
227         // exists it returns informational status (not error status)
228         // STATUS_OBJECT_NAME_EXISTS and also returns the same symbolic link.
229         //
230         //
231         // Free the symbolic link if already present
232         //
233         if (pDeviceInterface->m_SymbolicLinkName.Buffer != NULL) {
234             RtlFreeUnicodeString(&pDeviceInterface->m_SymbolicLinkName);
235             RtlZeroMemory(&pDeviceInterface->m_SymbolicLinkName,
236                           sizeof(pDeviceInterface->m_SymbolicLinkName));
237         }
238 
239         //
240         // Register. Note that if the interface was already registered, the
241         // call to IoRegisterDeviceInterface will return informational status
242         // (not error status) STATUS_OBJECT_NAME_EXISTS and also return the
243         // symbolic link.
244         //
245         status = pDeviceInterface->Register(
246             m_Device->GetPhysicalDevice()
247             );
248 
249         if (!NT_SUCCESS(status)) {
250             DoTraceLevelMessage(
251                 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP,
252                 "could not register device interface on PDO WDFDEVICE %p, "
253                 "!devobj %p, failing IRP_MN_QUERY_RESOURCE_REQUIREMENTS %!STATUS!",
254                 m_Device->GetHandle(),
255                 m_Device->GetDeviceObject(), status);
256             break;
257         }
258     }
259 
260     m_DeviceInterfaceLock.ReleaseLock(GetDriverGlobals());
261 
262     if (!NT_SUCCESS(status)) {
263         return CompletePnpRequest(Irp, status);
264     }
265 
266     //
267     // Driver writer is not interested in this callback, forgoe the allocation
268     // of the FxCollection and complete the request here.
269     //
270     if (m_DeviceResourceRequirementsQuery.m_Method == NULL) {
271         return CompletePnpRequest(Irp, status);
272     }
273 
274     pWdmRequirementsList = NULL;
275 
276     //
277     // Create a collection which will be populated by the
278     // bus driver.
279     //
280     status = FxIoResReqList::_CreateAndInit(&pIoResReqList,
281                                             GetDriverGlobals(),
282                                             WDF_NO_OBJECT_ATTRIBUTES,
283                                             FxResourceAllAccessAllowed);
284     if (!NT_SUCCESS(status)) {
285         goto exit;
286     }
287 
288     WDFIORESREQLIST reqlist;
289 
290     //
291     // Get a handle to the collection that can be passed to the driver.
292     //
293     status = pIoResReqList->Commit(NULL, (PWDFOBJECT) &reqlist);
294 
295     //
296     // We control the object's state, this should never fail
297     //
298     ASSERT(NT_SUCCESS(status));
299     UNREFERENCED_PARAMETER(status);
300 
301     //
302     // Call the driver.  The driver will populate the resource collection
303     // with a set of child collections which will contain each of the
304     // possible resource assignments.
305     //
306     status = m_DeviceResourceRequirementsQuery.Invoke(
307         m_Device->GetHandle(), reqlist);
308 
309     if (NT_SUCCESS(status)) {
310         if (pIoResReqList->Count()) {
311             //
312             // Create a IO_RESOURCE_REQUIREMENTS_LIST based on the
313             // contents of our collection.
314             //
315             pWdmRequirementsList = pIoResReqList->CreateWdmList();
316 
317             if (pWdmRequirementsList != NULL) {
318                 Irp->SetInformation((ULONG_PTR) pWdmRequirementsList);
319             }
320             else {
321                 status = STATUS_INSUFFICIENT_RESOURCES;
322             }
323         }
324         else {
325             //
326             // The driver didn't add any resources, so we'll just
327             // ignore this request.
328             //
329             // Return the status that was passed in.
330             //
331             status = Irp->GetStatus();
332         }
333     }
334 
335     pIoResReqList->DeleteObject();
336 
337     exit:
338         return CompletePnpRequest(Irp, status);
339 }
340 
341 _Must_inspect_result_
342 NTSTATUS
343 FxPkgPdo::_PnpFilterResourceRequirements(
344     __inout FxPkgPnp* This,
345     __inout FxIrp *Irp
346     )
347 /*++
348 
349 Routine Description:
350     Filter resource requirements for the PDO.  A chance to further muck with
351     the resources assigned to the device.
352 
353 Arguments:
354     This - the package
355 
356     Irp - the request
357 
358 Return Value:
359     NTSTATUS
360 
361   --*/
362 {
363     NTSTATUS status;
364 
365     //
366     // Give the Framework objects a pass at the list.
367     //
368     status = ((FxPkgPdo*) This)->FilterResourceRequirements(
369         (PIO_RESOURCE_REQUIREMENTS_LIST*)(&Irp->GetIrp()->IoStatus.Information)
370         );
371 
372     if (NT_SUCCESS(status)) {
373         //
374         // Upon successful internal filtering, return the embedded status.
375         //
376         status = Irp->GetStatus();
377     }
378     else {
379         //
380         // Only on failure do we change the status of the irp
381         //
382         Irp->SetStatus(status);
383     }
384 
385     return ((FxPkgPdo*) This)->CompletePnpRequest(Irp, status);
386 }
387 
388