1 // 2 // Copyright (C) Microsoft. All rights reserved. 3 // 4 #include "../pnppriv.hpp" 5 6 #include <initguid.h> 7 #include <wdmguid.h> 8 9 extern "C" { 10 #if defined(EVENT_TRACING) 11 #include "FxPkgPnpKM.tmh" 12 #endif 13 } 14 15 NTSTATUS 16 FxPkgPnp::FilterResourceRequirements( 17 __in IO_RESOURCE_REQUIREMENTS_LIST **IoList 18 ) 19 /*++ 20 21 Routine Description: 22 23 This routine traverses one or more alternate _IO_RESOURCE_LISTs in the input 24 IO_RESOURCE_REQUIREMENTS_LIST looking for interrupt descriptor and applies 25 the policy set by driver in the interrupt object to the resource descriptor. 26 27 LBI - Line based interrupt 28 MSI - Message Signalled interrupt 29 30 Here are the assumptions made about the order of descriptors. 31 32 - An IoRequirementList can have one or more alternate IoResourceList 33 - Each IoResourceList can have one or more resource descriptors 34 - A descriptor can be default (unique), preferred, or alternate descriptors 35 - A preferred descriptor can have zero or more alternate descriptors (P, A, A, A..) 36 - In an IoResourceList, there can be one or more LBI descriptors 37 (non-pci devices)(P,A,P,A) 38 - In an IoResourceList, there can be only one preferred MSI 2.2 39 (single or multi message) descriptor 40 - In an IoResourceList, there cannot be MSI2.2 and MSI-X descriptors 41 - In an IoResourceList, there can be one or more MSI-X descriptor 42 - An alternate descriptor cannot be a very first descriptor in the list 43 44 45 Now with that assumption, this routines parses the list looking for interrupt 46 descriptor. 47 48 - If it finds a LBI, it starts with the very first interrupt object and applies 49 the policy set by the driver to the resource descriptor. 50 - If it's finds an MSI2.2 then it starts with the first interrupt object and applies 51 the policy. If the MSI2.2 is a multi-message one then it loops thru looking for 52 as many interrupt object as there are messages. It doesn't fail the IRP, if the 53 interrupt objects are less than the messages. 54 - If there is an alternate descriptor then it applies the same policy from the 55 interrupt object that it used for the preceding preferred descriptor. 56 - Framework always uses FULLY_SPECIFIED connection type for both LBI and MSI 57 interrupts including MSI-X 58 - Framework will apply the policy on the descriptor set by the driver only 59 if the policy is already not included in the resource descriptor. This is 60 to allow the policy set in the registry to take precedence over the hard 61 coded driver policy. 62 - If the driver registers filter resource requirement and applies the policy 63 on its own (by escaping to WDM) then framework doesn't override that. 64 65 Arguments: 66 67 IoList - Pointer to the list part of an IRP_MN_FILTER_RESOURCE_REQUIREMENTS. 68 69 Return Value: 70 71 NTSTATUS 72 73 --*/ 74 { 75 ULONG altResListIndex; 76 PIO_RESOURCE_REQUIREMENTS_LIST pIoRequirementList; 77 PIO_RESOURCE_LIST pIoResList; 78 79 pIoRequirementList = *IoList; 80 81 if (pIoRequirementList == NULL) { 82 return STATUS_SUCCESS; 83 } 84 85 if (IsListEmpty(&m_InterruptListHead)) { 86 // 87 // No interrupt objects created to filter resource requirements. 88 // 89 return STATUS_SUCCESS; 90 } 91 92 pIoResList = pIoRequirementList->List; 93 94 // 95 // Parse one or more alternative resource lists. 96 // 97 for (altResListIndex = 0; 98 altResListIndex < pIoRequirementList->AlternativeLists; 99 altResListIndex++) { 100 PLIST_ENTRY pIntListEntryForMSI; 101 PLIST_ENTRY pIntListEntryForLBI; 102 BOOLEAN multiMessageMSI22Found; 103 BOOLEAN previousDescMSI; 104 ULONG descIndex; 105 106 multiMessageMSI22Found = FALSE; 107 previousDescMSI = FALSE; 108 109 pIntListEntryForMSI = &m_InterruptListHead; 110 pIntListEntryForLBI = &m_InterruptListHead; 111 112 // 113 // Traverse each _IO_RESOURCE_LISTs looking for interrupt descriptors 114 // and call FilterResourceRequirements method so that it can apply 115 // policy set on the interrupt object into the resource-descriptor. 116 // 117 118 for (descIndex = 0; descIndex < pIoResList->Count; descIndex++) { 119 ULONG messageCount; 120 PIO_RESOURCE_DESCRIPTOR pIoDesc; 121 FxInterrupt* pInterruptInstance; 122 123 pIoDesc = &pIoResList->Descriptors[descIndex]; 124 125 switch (pIoDesc->Type) { 126 case CmResourceTypeInterrupt: 127 128 if (FxInterrupt::_IsMessageInterrupt(pIoDesc->Flags)) { 129 130 previousDescMSI = TRUE; 131 132 // 133 // We will advance to the next interrupt object if the resource 134 // is not an alternate resource descriptor. A resource list can 135 // have a preferred and zero or more alternate resource descriptors 136 // for the same resource. We need to apply the same policy on the 137 // alternate desc that we applied on the preferred one in case one 138 // of the alernate desc is selected for this device. An alternate 139 // resource descriptor can't be the first descriptor in a list. 140 // 141 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 142 pIntListEntryForMSI = pIntListEntryForMSI->Flink; 143 } 144 145 if (pIntListEntryForMSI == &m_InterruptListHead) { 146 DoTraceLevelMessage( 147 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 148 "Not enough interrupt objects created for MSI by WDFDEVICE 0x%p ", 149 m_Device->GetHandle()); 150 break; 151 } 152 153 pInterruptInstance = CONTAINING_RECORD(pIntListEntryForMSI, FxInterrupt, m_PnpList); 154 messageCount = pIoDesc->u.Interrupt.MaximumVector - pIoDesc->u.Interrupt.MinimumVector + 1; 155 156 if (messageCount > 1) { 157 // 158 // PCI spec guarantees that there can be only one preferred/default 159 // MSI 2.2 descriptor in a single list. 160 // 161 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 162 #if DBG 163 ASSERT(multiMessageMSI22Found == FALSE); 164 #else 165 UNREFERENCED_PARAMETER(multiMessageMSI22Found); 166 #endif 167 multiMessageMSI22Found = TRUE; 168 169 } 170 } 171 else { 172 // 173 // This is either single message MSI 2.2 or MSI-X interrupts 174 // 175 DO_NOTHING(); 176 } 177 178 pInterruptInstance->FilterResourceRequirements(pIoDesc); 179 } 180 else { 181 182 // 183 // We will advance to next interrupt object if the desc is not an alternate 184 // descriptor. For non PCI devices, the first LBI interrupt desc can't be an 185 // alternate descriptor. 186 // 187 if ((pIoDesc->Option & IO_RESOURCE_ALTERNATIVE) == 0) { 188 pIntListEntryForLBI = pIntListEntryForLBI->Flink; 189 } 190 191 // 192 // An LBI can be first alternate resource if there are preceding MSI(X) descriptors 193 // listed in the list. In that case, this descriptor is the alternate interrupt resource 194 // for all of the MSI messages. As a result, we will use the first interrupt object from 195 // the list if this ends up being assigned by the system instead of MSI. 196 // 197 if (previousDescMSI) { 198 ASSERT(pIoDesc->Option & IO_RESOURCE_ALTERNATIVE); 199 pIntListEntryForLBI = m_InterruptListHead.Flink; 200 previousDescMSI = FALSE; 201 } 202 203 // 204 // There can be one or more LBI interrupts and each LBI interrupt 205 // could have zero or more alternate descriptors. 206 // 207 if (pIntListEntryForLBI == &m_InterruptListHead) { 208 DoTraceLevelMessage( 209 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGPNP, 210 "Not enough interrupt objects created for LBI by WDFDEVICE 0x%p ", 211 m_Device->GetHandle()); 212 break; 213 } 214 215 pInterruptInstance = CONTAINING_RECORD(pIntListEntryForLBI, FxInterrupt, m_PnpList); 216 217 pInterruptInstance->FilterResourceRequirements(pIoDesc); 218 } 219 220 break; 221 222 default: 223 break; 224 } 225 } 226 227 // 228 // Since the Descriptors is a variable length list, you cannot get to the next 229 // alternate list by doing pIoRequirementList->List[altResListIndex]. 230 // Descriptors[descIndex] will now point to the end of the descriptor list. 231 // If there is another alternate list, it would be begin there. 232 // 233 pIoResList = (PIO_RESOURCE_LIST) &pIoResList->Descriptors[descIndex]; 234 } 235 236 return STATUS_SUCCESS; 237 } 238 239 _Must_inspect_result_ 240 NTSTATUS 241 FxPkgPnp::AllocateDmaEnablerList( 242 VOID 243 ) 244 { 245 FxSpinLockTransactionedList* pList; 246 NTSTATUS status; 247 KIRQL irql; 248 249 if (m_DmaEnablerList != NULL) { 250 return STATUS_SUCCESS; 251 } 252 253 Lock(&irql); 254 if (m_DmaEnablerList == NULL) { 255 pList = new (GetDriverGlobals()) FxSpinLockTransactionedList(); 256 257 if (pList != NULL) { 258 m_DmaEnablerList = pList; 259 status = STATUS_SUCCESS; 260 } 261 else { 262 status = STATUS_INSUFFICIENT_RESOURCES; 263 } 264 } 265 else { 266 // 267 // Already have a DMA list 268 // 269 status = STATUS_SUCCESS; 270 } 271 Unlock(irql); 272 273 return status; 274 } 275 276 VOID 277 FxPkgPnp::AddDmaEnabler( 278 __in FxDmaEnabler* Enabler 279 ) 280 { 281 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 282 "Adding DmaEnabler %p, WDFDMAENABLER %p", 283 Enabler, Enabler->GetObjectHandle()); 284 285 m_DmaEnablerList->Add(GetDriverGlobals(), &Enabler->m_TransactionLink); 286 } 287 288 VOID 289 FxPkgPnp::RemoveDmaEnabler( 290 __in FxDmaEnabler* Enabler 291 ) 292 { 293 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 294 "Removing DmaEnabler %p, WDFDMAENABLER %p", 295 Enabler, Enabler->GetObjectHandle()); 296 297 m_DmaEnablerList->Remove(GetDriverGlobals(), &Enabler->m_TransactionLink); 298 } 299 300 VOID 301 FxPkgPnp::WriteStateToRegistry( 302 __in HANDLE RegKey, 303 __in PUNICODE_STRING ValueName, 304 __in ULONG Value 305 ) 306 { 307 ZwSetValueKey(RegKey, ValueName, 0, REG_DWORD, &Value, sizeof(Value)); 308 } 309 310 // NTSTATUS __REACTOS__ 311 // FxPkgPnp::UpdateWmiInstanceForS0Idle( 312 // __in FxWmiInstanceAction Action 313 // ) 314 // { 315 // FxWmiProvider* pProvider; 316 // NTSTATUS status; 317 318 // switch(Action) { 319 // case AddInstance: 320 // if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance == NULL) { 321 // FxWmiInstanceInternalCallbacks cb; 322 323 // cb.SetInstance = _S0IdleSetInstance; 324 // cb.QueryInstance = _S0IdleQueryInstance; 325 // cb.SetItem = _S0IdleSetItem; 326 327 // status = RegisterPowerPolicyWmiInstance( 328 // &GUID_POWER_DEVICE_ENABLE, 329 // &cb, 330 // &m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance); 331 332 // if (!NT_SUCCESS(status)) { 333 // return status; 334 // } 335 // } 336 // else { 337 // pProvider = m_PowerPolicyMachine.m_Owner->m_IdleSettings. 338 // WmiInstance->GetProvider(); 339 340 // // 341 // // Enable the WMI GUID by adding the instance back to the provider's 342 // // list. If there is an error, ignore it. It just means we were 343 // // racing with another thread removing or adding the instance. 344 // // 345 // (void) pProvider->AddInstance( 346 // m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance, 347 // TRUE 348 // ); 349 // } 350 // break; 351 352 // case RemoveInstance: 353 // if (m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance != NULL) { 354 // // 355 // // Disable the WMI guid by removing it from the provider's list of 356 // // instances. 357 // // 358 // pProvider = m_PowerPolicyMachine.m_Owner->m_IdleSettings. 359 // WmiInstance->GetProvider(); 360 361 // pProvider->RemoveInstance( 362 // m_PowerPolicyMachine.m_Owner->m_IdleSettings.WmiInstance 363 // ); 364 // } 365 // break; 366 367 // default: 368 // ASSERT(FALSE); 369 // break; 370 // } 371 372 // return STATUS_SUCCESS;; 373 // } 374 375 VOID 376 FxPkgPnp::ReadRegistryS0Idle( 377 __in PCUNICODE_STRING ValueName, 378 __out BOOLEAN *Enabled 379 ) 380 { 381 NTSTATUS status; 382 FxAutoRegKey hKey; 383 384 status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_READ); 385 386 // 387 // Modify the value of Enabled only if success 388 // 389 if (NT_SUCCESS(status)) { 390 ULONG value; 391 392 status = FxRegKey::_QueryULong( 393 hKey.m_Key, ValueName, &value); 394 395 if (NT_SUCCESS(status)) { 396 // 397 // Normalize the ULONG value into a BOOLEAN 398 // 399 *Enabled = (value == FALSE) ? FALSE : TRUE; 400 } 401 } 402 } 403 404 // NTSTATUS __REACTOS__ 405 // FxPkgPnp::UpdateWmiInstanceForSxWake( 406 // __in FxWmiInstanceAction Action 407 // ) 408 // { 409 // FxWmiProvider* pProvider; 410 // NTSTATUS status; 411 412 // switch(Action) { 413 // case AddInstance: 414 // if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance == NULL) { 415 // FxWmiInstanceInternalCallbacks cb; 416 417 // cb.SetInstance = _SxWakeSetInstance; 418 // cb.QueryInstance = _SxWakeQueryInstance; 419 // cb.SetItem = _SxWakeSetItem; 420 421 // status = RegisterPowerPolicyWmiInstance( 422 // &GUID_POWER_DEVICE_WAKE_ENABLE, 423 // &cb, 424 // &m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance); 425 426 // if (!NT_SUCCESS(status)) { 427 // return status; 428 // } 429 // } else { 430 // pProvider = m_PowerPolicyMachine.m_Owner->m_WakeSettings. 431 // WmiInstance->GetProvider(); 432 433 // // 434 // // Enable the WMI GUID by adding the instance back to the provider's 435 // // list. If there is an error, ignore it. It just means we were 436 // // racing with another thread removing or adding the instance. 437 // // 438 // (void) pProvider->AddInstance( 439 // m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance, 440 // TRUE 441 // ); 442 // } 443 // break; 444 445 // case RemoveInstance: 446 // if (m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance != NULL) { 447 // // 448 // // Disable the WMI guid by removing it from the provider's list of 449 // // instances. 450 // // 451 // pProvider = m_PowerPolicyMachine.m_Owner->m_WakeSettings. 452 // WmiInstance->GetProvider(); 453 454 // pProvider->RemoveInstance( 455 // m_PowerPolicyMachine.m_Owner->m_WakeSettings.WmiInstance 456 // ); 457 // } 458 // break; 459 460 // default: 461 // ASSERT(FALSE); 462 // break; 463 // } 464 465 // return STATUS_SUCCESS; 466 // } 467 468 VOID 469 FxPkgPnp::ReadRegistrySxWake( 470 __in PCUNICODE_STRING ValueName, 471 __out BOOLEAN *Enabled 472 ) 473 { 474 FxAutoRegKey hKey; 475 NTSTATUS status; 476 477 status = m_Device->OpenSettingsKey(&hKey.m_Key, STANDARD_RIGHTS_READ); 478 479 // 480 // Modify the value of Enabled only if success 481 // 482 if (NT_SUCCESS(status)) { 483 ULONG value; 484 485 status = FxRegKey::_QueryULong( 486 hKey.m_Key, ValueName, &value); 487 488 if (NT_SUCCESS(status)) { 489 // 490 // Normalize the ULONG value into a BOOLEAN 491 // 492 *Enabled = (value == FALSE) ? FALSE : TRUE; 493 } 494 } 495 } 496 497 VOID 498 PnpPassThroughQIWorker( 499 __in MxDeviceObject* Device, 500 __inout FxIrp* Irp, 501 __inout FxIrp* ForwardIrp 502 ) 503 { 504 PIO_STACK_LOCATION pFwdStack, pCurStack; 505 506 pCurStack = Irp->GetCurrentIrpStackLocation(); 507 508 ForwardIrp->SetStatus(STATUS_NOT_SUPPORTED); 509 510 pFwdStack = ForwardIrp->GetNextIrpStackLocation(); 511 pFwdStack->MajorFunction = Irp->GetMajorFunction(); 512 pFwdStack->MinorFunction = Irp->GetMinorFunction(); 513 514 RtlCopyMemory(&pFwdStack->Parameters.QueryInterface, 515 &pCurStack->Parameters.QueryInterface, 516 sizeof(pFwdStack->Parameters.QueryInterface)); 517 518 ForwardIrp->SetInformation(Irp->GetInformation()); 519 ForwardIrp->SendIrpSynchronously(Device->GetObject()); 520 521 pFwdStack = ForwardIrp->GetNextIrpStackLocation(); 522 523 RtlCopyMemory(&pCurStack->Parameters.QueryInterface, 524 &pFwdStack->Parameters.QueryInterface, 525 sizeof(pCurStack->Parameters.QueryInterface)); 526 } 527 528 VOID 529 FxPkgPnp::RevokeDmaEnablerResources( 530 __in FxDmaEnabler *DmaEnabler 531 ) 532 { 533 // DmaEnabler->RevokeResources(); 534 ROSWDFNOTIMPLEMENTED; 535 } 536 537 VOID 538 FxPkgPnp::QueryForD3ColdInterface( 539 VOID 540 ) 541 { 542 MxDeviceObject deviceObject; 543 PDEVICE_OBJECT topOfStack; 544 PDEVICE_OBJECT pdo; 545 FxAutoIrp irp; 546 NTSTATUS status; 547 548 // 549 // This function can be invoked multiple times, particularly if filters 550 // send IRP_MN_QUERY_CAPABILITIES. So bail out if the interface has already 551 // been acquired. 552 // 553 554 if ((m_D3ColdInterface.InterfaceDereference != NULL) || 555 (m_D3ColdInterface.GetIdleWakeInfo != NULL) || 556 (m_D3ColdInterface.SetD3ColdSupport != NULL)) { 557 return; 558 } 559 560 pdo = m_Device->GetPhysicalDevice(); 561 562 if (pdo == NULL) { 563 return; 564 } 565 566 // 567 // Get the top of stack device object, even though normal filters and the 568 // FDO may not have been added to the stack yet to ensure that this 569 // query-interface is seen by bus filters. Specifically, in a PCI device 570 // which is soldered to the motherboard, ACPI will be on the stack and it 571 // needs to see this IRP. 572 // 573 topOfStack = IoGetAttachedDeviceReference(pdo); 574 deviceObject.SetObject(topOfStack); 575 if (deviceObject.GetObject() != NULL) { 576 irp.SetIrp(FxIrp::AllocateIrp(deviceObject.GetStackSize())); 577 if (irp.GetIrp() == NULL) { 578 579 DoTraceLevelMessage( 580 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGPNP, 581 "Failed to allocate IRP to get D3COLD_SUPPORT_INTERFACE from !devobj %p", 582 pdo); 583 } else { 584 585 // 586 // Initialize the Irp 587 // 588 irp.SetStatus(STATUS_NOT_SUPPORTED); 589 590 irp.ClearNextStack(); 591 irp.SetMajorFunction(IRP_MJ_PNP); 592 irp.SetMinorFunction(IRP_MN_QUERY_INTERFACE); 593 irp.SetParameterQueryInterfaceType(&GUID_D3COLD_SUPPORT_INTERFACE); 594 irp.SetParameterQueryInterfaceVersion(D3COLD_SUPPORT_INTERFACE_VERSION); 595 irp.SetParameterQueryInterfaceSize(sizeof(m_D3ColdInterface)); 596 irp.SetParameterQueryInterfaceInterfaceSpecificData(NULL); 597 irp.SetParameterQueryInterfaceInterface((PINTERFACE)&m_D3ColdInterface); 598 599 status = irp.SendIrpSynchronously(deviceObject.GetObject()); 600 601 if (!NT_SUCCESS(status)) { 602 DoTraceLevelMessage( 603 GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGPNP, 604 "!devobj %p declined to supply D3COLD_SUPPORT_INTERFACE", 605 pdo); 606 607 RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface)); 608 } 609 } 610 } 611 ObDereferenceObject(topOfStack); 612 } 613 614 VOID 615 FxPkgPnp::DropD3ColdInterface( 616 VOID 617 ) 618 { 619 if (m_D3ColdInterface.InterfaceDereference != NULL) { 620 m_D3ColdInterface.InterfaceDereference(m_D3ColdInterface.Context); 621 } 622 623 RtlZeroMemory(&m_D3ColdInterface, sizeof(m_D3ColdInterface)); 624 } 625 626