xref: /reactos/hal/halx86/generic/dma.c (revision 02e84521)
1 /*
2  *
3  * COPYRIGHT:       See COPYING in the top level directory
4  * PROJECT:         ReactOS kernel
5  * FILE:            hal/halx86/generic/dma.c
6  * PURPOSE:         DMA functions
7  * PROGRAMMERS:     David Welch (welch@mcmail.com)
8  *                  Filip Navara (navaraf@reactos.com)
9  * UPDATE HISTORY:
10  *                  Created 22/05/98
11  */
12 
13 /**
14  * @page DMA Implementation Notes
15  *
16  * Concepts:
17  *
18  * - Map register
19  *
20  *   Abstract encapsulation of physically contiguous buffer that resides
21  *   in memory accessible by both the DMA device / controller and the system.
22  *   The map registers are allocated and distributed on demand and are
23  *   scarce resource.
24  *
25  *   The actual use of map registers is to allow transfers from/to buffer
26  *   located in physical memory at address inaccessible by the DMA device /
27  *   controller directly. For such transfers the map register buffers
28  *   are used as intermediate data storage.
29  *
30  * - Master adapter
31  *
32  *   A container for map registers (typically corresponding to one physical
33  *   bus connection type). There can be master adapters for 24-bit address
34  *   ranges, 32-bit address ranges, etc. Every time a new DMA adapter is
35  *   created it's associated with a corresponding master adapter that
36  *   is used for any map register allocation requests.
37  *
38  * - Bus-master / Slave DMA
39  *
40  *   Slave DMA is term used for DMA transfers done by the system (E)ISA
41  *   controller as opposed to transfers mastered by the device itself
42  *   (hence the name).
43  *
44  *   For slave DMA special care is taken to actually access the system
45  *   controller and handle the transfers. The relevant code is in
46  *   HalpDmaInitializeEisaAdapter, HalReadDmaCounter, IoFlushAdapterBuffers
47  *   and IoMapTransfer.
48  *
49  * Implementation:
50  *
51  * - Allocation of map registers
52  *
53  *   Initial set of map registers is allocated on the system start to
54  *   ensure that low memory won't get filled up later. Additional map
55  *   registers are allocated as needed by HalpGrowMapBuffers. This
56  *   routine is called on two places:
57  *
58  *   - HalGetAdapter, since we're at PASSIVE_LEVEL and it's known that
59  *     more map registers will probably be needed.
60  *   - IoAllocateAdapterChannel (indirectly using HalpGrowMapBufferWorker
61  *     since we're at DISPATCH_LEVEL and call HalpGrowMapBuffers directly)
62  *     when no more map registers are free.
63  *
64  *   Note that even if no more map registers can be allocated it's not
65  *   the end of the world. The adapters waiting for free map registers
66  *   are queued in the master adapter's queue and once one driver hands
67  *   back it's map registers (using IoFreeMapRegisters or indirectly using
68  *   the execution routine callback in IoAllocateAdapterChannel) the
69  *   queue gets processed and the map registers are reassigned.
70  */
71 
72 /* INCLUDES *****************************************************************/
73 
74 #include <hal.h>
75 #include <suppress.h>
76 
77 #define NDEBUG
78 #include <debug.h>
79 
80 #if defined(ALLOC_PRAGMA) && !defined(_MINIHAL_)
81 #pragma alloc_text(INIT, HalpInitDma)
82 #endif
83 
84 #define MAX_SG_ELEMENTS 0x10
85 
86 #ifndef _MINIHAL_
87 static KEVENT HalpDmaLock;
88 static LIST_ENTRY HalpDmaAdapterList;
89 static PADAPTER_OBJECT HalpEisaAdapter[8];
90 #endif
91 static BOOLEAN HalpEisaDma;
92 #ifndef _MINIHAL_
93 static PADAPTER_OBJECT HalpMasterAdapter;
94 #endif
95 
96 static const ULONG_PTR HalpEisaPortPage[8] = {
97    FIELD_OFFSET(DMA_PAGE, Channel0),
98    FIELD_OFFSET(DMA_PAGE, Channel1),
99    FIELD_OFFSET(DMA_PAGE, Channel2),
100    FIELD_OFFSET(DMA_PAGE, Channel3),
101    0,
102    FIELD_OFFSET(DMA_PAGE, Channel5),
103    FIELD_OFFSET(DMA_PAGE, Channel6),
104    FIELD_OFFSET(DMA_PAGE, Channel7)
105 };
106 
107 #ifndef _MINIHAL_
108 static DMA_OPERATIONS HalpDmaOperations = {
109    sizeof(DMA_OPERATIONS),
110    (PPUT_DMA_ADAPTER)HalPutDmaAdapter,
111    (PALLOCATE_COMMON_BUFFER)HalAllocateCommonBuffer,
112    (PFREE_COMMON_BUFFER)HalFreeCommonBuffer,
113    NULL, /* Initialized in HalpInitDma() */
114    NULL, /* Initialized in HalpInitDma() */
115    NULL, /* Initialized in HalpInitDma() */
116    NULL, /* Initialized in HalpInitDma() */
117    NULL, /* Initialized in HalpInitDma() */
118    (PGET_DMA_ALIGNMENT)HalpDmaGetDmaAlignment,
119    (PREAD_DMA_COUNTER)HalReadDmaCounter,
120    /* FIXME: Implement the S/G funtions. */
121    (PGET_SCATTER_GATHER_LIST)HalGetScatterGatherList,
122    (PPUT_SCATTER_GATHER_LIST)HalPutScatterGatherList,
123    NULL /*(PCALCULATE_SCATTER_GATHER_LIST_SIZE)HalCalculateScatterGatherListSize*/,
124    NULL /*(PBUILD_SCATTER_GATHER_LIST)HalBuildScatterGatherList*/,
125    NULL /*(PBUILD_MDL_FROM_SCATTER_GATHER_LIST)HalBuildMdlFromScatterGatherList*/
126 };
127 #endif
128 
129 #define MAX_MAP_REGISTERS 64
130 
131 #define TAG_DMA ' AMD'
132 
133 /* FUNCTIONS *****************************************************************/
134 
135 #ifndef _MINIHAL_
136 INIT_FUNCTION
137 VOID
138 HalpInitDma(VOID)
139 {
140     /*
141      * Initialize the DMA Operation table
142      */
143     HalpDmaOperations.AllocateAdapterChannel = (PALLOCATE_ADAPTER_CHANNEL)IoAllocateAdapterChannel;
144     HalpDmaOperations.FlushAdapterBuffers = (PFLUSH_ADAPTER_BUFFERS)IoFlushAdapterBuffers;
145     HalpDmaOperations.FreeAdapterChannel = (PFREE_ADAPTER_CHANNEL)IoFreeAdapterChannel;
146     HalpDmaOperations.FreeMapRegisters = (PFREE_MAP_REGISTERS)IoFreeMapRegisters;
147     HalpDmaOperations.MapTransfer = (PMAP_TRANSFER)IoMapTransfer;
148 
149     if (HalpBusType == MACHINE_TYPE_EISA)
150     {
151         /*
152         * Check if Extended DMA is available. We're just going to do a random
153         * read and write.
154         */
155         WRITE_PORT_UCHAR(UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2)), 0x2A);
156         if (READ_PORT_UCHAR(UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2))) == 0x2A)
157         {
158             DPRINT1("Machine supports EISA DMA. Bus type: %lu\n", HalpBusType);
159             HalpEisaDma = TRUE;
160         }
161     }
162 
163     /*
164      * Intialize all the global variables and allocate master adapter with
165      * first map buffers.
166      */
167     InitializeListHead(&HalpDmaAdapterList);
168     KeInitializeEvent(&HalpDmaLock, NotificationEvent, TRUE);
169     HalpMasterAdapter = HalpDmaAllocateMasterAdapter();
170 
171     /*
172      * Setup the HalDispatchTable callback for creating PnP DMA adapters. It's
173      * used by IoGetDmaAdapter in the kernel.
174      */
175     HalGetDmaAdapter = HalpGetDmaAdapter;
176 }
177 #endif
178 
179 /**
180  * @name HalpGetAdapterMaximumPhysicalAddress
181  *
182  * Get the maximum physical address acceptable by the device represented
183  * by the passed DMA adapter.
184  */
185 PHYSICAL_ADDRESS
186 NTAPI
187 HalpGetAdapterMaximumPhysicalAddress(IN PADAPTER_OBJECT AdapterObject)
188 {
189     PHYSICAL_ADDRESS HighestAddress;
190 
191     if (AdapterObject->MasterDevice)
192     {
193         if (AdapterObject->Dma64BitAddresses)
194         {
195             HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFULL;
196             return HighestAddress;
197         }
198         else if (AdapterObject->Dma32BitAddresses)
199         {
200             HighestAddress.QuadPart = 0xFFFFFFFF;
201             return HighestAddress;
202         }
203     }
204 
205     HighestAddress.QuadPart = 0xFFFFFF;
206     return HighestAddress;
207 }
208 
209 #ifndef _MINIHAL_
210 /**
211  * @name HalpGrowMapBuffers
212  *
213  * Allocate initial, or additional, map buffers for DMA master adapter.
214  *
215  * @param MasterAdapter
216  *        DMA master adapter to allocate buffers for.
217  * @param SizeOfMapBuffers
218  *        Size of the map buffers to allocate (not including the size
219  *        already allocated).
220  */
221 BOOLEAN
222 NTAPI
223 HalpGrowMapBuffers(IN PADAPTER_OBJECT AdapterObject,
224                   IN ULONG SizeOfMapBuffers)
225 {
226     PVOID VirtualAddress;
227     PHYSICAL_ADDRESS PhysicalAddress;
228     PHYSICAL_ADDRESS HighestAcceptableAddress;
229     PHYSICAL_ADDRESS LowestAcceptableAddress;
230     PHYSICAL_ADDRESS BoundryAddressMultiple;
231     KIRQL OldIrql;
232     ULONG MapRegisterCount;
233 
234     /* Check if enough map register slots are available. */
235     MapRegisterCount = BYTES_TO_PAGES(SizeOfMapBuffers);
236     if (MapRegisterCount + AdapterObject->NumberOfMapRegisters > MAX_MAP_REGISTERS)
237     {
238         DPRINT("No more map register slots available! (Current: %d | Requested: %d | Limit: %d)\n",
239                AdapterObject->NumberOfMapRegisters,
240                MapRegisterCount,
241                MAX_MAP_REGISTERS);
242         return FALSE;
243     }
244 
245     /*
246      * Allocate memory for the new map registers. For 32-bit adapters we use
247      * two passes in order not to waste scare resource (low memory).
248      */
249     HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
250     LowestAcceptableAddress.HighPart = 0;
251     LowestAcceptableAddress.LowPart = HighestAcceptableAddress.LowPart == 0xFFFFFFFF ? 0x1000000 : 0;
252     BoundryAddressMultiple.QuadPart = 0;
253 
254     VirtualAddress = MmAllocateContiguousMemorySpecifyCache(MapRegisterCount << PAGE_SHIFT,
255                                                             LowestAcceptableAddress,
256                                                             HighestAcceptableAddress,
257                                                             BoundryAddressMultiple,
258                                                             MmNonCached);
259     if (!(VirtualAddress) && (LowestAcceptableAddress.LowPart))
260     {
261         LowestAcceptableAddress.LowPart = 0;
262         VirtualAddress = MmAllocateContiguousMemorySpecifyCache(MapRegisterCount << PAGE_SHIFT,
263                                                                 LowestAcceptableAddress,
264                                                                 HighestAcceptableAddress,
265                                                                 BoundryAddressMultiple,
266                                                                 MmNonCached);
267     }
268 
269     if (!VirtualAddress) return FALSE;
270 
271     PhysicalAddress = MmGetPhysicalAddress(VirtualAddress);
272 
273     /*
274      * All the following must be done with the master adapter lock held
275      * to prevent corruption.
276      */
277     KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
278 
279     /*
280      * Setup map register entries for the buffer allocated. Each entry has
281      * a virtual and physical address and corresponds to PAGE_SIZE large
282      * buffer.
283      */
284     if (MapRegisterCount > 0)
285     {
286         PROS_MAP_REGISTER_ENTRY CurrentEntry, PreviousEntry;
287 
288         CurrentEntry = AdapterObject->MapRegisterBase + AdapterObject->NumberOfMapRegisters;
289         do
290         {
291             /*
292              * Leave one entry free for every non-contiguous memory region
293              * in the map register bitmap. This ensures that we can search
294              * using RtlFindClearBits for contiguous map register regions.
295              *
296              * Also for non-EISA DMA leave one free entry for every 64Kb
297              * break, because the DMA controller can handle only coniguous
298              * 64Kb regions.
299              */
300             if (CurrentEntry != AdapterObject->MapRegisterBase)
301             {
302                 PreviousEntry = CurrentEntry - 1;
303                 if ((PreviousEntry->PhysicalAddress.LowPart + PAGE_SIZE) == PhysicalAddress.LowPart)
304                 {
305                     if (!HalpEisaDma)
306                     {
307                         if ((PreviousEntry->PhysicalAddress.LowPart ^ PhysicalAddress.LowPart) & 0xFFFF0000)
308                         {
309                             CurrentEntry++;
310                             AdapterObject->NumberOfMapRegisters++;
311                         }
312                     }
313                 }
314                 else
315                 {
316                     CurrentEntry++;
317                     AdapterObject->NumberOfMapRegisters++;
318                 }
319             }
320 
321             RtlClearBit(AdapterObject->MapRegisters,
322                         (ULONG)(CurrentEntry - AdapterObject->MapRegisterBase));
323             CurrentEntry->VirtualAddress = VirtualAddress;
324             CurrentEntry->PhysicalAddress = PhysicalAddress;
325 
326             PhysicalAddress.LowPart += PAGE_SIZE;
327             VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE);
328 
329             CurrentEntry++;
330             AdapterObject->NumberOfMapRegisters++;
331             MapRegisterCount--;
332         } while (MapRegisterCount);
333     }
334 
335     KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
336 
337     return TRUE;
338 }
339 
340 /**
341  * @name HalpDmaAllocateMasterAdapter
342  *
343  * Helper routine to allocate and initialize master adapter object and it's
344  * associated map register buffers.
345  *
346  * @see HalpInitDma
347  */
348 PADAPTER_OBJECT
349 NTAPI
350 HalpDmaAllocateMasterAdapter(VOID)
351 {
352     PADAPTER_OBJECT MasterAdapter;
353     ULONG Size, SizeOfBitmap;
354 
355     SizeOfBitmap = MAX_MAP_REGISTERS;
356     Size = sizeof(ADAPTER_OBJECT);
357     Size += sizeof(RTL_BITMAP);
358     Size += (SizeOfBitmap + 7) >> 3;
359 
360     MasterAdapter = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_DMA);
361     if (!MasterAdapter) return NULL;
362 
363     RtlZeroMemory(MasterAdapter, Size);
364 
365     KeInitializeSpinLock(&MasterAdapter->SpinLock);
366     InitializeListHead(&MasterAdapter->AdapterQueue);
367 
368     MasterAdapter->MapRegisters = (PVOID)(MasterAdapter + 1);
369     RtlInitializeBitMap(MasterAdapter->MapRegisters,
370                         (PULONG)(MasterAdapter->MapRegisters + 1),
371                         SizeOfBitmap);
372     RtlSetAllBits(MasterAdapter->MapRegisters);
373     MasterAdapter->NumberOfMapRegisters = 0;
374     MasterAdapter->CommittedMapRegisters = 0;
375 
376     MasterAdapter->MapRegisterBase = ExAllocatePoolWithTag(NonPagedPool,
377                                                            SizeOfBitmap *
378                                                            sizeof(ROS_MAP_REGISTER_ENTRY),
379                                                            TAG_DMA);
380     if (!MasterAdapter->MapRegisterBase)
381     {
382         ExFreePool(MasterAdapter);
383         return NULL;
384     }
385 
386     RtlZeroMemory(MasterAdapter->MapRegisterBase,
387                   SizeOfBitmap * sizeof(ROS_MAP_REGISTER_ENTRY));
388     if (!HalpGrowMapBuffers(MasterAdapter, 0x10000))
389     {
390         ExFreePool(MasterAdapter);
391         return NULL;
392     }
393 
394     return MasterAdapter;
395 }
396 
397 /**
398  * @name HalpDmaAllocateChildAdapter
399  *
400  * Helper routine of HalGetAdapter. Allocate child adapter object and
401  * fill out some basic fields.
402  *
403  * @see HalGetAdapter
404  */
405 PADAPTER_OBJECT
406 NTAPI
407 HalpDmaAllocateChildAdapter(IN ULONG NumberOfMapRegisters,
408                             IN PDEVICE_DESCRIPTION DeviceDescription)
409 {
410     PADAPTER_OBJECT AdapterObject;
411     OBJECT_ATTRIBUTES ObjectAttributes;
412     NTSTATUS Status;
413     HANDLE Handle;
414 
415     InitializeObjectAttributes(&ObjectAttributes,
416                                NULL,
417                                OBJ_KERNEL_HANDLE | OBJ_PERMANENT,
418                                NULL,
419                                NULL);
420 
421     Status = ObCreateObject(KernelMode,
422                             IoAdapterObjectType,
423                             &ObjectAttributes,
424                             KernelMode,
425                             NULL,
426                             sizeof(ADAPTER_OBJECT),
427                             0,
428                             0,
429                             (PVOID)&AdapterObject);
430     if (!NT_SUCCESS(Status)) return NULL;
431 
432     Status = ObReferenceObjectByPointer(AdapterObject,
433                                         FILE_READ_DATA | FILE_WRITE_DATA,
434                                         IoAdapterObjectType,
435                                         KernelMode);
436     if (!NT_SUCCESS(Status)) return NULL;
437 
438     RtlZeroMemory(AdapterObject, sizeof(ADAPTER_OBJECT));
439 
440     Status = ObInsertObject(AdapterObject,
441                             NULL,
442                             FILE_READ_DATA | FILE_WRITE_DATA,
443                             0,
444                             NULL,
445                             &Handle);
446     if (!NT_SUCCESS(Status)) return NULL;
447 
448     ZwClose(Handle);
449 
450     AdapterObject->DmaHeader.Version = (USHORT)DeviceDescription->Version;
451     AdapterObject->DmaHeader.Size = sizeof(ADAPTER_OBJECT);
452     AdapterObject->DmaHeader.DmaOperations = &HalpDmaOperations;
453     AdapterObject->MapRegistersPerChannel = 1;
454     AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses;
455     AdapterObject->ChannelNumber = 0xFF;
456     AdapterObject->MasterAdapter = HalpMasterAdapter;
457     KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue);
458 
459     return AdapterObject;
460 }
461 #endif
462 
463 /**
464  * @name HalpDmaInitializeEisaAdapter
465  *
466  * Setup DMA modes and extended modes for (E)ISA DMA adapter object.
467  */
468 BOOLEAN
469 NTAPI
470 HalpDmaInitializeEisaAdapter(IN PADAPTER_OBJECT AdapterObject,
471                              IN PDEVICE_DESCRIPTION DeviceDescription)
472 {
473     UCHAR Controller;
474     DMA_MODE DmaMode = {{0 }};
475     DMA_EXTENDED_MODE ExtendedMode = {{ 0 }};
476     PVOID AdapterBaseVa;
477 
478     Controller = (DeviceDescription->DmaChannel & 4) ? 2 : 1;
479 
480     if (Controller == 1)
481     {
482         AdapterBaseVa = UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaController1));
483     }
484     else
485     {
486         AdapterBaseVa = UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaController2));
487     }
488 
489     AdapterObject->AdapterNumber = Controller;
490     AdapterObject->ChannelNumber = (UCHAR)(DeviceDescription->DmaChannel & 3);
491     AdapterObject->PagePort = (PUCHAR)HalpEisaPortPage[DeviceDescription->DmaChannel];
492     AdapterObject->Width16Bits = FALSE;
493     AdapterObject->AdapterBaseVa = AdapterBaseVa;
494 
495     if (HalpEisaDma)
496     {
497         ExtendedMode.ChannelNumber = AdapterObject->ChannelNumber;
498 
499         switch (DeviceDescription->DmaSpeed)
500         {
501             case Compatible: ExtendedMode.TimingMode = COMPATIBLE_TIMING; break;
502             case TypeA: ExtendedMode.TimingMode = TYPE_A_TIMING; break;
503             case TypeB: ExtendedMode.TimingMode = TYPE_B_TIMING; break;
504             case TypeC: ExtendedMode.TimingMode = BURST_TIMING; break;
505             default:
506                 return FALSE;
507         }
508 
509         switch (DeviceDescription->DmaWidth)
510         {
511             case Width8Bits: ExtendedMode.TransferSize = B_8BITS; break;
512             case Width16Bits: ExtendedMode.TransferSize = B_16BITS; break;
513             case Width32Bits: ExtendedMode.TransferSize = B_32BITS; break;
514             default:
515                 return FALSE;
516         }
517 
518         if (Controller == 1)
519         {
520             WRITE_PORT_UCHAR(UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode1)),
521                             ExtendedMode.Byte);
522         }
523         else
524         {
525             WRITE_PORT_UCHAR(UlongToPtr(FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode2)),
526                             ExtendedMode.Byte);
527         }
528     }
529     else
530     {
531         /*
532          * Validate setup for non-busmaster DMA adapter. Secondary controller
533          * supports only 16-bit transfers and main controller supports only
534          * 8-bit transfers. Anything else is invalid.
535          */
536         if (!DeviceDescription->Master)
537         {
538             if ((Controller == 2) && (DeviceDescription->DmaWidth == Width16Bits))
539             {
540                 AdapterObject->Width16Bits = TRUE;
541             }
542             else if ((Controller != 1) || (DeviceDescription->DmaWidth != Width8Bits))
543             {
544                 return FALSE;
545             }
546         }
547     }
548 
549     DmaMode.Channel = AdapterObject->ChannelNumber;
550     DmaMode.AutoInitialize = DeviceDescription->AutoInitialize;
551 
552     /*
553      * Set the DMA request mode.
554      *
555      * For (E)ISA bus master devices just unmask (enable) the DMA channel
556      * and set it to cascade mode. Otherwise just select the right one
557      * bases on the passed device description.
558      */
559     if (DeviceDescription->Master)
560     {
561         DmaMode.RequestMode = CASCADE_REQUEST_MODE;
562         if (Controller == 1)
563         {
564             /* Set the Request Data */
565             _PRAGMA_WARNING_SUPPRESS(__WARNING_DEREF_NULL_PTR)
566             WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->Mode, DmaMode.Byte);
567 
568             /* Unmask DMA Channel */
569             WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->SingleMask,
570                              AdapterObject->ChannelNumber | DMA_CLEARMASK);
571         }
572         else
573         {
574             /* Set the Request Data */
575             WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->Mode, DmaMode.Byte);
576 
577             /* Unmask DMA Channel */
578             WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->SingleMask,
579                              AdapterObject->ChannelNumber | DMA_CLEARMASK);
580         }
581     }
582     else
583     {
584         if (DeviceDescription->DemandMode)
585         {
586             DmaMode.RequestMode = DEMAND_REQUEST_MODE;
587         }
588         else
589         {
590             DmaMode.RequestMode = SINGLE_REQUEST_MODE;
591         }
592     }
593 
594     AdapterObject->AdapterMode = DmaMode;
595 
596     return TRUE;
597 }
598 
599 #ifndef _MINIHAL_
600 /**
601  * @name HalGetAdapter
602  *
603  * Allocate an adapter object for DMA device.
604  *
605  * @param DeviceDescription
606  *        Structure describing the attributes of the device.
607  * @param NumberOfMapRegisters
608  *        On return filled with the maximum number of map registers the
609  *        device driver can allocate for DMA transfer operations.
610  *
611  * @return The DMA adapter on success, NULL otherwise.
612  *
613  * @implemented
614  */
615 PADAPTER_OBJECT
616 NTAPI
617 HalGetAdapter(IN PDEVICE_DESCRIPTION DeviceDescription,
618               OUT PULONG NumberOfMapRegisters)
619 {
620     PADAPTER_OBJECT AdapterObject = NULL;
621     BOOLEAN EisaAdapter;
622     ULONG MapRegisters;
623     ULONG MaximumLength;
624 
625     /* Validate parameters in device description */
626     if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION2) return NULL;
627 
628     /*
629      * See if we're going to use ISA/EISA DMA adapter. These adapters are
630      * special since they're reused.
631      *
632      * Also note that we check for channel number since there are only 8 DMA
633      * channels on ISA, so any request above this requires new adapter.
634      */
635     if (((DeviceDescription->InterfaceType == Eisa) ||
636          (DeviceDescription->InterfaceType == Isa)) || !(DeviceDescription->Master))
637     {
638         if (((DeviceDescription->InterfaceType == Isa) ||
639              (DeviceDescription->InterfaceType == Eisa)) &&
640             (DeviceDescription->DmaChannel >= 8))
641         {
642             EisaAdapter = FALSE;
643         }
644         else
645         {
646             EisaAdapter = TRUE;
647         }
648     }
649     else
650     {
651         EisaAdapter = FALSE;
652     }
653 
654     /*
655      * Disallow creating adapter for ISA/EISA DMA channel 4 since it's used
656      * for cascading the controllers and it's not available for software use.
657      */
658     if ((EisaAdapter) && (DeviceDescription->DmaChannel == 4)) return NULL;
659 
660     /*
661      * Calculate the number of map registers.
662      *
663      * - For EISA and PCI scatter/gather no map registers are needed.
664      * - For ISA slave scatter/gather one map register is needed.
665      * - For all other cases the number of map registers depends on
666      *   DeviceDescription->MaximumLength.
667      */
668     MaximumLength = DeviceDescription->MaximumLength & MAXLONG;
669     if ((DeviceDescription->ScatterGather) &&
670         ((DeviceDescription->InterfaceType == Eisa) ||
671          (DeviceDescription->InterfaceType == PCIBus)))
672     {
673         MapRegisters = 0;
674     }
675     else if ((DeviceDescription->ScatterGather) && !(DeviceDescription->Master))
676     {
677         MapRegisters = 1;
678     }
679     else
680     {
681         /*
682          * In the equation below the additional map register added by
683          * the "+1" accounts for the case when a transfer does not start
684          * at a page-aligned address.
685          */
686         MapRegisters = BYTES_TO_PAGES(MaximumLength) + 1;
687         if (MapRegisters > 16) MapRegisters = 16;
688     }
689 
690     /*
691      * Acquire the DMA lock that is used to protect adapter lists and
692      * EISA adapter array.
693      */
694     KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL);
695 
696     /*
697      * Now we must get ahold of the adapter object. For first eight ISA/EISA
698      * channels there are static adapter objects that are reused and updated
699      * on succesive HalGetAdapter calls. In other cases a new adapter object
700      * is always created and it's to the DMA adapter list (HalpDmaAdapterList).
701      */
702     if (EisaAdapter)
703     {
704         AdapterObject = HalpEisaAdapter[DeviceDescription->DmaChannel];
705         if (AdapterObject)
706         {
707             if ((AdapterObject->NeedsMapRegisters) &&
708                 (MapRegisters > AdapterObject->MapRegistersPerChannel))
709             {
710                 AdapterObject->MapRegistersPerChannel = MapRegisters;
711             }
712         }
713     }
714 
715     if (AdapterObject == NULL)
716     {
717         AdapterObject = HalpDmaAllocateChildAdapter(MapRegisters, DeviceDescription);
718         if (AdapterObject == NULL)
719         {
720             KeSetEvent(&HalpDmaLock, 0, 0);
721             return NULL;
722         }
723 
724         if (EisaAdapter)
725         {
726             HalpEisaAdapter[DeviceDescription->DmaChannel] = AdapterObject;
727         }
728 
729         if (MapRegisters > 0)
730         {
731             AdapterObject->NeedsMapRegisters = TRUE;
732             AdapterObject->MapRegistersPerChannel = MapRegisters;
733         }
734         else
735         {
736             AdapterObject->NeedsMapRegisters = FALSE;
737             if (DeviceDescription->Master)
738             {
739                 AdapterObject->MapRegistersPerChannel = BYTES_TO_PAGES(MaximumLength) + 1;
740             }
741             else
742             {
743                 AdapterObject->MapRegistersPerChannel = 1;
744             }
745         }
746     }
747 
748     if (!EisaAdapter) InsertTailList(&HalpDmaAdapterList, &AdapterObject->AdapterList);
749 
750     /*
751      * Release the DMA lock. HalpDmaAdapterList and HalpEisaAdapter will
752      * no longer be touched, so we don't need it.
753      */
754     KeSetEvent(&HalpDmaLock, 0, 0);
755 
756     /*
757      * Setup the values in the adapter object that are common for all
758      * types of buses.
759      */
760     if (DeviceDescription->Version >= DEVICE_DESCRIPTION_VERSION1)
761     {
762         AdapterObject->IgnoreCount = DeviceDescription->IgnoreCount;
763     }
764     else
765     {
766         AdapterObject->IgnoreCount = 0;
767     }
768 
769     AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses;
770     AdapterObject->Dma64BitAddresses = DeviceDescription->Dma64BitAddresses;
771     AdapterObject->ScatterGather = DeviceDescription->ScatterGather;
772     AdapterObject->MasterDevice = DeviceDescription->Master;
773     *NumberOfMapRegisters = AdapterObject->MapRegistersPerChannel;
774 
775     /*
776      * For non-(E)ISA adapters we have already done all the work. On the
777      * other hand for (E)ISA adapters we must still setup the DMA modes
778      * and prepare the controller.
779      */
780     if (EisaAdapter)
781     {
782         if (!HalpDmaInitializeEisaAdapter(AdapterObject, DeviceDescription))
783         {
784             ObDereferenceObject(AdapterObject);
785             return NULL;
786         }
787     }
788 
789     return AdapterObject;
790 }
791 
792 /**
793  * @name HalpGetDmaAdapter
794  *
795  * Internal routine to allocate PnP DMA adapter object. It's exported through
796  * HalDispatchTable and used by IoGetDmaAdapter.
797  *
798  * @see HalGetAdapter
799  */
800 PDMA_ADAPTER
801 NTAPI
802 HalpGetDmaAdapter(IN PVOID Context,
803                   IN PDEVICE_DESCRIPTION DeviceDescription,
804                   OUT PULONG NumberOfMapRegisters)
805 {
806     return &HalGetAdapter(DeviceDescription, NumberOfMapRegisters)->DmaHeader;
807 }
808 
809 /**
810  * @name HalPutDmaAdapter
811  *
812  * Internal routine to free DMA adapter and resources for reuse. It's exported
813  * using the DMA_OPERATIONS interface by HalGetAdapter.
814  *
815  * @see HalGetAdapter
816  */
817 VOID
818 NTAPI
819 HalPutDmaAdapter(IN PADAPTER_OBJECT AdapterObject)
820 {
821     if (AdapterObject->ChannelNumber == 0xFF)
822     {
823         KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL);
824         RemoveEntryList(&AdapterObject->AdapterList);
825         KeSetEvent(&HalpDmaLock, 0, 0);
826     }
827 
828     ObDereferenceObject(AdapterObject);
829 }
830 
831 /**
832  * @name HalAllocateCommonBuffer
833  *
834  * Allocates memory that is visible to both the processor(s) and the DMA
835  * device.
836  *
837  * @param AdapterObject
838  *        Adapter object representing the bus master or system dma controller.
839  * @param Length
840  *        Number of bytes to allocate.
841  * @param LogicalAddress
842  *        Logical address the driver can use to access the buffer.
843  * @param CacheEnabled
844  *        Specifies if the memory can be cached.
845  *
846  * @return The base virtual address of the memory allocated or NULL on failure.
847  *
848  * @remarks
849  *    On real NT x86 systems the CacheEnabled parameter is ignored, we honour
850  *    it. If it proves to cause problems change it.
851  *
852  * @see HalFreeCommonBuffer
853  *
854  * @implemented
855  */
856 PVOID
857 NTAPI
858 HalAllocateCommonBuffer(IN PADAPTER_OBJECT AdapterObject,
859                         IN ULONG Length,
860                         IN PPHYSICAL_ADDRESS LogicalAddress,
861                         IN BOOLEAN CacheEnabled)
862 {
863     PHYSICAL_ADDRESS LowestAcceptableAddress;
864     PHYSICAL_ADDRESS HighestAcceptableAddress;
865     PHYSICAL_ADDRESS BoundryAddressMultiple;
866     PVOID VirtualAddress;
867 
868     LowestAcceptableAddress.QuadPart = 0;
869     HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
870     BoundryAddressMultiple.QuadPart = 0;
871 
872     /*
873      * For bus-master DMA devices the buffer mustn't cross 4Gb boundary. For
874      * slave DMA devices the 64Kb boundary mustn't be crossed since the
875      * controller wouldn't be able to handle it.
876      */
877     if (AdapterObject->MasterDevice)
878     {
879         BoundryAddressMultiple.HighPart = 1;
880     }
881     else
882     {
883         BoundryAddressMultiple.LowPart = 0x10000;
884     }
885 
886     VirtualAddress = MmAllocateContiguousMemorySpecifyCache(Length,
887                                                             LowestAcceptableAddress,
888                                                             HighestAcceptableAddress,
889                                                             BoundryAddressMultiple,
890                                                             CacheEnabled ? MmCached :
891                                                             MmNonCached);
892     if (VirtualAddress == NULL) return NULL;
893 
894     *LogicalAddress = MmGetPhysicalAddress(VirtualAddress);
895 
896     return VirtualAddress;
897 }
898 
899 /**
900  * @name HalFreeCommonBuffer
901  *
902  * Free common buffer allocated with HalAllocateCommonBuffer.
903  *
904  * @see HalAllocateCommonBuffer
905  *
906  * @implemented
907  */
908 VOID
909 NTAPI
910 HalFreeCommonBuffer(IN PADAPTER_OBJECT AdapterObject,
911                     IN ULONG Length,
912                     IN PHYSICAL_ADDRESS LogicalAddress,
913                     IN PVOID VirtualAddress,
914                     IN BOOLEAN CacheEnabled)
915 {
916     MmFreeContiguousMemorySpecifyCache(VirtualAddress,
917                                        Length,
918                                        CacheEnabled ? MmCached : MmNonCached);
919 }
920 
921 typedef struct _SCATTER_GATHER_CONTEXT {
922 	PADAPTER_OBJECT AdapterObject;
923 	PMDL Mdl;
924 	PUCHAR CurrentVa;
925 	ULONG Length;
926 	PDRIVER_LIST_CONTROL AdapterListControlRoutine;
927 	PVOID AdapterListControlContext, MapRegisterBase;
928 	ULONG MapRegisterCount;
929 	BOOLEAN WriteToDevice;
930 	WAIT_CONTEXT_BLOCK Wcb;
931 } SCATTER_GATHER_CONTEXT, *PSCATTER_GATHER_CONTEXT;
932 
933 
934 IO_ALLOCATION_ACTION
935 NTAPI
936 HalpScatterGatherAdapterControl(IN PDEVICE_OBJECT DeviceObject,
937                                 IN PIRP Irp,
938 								IN PVOID MapRegisterBase,
939 								IN PVOID Context)
940 {
941 	PSCATTER_GATHER_CONTEXT AdapterControlContext = Context;
942 	PADAPTER_OBJECT AdapterObject = AdapterControlContext->AdapterObject;
943 	PSCATTER_GATHER_LIST ScatterGatherList;
944 	SCATTER_GATHER_ELEMENT TempElements[MAX_SG_ELEMENTS];
945 	ULONG ElementCount = 0, RemainingLength = AdapterControlContext->Length;
946 	PUCHAR CurrentVa = AdapterControlContext->CurrentVa;
947 
948 	/* Store the map register base for later in HalPutScatterGatherList */
949 	AdapterControlContext->MapRegisterBase = MapRegisterBase;
950 
951 	while (RemainingLength > 0 && ElementCount < MAX_SG_ELEMENTS)
952 	{
953 	    TempElements[ElementCount].Length = RemainingLength;
954 		TempElements[ElementCount].Reserved = 0;
955 	    TempElements[ElementCount].Address = IoMapTransfer(AdapterObject,
956 		                                                   AdapterControlContext->Mdl,
957 														   MapRegisterBase,
958 														   CurrentVa + (AdapterControlContext->Length - RemainingLength),
959 														   &TempElements[ElementCount].Length,
960 														   AdapterControlContext->WriteToDevice);
961 		if (TempElements[ElementCount].Length == 0)
962 			break;
963 
964 		DPRINT("Allocated one S/G element: 0x%I64u with length: 0x%x\n",
965 		        TempElements[ElementCount].Address.QuadPart,
966 				TempElements[ElementCount].Length);
967 
968 		ASSERT(TempElements[ElementCount].Length <= RemainingLength);
969 		RemainingLength -= TempElements[ElementCount].Length;
970 		ElementCount++;
971 	}
972 
973 	if (RemainingLength > 0)
974 	{
975 		DPRINT1("Scatter/gather list construction failed!\n");
976 		return DeallocateObject;
977 	}
978 
979 	ScatterGatherList = ExAllocatePoolWithTag(NonPagedPool,
980 	                                          sizeof(SCATTER_GATHER_LIST) + sizeof(SCATTER_GATHER_ELEMENT) * ElementCount,
981 											  TAG_DMA);
982 	ASSERT(ScatterGatherList);
983 
984 	ScatterGatherList->NumberOfElements = ElementCount;
985 	ScatterGatherList->Reserved = (ULONG_PTR)AdapterControlContext;
986 	RtlCopyMemory(ScatterGatherList->Elements,
987 	              TempElements,
988 				  sizeof(SCATTER_GATHER_ELEMENT) * ElementCount);
989 
990 	DPRINT("Initiating S/G DMA with %d element(s)\n", ElementCount);
991 
992 	AdapterControlContext->AdapterListControlRoutine(DeviceObject,
993 	                                                 Irp,
994 													 ScatterGatherList,
995 													 AdapterControlContext->AdapterListControlContext);
996 
997 	return DeallocateObjectKeepRegisters;
998 }
999 
1000 /**
1001  * @name HalGetScatterGatherList
1002  *
1003  * Creates a scatter-gather list to be using in scatter/gather DMA
1004  *
1005  * @param AdapterObject
1006  *        Adapter object representing the bus master or system dma controller.
1007  * @param DeviceObject
1008  *        The device target for DMA.
1009  * @param Mdl
1010  *        The MDL that describes the buffer to be mapped.
1011  * @param CurrentVa
1012  *        The current VA in the buffer to be mapped for transfer.
1013  * @param Length
1014  *        Specifies the length of data in bytes to be mapped.
1015  * @param ExecutionRoutine
1016  *        A caller supplied AdapterListControl routine to be called when DMA is available.
1017  * @param Context
1018  *        Context passed to the AdapterListControl routine.
1019  * @param WriteToDevice
1020  *        Indicates direction of DMA operation.
1021  *
1022  * @return The status of the operation.
1023  *
1024  * @see HalPutScatterGatherList
1025  *
1026  * @implemented
1027  */
1028  NTSTATUS
1029  NTAPI
1030  HalGetScatterGatherList(IN PADAPTER_OBJECT AdapterObject,
1031                          IN PDEVICE_OBJECT DeviceObject,
1032 						 IN PMDL Mdl,
1033 						 IN PVOID CurrentVa,
1034 						 IN ULONG Length,
1035 						 IN PDRIVER_LIST_CONTROL ExecutionRoutine,
1036 						 IN PVOID Context,
1037 						 IN BOOLEAN WriteToDevice)
1038 {
1039 	PSCATTER_GATHER_CONTEXT AdapterControlContext;
1040 
1041 	AdapterControlContext = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCATTER_GATHER_CONTEXT), TAG_DMA);
1042 	if (!AdapterControlContext) return STATUS_INSUFFICIENT_RESOURCES;
1043 
1044 	AdapterControlContext->AdapterObject = AdapterObject;
1045 	AdapterControlContext->Mdl = Mdl;
1046 	AdapterControlContext->CurrentVa = CurrentVa;
1047 	AdapterControlContext->Length = Length;
1048 	AdapterControlContext->MapRegisterCount = PAGE_ROUND_UP(Length) >> PAGE_SHIFT;
1049 	AdapterControlContext->AdapterListControlRoutine = ExecutionRoutine;
1050 	AdapterControlContext->AdapterListControlContext = Context;
1051 	AdapterControlContext->WriteToDevice = WriteToDevice;
1052 
1053 	AdapterControlContext->Wcb.DeviceObject = DeviceObject;
1054 	AdapterControlContext->Wcb.DeviceContext = AdapterControlContext;
1055 	AdapterControlContext->Wcb.CurrentIrp = DeviceObject->CurrentIrp;
1056 
1057 	return HalAllocateAdapterChannel(AdapterObject,
1058 		&AdapterControlContext->Wcb,
1059 		AdapterControlContext->MapRegisterCount,
1060 		HalpScatterGatherAdapterControl);
1061 }
1062 
1063 /**
1064  * @name HalPutScatterGatherList
1065  *
1066  * Frees a scatter-gather list allocated from HalGetScatterGatherList
1067  *
1068  * @param AdapterObject
1069  *        Adapter object representing the bus master or system dma controller.
1070  * @param ScatterGather
1071  *        The scatter/gather list to be freed.
1072  * @param WriteToDevice
1073  *        Indicates direction of DMA operation.
1074  *
1075  * @return None
1076  *
1077  * @see HalGetScatterGatherList
1078  *
1079  * @implemented
1080  */
1081  VOID
1082  NTAPI
1083  HalPutScatterGatherList(IN PADAPTER_OBJECT AdapterObject,
1084                          IN PSCATTER_GATHER_LIST ScatterGather,
1085 						 IN BOOLEAN WriteToDevice)
1086 {
1087     PSCATTER_GATHER_CONTEXT AdapterControlContext = (PSCATTER_GATHER_CONTEXT)ScatterGather->Reserved;
1088 	ULONG i;
1089 
1090 	for (i = 0; i < ScatterGather->NumberOfElements; i++)
1091 	{
1092 	     IoFlushAdapterBuffers(AdapterObject,
1093 		                       AdapterControlContext->Mdl,
1094 							   AdapterControlContext->MapRegisterBase,
1095 							   AdapterControlContext->CurrentVa,
1096 							   ScatterGather->Elements[i].Length,
1097 							   AdapterControlContext->WriteToDevice);
1098 		 AdapterControlContext->CurrentVa += ScatterGather->Elements[i].Length;
1099 	}
1100 
1101 	IoFreeMapRegisters(AdapterObject,
1102 	                   AdapterControlContext->MapRegisterBase,
1103 					   AdapterControlContext->MapRegisterCount);
1104 
1105 	DPRINT("S/G DMA has finished!\n");
1106 
1107 	ExFreePoolWithTag(AdapterControlContext, TAG_DMA);
1108 	ExFreePoolWithTag(ScatterGather, TAG_DMA);
1109 }
1110 #endif
1111 
1112 /**
1113  * @name HalpDmaGetDmaAlignment
1114  *
1115  * Internal routine to return the DMA alignment requirement. It's exported
1116  * using the DMA_OPERATIONS interface by HalGetAdapter.
1117  *
1118  * @see HalGetAdapter
1119  */
1120 ULONG
1121 NTAPI
1122 HalpDmaGetDmaAlignment(IN PADAPTER_OBJECT AdapterObject)
1123 {
1124     return 1;
1125 }
1126 
1127 /*
1128  * @name HalReadDmaCounter
1129  *
1130  * Read DMA operation progress counter.
1131  *
1132  * @implemented
1133  */
1134 ULONG
1135 NTAPI
1136 HalReadDmaCounter(IN PADAPTER_OBJECT AdapterObject)
1137 {
1138     KIRQL OldIrql;
1139     ULONG Count, OldCount;
1140 
1141     ASSERT(!AdapterObject->MasterDevice);
1142 
1143     /*
1144      * Acquire the master adapter lock since we're going to mess with the
1145      * system DMA controller registers and we really don't want anyone
1146      * to do the same at the same time.
1147      */
1148     KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql);
1149 
1150     /* Send the request to the specific controller. */
1151     if (AdapterObject->AdapterNumber == 1)
1152     {
1153         PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
1154 
1155         Count = 0xffff00;
1156         do
1157         {
1158             OldCount = Count;
1159 
1160             /* Send Reset */
1161             WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0);
1162 
1163             /* Read Count */
1164             Count = READ_PORT_UCHAR(&DmaControl1->DmaAddressCount
1165                                     [AdapterObject->ChannelNumber].DmaBaseCount);
1166             Count |= READ_PORT_UCHAR(&DmaControl1->DmaAddressCount
1167                                      [AdapterObject->ChannelNumber].DmaBaseCount) << 8;
1168         } while (0xffff00 & (OldCount ^ Count));
1169     }
1170     else
1171     {
1172         PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
1173 
1174         Count = 0xffff00;
1175         do
1176         {
1177             OldCount = Count;
1178 
1179             /* Send Reset */
1180             WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0);
1181 
1182             /* Read Count */
1183             Count = READ_PORT_UCHAR(&DmaControl2->DmaAddressCount
1184                                     [AdapterObject->ChannelNumber].DmaBaseCount);
1185             Count |= READ_PORT_UCHAR(&DmaControl2->DmaAddressCount
1186                                      [AdapterObject->ChannelNumber].DmaBaseCount) << 8;
1187         } while (0xffff00 & (OldCount ^ Count));
1188     }
1189 
1190     KeReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql);
1191 
1192     Count++;
1193     Count &= 0xffff;
1194     if (AdapterObject->Width16Bits) Count *= 2;
1195 
1196     return Count;
1197 }
1198 
1199 #ifndef _MINIHAL_
1200 /**
1201  * @name HalpGrowMapBufferWorker
1202  *
1203  * Helper routine of HalAllocateAdapterChannel for allocating map registers
1204  * at PASSIVE_LEVEL in work item.
1205  */
1206 VOID
1207 NTAPI
1208 HalpGrowMapBufferWorker(IN PVOID DeferredContext)
1209 {
1210     PGROW_WORK_ITEM WorkItem = (PGROW_WORK_ITEM)DeferredContext;
1211     KIRQL OldIrql;
1212     BOOLEAN Succeeded;
1213 
1214     /*
1215      * Try to allocate new map registers for the adapter.
1216      *
1217      * NOTE: The NT implementation actually tries to allocate more map
1218      * registers than needed as an optimization.
1219      */
1220     KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL);
1221     Succeeded = HalpGrowMapBuffers(WorkItem->AdapterObject->MasterAdapter,
1222                                    WorkItem->NumberOfMapRegisters << PAGE_SHIFT);
1223     KeSetEvent(&HalpDmaLock, 0, 0);
1224 
1225     if (Succeeded)
1226     {
1227         /*
1228          * Flush the adapter queue now that new map registers are ready. The
1229          * easiest way to do that is to call IoFreeMapRegisters to not free
1230          * any registers. Note that we use the magic (PVOID)2 map register
1231          * base to bypass the parameter checking.
1232          */
1233         OldIrql = KfRaiseIrql(DISPATCH_LEVEL);
1234         IoFreeMapRegisters(WorkItem->AdapterObject, (PVOID)2, 0);
1235         KfLowerIrql(OldIrql);
1236     }
1237 
1238     ExFreePool(WorkItem);
1239 }
1240 
1241 /**
1242  * @name HalAllocateAdapterChannel
1243  *
1244  * Setup map registers for an adapter object.
1245  *
1246  * @param AdapterObject
1247  *        Pointer to an ADAPTER_OBJECT to set up.
1248  * @param WaitContextBlock
1249  *        Context block to be used with ExecutionRoutine.
1250  * @param NumberOfMapRegisters
1251  *        Number of map registers requested.
1252  * @param ExecutionRoutine
1253  *        Callback to call when map registers are allocated.
1254  *
1255  * @return
1256  *    If not enough map registers can be allocated then
1257  *    STATUS_INSUFFICIENT_RESOURCES is returned. If the function
1258  *    succeeds or the callback is queued for later delivering then
1259  *    STATUS_SUCCESS is returned.
1260  *
1261  * @see IoFreeAdapterChannel
1262  *
1263  * @implemented
1264  */
1265 NTSTATUS
1266 NTAPI
1267 HalAllocateAdapterChannel(IN PADAPTER_OBJECT AdapterObject,
1268                           IN PWAIT_CONTEXT_BLOCK WaitContextBlock,
1269                           IN ULONG NumberOfMapRegisters,
1270                           IN PDRIVER_CONTROL ExecutionRoutine)
1271 {
1272     PADAPTER_OBJECT MasterAdapter;
1273     PGROW_WORK_ITEM WorkItem;
1274     ULONG Index = MAXULONG;
1275     ULONG Result;
1276     KIRQL OldIrql;
1277 
1278     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1279 
1280     /* Set up the wait context block in case we can't run right away. */
1281     WaitContextBlock->DeviceRoutine = ExecutionRoutine;
1282     WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters;
1283 
1284     /* Returns true if queued, else returns false and sets the queue to busy */
1285     if (KeInsertDeviceQueue(&AdapterObject->ChannelWaitQueue,
1286                             &WaitContextBlock->WaitQueueEntry))
1287     {
1288         return STATUS_SUCCESS;
1289     }
1290 
1291     MasterAdapter = AdapterObject->MasterAdapter;
1292 
1293     AdapterObject->NumberOfMapRegisters = NumberOfMapRegisters;
1294     AdapterObject->CurrentWcb = WaitContextBlock;
1295 
1296     if ((NumberOfMapRegisters) && (AdapterObject->NeedsMapRegisters))
1297     {
1298         if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel)
1299         {
1300             AdapterObject->NumberOfMapRegisters = 0;
1301             IoFreeAdapterChannel(AdapterObject);
1302             return STATUS_INSUFFICIENT_RESOURCES;
1303         }
1304 
1305         /*
1306          * Get the map registers. This is partly complicated by the fact
1307          * that new map registers can only be allocated at PASSIVE_LEVEL
1308          * and we're currently at DISPATCH_LEVEL. The following code has
1309          * two code paths:
1310          *
1311          * - If there is no adapter queued for map register allocation,
1312          *   try to see if enough contiguous map registers are present.
1313          *   In case they're we can just get them and proceed further.
1314          *
1315          * - If some adapter is already present in the queue we must
1316          *   respect the order of adapters asking for map registers and
1317          *   so the fast case described above can't take place.
1318          *   This case is also entered if not enough coniguous map
1319          *   registers are present.
1320          *
1321          *   A work queue item is allocated and queued, the adapter is
1322          *   also queued into the master adapter queue. The worker
1323          *   routine does the job of allocating the map registers at
1324          *   PASSIVE_LEVEL and calling the ExecutionRoutine.
1325          */
1326 
1327         KeAcquireSpinLock(&MasterAdapter->SpinLock, &OldIrql);
1328 
1329         if (IsListEmpty(&MasterAdapter->AdapterQueue))
1330         {
1331             Index = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters, NumberOfMapRegisters, 0);
1332             if (Index != MAXULONG)
1333             {
1334                 AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index;
1335                 if (!AdapterObject->ScatterGather)
1336                 {
1337                     AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG);
1338                 }
1339             }
1340         }
1341 
1342         if (Index == MAXULONG)
1343         {
1344             InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue);
1345 
1346             WorkItem = ExAllocatePoolWithTag(NonPagedPool,
1347                                              sizeof(GROW_WORK_ITEM),
1348                                              TAG_DMA);
1349             if (WorkItem)
1350             {
1351                 ExInitializeWorkItem(&WorkItem->WorkQueueItem, HalpGrowMapBufferWorker, WorkItem);
1352                 WorkItem->AdapterObject = AdapterObject;
1353                 WorkItem->NumberOfMapRegisters = NumberOfMapRegisters;
1354 
1355                 ExQueueWorkItem(&WorkItem->WorkQueueItem, DelayedWorkQueue);
1356             }
1357 
1358             KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1359 
1360             return STATUS_SUCCESS;
1361         }
1362 
1363         KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1364     }
1365     else
1366     {
1367         AdapterObject->MapRegisterBase = NULL;
1368         AdapterObject->NumberOfMapRegisters = 0;
1369     }
1370 
1371     AdapterObject->CurrentWcb = WaitContextBlock;
1372 
1373     Result = ExecutionRoutine(WaitContextBlock->DeviceObject,
1374                               WaitContextBlock->CurrentIrp,
1375                               AdapterObject->MapRegisterBase,
1376                               WaitContextBlock->DeviceContext);
1377 
1378     /*
1379      * Possible return values:
1380      *
1381      * - KeepObject
1382      *   Don't free any resources, the ADAPTER_OBJECT is still in use and
1383      *   the caller will call IoFreeAdapterChannel later.
1384      *
1385      * - DeallocateObject
1386      *   Deallocate the map registers and release the ADAPTER_OBJECT, so
1387      *   someone else can use it.
1388      *
1389      * - DeallocateObjectKeepRegisters
1390      *   Release the ADAPTER_OBJECT, but hang on to the map registers. The
1391      *   client will later call IoFreeMapRegisters.
1392      *
1393      * NOTE:
1394      * IoFreeAdapterChannel runs the queue, so it must be called unless
1395      * the adapter object is not to be freed.
1396      */
1397     if (Result == DeallocateObject)
1398     {
1399         IoFreeAdapterChannel(AdapterObject);
1400     }
1401     else if (Result == DeallocateObjectKeepRegisters)
1402     {
1403         AdapterObject->NumberOfMapRegisters = 0;
1404         IoFreeAdapterChannel(AdapterObject);
1405     }
1406 
1407     return STATUS_SUCCESS;
1408 }
1409 
1410 /**
1411  * @name IoFreeAdapterChannel
1412  *
1413  * Free DMA resources allocated by IoAllocateAdapterChannel.
1414  *
1415  * @param AdapterObject
1416  *        Adapter object with resources to free.
1417  *
1418  * @remarks
1419  *    This function releases map registers registers assigned to the DMA
1420  *    adapter. After releasing the adapter, it checks the adapter's queue
1421  *    and runs each queued device object in series until the queue is
1422  *    empty. This is the only way the device queue is emptied.
1423  *
1424  * @see IoAllocateAdapterChannel
1425  *
1426  * @implemented
1427  */
1428 VOID
1429 NTAPI
1430 IoFreeAdapterChannel(IN PADAPTER_OBJECT AdapterObject)
1431 {
1432     PADAPTER_OBJECT MasterAdapter;
1433     PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
1434     PWAIT_CONTEXT_BLOCK WaitContextBlock;
1435     ULONG Index = MAXULONG;
1436     ULONG Result;
1437     KIRQL OldIrql;
1438 
1439     MasterAdapter = AdapterObject->MasterAdapter;
1440 
1441     for (;;)
1442     {
1443         /*
1444          * To keep map registers, call here with AdapterObject->
1445          * NumberOfMapRegisters set to zero. This trick is used in
1446          * HalAllocateAdapterChannel for example.
1447          */
1448         if (AdapterObject->NumberOfMapRegisters)
1449         {
1450             IoFreeMapRegisters(AdapterObject,
1451                                AdapterObject->MapRegisterBase,
1452                                AdapterObject->NumberOfMapRegisters);
1453         }
1454 
1455         DeviceQueueEntry = KeRemoveDeviceQueue(&AdapterObject->ChannelWaitQueue);
1456         if (!DeviceQueueEntry) break;
1457 
1458         WaitContextBlock = CONTAINING_RECORD(DeviceQueueEntry,
1459                                              WAIT_CONTEXT_BLOCK,
1460                                              WaitQueueEntry);
1461 
1462         AdapterObject->CurrentWcb = WaitContextBlock;
1463         AdapterObject->NumberOfMapRegisters = WaitContextBlock->NumberOfMapRegisters;
1464 
1465         if ((WaitContextBlock->NumberOfMapRegisters) && (AdapterObject->MasterAdapter))
1466         {
1467             KeAcquireSpinLock(&MasterAdapter->SpinLock, &OldIrql);
1468 
1469             if (IsListEmpty(&MasterAdapter->AdapterQueue))
1470             {
1471                 Index = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters,
1472                                                WaitContextBlock->NumberOfMapRegisters,
1473                                                0);
1474                 if (Index != MAXULONG)
1475                 {
1476                     AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index;
1477                     if (!AdapterObject->ScatterGather)
1478                     {
1479                         AdapterObject->MapRegisterBase =(PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG);
1480                     }
1481                 }
1482             }
1483 
1484             if (Index == MAXULONG)
1485             {
1486                 InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue);
1487                 KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1488                 break;
1489             }
1490 
1491             KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1492         }
1493         else
1494         {
1495             AdapterObject->MapRegisterBase = NULL;
1496             AdapterObject->NumberOfMapRegisters = 0;
1497         }
1498 
1499         /* Call the adapter control routine. */
1500         Result = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)(WaitContextBlock->DeviceObject,
1501                                                                     WaitContextBlock->CurrentIrp,
1502                                                                     AdapterObject->MapRegisterBase,
1503                                                                     WaitContextBlock->DeviceContext);
1504         switch (Result)
1505         {
1506             case KeepObject:
1507                 /*
1508                  * We're done until the caller manually calls IoFreeAdapterChannel
1509                  * or IoFreeMapRegisters.
1510                  */
1511                 return;
1512 
1513             case DeallocateObjectKeepRegisters:
1514                 /*
1515                  * Hide the map registers so they aren't deallocated next time
1516                  * around.
1517                  */
1518                 AdapterObject->NumberOfMapRegisters = 0;
1519                 break;
1520 
1521             default:
1522                 break;
1523         }
1524     }
1525 }
1526 
1527 /**
1528  * @name IoFreeMapRegisters
1529  *
1530  * Free map registers reserved by the system for a DMA.
1531  *
1532  * @param AdapterObject
1533  *        DMA adapter to free map registers on.
1534  * @param MapRegisterBase
1535  *        Handle to map registers to free.
1536  * @param NumberOfRegisters
1537  *        Number of map registers to be freed.
1538  *
1539  * @implemented
1540  */
1541 VOID
1542 NTAPI
1543 IoFreeMapRegisters(IN PADAPTER_OBJECT AdapterObject,
1544                    IN PVOID MapRegisterBase,
1545                    IN ULONG NumberOfMapRegisters)
1546 {
1547     PADAPTER_OBJECT MasterAdapter = AdapterObject->MasterAdapter;
1548     PLIST_ENTRY ListEntry;
1549     KIRQL OldIrql;
1550     ULONG Index;
1551     ULONG Result;
1552 
1553     ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
1554 
1555     if (!(MasterAdapter) || !(MapRegisterBase)) return;
1556 
1557     KeAcquireSpinLock(&MasterAdapter->SpinLock, &OldIrql);
1558 
1559     if (NumberOfMapRegisters != 0)
1560     {
1561         PROS_MAP_REGISTER_ENTRY RealMapRegisterBase;
1562 
1563         RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
1564         RtlClearBits(MasterAdapter->MapRegisters,
1565                      (ULONG)(RealMapRegisterBase - MasterAdapter->MapRegisterBase),
1566                      NumberOfMapRegisters);
1567     }
1568 
1569     /*
1570      * Now that we freed few map registers it's time to look at the master
1571      * adapter queue and see if there is someone waiting for map registers.
1572      */
1573     while (!IsListEmpty(&MasterAdapter->AdapterQueue))
1574     {
1575         ListEntry = RemoveHeadList(&MasterAdapter->AdapterQueue);
1576         AdapterObject = CONTAINING_RECORD(ListEntry, struct _ADAPTER_OBJECT, AdapterQueue);
1577 
1578         Index = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters,
1579                                        AdapterObject->NumberOfMapRegisters,
1580                                        0);
1581         if (Index == MAXULONG)
1582         {
1583             InsertHeadList(&MasterAdapter->AdapterQueue, ListEntry);
1584             break;
1585         }
1586 
1587         KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1588 
1589         AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index;
1590         if (!AdapterObject->ScatterGather)
1591         {
1592             AdapterObject->MapRegisterBase =
1593                 (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG);
1594         }
1595 
1596         Result = ((PDRIVER_CONTROL)AdapterObject->CurrentWcb->DeviceRoutine)(AdapterObject->CurrentWcb->DeviceObject,
1597                                                                              AdapterObject->CurrentWcb->CurrentIrp,
1598                                                                              AdapterObject->MapRegisterBase,
1599                                                                              AdapterObject->CurrentWcb->DeviceContext);
1600         switch (Result)
1601         {
1602             case DeallocateObjectKeepRegisters:
1603                 AdapterObject->NumberOfMapRegisters = 0;
1604                 /* fall through */
1605 
1606             case DeallocateObject:
1607                 if (AdapterObject->NumberOfMapRegisters)
1608                 {
1609                     KeAcquireSpinLock(&MasterAdapter->SpinLock, &OldIrql);
1610                     RtlClearBits(MasterAdapter->MapRegisters,
1611                                  (ULONG)(AdapterObject->MapRegisterBase -
1612                                          MasterAdapter->MapRegisterBase),
1613                                  AdapterObject->NumberOfMapRegisters);
1614                     KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1615                 }
1616 
1617                 IoFreeAdapterChannel(AdapterObject);
1618                 break;
1619 
1620             default:
1621                 break;
1622         }
1623 
1624         KeAcquireSpinLock(&MasterAdapter->SpinLock, &OldIrql);
1625     }
1626 
1627     KeReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql);
1628 }
1629 
1630 /**
1631  * @name HalpCopyBufferMap
1632  *
1633  * Helper function for copying data from/to map register buffers.
1634  *
1635  * @see IoFlushAdapterBuffers, IoMapTransfer
1636  */
1637 VOID
1638 NTAPI
1639 HalpCopyBufferMap(IN PMDL Mdl,
1640                   IN PROS_MAP_REGISTER_ENTRY MapRegisterBase,
1641                   IN PVOID CurrentVa,
1642                   IN ULONG Length,
1643                   IN BOOLEAN WriteToDevice)
1644 {
1645     ULONG CurrentLength;
1646     ULONG_PTR CurrentAddress;
1647     ULONG ByteOffset;
1648     PVOID VirtualAddress;
1649 
1650     VirtualAddress = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority);
1651     if (!VirtualAddress)
1652     {
1653         /*
1654          * NOTE: On real NT a mechanism with reserved pages is implemented
1655          * to handle this case in a slow, but graceful non-fatal way.
1656          */
1657          KeBugCheckEx(HAL_MEMORY_ALLOCATION, PAGE_SIZE, 0, (ULONG_PTR)__FILE__, 0);
1658     }
1659 
1660     CurrentAddress = (ULONG_PTR)VirtualAddress +
1661                      (ULONG_PTR)CurrentVa -
1662                      (ULONG_PTR)MmGetMdlVirtualAddress(Mdl);
1663 
1664     while (Length > 0)
1665     {
1666         ByteOffset = BYTE_OFFSET(CurrentAddress);
1667         CurrentLength = PAGE_SIZE - ByteOffset;
1668         if (CurrentLength > Length) CurrentLength = Length;
1669 
1670         if (WriteToDevice)
1671         {
1672             RtlCopyMemory((PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset),
1673                           (PVOID)CurrentAddress,
1674                           CurrentLength);
1675         }
1676         else
1677         {
1678             RtlCopyMemory((PVOID)CurrentAddress,
1679                           (PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset),
1680                           CurrentLength);
1681         }
1682 
1683         Length -= CurrentLength;
1684         CurrentAddress += CurrentLength;
1685         MapRegisterBase++;
1686     }
1687 }
1688 
1689 /**
1690  * @name IoFlushAdapterBuffers
1691  *
1692  * Flush any data remaining in the DMA controller's memory into the host
1693  * memory.
1694  *
1695  * @param AdapterObject
1696  *        The adapter object to flush.
1697  * @param Mdl
1698  *        Original MDL to flush data into.
1699  * @param MapRegisterBase
1700  *        Map register base that was just used by IoMapTransfer, etc.
1701  * @param CurrentVa
1702  *        Offset into Mdl to be flushed into, same as was passed to
1703  *        IoMapTransfer.
1704  * @param Length
1705  *        Length of the buffer to be flushed into.
1706  * @param WriteToDevice
1707  *        TRUE if it's a write, FALSE if it's a read.
1708  *
1709  * @return TRUE in all cases.
1710  *
1711  * @remarks
1712  *    This copies data from the map register-backed buffer to the user's
1713  *    target buffer. Data are not in the user buffer until this function
1714  *    is called.
1715  *    For slave DMA transfers the controller channel is masked effectively
1716  *    stopping the current transfer.
1717  *
1718  * @unimplemented.
1719  */
1720 BOOLEAN
1721 NTAPI
1722 IoFlushAdapterBuffers(IN PADAPTER_OBJECT AdapterObject,
1723                       IN PMDL Mdl,
1724                       IN PVOID MapRegisterBase,
1725                       IN PVOID CurrentVa,
1726                       IN ULONG Length,
1727                       IN BOOLEAN WriteToDevice)
1728 {
1729     BOOLEAN SlaveDma = FALSE;
1730     PROS_MAP_REGISTER_ENTRY RealMapRegisterBase;
1731     PHYSICAL_ADDRESS HighestAcceptableAddress;
1732     PHYSICAL_ADDRESS PhysicalAddress;
1733     PPFN_NUMBER MdlPagesPtr;
1734 
1735     /* Sanity checks */
1736     ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
1737     ASSERT(AdapterObject);
1738 
1739     if (!AdapterObject->MasterDevice)
1740     {
1741         /* Mask out (disable) the DMA channel. */
1742         if (AdapterObject->AdapterNumber == 1)
1743         {
1744             PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
1745             WRITE_PORT_UCHAR(&DmaControl1->SingleMask,
1746                              AdapterObject->ChannelNumber | DMA_SETMASK);
1747         }
1748         else
1749         {
1750             PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
1751             WRITE_PORT_UCHAR(&DmaControl2->SingleMask,
1752                              AdapterObject->ChannelNumber | DMA_SETMASK);
1753         }
1754         SlaveDma = TRUE;
1755     }
1756 
1757     /* This can happen if the device supports hardware scatter/gather. */
1758     if (MapRegisterBase == NULL) return TRUE;
1759 
1760     RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
1761 
1762     if (!WriteToDevice)
1763     {
1764         if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG)
1765         {
1766             if (RealMapRegisterBase->Counter != MAXULONG)
1767             {
1768                 if ((SlaveDma) && !(AdapterObject->IgnoreCount))
1769                 {
1770                     Length -= HalReadDmaCounter(AdapterObject);
1771                 }
1772             }
1773             HalpCopyBufferMap(Mdl,
1774                               RealMapRegisterBase,
1775                               CurrentVa,
1776                               Length,
1777                               FALSE);
1778         }
1779         else
1780         {
1781             MdlPagesPtr = MmGetMdlPfnArray(Mdl);
1782             MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT;
1783 
1784             PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT;
1785             PhysicalAddress.QuadPart += BYTE_OFFSET(CurrentVa);
1786 
1787             HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
1788             if ((PhysicalAddress.QuadPart + Length) > HighestAcceptableAddress.QuadPart)
1789             {
1790                 HalpCopyBufferMap(Mdl,
1791                                   RealMapRegisterBase,
1792                                   CurrentVa,
1793                                   Length,
1794                                   FALSE);
1795             }
1796         }
1797     }
1798 
1799     RealMapRegisterBase->Counter = 0;
1800 
1801     return TRUE;
1802 }
1803 
1804 /**
1805  * @name IoMapTransfer
1806  *
1807  * Map a DMA for transfer and do the DMA if it's a slave.
1808  *
1809  * @param AdapterObject
1810  *        Adapter object to do the DMA on. Bus-master may pass NULL.
1811  * @param Mdl
1812  *        Locked-down user buffer to DMA in to or out of.
1813  * @param MapRegisterBase
1814  *        Handle to map registers to use for this dma.
1815  * @param CurrentVa
1816  *        Index into Mdl to transfer into/out of.
1817  * @param Length
1818  *        Length of transfer. Number of bytes actually transferred on
1819  *        output.
1820  * @param WriteToDevice
1821  *        TRUE if it's an output DMA, FALSE otherwise.
1822  *
1823  * @return
1824  *    A logical address that can be used to program a DMA controller, it's
1825  *    not meaningful for slave DMA device.
1826  *
1827  * @remarks
1828  *    This function does a copyover to contiguous memory <16MB represented
1829  *    by the map registers if needed. If the buffer described by MDL can be
1830  *    used as is no copyover is done.
1831  *    If it's a slave transfer, this function actually performs it.
1832  *
1833  * @implemented
1834  */
1835 PHYSICAL_ADDRESS
1836 NTAPI
1837 IoMapTransfer(IN PADAPTER_OBJECT AdapterObject,
1838               IN PMDL Mdl,
1839               IN PVOID MapRegisterBase,
1840               IN PVOID CurrentVa,
1841               IN OUT PULONG Length,
1842               IN BOOLEAN WriteToDevice)
1843 {
1844     PPFN_NUMBER MdlPagesPtr;
1845     PFN_NUMBER MdlPage1, MdlPage2;
1846     ULONG ByteOffset;
1847     ULONG TransferOffset;
1848     ULONG TransferLength;
1849     BOOLEAN UseMapRegisters;
1850     PROS_MAP_REGISTER_ENTRY RealMapRegisterBase;
1851     PHYSICAL_ADDRESS PhysicalAddress;
1852     PHYSICAL_ADDRESS HighestAcceptableAddress;
1853     ULONG Counter;
1854     DMA_MODE AdapterMode;
1855     KIRQL OldIrql;
1856 
1857     /*
1858      * Precalculate some values that are used in all cases.
1859      *
1860      * ByteOffset is offset inside the page at which the transfer starts.
1861      * MdlPagesPtr is pointer inside the MDL page chain at the page where the
1862      *             transfer start.
1863      * PhysicalAddress is physical address corresponding to the transfer
1864      *                 start page and offset.
1865      * TransferLength is the initial length of the transfer, which is reminder
1866      *                of the first page. The actual value is calculated below.
1867      *
1868      * Note that all the variables can change during the processing which
1869      * takes place below. These are just initial values.
1870      */
1871     ByteOffset = BYTE_OFFSET(CurrentVa);
1872 
1873     MdlPagesPtr = MmGetMdlPfnArray(Mdl);
1874     MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT;
1875 
1876     PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT;
1877     PhysicalAddress.QuadPart += ByteOffset;
1878 
1879     TransferLength = PAGE_SIZE - ByteOffset;
1880 
1881     /*
1882      * Special case for bus master adapters with S/G support. We can directly
1883      * use the buffer specified by the MDL, so not much work has to be done.
1884      *
1885      * Just return the passed VA's corresponding physical address and update
1886      * length to the number of physically contiguous bytes found. Also
1887      * pages crossing the 4Gb boundary aren't considered physically contiguous.
1888      */
1889     if (MapRegisterBase == NULL)
1890     {
1891         while (TransferLength < *Length)
1892         {
1893             MdlPage1 = *MdlPagesPtr;
1894             MdlPage2 = *(MdlPagesPtr + 1);
1895             if (MdlPage1 + 1 != MdlPage2) break;
1896             if ((MdlPage1 ^ MdlPage2) & ~0xFFFFF) break;
1897             TransferLength += PAGE_SIZE;
1898             MdlPagesPtr++;
1899         }
1900 
1901         if (TransferLength < *Length) *Length = TransferLength;
1902 
1903         return PhysicalAddress;
1904     }
1905 
1906     /*
1907      * The code below applies to slave DMA adapters and bus master adapters
1908      * without hardward S/G support.
1909      */
1910     RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG);
1911 
1912     /*
1913      * Try to calculate the size of the transfer. We can only transfer
1914      * pages that are physically contiguous and that don't cross the
1915      * 64Kb boundary (this limitation applies only for ISA controllers).
1916      */
1917     while (TransferLength < *Length)
1918     {
1919         MdlPage1 = *MdlPagesPtr;
1920         MdlPage2 = *(MdlPagesPtr + 1);
1921         if (MdlPage1 + 1 != MdlPage2) break;
1922         if (!HalpEisaDma && ((MdlPage1 ^ MdlPage2) & ~0xF)) break;
1923         TransferLength += PAGE_SIZE;
1924         MdlPagesPtr++;
1925     }
1926 
1927     if (TransferLength > *Length) TransferLength = *Length;
1928 
1929     /*
1930      * If we're about to simulate software S/G and not all the pages are
1931      * physically contiguous then we must use the map registers to store
1932      * the data and allow the whole transfer to proceed at once.
1933      */
1934     if (((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG) && (TransferLength < *Length))
1935     {
1936         UseMapRegisters = TRUE;
1937         PhysicalAddress = RealMapRegisterBase->PhysicalAddress;
1938         PhysicalAddress.QuadPart += ByteOffset;
1939         TransferLength = *Length;
1940         RealMapRegisterBase->Counter = MAXULONG;
1941         Counter = 0;
1942     }
1943     else
1944     {
1945         /*
1946          * This is ordinary DMA transfer, so just update the progress
1947          * counters. These are used by IoFlushAdapterBuffers to track
1948          * the transfer progress.
1949          */
1950         UseMapRegisters = FALSE;
1951         Counter = RealMapRegisterBase->Counter;
1952         RealMapRegisterBase->Counter += BYTES_TO_PAGES(ByteOffset + TransferLength);
1953 
1954         /*
1955          * Check if the buffer doesn't exceed the highest physical address
1956          * limit of the device. In that case we must use the map registers to
1957          * store the data.
1958          */
1959         HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject);
1960         if ((PhysicalAddress.QuadPart + TransferLength) > HighestAcceptableAddress.QuadPart)
1961         {
1962             UseMapRegisters = TRUE;
1963             PhysicalAddress = RealMapRegisterBase[Counter].PhysicalAddress;
1964             PhysicalAddress.QuadPart += ByteOffset;
1965             if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG)
1966             {
1967                 RealMapRegisterBase->Counter = MAXULONG;
1968                 Counter = 0;
1969             }
1970         }
1971     }
1972 
1973     /*
1974      * If we decided to use the map registers (see above) and we're about
1975      * to transfer data to the device then copy the buffers into the map
1976      * register memory.
1977      */
1978     if ((UseMapRegisters) && (WriteToDevice))
1979     {
1980         HalpCopyBufferMap(Mdl,
1981                           RealMapRegisterBase + Counter,
1982                           CurrentVa,
1983                           TransferLength,
1984                           WriteToDevice);
1985     }
1986 
1987     /*
1988      * Return the length of transfer that actually takes place.
1989      */
1990     *Length = TransferLength;
1991 
1992     /*
1993      * If we're doing slave (system) DMA then program the (E)ISA controller
1994      * to actually start the transfer.
1995      */
1996     if ((AdapterObject) && !(AdapterObject->MasterDevice))
1997     {
1998         AdapterMode = AdapterObject->AdapterMode;
1999 
2000         if (WriteToDevice)
2001         {
2002             AdapterMode.TransferType = WRITE_TRANSFER;
2003         }
2004         else
2005         {
2006             AdapterMode.TransferType = READ_TRANSFER;
2007             if (AdapterObject->IgnoreCount)
2008             {
2009                 RtlZeroMemory((PUCHAR)RealMapRegisterBase[Counter].VirtualAddress + ByteOffset,
2010                               TransferLength);
2011             }
2012         }
2013 
2014         TransferOffset = PhysicalAddress.LowPart & 0xFFFF;
2015         if (AdapterObject->Width16Bits)
2016         {
2017             TransferLength >>= 1;
2018             TransferOffset >>= 1;
2019         }
2020 
2021         KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql);
2022 
2023         if (AdapterObject->AdapterNumber == 1)
2024         {
2025             PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa;
2026 
2027             /* Reset Register */
2028             WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0);
2029 
2030             /* Set the Mode */
2031             WRITE_PORT_UCHAR(&DmaControl1->Mode, AdapterMode.Byte);
2032 
2033             /* Set the Offset Register */
2034             WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
2035                              (UCHAR)(TransferOffset));
2036             WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
2037                              (UCHAR)(TransferOffset >> 8));
2038 
2039             /* Set the Page Register */
2040             WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages),
2041                              (UCHAR)(PhysicalAddress.LowPart >> 16));
2042             if (HalpEisaDma)
2043             {
2044                 WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages),
2045                                  0);
2046             }
2047 
2048             /* Set the Length */
2049             WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
2050                              (UCHAR)(TransferLength - 1));
2051             WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
2052                              (UCHAR)((TransferLength - 1) >> 8));
2053 
2054             /* Unmask the Channel */
2055             WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK);
2056         }
2057         else
2058         {
2059             PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa;
2060 
2061             /* Reset Register */
2062             WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0);
2063 
2064             /* Set the Mode */
2065             WRITE_PORT_UCHAR(&DmaControl2->Mode, AdapterMode.Byte);
2066 
2067             /* Set the Offset Register */
2068             WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
2069                              (UCHAR)(TransferOffset));
2070             WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress,
2071                              (UCHAR)(TransferOffset >> 8));
2072 
2073             /* Set the Page Register */
2074             WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages),
2075                              (UCHAR)(PhysicalAddress.u.LowPart >> 16));
2076             if (HalpEisaDma)
2077             {
2078                 WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages),
2079                                  0);
2080             }
2081 
2082             /* Set the Length */
2083             WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
2084                              (UCHAR)(TransferLength - 1));
2085             WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount,
2086                              (UCHAR)((TransferLength - 1) >> 8));
2087 
2088             /* Unmask the Channel */
2089             WRITE_PORT_UCHAR(&DmaControl2->SingleMask,
2090                              AdapterObject->ChannelNumber | DMA_CLEARMASK);
2091         }
2092 
2093         KeReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql);
2094     }
2095 
2096     /*
2097      * Return physical address of the buffer with data that is used for the
2098      * transfer. It can either point inside the Mdl that was passed by the
2099      * caller or into the map registers if the Mdl buffer can't be used
2100      * directly.
2101      */
2102      return PhysicalAddress;
2103 }
2104 #endif
2105 
2106 /**
2107  * @name HalFlushCommonBuffer
2108  *
2109  * @implemented
2110  */
2111 BOOLEAN
2112 NTAPI
2113 HalFlushCommonBuffer(IN PADAPTER_OBJECT AdapterObject,
2114                      IN ULONG Length,
2115                      IN PHYSICAL_ADDRESS LogicalAddress,
2116                      IN PVOID VirtualAddress)
2117 {
2118     /* Function always returns true */
2119     return TRUE;
2120 }
2121 
2122 /*
2123  * @implemented
2124  */
2125 PVOID
2126 NTAPI
2127 HalAllocateCrashDumpRegisters(IN PADAPTER_OBJECT AdapterObject,
2128                               IN OUT PULONG NumberOfMapRegisters)
2129 {
2130     PADAPTER_OBJECT MasterAdapter = AdapterObject->MasterAdapter;
2131     ULONG MapRegisterNumber;
2132 
2133     /* Check if it needs map registers */
2134     if (AdapterObject->NeedsMapRegisters)
2135     {
2136         /* Check if we have enough */
2137         if (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel)
2138         {
2139             /* We don't, fail */
2140             AdapterObject->NumberOfMapRegisters = 0;
2141             return NULL;
2142         }
2143 
2144         /* Try to find free map registers */
2145         MapRegisterNumber = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters,
2146                                                    *NumberOfMapRegisters,
2147                                                    0);
2148 
2149         /* Check if nothing was found */
2150         if (MapRegisterNumber == MAXULONG)
2151         {
2152             /* No free registers found, so use the base registers */
2153             RtlSetBits(MasterAdapter->MapRegisters,
2154                        0,
2155                        *NumberOfMapRegisters);
2156             MapRegisterNumber = 0;
2157         }
2158 
2159         /* Calculate the new base */
2160         AdapterObject->MapRegisterBase =
2161             (PROS_MAP_REGISTER_ENTRY)(MasterAdapter->MapRegisterBase +
2162                                       MapRegisterNumber);
2163 
2164         /* Check if scatter gather isn't supported */
2165         if (!AdapterObject->ScatterGather)
2166         {
2167             /* Set the flag */
2168             AdapterObject->MapRegisterBase =
2169                 (PROS_MAP_REGISTER_ENTRY)
2170                 ((ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG);
2171         }
2172     }
2173     else
2174     {
2175         AdapterObject->MapRegisterBase = NULL;
2176         AdapterObject->NumberOfMapRegisters = 0;
2177     }
2178 
2179     /* Return the base */
2180     return AdapterObject->MapRegisterBase;
2181 }
2182 
2183 /* EOF */
2184