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