[ros-kernel] DMA patch
Filip Navara
xnavara at volny.cz
Mon Jul 19 23:22:14 CEST 2004
Hi,
after few days of working on the sound support I finally got it
working up to the state where 3rd party sound drivers should work. I
tested it only with a pure Sound Blaster 16 driver, but since all the
NT4 drivers are based on common libraries I guess a lot of these drivers
will work. In order to get the sound working I had to dig into HAL and
implement some DMA options. Since I'm not a DMA kind of guy and all the
info I got was from QEMU sources and 15 minute reading of DMA for
dummies tutorial I'm posting the patch here for review first. And
comments are welcome.
Regards,
Filip
-------------- next part --------------
Changelog:
- Implementation of HalReadDmaCounter.
- Cleaned up implementation of HalGetAdapter. Moved the initialization
stuff from there to HalpInitDma that is called by HalInitSystem.
- Let HalAllocateCommonBuffer allocated 64K aligned memory.
- Change IoMapTransfer to support auto initialize and single transfer mode,
16-bit DMA and common buffers.
- Stop DMA transfer in IoFlushAdapterBuffers.
Index: halx86/adapter.c
===================================================================
--- halx86/adapter.c (revision 105)
+++ halx86/adapter.c (working copy)
@@ -66,7 +66,7 @@
WaitContextBlock->NumberOfMapRegisters = NumberOfMapRegisters;
/* returns true if queued, else returns false and sets the queue to busy */
- if(KeInsertDeviceQueue(&AdapterObject->DeviceQueue, (PKDEVICE_QUEUE_ENTRY)WaitContextBlock))
+ if(KeInsertDeviceQueue(&AdapterObject->DeviceQueue, &WaitContextBlock->WaitQueueEntry))
return STATUS_SUCCESS;
/* 24-bit max address due to 16-bit dma controllers */
@@ -79,6 +79,10 @@
* X86 lacks map registers, so for now, we allocate a contiguous
* block of physical memory <16MB and copy all DMA buffers into
* that. This can be optimized.
+ *
+ * FIXME: We propably shouldn't allocate the memory here for common
+ * buffer transfers. See a comment in IoMapTransfer about common buffer
+ * support.
*/
AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory(
NumberOfMapRegisters * PAGE_SIZE,
@@ -159,6 +163,12 @@
if(!MapRegisterBase)
return TRUE;
+ /* mask out (disable) the dma channel */
+ if (AdapterObject->Channel < 4)
+ WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)(AdapterObject->Channel | 0x4) );
+ else
+ WRITE_PORT_UCHAR( (PVOID)0xD4, (UCHAR)((AdapterObject->Channel - 4) | 0x4) );
+
if(WriteToDevice)
return TRUE;
@@ -166,19 +176,6 @@
(PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )),
MapRegisterBase, Length );
- /*
- FIXME: mask off (disable) channel if doing System DMA?
-
- From linux:
-
- if (dmanr<=3)
- dma_outb(dmanr | 4, DMA1_MASK_REG 0x0A) ;
- else
- dma_outb((dmanr & 3) | 4, DMA2_MASK_REG 0x0A);
-
- */
-
-
return TRUE;
}
@@ -304,14 +301,18 @@
* - If the controller supports scatter/gather, the copyover should not happen
*/
{
- PHYSICAL_ADDRESS Address;
+ PHYSICAL_ADDRESS Address;
+ PVOID MaskReg, ClearReg, ModeReg;
+ UCHAR ModeMask, LengthShift;
+ KIRQL OldIrql;
+
#if defined(__GNUC__)
- Address.QuadPart = 0ULL;
+ Address.QuadPart = 0ULL;
#else
- Address.QuadPart = 0;
+ Address.QuadPart = 0;
#endif
- /* Isa System (slave) DMA? */
+ /* Isa System (slave) DMA? */
if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
{
#if 0
@@ -321,39 +322,77 @@
assert(AdapterObject->Channel != 4);
#endif
+ KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
+
/*
- FIXME: Handle case when doing common-buffer System DMA. In this case, the buffer described
- by MDL is allready phys. contiguous and below 16 mega. Driver makes a one-shot call to
- IoMapTransfer during init. to program controller with the common-buffer.
- */
-
+ * FIXME: Handle case when doing common-buffer System DMA. In this case,
+ * the buffer described by MDL is already phys. contiguous and below
+ * 16 mega. Driver makes a one-shot call to IoMapTransfer during init.
+ * to program controller with the common-buffer.
+ *
+ * UPDATE: Common buffer support is in place, but it's not done in a
+ * clean way. We use the buffer passed by the MDL in case that the
+ * adapter object is marked as auto initialize. I'm not sure if this
+ * is correct and if not, how to do it properly. Note that it's also
+ * possible to allocate the common buffer with different adapter object
+ * and IoMapTransfer must still work in this case. Eventually this should
+ * be cleaned up somehow or at least this comment modified to reflect
+ * the reality.
+ * -- Filip Navara, 19/07/2004
+ */
+
/* if it is a write to the device, copy the caller buffer to the low buffer */
- if( WriteToDevice )
+ if( WriteToDevice && !AdapterObject->AutoInitialize )
{
memcpy(MapRegisterBase,
(char*)MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
*Length );
}
+ // 16-bit DMA
+ if( AdapterObject->Channel >= 4 )
+ {
+ MaskReg = (PVOID)0xD4; ClearReg = (PVOID)0xD8; ModeReg = (PVOID)0xD6;
+ LengthShift = 1;
+ }
+ else
+ {
+ MaskReg = (PVOID)0x0A; ClearReg = (PVOID)0x0C; ModeReg = (PVOID)0x0B;
+ LengthShift = 0;
+ }
+
+ // calculate the mask we will later set to the mode register
+ ModeMask = (AdapterObject->Channel & 3) | ( WriteToDevice ? 0x8 : 0x4 );
+ // FIXME: if not demand mode, which mode to use? 0x40 for single mode
+ if (!AdapterObject->DemandMode)
+ ModeMask |= 0x40;
+ if (AdapterObject->AutoInitialize)
+ ModeMask |= 0x10;
+
// program up the dma controller, and return
- Address = MmGetPhysicalAddress( MapRegisterBase );
- // port 0xA is the dma mask register, or a 0x10 on to the channel number to mask it
- WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)(AdapterObject->Channel | 0x10));
+ if (!AdapterObject->AutoInitialize)
+ Address = MmGetPhysicalAddress( MapRegisterBase );
+ else
+ Address = MmGetPhysicalAddress( CurrentVa );
+ // disable and select the channel number
+ WRITE_PORT_UCHAR( MaskReg, (UCHAR)((AdapterObject->Channel & 3) | 0x4) );
// write zero to the reset register
- WRITE_PORT_UCHAR( (PVOID)0x0C, 0 );
- // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for non auto initialize
- WRITE_PORT_UCHAR( (PVOID)0x0B, (UCHAR)(AdapterObject->Channel | ( WriteToDevice ? 0x8 : 0x4 )) );
+ WRITE_PORT_UCHAR( ClearReg, 0 );
+ // mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for auto initialize
+ WRITE_PORT_UCHAR( ModeReg, ModeMask);
// set the 64k page register for the channel
- WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(((ULONG)Address.QuadPart)>>16) );
+ WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(Address.u.LowPart >> 16) );
// low, then high address byte, which is always 0 for us, because we have a 64k alligned address
WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
// count is 1 less than length, low then high
- WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(*Length - 1) );
- WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length - 1)>>8) );
+ WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length >> LengthShift) - 1) );
+ WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(((*Length >> LengthShift) - 1)>>8) );
// unmask the channel to let it rip
- WRITE_PORT_UCHAR( (PVOID)0x0A, (UCHAR)AdapterObject->Channel );
+ WRITE_PORT_UCHAR( MaskReg, AdapterObject->Channel & 3 );
+ KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
+
/*
NOTE: Return value should be ignored when doing System DMA.
Maybe return some more obvious invalid address here (thou returning
@@ -436,7 +475,7 @@
return MmGetPhysicalAddress(MapRegisterBase);
}
-
+
DPRINT1("IoMapTransfer: Unsupported operation\n");
KEBUGCHECK(0);
return Address;
Index: halx86/halinit.c
===================================================================
--- halx86/halinit.c (revision 1)
+++ halx86/halinit.c (working copy)
@@ -63,6 +63,7 @@
else if (BootPhase == 1)
{
HalpInitBusHandlers();
+ HalpInitDma();
HalpCalibrateStallExecution();
/* Enumerate the devices on the motherboard */
Index: halx86/dma.c
===================================================================
--- halx86/dma.c (revision 105)
+++ halx86/dma.c (working copy)
@@ -18,21 +18,38 @@
/* XXX This initialization is out of date - ADAPTER_OBJECT has changed */
/* NOTE: The following initializations have to be kept in synch with ADAPTER_OBJECT in hal.h */
-/* FIXME: we need the 16-bit dma channels */
ADAPTER_OBJECT IsaSlaveAdapterObjects[] = {
{ Isa, FALSE, 0, (PVOID)0x87, (PVOID)0x1, (PVOID)0x0, 0, NULL },
{ Isa, FALSE, 1, (PVOID)0x83, (PVOID)0x3, (PVOID)0x2, 0, NULL },
{ Isa, FALSE, 2, (PVOID)0x81, (PVOID)0x5, (PVOID)0x4, 0, NULL },
- { Isa, FALSE, 3, (PVOID)0x82, (PVOID)0x7, (PVOID)0x6, 0, NULL } };
+ { Isa, FALSE, 3, (PVOID)0x82, (PVOID)0x7, (PVOID)0x6, 0, NULL },
+ /* 16-bit DMA */
+ { Isa, FALSE, 4, (PVOID)0x8F, (PVOID)0xC2, (PVOID)0xC0, 0, NULL },
+ { Isa, FALSE, 5, (PVOID)0x8B, (PVOID)0xC6, (PVOID)0xC4, 0, NULL },
+ { Isa, FALSE, 6, (PVOID)0x89, (PVOID)0xCA, (PVOID)0xC8, 0, NULL },
+ { Isa, FALSE, 7, (PVOID)0x8A, (PVOID)0xCE, (PVOID)0xCC, 0, NULL } };
ADAPTER_OBJECT PciBusMasterAdapterObjects[] = {
{ PCIBus, TRUE, 0, (PVOID)0, (PVOID)0, (PVOID)0x0, 0, NULL } };
-/* Global flag to tell whether or not the adapter's device queue should be initialized (first call only) */
-BOOLEAN AdaptersInitialized = FALSE;
-
/* FUNCTIONS *****************************************************************/
+VOID
+HalpInitDma (VOID)
+{
+ ULONG Index;
+
+ KeInitializeDeviceQueue(&PciBusMasterAdapterObjects[0].DeviceQueue);
+ KeInitializeSpinLock(&PciBusMasterAdapterObjects[0].SpinLock);
+ PciBusMasterAdapterObjects[0].Inuse = FALSE;
+ for (Index = 0; Index < 8; Index++)
+ {
+ KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[Index].DeviceQueue);
+ KeInitializeSpinLock(&IsaSlaveAdapterObjects[Index].SpinLock);
+ IsaSlaveAdapterObjects[Index].Inuse = FALSE;
+ }
+}
+
PVOID STDCALL
HalAllocateCommonBuffer (PADAPTER_OBJECT AdapterObject,
ULONG Length,
@@ -54,9 +71,11 @@
* CacheEnabled is ignored - it's all cache-disabled (like in NT)
*/
{
- PHYSICAL_ADDRESS HighestAddress;
+ PHYSICAL_ADDRESS LowestAddress, HighestAddress, BoundryAddressMultiple;
PVOID BaseAddress;
+ LowestAddress.QuadPart = 0;
+ BoundryAddressMultiple.QuadPart = 0;
HighestAddress.u.HighPart = 0;
if (AdapterObject->InterfaceType == Isa ||
(AdapterObject->InterfaceType == MicroChannel && AdapterObject->Master == FALSE))
@@ -68,7 +87,13 @@
HighestAddress.u.LowPart = 0xFFFFFFFF; /* 32Bit: 4GB address range */
}
- BaseAddress = MmAllocateContiguousMemory(Length, HighestAddress);
+ BaseAddress = MmAllocateContiguousAlignedMemory(
+ Length,
+ LowestAddress,
+ HighestAddress,
+ BoundryAddressMultiple,
+ MmCached,
+ 0x10000 );
if (!BaseAddress)
return 0;
@@ -114,56 +139,80 @@
* RETURNS: The allocated adapter object on success
* NULL on failure
* TODO:
- * Figure out what to do with the commented-out cases
+ * Honour all the fields in DeviceDescription structure.
*/
{
- /* TODO: find a better home for this */
- if(!AdaptersInitialized)
- {
- KeInitializeDeviceQueue(&PciBusMasterAdapterObjects[0].DeviceQueue);
- KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[0].DeviceQueue);
- KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[1].DeviceQueue);
- KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[2].DeviceQueue);
- KeInitializeDeviceQueue(&IsaSlaveAdapterObjects[3].DeviceQueue);
- AdaptersInitialized = TRUE;
- }
+ PADAPTER_OBJECT AdapterObject;
/* Validate parameters in device description, and return a pointer to
the adapter object for the requested dma channel */
if( DeviceDescription->Version != DEVICE_DESCRIPTION_VERSION )
return NULL;
- if (DeviceDescription->InterfaceType == PCIBus)
+ switch (DeviceDescription->InterfaceType)
{
- if (DeviceDescription->Master == FALSE)
- return NULL;
+ case PCIBus:
+ if (DeviceDescription->Master == FALSE)
+ return NULL;
+ return &PciBusMasterAdapterObjects[0];
- return &PciBusMasterAdapterObjects[0];
+ case Isa:
+ /* There are only 8 DMA channels on ISA. */
+ if (DeviceDescription->DmaChannel >= 8)
+ return NULL;
+ /* Channels 1-4 are for 8-bit transfers... */
+ if (DeviceDescription->DmaWidth != Width8Bits &&
+ DeviceDescription->DmaChannel < 4)
+ return NULL;
+ /* ...and the rest is for 16-bit transfers. */
+ if (DeviceDescription->DmaWidth != Width16Bits &&
+ DeviceDescription->DmaChannel >= 4)
+ return NULL;
+ AdapterObject = &IsaSlaveAdapterObjects[DeviceDescription->DmaChannel];
+ AdapterObject->Master = DeviceDescription->Master;
+ AdapterObject->ScatterGather = DeviceDescription->ScatterGather;
+ AdapterObject->AutoInitialize = DeviceDescription->AutoInitialize;
+ AdapterObject->DemandMode = DeviceDescription->DemandMode;
+ AdapterObject->Buffer = 0;
+ /* FIXME: Is this correct? */
+ *NumberOfMapRegisters = 16;
+ return AdapterObject;
+
+ default:
+ /* Unsupported bus. */
+ return NULL;
}
-
- /*
- if( DeviceDescription->Master )
- return NULL;
- if( DeviceDescription->ScatterGather )
- return NULL;
- if( DeviceDescription->AutoInitialize )
- return NULL;
- if( DeviceDescription->Dma32BitAddresses )
- return NULL;
- if( DeviceDescription->InterfaceType != Isa )
- return NULL;
- */
- /* if( DeviceDescription->DmaWidth != Width8Bits )
- return NULL;*/
- *NumberOfMapRegisters = 0x10;
- IsaSlaveAdapterObjects[DeviceDescription->DmaChannel].Buffer = 0;
- return &IsaSlaveAdapterObjects[DeviceDescription->DmaChannel];
}
ULONG STDCALL
HalReadDmaCounter (PADAPTER_OBJECT AdapterObject)
{
- UNIMPLEMENTED;
+ KIRQL OldIrql;
+ ULONG Count;
+
+ if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
+ {
+ KeAcquireSpinLock(&AdapterObject->SpinLock, &OldIrql);
+
+ /* Clear the flip/flop register */
+ WRITE_PORT_UCHAR( AdapterObject->Channel < 4 ? (PVOID)0x0C : (PVOID)0xD8, 0 );
+ /* Read the offset */
+ Count = READ_PORT_UCHAR( AdapterObject->CountPort );
+ Count |= READ_PORT_UCHAR( AdapterObject->CountPort ) << 8;
+
+ KeReleaseSpinLock(&AdapterObject->SpinLock, OldIrql);
+
+ /*
+ * We must return twice the sound for channel >= 4 because it's the size
+ * of words (16-bit) and not bytes.
+ */
+ if (AdapterObject->Channel < 4)
+ return Count;
+ else
+ return Count << 1;
+ }
+
+ return 0;
}
/* EOF */
Index: halx86/include/hal.h
===================================================================
--- halx86/include/hal.h (revision 105)
+++ halx86/include/hal.h (working copy)
@@ -19,6 +19,7 @@
VOID FASTCALL HalInitializeDisplay (PLOADER_PARAMETER_BLOCK LoaderBlock);
VOID FASTCALL HalClearDisplay (UCHAR CharAttribute);
+/* bus.c */
VOID HalpInitBusHandlers (VOID);
/* irql.c */
@@ -33,6 +34,9 @@
/* enum.c */
VOID HalpStartEnumerator (VOID);
+/* dma.c */
+VOID HalpInitDma (VOID);
+
/*
* ADAPTER_OBJECT - Track a busmaster DMA adapter and its associated resources
*
@@ -59,6 +63,15 @@
PWAIT_CONTEXT_BLOCK WaitContextBlock;
KDEVICE_QUEUE DeviceQueue;
BOOLEAN ScatterGather;
+
+ /*
+ * 18/07/04: Added these members. It's propably not the exact place where
+ * this should be stored, but I can't find better one. I haven't checked
+ * how Windows handles this.
+ * -- Filip Navara
+ */
+ BOOLEAN DemandMode;
+ BOOLEAN AutoInitialize;
};
/* sysinfo.c */
More information about the Ros-kernel
mailing list