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
FxDmaEnabler(__in PFX_DRIVER_GLOBALS FxDriverGlobals)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
~FxDmaEnabler()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
Dispose()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
Initialize(__in PWDF_DMA_ENABLER_CONFIG Config,__inout FxDeviceBase * Device)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
ConfigureSystemAdapter(__in PWDF_DMA_SYSTEM_PROFILE_CONFIG Config,__in WDF_DMA_DIRECTION ConfigDirection)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
ConfigureBusMasterAdapters(__in PDEVICE_DESCRIPTION DeviceDescription,__in PWDF_DMA_ENABLER_CONFIG Config)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
ConfigureDmaAdapter(__in PDEVICE_DESCRIPTION DeviceDescription,__in WDF_DMA_DIRECTION ConfigDirection)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
InitializeResources(__inout FxDmaDescription * AdapterInfo)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
FreeResources(__inout FxDmaDescription * AdapterInfo)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
ReleaseResources(VOID)893 FxDmaEnabler::ReleaseResources(
894 VOID
895 )
896 {
897 FreeResources(GetReadDmaDescription());
898 FreeResources(GetWriteDmaDescription());
899
900 m_IsConfigured = FALSE;
901
902 }
903
904 VOID
RevokeResources(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
PowerUp(VOID)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
PowerDown(VOID)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
AllocateCommonBuffer(__in size_t Length,__deref_out_opt PVOID * BufferVA,__out PHYSICAL_ADDRESS * BufferPA)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
FreeCommonBuffer(__in size_t Length,__in PVOID BufferVA,__in PHYSICAL_ADDRESS BufferPA)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
InitializeTransferContext(__out PVOID Context,__in WDF_DMA_DIRECTION Direction)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