1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 Module Name: 6 7 FxDmaEnabler.cpp 8 9 Abstract: 10 11 Base for WDF DMA Enabler object 12 13 Environment: 14 15 Kernel mode only. 16 17 Notes: 18 19 20 Revision History: 21 22 --*/ 23 24 #include "fxdmapch.hpp" 25 26 extern "C" { 27 // #include "FxDmaEnabler.tmh" 28 } 29 30 FxDmaEnabler::FxDmaEnabler( 31 __in PFX_DRIVER_GLOBALS FxDriverGlobals 32 ) : 33 FxNonPagedObject(FX_TYPE_DMA_ENABLER, sizeof(FxDmaEnabler), FxDriverGlobals) 34 { 35 RtlZeroMemory(&m_SimplexAdapterInfo, sizeof(FxDmaDescription)); 36 RtlZeroMemory(&m_DuplexAdapterInfo, sizeof(m_DuplexAdapterInfo)); 37 38 // 39 // Transaction link into list of FxDmaEnabler pointers maintained by 40 // FxDevice's pnp package. 41 // 42 m_TransactionLink.SetTransactionedObject(this); 43 44 m_FDO = NULL; 45 m_PDO = NULL; 46 m_CommonBufferAlignment = 0; 47 m_MaxSGElements = WDF_DMA_ENABLER_UNLIMITED_FRAGMENTS; 48 m_IsScatterGather = FALSE; 49 m_IsDuplexTransfer = FALSE; 50 m_IsSGListAllocated = FALSE; 51 m_SGListSize = 0; 52 53 m_IsAdded = FALSE; 54 55 m_EvtDmaEnablerFill.m_Method = NULL; 56 m_EvtDmaEnablerFlush.m_Method = NULL; 57 m_EvtDmaEnablerEnable.m_Method = NULL; 58 m_EvtDmaEnablerDisable.m_Method = NULL; 59 m_EvtDmaEnablerSelfManagedIoStart.m_Method = NULL; 60 m_EvtDmaEnablerSelfManagedIoStop.m_Method = NULL; 61 62 m_DmaEnablerFillFailed = FALSE; 63 m_DmaEnablerEnableFailed = FALSE; 64 m_DmaEnablerSelfManagedIoStartFailed = FALSE; 65 66 RtlZeroMemory(&m_SGList, sizeof(m_SGList)); 67 68 MarkDisposeOverride(ObjectDoNotLock); 69 } 70 71 FxDmaEnabler::~FxDmaEnabler() 72 { 73 if (m_IsSGListAllocated) { 74 if (m_IsScatterGather) { 75 // 76 // Scatter Gather profile - cleanup the lookaside list 77 // 78 ExDeleteNPagedLookasideList(&m_SGList.ScatterGatherProfile.Lookaside); 79 80 } else if (!m_IsBusMaster) { 81 // 82 // System profile (not busmastering) - cleanup the preallocated 83 // SG list 84 // 85 ExFreePool(m_SGList.SystemProfile.List); 86 87 } else { 88 // 89 // Packet profile. No special cleanup to do. 90 // 91 92 } 93 94 #if DBG 95 RtlZeroMemory(&m_SGList, sizeof(m_SGList)); 96 #endif 97 m_IsSGListAllocated = FALSE; 98 } 99 } 100 101 102 BOOLEAN 103 FxDmaEnabler::Dispose() 104 { 105 ReleaseResources(); 106 107 if (m_IsAdded) { 108 ASSERT(m_DeviceBase != NULL); 109 m_DeviceBase->RemoveDmaEnabler(this); 110 } 111 112 return TRUE; 113 } 114 115 _Must_inspect_result_ 116 NTSTATUS 117 FxDmaEnabler::Initialize( 118 __in PWDF_DMA_ENABLER_CONFIG Config, 119 __inout FxDeviceBase *Device 120 ) 121 { 122 NTSTATUS status; 123 DEVICE_DESCRIPTION deviceDescription; 124 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 125 ULONG mapRegistersAllocated; 126 127 RtlZeroMemory(&deviceDescription, sizeof(DEVICE_DESCRIPTION)); 128 129 // 130 // Default to version 2 description (except on ARM platforms) 131 // 132 133 #ifdef _ARM_ 134 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION3; 135 #else 136 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION2; 137 #endif 138 139 // 140 // Make sure the device's list of enablers has been created. 141 // 142 143 status = Device->AllocateDmaEnablerList(); 144 if (!NT_SUCCESS(status)) { 145 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA, 146 "Unable to allocate DmaEnablerList for " 147 "WDFDEVICE %p, %!STATUS!", 148 Device->GetHandle(), status); 149 return status; 150 } 151 152 // 153 // Retain parent FxDeviceBase object 154 // 155 m_DeviceBase = Device; 156 157 // 158 // Save the profile. 159 // 160 m_Profile = Config->Profile; 161 162 // 163 // Invariant parameters vis-a-vis kernel-mode bus-mastering DMA APIs 164 // (overrided below if using system-DMA 165 // 166 deviceDescription.Master = TRUE; 167 deviceDescription.Dma32BitAddresses = TRUE; 168 deviceDescription.InterfaceType = PCIBus; 169 170 // 171 // Assume enabler is a bus-master 172 // 173 m_IsBusMaster = TRUE; 174 175 // 176 // Expand the profile into settings. 177 // 178 switch (m_Profile) { 179 // 180 // Packet based profiles. 181 // 182 183 case WdfDmaProfilePacket: 184 deviceDescription.ScatterGather = FALSE; 185 deviceDescription.Dma64BitAddresses = FALSE; 186 break; 187 case WdfDmaProfilePacket64: 188 deviceDescription.ScatterGather = FALSE; 189 deviceDescription.Dma64BitAddresses = TRUE; 190 break; 191 192 // 193 // Scatter-gather profiles 194 // 195 196 case WdfDmaProfileScatterGather: 197 deviceDescription.ScatterGather = TRUE; 198 deviceDescription.Dma64BitAddresses = FALSE; 199 m_IsScatterGather = TRUE; 200 break; 201 case WdfDmaProfileScatterGatherDuplex: 202 deviceDescription.ScatterGather = TRUE; 203 deviceDescription.Dma64BitAddresses = FALSE; 204 m_IsDuplexTransfer = TRUE; 205 m_IsScatterGather = TRUE; 206 break; 207 case WdfDmaProfileScatterGather64: 208 deviceDescription.ScatterGather = TRUE; 209 deviceDescription.Dma64BitAddresses = TRUE; 210 m_IsScatterGather = TRUE; 211 break; 212 case WdfDmaProfileScatterGather64Duplex: 213 deviceDescription.ScatterGather = TRUE; 214 deviceDescription.Dma64BitAddresses = TRUE; 215 m_IsDuplexTransfer = TRUE; 216 m_IsScatterGather = TRUE; 217 break; 218 219 // 220 // Non-PC System-mode (non-bus-mastering) profiles. These 221 // require DMA v3. 222 // 223 224 case WdfDmaProfileSystem: 225 deviceDescription.ScatterGather = FALSE; 226 deviceDescription.Master = FALSE; 227 deviceDescription.Dma32BitAddresses = FALSE; 228 deviceDescription.Dma64BitAddresses = FALSE; 229 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION3; 230 m_IsBusMaster = FALSE; 231 m_DeviceBase->SetDeviceTelemetryInfoFlags(DeviceInfoDmaSystem); 232 break; 233 case WdfDmaProfileSystemDuplex: 234 deviceDescription.ScatterGather = FALSE; 235 deviceDescription.Master = FALSE; 236 deviceDescription.Dma32BitAddresses = FALSE; 237 deviceDescription.Dma64BitAddresses = FALSE; 238 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION3; 239 m_IsBusMaster = FALSE; 240 m_IsDuplexTransfer = TRUE; 241 m_DeviceBase->SetDeviceTelemetryInfoFlags(DeviceInfoDmaSystemDuplex); 242 break; 243 244 // 245 // Unknown profile. 246 // 247 248 default: 249 // 250 // Just do quick exit as no resource have been allocated. 251 // 252 return STATUS_INVALID_PARAMETER; 253 } 254 255 // 256 // Save the maximum length. 257 // 258 m_MaximumLength = (ULONG) Config->MaximumLength; 259 260 // 261 // An override of address width requires the DMA v3 engine. it also requires 262 // that we explicitly specify the DMA width the controller can support, but 263 // we do that down below. 264 // 265 if (Config->AddressWidthOverride != 0) { 266 267 // 268 // Address width override is not supported for system mode DMA, since 269 // the HAL runs the DMA controller in that case and it knows the 270 // controller's address limitations better than the driver does. 271 // 272 if (m_IsBusMaster == FALSE) { 273 status = STATUS_INVALID_PARAMETER; 274 DoTraceLevelMessage( 275 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 276 "AddressWidthOverride set to %d. AddressWidthOverride " 277 "must be zero when using a system DMA profile " 278 "(%!WDF_DMA_PROFILE!) - %!STATUS!", 279 Config->AddressWidthOverride, 280 Config->Profile, 281 status 282 ); 283 FxVerifierDbgBreakPoint(GetDriverGlobals()); 284 return status; 285 } 286 287 if ((deviceDescription.Dma64BitAddresses == FALSE) && 288 (Config->AddressWidthOverride > 32)) { 289 status = STATUS_INVALID_PARAMETER; 290 DoTraceLevelMessage( 291 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 292 "AddressWidthOverride set to %d. AddressWidthOverride " 293 "must be <= 32 when using a 32-bit DMA profile " 294 "(%!WDF_DMA_PROFILE!) - %!STATUS!", 295 Config->AddressWidthOverride, 296 Config->Profile, 297 status 298 ); 299 FxVerifierDbgBreakPoint(GetDriverGlobals()); 300 return status; 301 } 302 303 // 304 // Handle the AddressWidthOverride. For Win8 use DMA v3 and pass the 305 // value through to the HAL. For Win7 downgrade to the next lower 306 // address width. 307 // 308 if (IsOsVersionGreaterThanOrEqualTo(6, 2)) { 309 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION3; 310 deviceDescription.DmaAddressWidth = Config->AddressWidthOverride; 311 } 312 else { 313 314 NT_ASSERTMSGW(L"Ensure driver is not doing something earlier that " 315 L"would require DMA v3 before we downgrade them to " 316 L"DMA v2", 317 (deviceDescription.Version == DEVICE_DESCRIPTION_VERSION2)); 318 319 if (Config->AddressWidthOverride < 64) { 320 deviceDescription.Dma64BitAddresses = FALSE; 321 } 322 323 if (Config->AddressWidthOverride < 32) { 324 deviceDescription.Dma32BitAddresses = FALSE; 325 } 326 327 // 328 // DMA V2 can't handle an address width restriction smaller than 329 // 24 bits (ISA DMA). DMA V3 will fail that also - return the same 330 // error here that DMA V3 would have. 331 // 332 if (Config->AddressWidthOverride < 24) { 333 DoTraceLevelMessage( 334 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 335 "AddressWidthOverride of less than 24 bits is not supported" 336 ); 337 return STATUS_UNSUCCESSFUL; 338 } 339 else if ((Config->AddressWidthOverride != 64) && 340 (Config->AddressWidthOverride != 32)) { 341 342 // 343 // Log a warning about downgrading DMA if we are actually 344 // downgrading. if the caller uses a 64-bit DMA 345 // profile with an override of 64, or 32-bit with an override 346 // of 32 then silently let it go through. 347 // 348 349 DoTraceLevelMessage( 350 GetDriverGlobals(), TRACE_LEVEL_WARNING, TRACINGDMA, 351 "DMA AddressWidthOverride requires Windows version 6.2 or " 352 "higher. Windows cannot support %d bit DMA is falling back to " 353 "the next lower supported width (%d-bit)", 354 Config->AddressWidthOverride, 355 (deviceDescription.Dma32BitAddresses ? 32 : 24) 356 ); 357 } 358 } 359 } 360 361 // 362 // Allow for a specific version override (and fail if 363 // that override is inconsistent with the settings). On Win7 this will 364 // fail when we get the DMA adapter. 365 // 366 if (Config->WdmDmaVersionOverride != 0) { 367 368 if (Config->WdmDmaVersionOverride < deviceDescription.Version) { 369 status = STATUS_INVALID_PARAMETER; 370 371 // 372 // Driver is asking for a lower version of the DMA engine than the 373 // config settings imply it needs. Fail with invalid parameter. 374 // 375 376 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 377 "WdmDmaVersionOverride set to %d, conflicts with required version of %d, " 378 "%!STATUS!", 379 Config->WdmDmaVersionOverride, 380 deviceDescription.Version, 381 status 382 ); 383 384 FxVerifierDbgBreakPoint(GetDriverGlobals()); 385 return status; 386 } 387 388 deviceDescription.Version = Config->WdmDmaVersionOverride; 389 } 390 391 // 392 // Propagate some settings from the old engine's location to the new ones. 393 // 394 if (deviceDescription.Version >= DEVICE_DESCRIPTION_VERSION3) { 395 396 if (deviceDescription.DmaAddressWidth == 0) { 397 398 if (deviceDescription.Dma64BitAddresses) { 399 deviceDescription.DmaAddressWidth = 64; 400 } else if (deviceDescription.Dma32BitAddresses) { 401 deviceDescription.DmaAddressWidth = 32; 402 } else { 403 // 404 // Assume ISA access width. 405 // 406 407 deviceDescription.DmaAddressWidth = 24; 408 } 409 } 410 } 411 412 // 413 // Get the FDO 414 // 415 m_FDO = m_DeviceBase->GetDeviceObject(); 416 ASSERT(m_FDO != NULL); 417 418 // 419 // Get the PDO. PDO may be NULL in the miniport case, but on 420 // x86 that will still allow for DMA (IoGetDmaAdapter special 421 // cases that on x86). On amd64 the attempt to get the DMA 422 // adapter later will fail cleanly. 423 // 424 m_PDO = m_DeviceBase->GetPhysicalDevice(); 425 426 mapRegistersAllocated = 0; 427 428 // 429 // If this device is a bus-master then configure the profile 430 // right now, since we don't need to wait for PrepareHardware 431 // to find out the DMA resource. 432 // 433 if (m_IsBusMaster) { 434 status = ConfigureBusMasterAdapters(&deviceDescription, Config); 435 if (!NT_SUCCESS(status)) { 436 goto End; 437 } 438 } 439 440 // 441 // Retain the Power event callbacks. 442 // 443 m_EvtDmaEnablerFill.m_Method = Config->EvtDmaEnablerFill; 444 m_EvtDmaEnablerFlush.m_Method = Config->EvtDmaEnablerFlush; 445 m_EvtDmaEnablerEnable.m_Method = Config->EvtDmaEnablerEnable; 446 m_EvtDmaEnablerDisable.m_Method = Config->EvtDmaEnablerDisable; 447 m_EvtDmaEnablerSelfManagedIoStart.m_Method = Config->EvtDmaEnablerSelfManagedIoStart; 448 m_EvtDmaEnablerSelfManagedIoStop.m_Method = Config->EvtDmaEnablerSelfManagedIoStop; 449 450 // 451 // Add this DmaEnabler to the parent device's list of dma enablers. 452 // 453 m_DeviceBase->AddDmaEnabler(this); 454 m_IsAdded = TRUE; 455 456 // 457 // update hardware info for Telemetry 458 // 459 if (m_IsBusMaster) { 460 m_DeviceBase->SetDeviceTelemetryInfoFlags(DeviceInfoDmaBusMaster); 461 } 462 463 // 464 // Success: 465 // 466 status = STATUS_SUCCESS; 467 468 End: 469 // 470 // If errors then clean-up resources accumulated. 471 // 472 if (!NT_SUCCESS(status)) { 473 ReleaseResources(); 474 } 475 476 return status; 477 } 478 479 _Must_inspect_result_ 480 NTSTATUS 481 FxDmaEnabler::ConfigureSystemAdapter( 482 __in PWDF_DMA_SYSTEM_PROFILE_CONFIG Config, 483 __in WDF_DMA_DIRECTION ConfigDirection 484 ) 485 { 486 DEVICE_DESCRIPTION deviceDescription; 487 488 NTSTATUS status; 489 490 // 491 // Check to make sure this direction isn't currently configured. 492 // 493 if (GetDmaDescription(ConfigDirection)->AdapterObject != NULL) { 494 status = STATUS_INVALID_PARAMETER; 495 496 DoTraceLevelMessage(GetDriverGlobals(), TRACE_LEVEL_VERBOSE, TRACINGDMA, 497 "WDFDMAENABLER %p, profile %!WDF_DMA_PROFILE! " 498 "Enabler has already been configured for %!WDF_DMA_DIRECTION!, %!STATUS!", 499 GetHandle(), m_Profile, 500 ConfigDirection, 501 status 502 ); 503 504 FxVerifierDbgBreakPoint(GetDriverGlobals()); 505 506 return status; 507 } 508 509 // 510 // Initialize the adapter info from scratch given the Config structure 511 // then copy it to the appropriate channel and do the allocation. 512 // 513 RtlZeroMemory(&deviceDescription, sizeof(DEVICE_DESCRIPTION)); 514 515 deviceDescription.Version = DEVICE_DESCRIPTION_VERSION3; 516 deviceDescription.MaximumLength = m_MaximumLength; 517 518 deviceDescription.DemandMode = Config->DemandMode; 519 deviceDescription.AutoInitialize = Config->LoopedTransfer; 520 521 deviceDescription.DmaWidth = Config->DmaWidth; 522 523 deviceDescription.DeviceAddress = Config->DeviceAddress; 524 525 // 526 // Pull the remainder of the description from the provided resource. 527 // 528 deviceDescription.InterfaceType = Internal; 529 deviceDescription.DmaChannel = Config->DmaDescriptor->u.Dma.Channel; 530 deviceDescription.DmaRequestLine = Config->DmaDescriptor->u.Dma.Port; 531 532 533 // 534 // Run the common adapter configuration. 535 // 536 status = ConfigureDmaAdapter( 537 &deviceDescription, 538 ConfigDirection 539 ); 540 541 if (!NT_SUCCESS(status)) { 542 goto End; 543 } 544 545 // 546 // Allocate a single SGList to pass to MapTransferEx. Since we 547 // only run a single system transfer at a time we can use the same 548 // list for each transfer 549 // 550 551 { 552 size_t systemSGListSize = 0; 553 554 if (m_IsDuplexTransfer) { 555 556 systemSGListSize = max(GetReadDmaDescription()->PreallocatedSGListSize, 557 GetWriteDmaDescription()->PreallocatedSGListSize); 558 } else { 559 560 systemSGListSize = m_SimplexAdapterInfo.PreallocatedSGListSize; 561 } 562 563 // 564 // Allocate the SG list. 565 // 566 m_SGList.SystemProfile.List = 567 (PSCATTER_GATHER_LIST) ExAllocatePoolWithTag(NonPagedPool, 568 systemSGListSize, 569 GetDriverGlobals()->Tag); 570 571 if (m_SGList.SystemProfile.List == NULL) { 572 status = STATUS_INSUFFICIENT_RESOURCES; 573 DoTraceLevelMessage( 574 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 575 "Unable to allocate scatter gather list for system DMA " 576 "enabler %p, %!STATUS!", 577 GetHandle(), status 578 ); 579 goto End; 580 } 581 582 m_IsSGListAllocated = TRUE; 583 m_SGListSize = systemSGListSize; 584 } 585 586 // 587 // For a simple enabler, both of these calls will return the same 588 // DMA description entry. 589 // 590 if ((GetDmaDescription( 591 WdfDmaDirectionReadFromDevice 592 )->AdapterObject != NULL) && 593 (GetDmaDescription( 594 WdfDmaDirectionWriteToDevice 595 )->AdapterObject != NULL)) { 596 m_IsConfigured = TRUE; 597 } 598 599 End: 600 601 return status; 602 } 603 604 _Must_inspect_result_ 605 NTSTATUS 606 FxDmaEnabler::ConfigureBusMasterAdapters( 607 __in PDEVICE_DESCRIPTION DeviceDescription, 608 __in PWDF_DMA_ENABLER_CONFIG Config 609 ) 610 { 611 ULONG alignment; 612 NTSTATUS status; 613 614 // 615 // Initialize map register management 616 // 617 DeviceDescription->MaximumLength = m_MaximumLength; 618 619 if (m_IsDuplexTransfer) { 620 status = ConfigureDmaAdapter(DeviceDescription, 621 WdfDmaDirectionReadFromDevice); 622 623 if (!NT_SUCCESS(status)) { 624 goto End; 625 } 626 627 status = ConfigureDmaAdapter(DeviceDescription, 628 WdfDmaDirectionWriteToDevice); 629 } else { 630 // 631 // Direction is ignored in this case. 632 // 633 634 status = ConfigureDmaAdapter(DeviceDescription, 635 WdfDmaDirectionReadFromDevice); 636 } 637 638 if (!NT_SUCCESS(status)) { 639 goto End; 640 } 641 642 // 643 // Allocate a scatter gather lookaside list if we need one. 644 // 645 646 if (m_IsScatterGather) { 647 size_t sgLookasideListSize; 648 649 sgLookasideListSize = 0; 650 651 if (m_IsDuplexTransfer) { 652 FxDmaDescription *readDmaDesc = GetReadDmaDescription(); 653 FxDmaDescription *writeDmaDesc = GetWriteDmaDescription(); 654 655 alignment = readDmaDesc->AdapterObject->DmaOperations-> 656 GetDmaAlignment(readDmaDesc->AdapterObject); 657 658 // 659 // GetDmaAlignment returns alignment in terms of bytes 660 // while we treat alignment as a mask (which is how it is set 661 // in _DEVICE_OBJECT as well. 662 // For example, for byte alignment GetDmaAlignment returns 1 while 663 // the alignment mask is 0x00000000 664 // 665 // For < 1.11 drivers we keep the same behaviour as before for 666 // compatibility. 667 // 668 if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1, 11) && 669 alignment > 0) { 670 alignment -= 1; 671 } 672 673 m_CommonBufferAlignment = (ULONG) FxSizeTMax(m_FDO->AlignmentRequirement, 674 alignment); 675 676 // 677 // We will create a lookaside list based on the larger of the read & 678 // write SGListSize. It's done this way so that we can allocate 679 // sglist buffer when the dma-transaction object is created, where 680 // we don't know the direction of DMA transfer, and make the 681 // transaction initialize call fail-proof. 682 // 683 sgLookasideListSize = FxSizeTMax( 684 readDmaDesc->PreallocatedSGListSize, 685 writeDmaDesc->PreallocatedSGListSize 686 ); 687 } else { 688 689 FxDmaDescription *simplexDmaDesc = &m_SimplexAdapterInfo; 690 691 alignment = simplexDmaDesc->AdapterObject->DmaOperations-> 692 GetDmaAlignment(simplexDmaDesc->AdapterObject); 693 694 // 695 // GetDmaAlignment returns alignment in terms of bytes 696 // while we treat alignment as a mask (which is how it is set 697 // in _DEVICE_OBJECT as well. 698 // For example, for byte alignment GetDmaAlignment returns 1 while 699 // the alignment mask is 0x00000000 700 // 701 // For < 1.11 drivers we keep the same behaviour as before for 702 // compatibility. 703 // 704 if (GetDriverGlobals()->IsVersionGreaterThanOrEqualTo(1, 11) && 705 alignment > 0) { 706 alignment -= 1; 707 } 708 709 m_CommonBufferAlignment = (ULONG) FxSizeTMax(m_FDO->AlignmentRequirement, 710 alignment); 711 712 sgLookasideListSize = simplexDmaDesc->PreallocatedSGListSize; 713 } 714 715 // 716 // Initialize a LookasideList for ScatterGather list 717 // 718 if ((Config->Flags & WDF_DMA_ENABLER_CONFIG_NO_SGLIST_PREALLOCATION) == 0) { 719 ASSERT(m_IsSGListAllocated == FALSE); 720 721 m_SGListSize = sgLookasideListSize; 722 723 ExInitializeNPagedLookasideList( &m_SGList.ScatterGatherProfile.Lookaside, 724 NULL, // Allocate OPTIONAL 725 NULL, // Free OPTIONAL 726 0, // Flag - Reserved. Must be zero. 727 m_SGListSize, 728 GetDriverGlobals()->Tag, 729 0 ); // Depth - Reserved. Must be zero. 730 731 m_IsSGListAllocated = TRUE; 732 } 733 } 734 735 // 736 // The DMA enabler is configured now. 737 // 738 739 m_IsConfigured = TRUE; 740 741 End: 742 743 return status; 744 } 745 746 _Must_inspect_result_ 747 NTSTATUS 748 FxDmaEnabler::ConfigureDmaAdapter( 749 __in PDEVICE_DESCRIPTION DeviceDescription, 750 __in WDF_DMA_DIRECTION ConfigDirection 751 ) 752 { 753 FxDmaDescription *dmaDesc; 754 NTSTATUS status; 755 756 // 757 // Select the adapter to configure. 758 // 759 760 dmaDesc = GetDmaDescription(ConfigDirection); 761 762 // 763 // Copy the device-description we have built up so far 764 // into the read and write dma description field. These 765 // settings are common to both channels. 766 // 767 RtlCopyMemory(&dmaDesc->DeviceDescription, 768 DeviceDescription, 769 sizeof(DEVICE_DESCRIPTION)); 770 771 // 772 // Then initialize resources that are private to read and write. 773 // 774 775 status = InitializeResources(dmaDesc); 776 return status; 777 } 778 779 _Must_inspect_result_ 780 NTSTATUS 781 FxDmaEnabler::InitializeResources( 782 __inout FxDmaDescription *AdapterInfo 783 ) 784 { 785 NTSTATUS status; 786 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 787 788 NT_ASSERTMSG("expected caller to set DMA version", 789 AdapterInfo->DeviceDescription.Version != 0); 790 791 // 792 // Submit IoGetDmaAdapter and retain the DmaAdapter pointer. 793 // 794 AdapterInfo->AdapterObject = 795 IoGetDmaAdapter(m_PDO, 796 &AdapterInfo->DeviceDescription, 797 (PULONG)&AdapterInfo->NumberOfMapRegisters); 798 799 if (AdapterInfo->AdapterObject == NULL) { 800 status = STATUS_UNSUCCESSFUL; 801 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA, 802 "Unable to allocate DmaAdapter object for " 803 "WDFDMAENABLER %p, %!STATUS!", 804 GetHandle(), status); 805 return status; 806 } 807 808 // 809 // Calculate the size of the SGList. 810 // 811 if (m_IsScatterGather) { 812 813 // 814 // For scatter gather DMA we ask the HAL how many bytes it needs for 815 // each SGList. The HAL allocates some scratch space of its own in 816 // each SGList, which BuildScatterGatherList depends on. 817 // 818 ULONG mapRegistersCount; 819 820 status = AdapterInfo->AdapterObject->DmaOperations-> 821 CalculateScatterGatherList( AdapterInfo->AdapterObject, 822 NULL, // Optional MDL 823 NULL, // CurrentVa 824 AdapterInfo->NumberOfMapRegisters * PAGE_SIZE, 825 (PULONG) &AdapterInfo->PreallocatedSGListSize, 826 &mapRegistersCount); 827 828 if (!NT_SUCCESS(status)) { 829 DoTraceLevelMessage( 830 GetDriverGlobals(), TRACE_LEVEL_ERROR, TRACINGDMA, 831 "CalculateScatterGatherList failed for " 832 "WDFDMAENABLER %p, %!STATUS!", GetHandle(), status); 833 return status; 834 } 835 836 ASSERT(AdapterInfo->NumberOfMapRegisters == mapRegistersCount); 837 838 } else if (m_IsBusMaster) { 839 840 // 841 // For packet based DMA we only need a single SGList entry because 842 // the HAL moves all of the data into a single continguous buffer 843 // 844 AdapterInfo->PreallocatedSGListSize = sizeof(SCATTER_GATHER_LIST) + 845 sizeof(SCATTER_GATHER_ELEMENT); 846 847 } else { 848 849 // 850 // For system DMA we need a single SGList entry per map-register 851 // 852 AdapterInfo->PreallocatedSGListSize = sizeof(SCATTER_GATHER_LIST) + 853 (sizeof(SCATTER_GATHER_ELEMENT) * 854 AdapterInfo->NumberOfMapRegisters); 855 } 856 857 ASSERT(AdapterInfo->NumberOfMapRegisters > 1); 858 859 AdapterInfo->MaximumFragmentLength = FxSizeTMin(m_MaximumLength, 860 ((size_t) (AdapterInfo->NumberOfMapRegisters - 1)) << PAGE_SHIFT); 861 862 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA, 863 "WDFDMAENABLER %p, profile %!WDF_DMA_PROFILE! " 864 "DmaAdapterObject %p, MapRegisters %d, " 865 "MaximumFragmentLength %I64d ", GetHandle(), m_Profile, 866 AdapterInfo->AdapterObject, 867 AdapterInfo->NumberOfMapRegisters, 868 AdapterInfo->MaximumFragmentLength); 869 870 if (AdapterInfo->MaximumFragmentLength < m_MaximumLength) { 871 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA, 872 "The maximum transfer length for WDFDMAENABLER %p " 873 "is reduced to %I64d from %I64d due to mapregisters limit", 874 GetHandle(), m_MaximumLength, 875 AdapterInfo->MaximumFragmentLength); 876 } 877 878 return STATUS_SUCCESS; 879 } 880 881 VOID 882 FxDmaEnabler::FreeResources( 883 __inout FxDmaDescription *AdapterInfo 884 ) 885 { 886 if (AdapterInfo->AdapterObject != NULL) { 887 AdapterInfo->AdapterObject->DmaOperations->PutDmaAdapter(AdapterInfo->AdapterObject); 888 AdapterInfo->AdapterObject = NULL; 889 } 890 } 891 892 VOID 893 FxDmaEnabler::ReleaseResources( 894 VOID 895 ) 896 { 897 FreeResources(GetReadDmaDescription()); 898 FreeResources(GetWriteDmaDescription()); 899 900 m_IsConfigured = FALSE; 901 902 } 903 904 VOID 905 FxDmaEnabler::RevokeResources( 906 VOID 907 ) 908 { 909 // 910 // Give back any system DMA resources allocated for this device 911 // 912 913 if (m_IsBusMaster == FALSE) 914 { 915 916 917 918 919 920 } 921 } 922 923 // ---------------------------------------------------------------------------- 924 // ------------------------ Pnp/Power notification ----------------------------- 925 // ---------------------------------------------------------------------------- 926 927 _Must_inspect_result_ 928 NTSTATUS 929 FxDmaEnabler::PowerUp( 930 VOID 931 ) 932 { 933 NTSTATUS status = STATUS_SUCCESS; 934 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 935 WDFDMAENABLER handle = GetHandle(); 936 FxDmaEnablerCallbacks tag = FxEvtDmaEnablerInvalid; 937 938 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA, 939 "WDFDMAENABLER %p: PowerUp notification", GetHandle()); 940 941 do { 942 if (m_EvtDmaEnablerFill.m_Method) { 943 944 status = m_EvtDmaEnablerFill.Invoke( handle ); 945 946 if (!NT_SUCCESS(status)) { 947 m_DmaEnablerFillFailed = TRUE; 948 tag = FxEvtDmaEnablerFill; 949 break; 950 } 951 } 952 953 if (m_EvtDmaEnablerEnable.m_Method) { 954 955 status = m_EvtDmaEnablerEnable.Invoke( handle ); 956 957 if (!NT_SUCCESS(status)) { 958 m_DmaEnablerEnableFailed = TRUE; 959 tag = FxEvtDmaEnablerEnable; 960 break; 961 } 962 } 963 964 if (m_EvtDmaEnablerSelfManagedIoStart.m_Method) { 965 966 status = m_EvtDmaEnablerSelfManagedIoStart.Invoke( handle ); 967 968 if (!NT_SUCCESS(status)) { 969 m_DmaEnablerSelfManagedIoStartFailed = TRUE; 970 tag = FxEvtDmaEnablerSelfManagedIoStart; 971 break; 972 } 973 } 974 975 } WHILE (0); 976 977 if (!NT_SUCCESS(status)) { 978 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA, 979 "WDFDMAENABLER %p: PowerUp: " 980 "%!WdfDmaEnablerCallback! failed %!STATUS!", 981 GetHandle(), tag, status); 982 } 983 return status; 984 } 985 986 _Must_inspect_result_ 987 NTSTATUS 988 FxDmaEnabler::PowerDown( 989 VOID 990 ) 991 { 992 NTSTATUS status = STATUS_SUCCESS; 993 NTSTATUS localStatus; 994 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 995 WDFDMAENABLER handle = GetHandle(); 996 FxDmaEnablerCallbacks tag = FxEvtDmaEnablerInvalid; 997 998 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGDMA, 999 "WDFDMAENABLER %p: PowerDown notification", GetHandle()); 1000 1001 do { 1002 1003 if (m_EvtDmaEnablerSelfManagedIoStop.m_Method) { 1004 1005 localStatus = m_EvtDmaEnablerSelfManagedIoStop.Invoke( handle ); 1006 1007 if (!NT_SUCCESS(localStatus)) { 1008 tag = FxEvtDmaEnablerSelfManagedIoStop; 1009 status = (NT_SUCCESS(status)) ? localStatus : status; 1010 } 1011 } 1012 1013 if (m_EvtDmaEnablerDisable.m_Method && 1014 m_DmaEnablerFillFailed == FALSE) 1015 { 1016 localStatus = m_EvtDmaEnablerDisable.Invoke( handle ); 1017 1018 if (!NT_SUCCESS(localStatus)) { 1019 tag = FxEvtDmaEnablerDisable; 1020 status = (NT_SUCCESS(status)) ? localStatus : status; 1021 } 1022 } 1023 1024 if (m_EvtDmaEnablerFlush.m_Method && 1025 m_DmaEnablerFillFailed == FALSE && 1026 m_DmaEnablerEnableFailed == FALSE) 1027 { 1028 localStatus = m_EvtDmaEnablerFlush.Invoke( handle ); 1029 1030 if (!NT_SUCCESS(localStatus)) { 1031 tag = FxEvtDmaEnablerFlush; 1032 status = (NT_SUCCESS(status)) ? localStatus : status; 1033 } 1034 } 1035 1036 } WHILE (0); 1037 1038 if (!NT_SUCCESS(status)) { 1039 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA, 1040 "WDFDMAENABLER %p: PowerDown: " 1041 "%!WdfDmaEnablerCallback! failed %!STATUS!", 1042 GetHandle(), tag, status); 1043 } 1044 1045 return status; 1046 } 1047 1048 // ---------------------------------------------------------------------------- 1049 // ------------------------ COMMON BUFFER SECTION ----------------------------- 1050 // ---------------------------------------------------------------------------- 1051 1052 VOID 1053 FxDmaEnabler::AllocateCommonBuffer( 1054 __in size_t Length, 1055 __deref_out_opt PVOID * BufferVA, 1056 __out PHYSICAL_ADDRESS * BufferPA 1057 ) 1058 { 1059 ULONG result; 1060 PDMA_ADAPTER adapterObject; 1061 PFX_DRIVER_GLOBALS pFxDriverGlobals = GetDriverGlobals(); 1062 1063 *BufferVA = NULL; 1064 BufferPA->QuadPart = 0; 1065 1066 if (!NT_SUCCESS(RtlSizeTToULong(Length, &result))) { 1067 DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDMA, 1068 "WDFDMAENABLER %p AllocateCommonBuffer: could cast value %I64d to a " 1069 "ULONG", GetHandle(), Length); 1070 FxVerifierDbgBreakPoint(pFxDriverGlobals); 1071 return; 1072 } 1073 1074 // 1075 // It doesn't matter which channel we use for allocating common buffers 1076 // because the addressing capability of all the channels of this DMA enablers 1077 // are same. 1078 // 1079 adapterObject = GetReadDmaDescription()->AdapterObject; 1080 1081 *BufferVA = adapterObject->DmaOperations-> 1082 AllocateCommonBuffer( adapterObject, 1083 result, 1084 BufferPA, 1085 TRUE /* CacheEnabled */ ); 1086 } 1087 1088 1089 VOID 1090 FxDmaEnabler::FreeCommonBuffer( 1091 __in size_t Length, 1092 __in PVOID BufferVA, 1093 __in PHYSICAL_ADDRESS BufferPA 1094 ) 1095 { 1096 PDMA_ADAPTER adapterObject; 1097 1098 adapterObject = GetReadDmaDescription()->AdapterObject; 1099 1100 adapterObject->DmaOperations-> 1101 FreeCommonBuffer( adapterObject, 1102 (ULONG) Length, 1103 BufferPA, 1104 BufferVA, 1105 TRUE /* CacheEnabled */ ); 1106 } 1107 1108 VOID 1109 FxDmaEnabler::InitializeTransferContext( 1110 __out PVOID Context, 1111 __in WDF_DMA_DIRECTION Direction 1112 ) 1113 { 1114 PDMA_ADAPTER adapter = GetDmaDescription(Direction)->AdapterObject; 1115 1116 NT_ASSERTMSG( 1117 "should not call this routine if enabler is not using DMAv3", 1118 UsesDmaV3() 1119 ); 1120 1121 PDMA_OPERATIONS dmaOperations = 1122 adapter->DmaOperations; 1123 1124 dmaOperations->InitializeDmaTransferContext(adapter, Context); 1125 } 1126