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