/* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS kernel * FILE: hal/halppc/generic/dma.c * PURPOSE: DMA functions * PROGRAMMERS: David Welch (welch@mcmail.com) * Filip Navara (navaraf@reactos.com) * UPDATE HISTORY: * Created 22/05/98 */ /** * @page DMA Implementation Notes * * Concepts: * * - Map register * * Abstract encapsulation of physically contiguous buffer that resides * in memory accessible by both the DMA device / controller and the system. * The map registers are allocated and distributed on demand and are * scarce resource. * * The actual use of map registers is to allow transfers from/to buffer * located in physical memory at address inaccessible by the DMA device / * controller directly. For such transfers the map register buffers * are used as intermediate data storage. * * - Master adapter * * A container for map registers (typically corresponding to one physical * bus connection type). There can be master adapters for 24-bit address * ranges, 32-bit address ranges, etc. Every time a new DMA adapter is * created it's associated with a corresponding master adapter that * is used for any map register allocation requests. * * - Bus-master / Slave DMA * * Slave DMA is term used for DMA transfers done by the system (E)ISA * controller as opposed to transfers mastered by the device itself * (hence the name). * * For slave DMA special care is taken to actually access the system * controller and handle the transfers. The relevant code is in * HalpDmaInitializeEisaAdapter, HalReadDmaCounter, IoFlushAdapterBuffers * and IoMapTransfer. * * Implementation: * * - Allocation of map registers * * Initial set of map registers is allocated on the system start to * ensure that low memory won't get filled up later. Additional map * registers are allocated as needed by HalpGrowMapBuffers. This * routine is called on two places: * * - HalGetAdapter, since we're at PASSIVE_LEVEL and it's known that * more map registers will probably be needed. * - IoAllocateAdapterChannel (indirectly using HalpGrowMapBufferWorker * since we're at DISPATCH_LEVEL and call HalpGrowMapBuffers directly) * when no more map registers are free. * * Note that even if no more map registers can be allocated it's not * the end of the world. The adapters waiting for free map registers * are queued in the master adapter's queue and once one driver hands * back it's map registers (using IoFreeMapRegisters or indirectly using * the execution routine callback in IoAllocateAdapterChannel) the * queue gets processed and the map registers are reassigned. */ /* INCLUDES *****************************************************************/ #include #define NDEBUG #include static KEVENT HalpDmaLock; static LIST_ENTRY HalpDmaAdapterList; static PADAPTER_OBJECT HalpEisaAdapter[8]; static BOOLEAN HalpEisaDma; static PADAPTER_OBJECT HalpMasterAdapter; static const ULONG_PTR HalpEisaPortPage[8] = { FIELD_OFFSET(DMA_PAGE, Channel0), FIELD_OFFSET(DMA_PAGE, Channel1), FIELD_OFFSET(DMA_PAGE, Channel2), FIELD_OFFSET(DMA_PAGE, Channel3), 0, FIELD_OFFSET(DMA_PAGE, Channel5), FIELD_OFFSET(DMA_PAGE, Channel6), FIELD_OFFSET(DMA_PAGE, Channel7) }; static DMA_OPERATIONS HalpDmaOperations = { sizeof(DMA_OPERATIONS), (PPUT_DMA_ADAPTER)HalPutDmaAdapter, (PALLOCATE_COMMON_BUFFER)HalAllocateCommonBuffer, (PFREE_COMMON_BUFFER)HalFreeCommonBuffer, NULL, /* Initialized in HalpInitDma() */ NULL, /* Initialized in HalpInitDma() */ NULL, /* Initialized in HalpInitDma() */ NULL, /* Initialized in HalpInitDma() */ NULL, /* Initialized in HalpInitDma() */ (PGET_DMA_ALIGNMENT)HalpDmaGetDmaAlignment, (PREAD_DMA_COUNTER)HalReadDmaCounter, /* FIXME: Implement the S/G funtions. */ NULL /*(PGET_SCATTER_GATHER_LIST)HalGetScatterGatherList*/, NULL /*(PPUT_SCATTER_GATHER_LIST)HalPutScatterGatherList*/, NULL /*(PCALCULATE_SCATTER_GATHER_LIST_SIZE)HalCalculateScatterGatherListSize*/, NULL /*(PBUILD_SCATTER_GATHER_LIST)HalBuildScatterGatherList*/, NULL /*(PBUILD_MDL_FROM_SCATTER_GATHER_LIST)HalBuildMdlFromScatterGatherList*/ }; #define MAX_MAP_REGISTERS 64 #define TAG_DMA ' AMD' /* FUNCTIONS *****************************************************************/ VOID HalpInitDma(VOID) { /* * Initialize the DMA Operation table */ HalpDmaOperations.AllocateAdapterChannel = (PALLOCATE_ADAPTER_CHANNEL)IoAllocateAdapterChannel; HalpDmaOperations.FlushAdapterBuffers = (PFLUSH_ADAPTER_BUFFERS)IoFlushAdapterBuffers; HalpDmaOperations.FreeAdapterChannel = (PFREE_ADAPTER_CHANNEL)IoFreeAdapterChannel; HalpDmaOperations.FreeMapRegisters = (PFREE_MAP_REGISTERS)IoFreeMapRegisters; HalpDmaOperations.MapTransfer = (PMAP_TRANSFER)IoMapTransfer; /* * Check if Extended DMA is available. We're just going to do a random * read and write. */ WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2), 0x2A); if (READ_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaController2Pages.Channel2)) == 0x2A) HalpEisaDma = TRUE; /* * Intialize all the global variables and allocate master adapter with * first map buffers. */ InitializeListHead(&HalpDmaAdapterList); KeInitializeEvent(&HalpDmaLock, NotificationEvent, TRUE); HalpMasterAdapter = HalpDmaAllocateMasterAdapter(); /* * Setup the HalDispatchTable callback for creating PnP DMA adapters. It's * used by IoGetDmaAdapter in the kernel. */ HalGetDmaAdapter = HalpGetDmaAdapter; } /** * @name HalpGetAdapterMaximumPhysicalAddress * * Get the maximum physical address acceptable by the device represented * by the passed DMA adapter. */ PHYSICAL_ADDRESS NTAPI HalpGetAdapterMaximumPhysicalAddress( IN PADAPTER_OBJECT AdapterObject) { PHYSICAL_ADDRESS HighestAddress; if (AdapterObject->MasterDevice) { if (AdapterObject->Dma64BitAddresses) { HighestAddress.QuadPart = 0xFFFFFFFFFFFFFFFFULL; return HighestAddress; } else if (AdapterObject->Dma32BitAddresses) { HighestAddress.QuadPart = 0xFFFFFFFF; return HighestAddress; } } HighestAddress.QuadPart = 0xFFFFFF; return HighestAddress; } /** * @name HalpGrowMapBuffers * * Allocate initial, or additional, map buffers for DMA master adapter. * * @param MasterAdapter * DMA master adapter to allocate buffers for. * @param SizeOfMapBuffers * Size of the map buffers to allocate (not including the size * already allocated). */ BOOLEAN NTAPI HalpGrowMapBuffers( IN PADAPTER_OBJECT AdapterObject, IN ULONG SizeOfMapBuffers) { PVOID VirtualAddress; PHYSICAL_ADDRESS PhysicalAddress; PHYSICAL_ADDRESS HighestAcceptableAddress; PHYSICAL_ADDRESS LowestAcceptableAddress; PHYSICAL_ADDRESS BoundryAddressMultiple; KIRQL OldIrql; ULONG MapRegisterCount; /* FIXME: Check if enough map register slots are available. */ MapRegisterCount = BYTES_TO_PAGES(SizeOfMapBuffers); /* * Allocate memory for the new map registers. For 32-bit adapters we use * two passes in order not to waste scare resource (low memory). */ HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject); LowestAcceptableAddress.HighPart = 0; LowestAcceptableAddress.LowPart = HighestAcceptableAddress.LowPart == 0xFFFFFFFF ? 0x1000000 : 0; BoundryAddressMultiple.QuadPart = 0; VirtualAddress = MmAllocateContiguousMemorySpecifyCache( MapRegisterCount << PAGE_SHIFT, LowestAcceptableAddress, HighestAcceptableAddress, BoundryAddressMultiple, MmNonCached); if (VirtualAddress == NULL && LowestAcceptableAddress.LowPart != 0) { LowestAcceptableAddress.LowPart = 0; VirtualAddress = MmAllocateContiguousMemorySpecifyCache( MapRegisterCount << PAGE_SHIFT, LowestAcceptableAddress, HighestAcceptableAddress, BoundryAddressMultiple, MmNonCached); } if (VirtualAddress == NULL) return FALSE; PhysicalAddress = MmGetPhysicalAddress(VirtualAddress); /* * All the following must be done with the master adapter lock held * to prevent corruption. */ OldIrql = KfAcquireSpinLock(&AdapterObject->SpinLock); /* * Setup map register entries for the buffer allocated. Each entry has * a virtual and physical address and corresponds to PAGE_SIZE large * buffer. */ if (MapRegisterCount > 0) { PROS_MAP_REGISTER_ENTRY CurrentEntry, PreviousEntry; CurrentEntry = AdapterObject->MapRegisterBase + AdapterObject->NumberOfMapRegisters; do { /* * Leave one entry free for every non-contiguous memory region * in the map register bitmap. This ensures that we can search * using RtlFindClearBits for contiguous map register regions. * * Also for non-EISA DMA leave one free entry for every 64Kb * break, because the DMA controller can handle only coniguous * 64Kb regions. */ if (CurrentEntry != AdapterObject->MapRegisterBase) { PreviousEntry = CurrentEntry - 1; if (PreviousEntry->PhysicalAddress.LowPart + PAGE_SIZE == PhysicalAddress.LowPart) { if (!HalpEisaDma) { if ((PreviousEntry->PhysicalAddress.LowPart ^ PhysicalAddress.LowPart) & 0xFFFF0000) { CurrentEntry++; AdapterObject->NumberOfMapRegisters++; } } } else { CurrentEntry++; AdapterObject->NumberOfMapRegisters++; } } RtlClearBit(AdapterObject->MapRegisters, CurrentEntry - AdapterObject->MapRegisterBase); CurrentEntry->VirtualAddress = VirtualAddress; CurrentEntry->PhysicalAddress = PhysicalAddress; PhysicalAddress.LowPart += PAGE_SIZE; VirtualAddress = (PVOID)((ULONG_PTR)VirtualAddress + PAGE_SIZE); CurrentEntry++; AdapterObject->NumberOfMapRegisters++; MapRegisterCount--; } while (MapRegisterCount != 0); } KfReleaseSpinLock(&AdapterObject->SpinLock, OldIrql); return TRUE; } /** * @name HalpDmaAllocateMasterAdapter * * Helper routine to allocate and initialize master adapter object and it's * associated map register buffers. * * @see HalpInitDma */ PADAPTER_OBJECT NTAPI HalpDmaAllocateMasterAdapter(VOID) { PADAPTER_OBJECT MasterAdapter; ULONG Size, SizeOfBitmap; SizeOfBitmap = MAX_MAP_REGISTERS; Size = sizeof(ADAPTER_OBJECT); Size += sizeof(RTL_BITMAP); Size += (SizeOfBitmap + 7) >> 3; MasterAdapter = ExAllocatePoolWithTag(NonPagedPool, Size, TAG_DMA); if (MasterAdapter == NULL) return NULL; RtlZeroMemory(MasterAdapter, Size); KeInitializeSpinLock(&MasterAdapter->SpinLock); InitializeListHead(&MasterAdapter->AdapterQueue); MasterAdapter->MapRegisters = (PVOID)(MasterAdapter + 1); RtlInitializeBitMap( MasterAdapter->MapRegisters, (PULONG)(MasterAdapter->MapRegisters + 1), SizeOfBitmap); RtlSetAllBits(MasterAdapter->MapRegisters); MasterAdapter->NumberOfMapRegisters = 0; MasterAdapter->CommittedMapRegisters = 0; MasterAdapter->MapRegisterBase = ExAllocatePoolWithTag( NonPagedPool, SizeOfBitmap * sizeof(ROS_MAP_REGISTER_ENTRY), TAG_DMA); if (MasterAdapter->MapRegisterBase == NULL) { ExFreePool(MasterAdapter); return NULL; } RtlZeroMemory(MasterAdapter->MapRegisterBase, SizeOfBitmap * sizeof(ROS_MAP_REGISTER_ENTRY)); if (!HalpGrowMapBuffers(MasterAdapter, 0x10000)) { ExFreePool(MasterAdapter); return NULL; } return MasterAdapter; } /** * @name HalpDmaAllocateChildAdapter * * Helper routine of HalGetAdapter. Allocate child adapter object and * fill out some basic fields. * * @see HalGetAdapter */ PADAPTER_OBJECT NTAPI HalpDmaAllocateChildAdapter( ULONG NumberOfMapRegisters, PDEVICE_DESCRIPTION DeviceDescription) { PADAPTER_OBJECT AdapterObject; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; HANDLE Handle; InitializeObjectAttributes( &ObjectAttributes, NULL, OBJ_KERNEL_HANDLE | OBJ_PERMANENT, NULL, NULL); Status = ObCreateObject( KernelMode, IoAdapterObjectType, &ObjectAttributes, KernelMode, NULL, sizeof(ADAPTER_OBJECT), 0, 0, (PVOID)&AdapterObject); if (!NT_SUCCESS(Status)) return NULL; Status = ObReferenceObjectByPointer( AdapterObject, FILE_READ_DATA | FILE_WRITE_DATA, IoAdapterObjectType, KernelMode); if (!NT_SUCCESS(Status)) return NULL; RtlZeroMemory(AdapterObject, sizeof(ADAPTER_OBJECT)); Status = ObInsertObject( AdapterObject, NULL, FILE_READ_DATA | FILE_WRITE_DATA, 0, NULL, &Handle); if (!NT_SUCCESS(Status)) return NULL; ZwClose(Handle); AdapterObject->DmaHeader.Version = (USHORT)DeviceDescription->Version; AdapterObject->DmaHeader.Size = sizeof(ADAPTER_OBJECT); AdapterObject->DmaHeader.DmaOperations = &HalpDmaOperations; AdapterObject->MapRegistersPerChannel = 1; AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses; AdapterObject->ChannelNumber = 0xFF; AdapterObject->MasterAdapter = HalpMasterAdapter; KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue); return AdapterObject; } /** * @name HalpDmaInitializeEisaAdapter * * Setup DMA modes and extended modes for (E)ISA DMA adapter object. */ BOOLEAN NTAPI HalpDmaInitializeEisaAdapter( PADAPTER_OBJECT AdapterObject, PDEVICE_DESCRIPTION DeviceDescription) { UCHAR Controller; DMA_MODE DmaMode = {{0 }}; DMA_EXTENDED_MODE ExtendedMode = {{ 0 }}; PVOID AdapterBaseVa; Controller = (DeviceDescription->DmaChannel & 4) ? 2 : 1; if (Controller == 1) AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController1); else AdapterBaseVa = (PVOID)FIELD_OFFSET(EISA_CONTROL, DmaController2); AdapterObject->AdapterNumber = Controller; AdapterObject->ChannelNumber = (UCHAR)(DeviceDescription->DmaChannel & 3); AdapterObject->PagePort = (PUCHAR)HalpEisaPortPage[DeviceDescription->DmaChannel]; AdapterObject->Width16Bits = FALSE; AdapterObject->AdapterBaseVa = AdapterBaseVa; if (HalpEisaDma) { ExtendedMode.ChannelNumber = AdapterObject->ChannelNumber; switch (DeviceDescription->DmaSpeed) { case Compatible: ExtendedMode.TimingMode = COMPATIBLE_TIMING; break; case TypeA: ExtendedMode.TimingMode = TYPE_A_TIMING; break; case TypeB: ExtendedMode.TimingMode = TYPE_B_TIMING; break; case TypeC: ExtendedMode.TimingMode = BURST_TIMING; break; default: return FALSE; } switch (DeviceDescription->DmaWidth) { case Width8Bits: ExtendedMode.TransferSize = B_8BITS; break; case Width16Bits: ExtendedMode.TransferSize = B_16BITS; break; case Width32Bits: ExtendedMode.TransferSize = B_32BITS; break; default: return FALSE; } if (Controller == 1) WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode1), ExtendedMode.Byte); else WRITE_PORT_UCHAR((PUCHAR)FIELD_OFFSET(EISA_CONTROL, DmaExtendedMode2), ExtendedMode.Byte); } else { /* * Validate setup for non-busmaster DMA adapter. Secondary controller * supports only 16-bit transfers and main controller supports only * 8-bit transfers. Anything else is invalid. */ if (!DeviceDescription->Master) { if (Controller == 2 && DeviceDescription->DmaWidth == Width16Bits) AdapterObject->Width16Bits = TRUE; else if (Controller != 1 || DeviceDescription->DmaWidth != Width8Bits) return FALSE; } } DmaMode.Channel = AdapterObject->ChannelNumber; DmaMode.AutoInitialize = DeviceDescription->AutoInitialize; /* * Set the DMA request mode. * * For (E)ISA bus master devices just unmask (enable) the DMA channel * and set it to cascade mode. Otherwise just select the right one * bases on the passed device description. */ if (DeviceDescription->Master) { DmaMode.RequestMode = CASCADE_REQUEST_MODE; if (Controller == 1) { /* Set the Request Data */ WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->Mode, DmaMode.Byte); /* Unmask DMA Channel */ WRITE_PORT_UCHAR(&((PDMA1_CONTROL)AdapterBaseVa)->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK); } else { /* Set the Request Data */ WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->Mode, DmaMode.Byte); /* Unmask DMA Channel */ WRITE_PORT_UCHAR(&((PDMA2_CONTROL)AdapterBaseVa)->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK); } } else { if (DeviceDescription->DemandMode) DmaMode.RequestMode = DEMAND_REQUEST_MODE; else DmaMode.RequestMode = SINGLE_REQUEST_MODE; } AdapterObject->AdapterMode = DmaMode; return TRUE; } /** * @name HalGetAdapter * * Allocate an adapter object for DMA device. * * @param DeviceDescription * Structure describing the attributes of the device. * @param NumberOfMapRegisters * On return filled with the maximum number of map registers the * device driver can allocate for DMA transfer operations. * * @return The DMA adapter on success, NULL otherwise. * * @implemented */ PADAPTER_OBJECT NTAPI HalGetAdapter( PDEVICE_DESCRIPTION DeviceDescription, PULONG NumberOfMapRegisters) { PADAPTER_OBJECT AdapterObject = NULL; PADAPTER_OBJECT MasterAdapter; BOOLEAN EisaAdapter; ULONG MapRegisters; ULONG MaximumLength; /* Validate parameters in device description */ if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION2) return NULL; /* * See if we're going to use ISA/EISA DMA adapter. These adapters are * special since they're reused. * * Also note that we check for channel number since there are only 8 DMA * channels on ISA, so any request above this requires new adapter. */ if (DeviceDescription->InterfaceType == Isa || !DeviceDescription->Master) { if (DeviceDescription->InterfaceType == Isa && DeviceDescription->DmaChannel >= 8) EisaAdapter = FALSE; else EisaAdapter = TRUE; } else { EisaAdapter = FALSE; } /* * Disallow creating adapter for ISA/EISA DMA channel 4 since it's used * for cascading the controllers and it's not available for software use. */ if (EisaAdapter && DeviceDescription->DmaChannel == 4) return NULL; /* * Calculate the number of map registers. * * - For EISA and PCI scatter/gather no map registers are needed. * - For ISA slave scatter/gather one map register is needed. * - For all other cases the number of map registers depends on * DeviceDescription->MaximumLength. */ MaximumLength = DeviceDescription->MaximumLength & MAXLONG; if (DeviceDescription->ScatterGather && (DeviceDescription->InterfaceType == Eisa || DeviceDescription->InterfaceType == PCIBus)) { MapRegisters = 0; } else if (DeviceDescription->ScatterGather && !DeviceDescription->Master) { MapRegisters = 1; } else { /* * In the equation below the additional map register added by * the "+1" accounts for the case when a transfer does not start * at a page-aligned address. */ MapRegisters = BYTES_TO_PAGES(MaximumLength) + 1; if (MapRegisters > 16) MapRegisters = 16; } /* * Acquire the DMA lock that is used to protect adapter lists and * EISA adapter array. */ KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL); /* * Now we must get ahold of the adapter object. For first eight ISA/EISA * channels there are static adapter objects that are reused and updated * on succesive HalGetAdapter calls. In other cases a new adapter object * is always created and it's to the DMA adapter list (HalpDmaAdapterList). */ if (EisaAdapter) { AdapterObject = HalpEisaAdapter[DeviceDescription->DmaChannel]; if (AdapterObject != NULL) { if (AdapterObject->NeedsMapRegisters && MapRegisters > AdapterObject->MapRegistersPerChannel) AdapterObject->MapRegistersPerChannel = MapRegisters; } } if (AdapterObject == NULL) { AdapterObject = HalpDmaAllocateChildAdapter( MapRegisters, DeviceDescription); if (AdapterObject == NULL) { KeSetEvent(&HalpDmaLock, 0, 0); return NULL; } if (EisaAdapter) { HalpEisaAdapter[DeviceDescription->DmaChannel] = AdapterObject; } if (MapRegisters > 0) { AdapterObject->NeedsMapRegisters = TRUE; MasterAdapter = HalpMasterAdapter; AdapterObject->MapRegistersPerChannel = MapRegisters; /* * FIXME: Verify that the following makes sense. Actually * MasterAdapter->NumberOfMapRegisters contains even the number * of gaps, so this will not work correctly all the time. It * doesn't matter much since it's only optimization to avoid * queuing work items in HalAllocateAdapterChannel. */ MasterAdapter->CommittedMapRegisters += MapRegisters; if (MasterAdapter->CommittedMapRegisters > MasterAdapter->NumberOfMapRegisters) HalpGrowMapBuffers(MasterAdapter, 0x10000); } else { AdapterObject->NeedsMapRegisters = FALSE; if (DeviceDescription->Master) AdapterObject->MapRegistersPerChannel = BYTES_TO_PAGES(MaximumLength) + 1; else AdapterObject->MapRegistersPerChannel = 1; } } if (!EisaAdapter) InsertTailList(&HalpDmaAdapterList, &AdapterObject->AdapterList); /* * Release the DMA lock. HalpDmaAdapterList and HalpEisaAdapter will * no longer be touched, so we don't need it. */ KeSetEvent(&HalpDmaLock, 0, 0); /* * Setup the values in the adapter object that are common for all * types of buses. */ if (DeviceDescription->Version >= DEVICE_DESCRIPTION_VERSION1) AdapterObject->IgnoreCount = DeviceDescription->IgnoreCount; else AdapterObject->IgnoreCount = 0; AdapterObject->Dma32BitAddresses = DeviceDescription->Dma32BitAddresses; AdapterObject->Dma64BitAddresses = DeviceDescription->Dma64BitAddresses; AdapterObject->ScatterGather = DeviceDescription->ScatterGather; AdapterObject->MasterDevice = DeviceDescription->Master; *NumberOfMapRegisters = AdapterObject->MapRegistersPerChannel; /* * For non-(E)ISA adapters we have already done all the work. On the * other hand for (E)ISA adapters we must still setup the DMA modes * and prepare the controller. */ if (EisaAdapter) { if (!HalpDmaInitializeEisaAdapter(AdapterObject, DeviceDescription)) { ObDereferenceObject(AdapterObject); return NULL; } } return AdapterObject; } /** * @name HalpGetDmaAdapter * * Internal routine to allocate PnP DMA adapter object. It's exported through * HalDispatchTable and used by IoGetDmaAdapter. * * @see HalGetAdapter */ PDMA_ADAPTER NTAPI HalpGetDmaAdapter( IN PVOID Context, IN PDEVICE_DESCRIPTION DeviceDescription, OUT PULONG NumberOfMapRegisters) { return &HalGetAdapter(DeviceDescription, NumberOfMapRegisters)->DmaHeader; } /** * @name HalPutDmaAdapter * * Internal routine to free DMA adapter and resources for reuse. It's exported * using the DMA_OPERATIONS interface by HalGetAdapter. * * @see HalGetAdapter */ VOID NTAPI HalPutDmaAdapter( PADAPTER_OBJECT AdapterObject) { if (AdapterObject->ChannelNumber == 0xFF) { KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL); RemoveEntryList(&AdapterObject->AdapterList); KeSetEvent(&HalpDmaLock, 0, 0); } ObDereferenceObject(AdapterObject); } /** * @name HalAllocateCommonBuffer * * Allocates memory that is visible to both the processor(s) and the DMA * device. * * @param AdapterObject * Adapter object representing the bus master or system dma controller. * @param Length * Number of bytes to allocate. * @param LogicalAddress * Logical address the driver can use to access the buffer. * @param CacheEnabled * Specifies if the memory can be cached. * * @return The base virtual address of the memory allocated or NULL on failure. * * @remarks * On real NT x86 systems the CacheEnabled parameter is ignored, we honour * it. If it proves to cause problems change it. * * @see HalFreeCommonBuffer * * @implemented */ PVOID NTAPI HalAllocateCommonBuffer( PADAPTER_OBJECT AdapterObject, ULONG Length, PPHYSICAL_ADDRESS LogicalAddress, BOOLEAN CacheEnabled) { PHYSICAL_ADDRESS LowestAcceptableAddress; PHYSICAL_ADDRESS HighestAcceptableAddress; PHYSICAL_ADDRESS BoundryAddressMultiple; PVOID VirtualAddress; LowestAcceptableAddress.QuadPart = 0; HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject); BoundryAddressMultiple.QuadPart = 0; /* * For bus-master DMA devices the buffer mustn't cross 4Gb boundary. For * slave DMA devices the 64Kb boundary mustn't be crossed since the * controller wouldn't be able to handle it. */ if (AdapterObject->MasterDevice) BoundryAddressMultiple.HighPart = 1; else BoundryAddressMultiple.LowPart = 0x10000; VirtualAddress = MmAllocateContiguousMemorySpecifyCache( Length, LowestAcceptableAddress, HighestAcceptableAddress, BoundryAddressMultiple, CacheEnabled ? MmCached : MmNonCached); if (VirtualAddress == NULL) return NULL; *LogicalAddress = MmGetPhysicalAddress(VirtualAddress); return VirtualAddress; } /** * @name HalFreeCommonBuffer * * Free common buffer allocated with HalAllocateCommonBuffer. * * @see HalAllocateCommonBuffer * * @implemented */ VOID NTAPI HalFreeCommonBuffer( PADAPTER_OBJECT AdapterObject, ULONG Length, PHYSICAL_ADDRESS LogicalAddress, PVOID VirtualAddress, BOOLEAN CacheEnabled) { MmFreeContiguousMemory(VirtualAddress); } /** * @name HalpDmaGetDmaAlignment * * Internal routine to return the DMA alignment requirement. It's exported * using the DMA_OPERATIONS interface by HalGetAdapter. * * @see HalGetAdapter */ ULONG NTAPI HalpDmaGetDmaAlignment( PADAPTER_OBJECT AdapterObject) { return 1; } /* * @name HalReadDmaCounter * * Read DMA operation progress counter. * * @implemented */ ULONG NTAPI HalReadDmaCounter( PADAPTER_OBJECT AdapterObject) { KIRQL OldIrql; ULONG Count, OldCount; ASSERT(!AdapterObject->MasterDevice); /* * Acquire the master adapter lock since we're going to mess with the * system DMA controller registers and we really don't want anyone * to do the same at the same time. */ KeAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock, &OldIrql); /* Send the request to the specific controller. */ if (AdapterObject->AdapterNumber == 1) { PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; Count = 0xffff00; do { OldCount = Count; /* Send Reset */ WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0); /* Read Count */ Count = READ_PORT_UCHAR(&DmaControl1->DmaAddressCount [AdapterObject->ChannelNumber].DmaBaseCount); Count |= READ_PORT_UCHAR(&DmaControl1->DmaAddressCount [AdapterObject->ChannelNumber].DmaBaseCount) << 8; } while (0xffff00 & (OldCount ^ Count)); } else { PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa; Count = 0xffff00; do { OldCount = Count; /* Send Reset */ WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0); /* Read Count */ Count = READ_PORT_UCHAR(&DmaControl2->DmaAddressCount [AdapterObject->ChannelNumber].DmaBaseCount); Count |= READ_PORT_UCHAR(&DmaControl2->DmaAddressCount [AdapterObject->ChannelNumber].DmaBaseCount) << 8; } while (0xffff00 & (OldCount ^ Count)); } KeReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql); Count++; Count &= 0xffff; if (AdapterObject->Width16Bits) Count *= 2; return Count; } /** * @name HalpGrowMapBufferWorker * * Helper routine of HalAllocateAdapterChannel for allocating map registers * at PASSIVE_LEVEL in work item. */ VOID NTAPI HalpGrowMapBufferWorker(PVOID DeferredContext) { PGROW_WORK_ITEM WorkItem = (PGROW_WORK_ITEM)DeferredContext; KIRQL OldIrql; BOOLEAN Succeeded; /* * Try to allocate new map registers for the adapter. * * NOTE: The NT implementation actually tries to allocate more map * registers than needed as an optimization. */ KeWaitForSingleObject(&HalpDmaLock, Executive, KernelMode, FALSE, NULL); Succeeded = HalpGrowMapBuffers(WorkItem->AdapterObject->MasterAdapter, WorkItem->NumberOfMapRegisters); KeSetEvent(&HalpDmaLock, 0, 0); if (Succeeded) { /* * Flush the adapter queue now that new map registers are ready. The * easiest way to do that is to call IoFreeMapRegisters to not free * any registers. Note that we use the magic (PVOID)2 map register * base to bypass the parameter checking. */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); IoFreeMapRegisters(WorkItem->AdapterObject, (PVOID)2, 0); KeLowerIrql(OldIrql); } ExFreePool(WorkItem); } /** * @name HalAllocateAdapterChannel * * Setup map registers for an adapter object. * * @param AdapterObject * Pointer to an ADAPTER_OBJECT to set up. * @param WaitContextBlock * Context block to be used with ExecutionRoutine. * @param NumberOfMapRegisters * Number of map registers requested. * @param ExecutionRoutine * Callback to call when map registers are allocated. * * @return * If not enough map registers can be allocated then * STATUS_INSUFFICIENT_RESOURCES is returned. If the function * succeeds or the callback is queued for later delivering then * STATUS_SUCCESS is returned. * * @see IoFreeAdapterChannel * * @implemented */ NTSTATUS NTAPI HalAllocateAdapterChannel( PADAPTER_OBJECT AdapterObject, PWAIT_CONTEXT_BLOCK WaitContextBlock, ULONG NumberOfMapRegisters, PDRIVER_CONTROL ExecutionRoutine) { PADAPTER_OBJECT MasterAdapter; PGROW_WORK_ITEM WorkItem; ULONG Index = MAXULONG; ULONG Result; KIRQL OldIrql; ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); /* Set up the wait context block in case we can't run right away. */ WaitContextBlock->DeviceRoutine = ExecutionRoutine; WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters; /* Returns true if queued, else returns false and sets the queue to busy */ if (KeInsertDeviceQueue(&AdapterObject->ChannelWaitQueue, &WaitContextBlock->WaitQueueEntry)) return STATUS_SUCCESS; MasterAdapter = AdapterObject->MasterAdapter; AdapterObject->NumberOfMapRegisters = NumberOfMapRegisters; AdapterObject->CurrentWcb = WaitContextBlock; if (NumberOfMapRegisters && AdapterObject->NeedsMapRegisters) { if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { AdapterObject->NumberOfMapRegisters = 0; IoFreeAdapterChannel(AdapterObject); return STATUS_INSUFFICIENT_RESOURCES; } /* * Get the map registers. This is partly complicated by the fact * that new map registers can only be allocated at PASSIVE_LEVEL * and we're currently at DISPATCH_LEVEL. The following code has * two code paths: * * - If there is no adapter queued for map register allocation, * try to see if enough contiguous map registers are present. * In case they're we can just get them and proceed further. * * - If some adapter is already present in the queue we must * respect the order of adapters asking for map registers and * so the fast case described above can't take place. * This case is also entered if not enough coniguous map * registers are present. * * A work queue item is allocated and queued, the adapter is * also queued into the master adapter queue. The worker * routine does the job of allocating the map registers at * PASSIVE_LEVEL and calling the ExecutionRoutine. */ OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock); if (IsListEmpty(&MasterAdapter->AdapterQueue)) { Index = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, NumberOfMapRegisters, 0); if (Index != MAXULONG) { AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index; if (!AdapterObject->ScatterGather) { AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY)( (ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG); } } } if (Index == MAXULONG) { WorkItem = ExAllocatePoolWithTag( NonPagedPool, sizeof(GROW_WORK_ITEM), TAG_DMA); if (WorkItem == NULL) { KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); AdapterObject->NumberOfMapRegisters = 0; IoFreeAdapterChannel(AdapterObject); return STATUS_INSUFFICIENT_RESOURCES; } InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue); ExInitializeWorkItem( &WorkItem->WorkQueueItem, HalpGrowMapBufferWorker, WorkItem); WorkItem->AdapterObject = AdapterObject; WorkItem->NumberOfMapRegisters = NumberOfMapRegisters; ExQueueWorkItem(&WorkItem->WorkQueueItem, DelayedWorkQueue); KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); return STATUS_SUCCESS; } KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); } else { AdapterObject->MapRegisterBase = NULL; AdapterObject->NumberOfMapRegisters = 0; } AdapterObject->CurrentWcb = WaitContextBlock; Result = ExecutionRoutine( WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp, AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext); /* * Possible return values: * * - KeepObject * Don't free any resources, the ADAPTER_OBJECT is still in use and * the caller will call IoFreeAdapterChannel later. * * - DeallocateObject * Deallocate the map registers and release the ADAPTER_OBJECT, so * someone else can use it. * * - DeallocateObjectKeepRegisters * Release the ADAPTER_OBJECT, but hang on to the map registers. The * client will later call IoFreeMapRegisters. * * NOTE: * IoFreeAdapterChannel runs the queue, so it must be called unless * the adapter object is not to be freed. */ if (Result == DeallocateObject) { IoFreeAdapterChannel(AdapterObject); } else if (Result == DeallocateObjectKeepRegisters) { AdapterObject->NumberOfMapRegisters = 0; IoFreeAdapterChannel(AdapterObject); } return STATUS_SUCCESS; } /** * @name IoFreeAdapterChannel * * Free DMA resources allocated by IoAllocateAdapterChannel. * * @param AdapterObject * Adapter object with resources to free. * * @remarks * This function releases map registers registers assigned to the DMA * adapter. After releasing the adapter, it checks the adapter's queue * and runs each queued device object in series until the queue is * empty. This is the only way the device queue is emptied. * * @see IoAllocateAdapterChannel * * @implemented */ VOID NTAPI IoFreeAdapterChannel( PADAPTER_OBJECT AdapterObject) { PADAPTER_OBJECT MasterAdapter; PKDEVICE_QUEUE_ENTRY DeviceQueueEntry; PWAIT_CONTEXT_BLOCK WaitContextBlock; ULONG Index = MAXULONG; ULONG Result; KIRQL OldIrql; MasterAdapter = AdapterObject->MasterAdapter; for (;;) { /* * To keep map registers, call here with AdapterObject-> * NumberOfMapRegisters set to zero. This trick is used in * HalAllocateAdapterChannel for example. */ if (AdapterObject->NumberOfMapRegisters) { IoFreeMapRegisters( AdapterObject, AdapterObject->MapRegisterBase, AdapterObject->NumberOfMapRegisters); } DeviceQueueEntry = KeRemoveDeviceQueue(&AdapterObject->ChannelWaitQueue); if (DeviceQueueEntry == NULL) { break; } WaitContextBlock = CONTAINING_RECORD( DeviceQueueEntry, WAIT_CONTEXT_BLOCK, WaitQueueEntry); AdapterObject->CurrentWcb = WaitContextBlock; AdapterObject->NumberOfMapRegisters = WaitContextBlock->NumberOfMapRegisters; if (WaitContextBlock->NumberOfMapRegisters && AdapterObject->MasterAdapter) { OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock); if (IsListEmpty(&MasterAdapter->AdapterQueue)) { Index = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, WaitContextBlock->NumberOfMapRegisters, 0); if (Index != MAXULONG) { AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index; if (!AdapterObject->ScatterGather) { AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY)( (ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG); } } } if (Index == MAXULONG) { InsertTailList(&MasterAdapter->AdapterQueue, &AdapterObject->AdapterQueue); KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); break; } KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); } else { AdapterObject->MapRegisterBase = NULL; AdapterObject->NumberOfMapRegisters = 0; } /* Call the adapter control routine. */ Result = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)( WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp, AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext); switch (Result) { case KeepObject: /* * We're done until the caller manually calls IoFreeAdapterChannel * or IoFreeMapRegisters. */ return; case DeallocateObjectKeepRegisters: /* * Hide the map registers so they aren't deallocated next time * around. */ AdapterObject->NumberOfMapRegisters = 0; break; default: break; } } } /** * @name IoFreeMapRegisters * * Free map registers reserved by the system for a DMA. * * @param AdapterObject * DMA adapter to free map registers on. * @param MapRegisterBase * Handle to map registers to free. * @param NumberOfRegisters * Number of map registers to be freed. * * @implemented */ VOID NTAPI IoFreeMapRegisters( IN PADAPTER_OBJECT AdapterObject, IN PVOID MapRegisterBase, IN ULONG NumberOfMapRegisters) { PADAPTER_OBJECT MasterAdapter = AdapterObject->MasterAdapter; PLIST_ENTRY ListEntry; KIRQL OldIrql; ULONG Index; ULONG Result; ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL); if (MasterAdapter == NULL || MapRegisterBase == NULL) return; OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock); if (NumberOfMapRegisters != 0) { PROS_MAP_REGISTER_ENTRY RealMapRegisterBase; RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG); RtlClearBits(MasterAdapter->MapRegisters, RealMapRegisterBase - MasterAdapter->MapRegisterBase, NumberOfMapRegisters); } /* * Now that we freed few map registers it's time to look at the master * adapter queue and see if there is someone waiting for map registers. */ while (!IsListEmpty(&MasterAdapter->AdapterQueue)) { ListEntry = RemoveHeadList(&MasterAdapter->AdapterQueue); AdapterObject = CONTAINING_RECORD( ListEntry, struct _ADAPTER_OBJECT, AdapterQueue); Index = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters, AdapterObject->NumberOfMapRegisters, MasterAdapter->NumberOfMapRegisters); if (Index == MAXULONG) { InsertHeadList(&MasterAdapter->AdapterQueue, ListEntry); break; } KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); AdapterObject->MapRegisterBase = MasterAdapter->MapRegisterBase + Index; if (!AdapterObject->ScatterGather) { AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY)( (ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG); } Result = ((PDRIVER_CONTROL)AdapterObject->CurrentWcb->DeviceRoutine)( AdapterObject->CurrentWcb->DeviceObject, AdapterObject->CurrentWcb->CurrentIrp, AdapterObject->MapRegisterBase, AdapterObject->CurrentWcb->DeviceContext); switch (Result) { case DeallocateObjectKeepRegisters: AdapterObject->NumberOfMapRegisters = 0; /* fall through */ case DeallocateObject: if (AdapterObject->NumberOfMapRegisters) { OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock); RtlClearBits(MasterAdapter->MapRegisters, AdapterObject->MapRegisterBase - MasterAdapter->MapRegisterBase, AdapterObject->NumberOfMapRegisters); KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); } IoFreeAdapterChannel(AdapterObject); break; default: break; } OldIrql = KfAcquireSpinLock(&MasterAdapter->SpinLock); } KfReleaseSpinLock(&MasterAdapter->SpinLock, OldIrql); } /** * @name HalpCopyBufferMap * * Helper function for copying data from/to map register buffers. * * @see IoFlushAdapterBuffers, IoMapTransfer */ VOID NTAPI HalpCopyBufferMap( PMDL Mdl, PROS_MAP_REGISTER_ENTRY MapRegisterBase, PVOID CurrentVa, ULONG Length, BOOLEAN WriteToDevice) { ULONG CurrentLength; ULONG_PTR CurrentAddress; ULONG ByteOffset; PVOID VirtualAddress; VirtualAddress = MmGetSystemAddressForMdlSafe(Mdl, HighPagePriority); if (VirtualAddress == NULL) { /* * NOTE: On real NT a mechanism with reserved pages is implemented * to handle this case in a slow, but graceful non-fatal way. */ KeBugCheckEx(HAL_MEMORY_ALLOCATION, PAGE_SIZE, 0, (ULONG_PTR)__FILE__, 0); } CurrentAddress = (ULONG_PTR)VirtualAddress + (ULONG_PTR)CurrentVa - (ULONG_PTR)MmGetMdlVirtualAddress(Mdl); while (Length > 0) { ByteOffset = BYTE_OFFSET(CurrentAddress); CurrentLength = PAGE_SIZE - ByteOffset; if (CurrentLength > Length) CurrentLength = Length; if (WriteToDevice) { RtlCopyMemory( (PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset), (PVOID)CurrentAddress, CurrentLength); } else { RtlCopyMemory( (PVOID)CurrentAddress, (PVOID)((ULONG_PTR)MapRegisterBase->VirtualAddress + ByteOffset), CurrentLength); } Length -= CurrentLength; CurrentAddress += CurrentLength; MapRegisterBase++; } } /** * @name IoFlushAdapterBuffers * * Flush any data remaining in the DMA controller's memory into the host * memory. * * @param AdapterObject * The adapter object to flush. * @param Mdl * Original MDL to flush data into. * @param MapRegisterBase * Map register base that was just used by IoMapTransfer, etc. * @param CurrentVa * Offset into Mdl to be flushed into, same as was passed to * IoMapTransfer. * @param Length * Length of the buffer to be flushed into. * @param WriteToDevice * TRUE if it's a write, FALSE if it's a read. * * @return TRUE in all cases. * * @remarks * This copies data from the map register-backed buffer to the user's * target buffer. Data are not in the user buffer until this function * is called. * For slave DMA transfers the controller channel is masked effectively * stopping the current transfer. * * @unimplemented. */ BOOLEAN NTAPI IoFlushAdapterBuffers( PADAPTER_OBJECT AdapterObject, PMDL Mdl, PVOID MapRegisterBase, PVOID CurrentVa, ULONG Length, BOOLEAN WriteToDevice) { BOOLEAN SlaveDma = FALSE; PROS_MAP_REGISTER_ENTRY RealMapRegisterBase; PHYSICAL_ADDRESS HighestAcceptableAddress; PHYSICAL_ADDRESS PhysicalAddress; PPFN_NUMBER MdlPagesPtr; ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL); if (AdapterObject != NULL && !AdapterObject->MasterDevice) { /* Mask out (disable) the DMA channel. */ if (AdapterObject->AdapterNumber == 1) { PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_SETMASK); } else { PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa; WRITE_PORT_UCHAR(&DmaControl2->SingleMask, AdapterObject->ChannelNumber | DMA_SETMASK); } SlaveDma = TRUE; } /* This can happen if the device supports hardware scatter/gather. */ if (MapRegisterBase == NULL) return TRUE; RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG); if (!WriteToDevice) { if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG) { if (RealMapRegisterBase->Counter != MAXULONG) { if (SlaveDma && !AdapterObject->IgnoreCount) Length -= HalReadDmaCounter(AdapterObject); HalpCopyBufferMap(Mdl, RealMapRegisterBase, CurrentVa, Length, FALSE); } } else { MdlPagesPtr = MmGetMdlPfnArray(Mdl); MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT; PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT; PhysicalAddress.QuadPart += BYTE_OFFSET(CurrentVa); HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject); if (PhysicalAddress.QuadPart + Length > HighestAcceptableAddress.QuadPart) { HalpCopyBufferMap(Mdl, RealMapRegisterBase, CurrentVa, Length, FALSE); } } } RealMapRegisterBase->Counter = 0; return TRUE; } /** * @name IoMapTransfer * * Map a DMA for transfer and do the DMA if it's a slave. * * @param AdapterObject * Adapter object to do the DMA on. Bus-master may pass NULL. * @param Mdl * Locked-down user buffer to DMA in to or out of. * @param MapRegisterBase * Handle to map registers to use for this dma. * @param CurrentVa * Index into Mdl to transfer into/out of. * @param Length * Length of transfer. Number of bytes actually transferred on * output. * @param WriteToDevice * TRUE if it's an output DMA, FALSE otherwise. * * @return * A logical address that can be used to program a DMA controller, it's * not meaningful for slave DMA device. * * @remarks * This function does a copyover to contiguous memory <16MB represented * by the map registers if needed. If the buffer described by MDL can be * used as is no copyover is done. * If it's a slave transfer, this function actually performs it. * * @implemented */ PHYSICAL_ADDRESS NTAPI IoMapTransfer( IN PADAPTER_OBJECT AdapterObject, IN PMDL Mdl, IN PVOID MapRegisterBase, IN PVOID CurrentVa, IN OUT PULONG Length, IN BOOLEAN WriteToDevice) { PPFN_NUMBER MdlPagesPtr; PFN_NUMBER MdlPage1, MdlPage2; ULONG ByteOffset; ULONG TransferOffset; ULONG TransferLength; BOOLEAN UseMapRegisters; PROS_MAP_REGISTER_ENTRY RealMapRegisterBase; PHYSICAL_ADDRESS PhysicalAddress; PHYSICAL_ADDRESS HighestAcceptableAddress; ULONG Counter; DMA_MODE AdapterMode; KIRQL OldIrql; /* * Precalculate some values that are used in all cases. * * ByteOffset is offset inside the page at which the transfer starts. * MdlPagesPtr is pointer inside the MDL page chain at the page where the * transfer start. * PhysicalAddress is physical address corresponding to the transfer * start page and offset. * TransferLength is the initial length of the transfer, which is reminder * of the first page. The actual value is calculated below. * * Note that all the variables can change during the processing which * takes place below. These are just initial values. */ ByteOffset = BYTE_OFFSET(CurrentVa); MdlPagesPtr = MmGetMdlPfnArray(Mdl); MdlPagesPtr += ((ULONG_PTR)CurrentVa - (ULONG_PTR)Mdl->StartVa) >> PAGE_SHIFT; PhysicalAddress.QuadPart = *MdlPagesPtr << PAGE_SHIFT; PhysicalAddress.QuadPart += ByteOffset; TransferLength = PAGE_SIZE - ByteOffset; /* * Special case for bus master adapters with S/G support. We can directly * use the buffer specified by the MDL, so not much work has to be done. * * Just return the passed VA's corresponding physical address and update * length to the number of physically contiguous bytes found. Also * pages crossing the 4Gb boundary aren't considered physically contiguous. */ if (MapRegisterBase == NULL) { while (TransferLength < *Length) { MdlPage1 = *MdlPagesPtr; MdlPage2 = *(MdlPagesPtr + 1); if (MdlPage1 + 1 != MdlPage2) break; if ((MdlPage1 ^ MdlPage2) & ~0xFFFFF) break; TransferLength += PAGE_SIZE; MdlPagesPtr++; } if (TransferLength < *Length) *Length = TransferLength; return PhysicalAddress; } /* * The code below applies to slave DMA adapters and bus master adapters * without hardward S/G support. */ RealMapRegisterBase = (PROS_MAP_REGISTER_ENTRY)((ULONG_PTR)MapRegisterBase & ~MAP_BASE_SW_SG); /* * Try to calculate the size of the transfer. We can only transfer * pages that are physically contiguous and that don't cross the * 64Kb boundary (this limitation applies only for ISA controllers). */ while (TransferLength < *Length) { MdlPage1 = *MdlPagesPtr; MdlPage2 = *(MdlPagesPtr + 1); if (MdlPage1 + 1 != MdlPage2) break; if (!HalpEisaDma && ((MdlPage1 ^ MdlPage2) & ~0xF)) break; TransferLength += PAGE_SIZE; MdlPagesPtr++; } if (TransferLength > *Length) TransferLength = *Length; /* * If we're about to simulate software S/G and not all the pages are * physically contiguous then we must use the map registers to store * the data and allow the whole transfer to proceed at once. */ if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG && TransferLength < *Length) { UseMapRegisters = TRUE; PhysicalAddress = RealMapRegisterBase->PhysicalAddress; PhysicalAddress.QuadPart += ByteOffset; TransferLength = *Length; RealMapRegisterBase->Counter = MAXULONG; Counter = 0; } else { /* * This is ordinary DMA transfer, so just update the progress * counters. These are used by IoFlushAdapterBuffers to track * the transfer progress. */ UseMapRegisters = FALSE; Counter = RealMapRegisterBase->Counter; RealMapRegisterBase->Counter += BYTES_TO_PAGES(ByteOffset + TransferLength); /* * Check if the buffer doesn't exceed the highest physical address * limit of the device. In that case we must use the map registers to * store the data. */ HighestAcceptableAddress = HalpGetAdapterMaximumPhysicalAddress(AdapterObject); if (PhysicalAddress.QuadPart + TransferLength > HighestAcceptableAddress.QuadPart) { UseMapRegisters = TRUE; PhysicalAddress = RealMapRegisterBase[Counter].PhysicalAddress; PhysicalAddress.QuadPart += ByteOffset; if ((ULONG_PTR)MapRegisterBase & MAP_BASE_SW_SG) { RealMapRegisterBase->Counter = MAXULONG; Counter = 0; } } } /* * If we decided to use the map registers (see above) and we're about * to transfer data to the device then copy the buffers into the map * register memory. */ if (UseMapRegisters && WriteToDevice) { HalpCopyBufferMap(Mdl, RealMapRegisterBase + Counter, CurrentVa, TransferLength, WriteToDevice); } /* * Return the length of transfer that actually takes place. */ *Length = TransferLength; /* * If we're doing slave (system) DMA then program the (E)ISA controller * to actually start the transfer. */ if (AdapterObject != NULL && !AdapterObject->MasterDevice) { AdapterMode = AdapterObject->AdapterMode; if (WriteToDevice) { AdapterMode.TransferType = WRITE_TRANSFER; } else { AdapterMode.TransferType = READ_TRANSFER; if (AdapterObject->IgnoreCount) { RtlZeroMemory((PUCHAR)RealMapRegisterBase[Counter].VirtualAddress + ByteOffset, TransferLength); } } TransferOffset = PhysicalAddress.LowPart & 0xFFFF; if (AdapterObject->Width16Bits) { TransferLength >>= 1; TransferOffset >>= 1; } OldIrql = KfAcquireSpinLock(&AdapterObject->MasterAdapter->SpinLock); if (AdapterObject->AdapterNumber == 1) { PDMA1_CONTROL DmaControl1 = AdapterObject->AdapterBaseVa; /* Reset Register */ WRITE_PORT_UCHAR(&DmaControl1->ClearBytePointer, 0); /* Set the Mode */ WRITE_PORT_UCHAR(&DmaControl1->Mode, AdapterMode.Byte); /* Set the Offset Register */ WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, (UCHAR)(TransferOffset)); WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, (UCHAR)(TransferOffset >> 8)); /* Set the Page Register */ WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages), (UCHAR)(PhysicalAddress.LowPart >> 16)); if (HalpEisaDma) { WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages), 0); } /* Set the Length */ WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, (UCHAR)(TransferLength - 1)); WRITE_PORT_UCHAR(&DmaControl1->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, (UCHAR)((TransferLength - 1) >> 8)); /* Unmask the Channel */ WRITE_PORT_UCHAR(&DmaControl1->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK); } else { PDMA2_CONTROL DmaControl2 = AdapterObject->AdapterBaseVa; /* Reset Register */ WRITE_PORT_UCHAR(&DmaControl2->ClearBytePointer, 0); /* Set the Mode */ WRITE_PORT_UCHAR(&DmaControl2->Mode, AdapterMode.Byte); /* Set the Offset Register */ WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, (UCHAR)(TransferOffset)); WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseAddress, (UCHAR)(TransferOffset >> 8)); /* Set the Page Register */ WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController1Pages), (UCHAR)(PhysicalAddress.u.LowPart >> 16)); if (HalpEisaDma) { WRITE_PORT_UCHAR(AdapterObject->PagePort + FIELD_OFFSET(EISA_CONTROL, DmaController2Pages), 0); } /* Set the Length */ WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, (UCHAR)(TransferLength - 1)); WRITE_PORT_UCHAR(&DmaControl2->DmaAddressCount[AdapterObject->ChannelNumber].DmaBaseCount, (UCHAR)((TransferLength - 1) >> 8)); /* Unmask the Channel */ WRITE_PORT_UCHAR(&DmaControl2->SingleMask, AdapterObject->ChannelNumber | DMA_CLEARMASK); } KfReleaseSpinLock(&AdapterObject->MasterAdapter->SpinLock, OldIrql); } /* * Return physical address of the buffer with data that is used for the * transfer. It can either point inside the Mdl that was passed by the * caller or into the map registers if the Mdl buffer can't be used * directly. */ return PhysicalAddress; } /** * @name HalFlushCommonBuffer * * @implemented */ BOOLEAN NTAPI HalFlushCommonBuffer(IN PADAPTER_OBJECT AdapterObject, IN ULONG Length, IN PHYSICAL_ADDRESS LogicalAddress, IN PVOID VirtualAddress) { /* Function always returns true */ return TRUE; } /* * @implemented */ PVOID NTAPI HalAllocateCrashDumpRegisters(IN PADAPTER_OBJECT AdapterObject, IN OUT PULONG NumberOfMapRegisters) { PADAPTER_OBJECT MasterAdapter = AdapterObject->MasterAdapter; ULONG MapRegisterNumber; /* Check if it needs map registers */ if (AdapterObject->NeedsMapRegisters) { /* Check if we have enough */ if (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) { /* We don't, fail */ AdapterObject->NumberOfMapRegisters = 0; return NULL; } /* Try to find free map registers */ MapRegisterNumber = MAXULONG; MapRegisterNumber = RtlFindClearBitsAndSet(MasterAdapter->MapRegisters, *NumberOfMapRegisters, 0); /* Check if nothing was found */ if (MapRegisterNumber == MAXULONG) { /* No free registers found, so use the base registers */ RtlSetBits(MasterAdapter->MapRegisters, 0, *NumberOfMapRegisters); MapRegisterNumber = 0; } /* Calculate the new base */ AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY)(MasterAdapter->MapRegisterBase + MapRegisterNumber); /* Check if scatter gather isn't supported */ if (!AdapterObject->ScatterGather) { /* Set the flag */ AdapterObject->MapRegisterBase = (PROS_MAP_REGISTER_ENTRY) ((ULONG_PTR)AdapterObject->MapRegisterBase | MAP_BASE_SW_SG); } } else { AdapterObject->MapRegisterBase = NULL; AdapterObject->NumberOfMapRegisters = 0; } /* Return the base */ return AdapterObject->MapRegisterBase; } /* EOF */