improved IoMapTransfer (hopefylly)

svn path=/trunk/; revision=6476
This commit is contained in:
Gunnar Dalsnes 2003-10-31 01:08:00 +00:00
parent b872ea2cad
commit 888ca1574d

View file

@ -1,4 +1,4 @@
/* $Id: adapter.c,v 1.7 2003/10/23 09:03:51 vizzini Exp $ /* $Id: adapter.c,v 1.8 2003/10/31 01:08:00 gdalsnes Exp $
* *
* COPYRIGHT: See COPYING in the top level directory * COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel * PROJECT: ReactOS kernel
@ -24,10 +24,11 @@
NTSTATUS STDCALL NTSTATUS STDCALL
HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject, HalAllocateAdapterChannel(
PWAIT_CONTEXT_BLOCK WaitContextBlock, PADAPTER_OBJECT AdapterObject,
ULONG NumberOfMapRegisters, PWAIT_CONTEXT_BLOCK WaitContextBlock,
PDRIVER_CONTROL ExecutionRoutine) ULONG NumberOfMapRegisters,
PDRIVER_CONTROL ExecutionRoutine)
/* /*
* FUNCTION: Sets up an ADAPTER_OBJECT with map registers * FUNCTION: Sets up an ADAPTER_OBJECT with map registers
* ARGUMENTS: * ARGUMENTS:
@ -50,6 +51,13 @@ HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject,
{ {
LARGE_INTEGER MaxAddress; LARGE_INTEGER MaxAddress;
IO_ALLOCATION_ACTION Retval; IO_ALLOCATION_ACTION Retval;
assert(KeGetCurrentIrql() == DISPATCH_LEVEL);
/*
FIXME: return STATUS_INSUFFICIENT_RESOURCES if the NumberOfMapRegisters
requested is larger than the value returned by IoGetDmaAdapter.
*/
/* set up the wait context block in case we can't run right away */ /* set up the wait context block in case we can't run right away */
WaitContextBlock->DeviceRoutine = ExecutionRoutine; WaitContextBlock->DeviceRoutine = ExecutionRoutine;
@ -112,12 +120,13 @@ HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject,
BOOLEAN STDCALL BOOLEAN STDCALL
IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject, IoFlushAdapterBuffers (
PMDL Mdl, PADAPTER_OBJECT AdapterObject,
PVOID MapRegisterBase, PMDL Mdl,
PVOID CurrentVa, PVOID MapRegisterBase,
ULONG Length, PVOID CurrentVa,
BOOLEAN WriteToDevice) ULONG Length,
BOOLEAN WriteToDevice)
/* /*
* FUNCTION: flush any data remaining in the dma controller's memory into the host memory * FUNCTION: flush any data remaining in the dma controller's memory into the host memory
* ARGUMENTS: * ARGUMENTS:
@ -135,8 +144,7 @@ IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject,
* - This is only meaningful on a read operation. Return immediately for a write. * - This is only meaningful on a read operation. Return immediately for a write.
*/ */
{ {
/* FIXME we don't have ASSERT */ assert(KeGetCurrentIrql() <= DISPATCH_LEVEL);
//ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
/* this can happen if the card supports scatter/gather */ /* this can happen if the card supports scatter/gather */
if(!MapRegisterBase) if(!MapRegisterBase)
@ -144,11 +152,24 @@ IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject,
if(WriteToDevice) if(WriteToDevice)
return TRUE; return TRUE;
memcpy( memcpy(
(PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )), (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )),
MapRegisterBase, Length ); 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; return TRUE;
} }
@ -212,9 +233,10 @@ IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject)
VOID STDCALL VOID STDCALL
IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject, IoFreeMapRegisters (
PVOID MapRegisterBase, IN PADAPTER_OBJECT AdapterObject,
ULONG NumberOfMapRegisters) IN PVOID MapRegisterBase,
IN ULONG NumberOfMapRegisters)
/* /*
* FUNCTION: free map registers reserved by the system for a DMA * FUNCTION: free map registers reserved by the system for a DMA
* ARGUMENTS: * ARGUMENTS:
@ -237,20 +259,22 @@ IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject,
PHYSICAL_ADDRESS STDCALL PHYSICAL_ADDRESS STDCALL
IoMapTransfer (PADAPTER_OBJECT AdapterObject, IoMapTransfer (
PMDL Mdl, IN PADAPTER_OBJECT AdapterObject,
PVOID MapRegisterBase, IN PMDL Mdl,
PVOID CurrentVa, IN PVOID MapRegisterBase,
PULONG Length, IN PVOID CurrentVa,
BOOLEAN WriteToDevice) IN OUT PULONG Length,
IN BOOLEAN WriteToDevice)
/* /*
* FUNCTION: map a dma for transfer and do the dma if it's a slave * FUNCTION: map a dma for transfer and do the dma if it's a slave
* ARGUMENTS: * ARGUMENTS:
* AdapterObject: adapter object to do the dma on * AdapterObject: adapter object to do the dma on. busmaster may pass NULL.
* Mdl: locked-down user buffer to DMA in to or out of * Mdl: locked-down user buffer to DMA in to or out of
* MapRegisterBase: handle to map registers to use for this dma * MapRegisterBase: handle to map registers to use for this dma. allways NULL
* when doing s/g.
* CurrentVa: index into Mdl to transfer into/out of * CurrentVa: index into Mdl to transfer into/out of
* Length: length of transfer in/out * Length: length of transfer in/out. Only modified on out when doing s/g.
* WriteToDevice: TRUE if it's an output dma, FALSE otherwise * WriteToDevice: TRUE if it's an output dma, FALSE otherwise
* RETURNS: * RETURNS:
* If a busmaster: A logical address that can be used to program a dma controller * If a busmaster: A logical address that can be used to program a dma controller
@ -263,30 +287,136 @@ IoMapTransfer (PADAPTER_OBJECT AdapterObject,
*/ */
{ {
PHYSICAL_ADDRESS Address; PHYSICAL_ADDRESS Address;
// program up the dma controller, and return
// if it is a write to the device, copy the caller buffer to the low buffer
if( WriteToDevice ) /* Isa System (slave) DMA? */
memcpy( MapRegisterBase, if (AdapterObject && AdapterObject->InterfaceType == Isa && !AdapterObject->Master)
MmGetSystemAddressForMdl( Mdl ) + ( (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl ) ), {
*Length ); #if 0
Address = MmGetPhysicalAddress( MapRegisterBase ); /* channel 0 is reserved for DRAM refresh */
// port 0xA is the dma mask register, or a 0x10 on to the channel number to mask it assert(AdapterObject->Channel != 0);
WRITE_PORT_UCHAR( (PVOID)0x0A, AdapterObject->Channel | 0x10 ); /* channel 4 is reserved for cascade */
// write zero to the reset register assert(AdapterObject->Channel != 4);
WRITE_PORT_UCHAR( (PVOID)0x0C, 0 ); #endif
// mode register, or channel with 0x4 for write memory, 0x8 for read memory, 0x10 for non auto initialize
WRITE_PORT_UCHAR( (PVOID)0x0B, AdapterObject->Channel | ( WriteToDevice ? 0x8 : 0x4 ) ); /*
// set the 64k page register for the channel FIXME: Handle case when doing common-buffer System DMA. In this case, the buffer described
WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(((ULONG)Address.QuadPart)>>16) ); by MDL is allready phys. contiguous and below 16 mega. Driver makes a one-shot call to
// low, then high address byte, which is always 0 for us, because we have a 64k alligned address IoMapTransfer during init. to program controller with the common-buffer.
WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 ); */
WRITE_PORT_UCHAR( AdapterObject->OffsetPort, 0 );
// count is 1 less than length, low then high /* if it is a write to the device, copy the caller buffer to the low buffer */
WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)(*Length - 1) ); if( WriteToDevice )
WRITE_PORT_UCHAR( AdapterObject->CountPort, (UCHAR)((*Length - 1)>>8) ); {
// unmask the channel to let it rip memcpy(MapRegisterBase,
WRITE_PORT_UCHAR( (PVOID)0x0A, AdapterObject->Channel ); MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
Address.QuadPart = (DWORD)MapRegisterBase; *Length );
}
// 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, AdapterObject->Channel | 0x10 );
// 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, AdapterObject->Channel | ( WriteToDevice ? 0x8 : 0x4 ) );
// set the 64k page register for the channel
WRITE_PORT_UCHAR( AdapterObject->PagePort, (UCHAR)(((ULONG)Address.QuadPart)>>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) );
// unmask the channel to let it rip
WRITE_PORT_UCHAR( (PVOID)0x0A, AdapterObject->Channel );
/*
NOTE: Return value should be ignored when doing System DMA.
Maybe return some more obvious invalid address here (thou returning
MapRegisterBase is also wrong;-)to catch invalid use?
*/
Address.QuadPart = (ULONG)MapRegisterBase;
return Address;
}
/*
Busmaster with s/g support?
NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
being NULL is used to detect a s/g busmaster.
*/
if ((!AdapterObject && !MapRegisterBase) ||
(AdapterObject && AdapterObject->Master && AdapterObject->ScatterGather))
{
/*
Just return the passed VA's corresponding phys. address.
Update length to the number of phys. contiguous bytes found.
*/
PULONG MdlPages;
ULONG MdlPageIndex, PhysContiguousLen;
ULONG PhysAddress;
MdlPages = (PULONG)(Mdl + 1);
/* Get VA's corresponding mdl phys. page index */
MdlPageIndex = ((ULONG)CurrentVa - (ULONG)Mdl->StartVa) / PAGE_SIZE;
/* Get phys. page containing the VA */
PhysAddress = MdlPages[MdlPageIndex];
PhysContiguousLen = PAGE_SIZE - BYTE_OFFSET(CurrentVa);
/* VA to map may span several contiguous phys. pages (unlikely) */
while (PhysContiguousLen < *Length &&
MdlPages[MdlPageIndex++] + PAGE_SIZE == MdlPages[MdlPageIndex])
{
/*
Note that allways adding PAGE_SIZE may make PhysContiguousLen greater
than Length if buffer doesn't end on page boundary. Take this
into consideration below.
*/
PhysContiguousLen += PAGE_SIZE;
}
if (PhysContiguousLen < *Length)
{
*Length = PhysContiguousLen;
}
//add offset to phys. page address
Address.QuadPart = PhysAddress + BYTE_OFFSET(CurrentVa);
return Address;
}
/*
Busmaster without s/g support?
NOTE: old docs allowed busmasters to pass a NULL Adapter. In this case, MapRegisterBase
not being NULL is used to detect a non s/g busmaster.
*/
if ((!AdapterObject && MapRegisterBase) ||
(AdapterObject && AdapterObject->Master && !AdapterObject->ScatterGather))
{
/*
NOTE: Busmasters doing common-buffer DMA shouldn't call IoMapTransfer, but I don't
know if it's illegal... Maybe figure out what to do in this case...
*/
if( WriteToDevice )
{
memcpy(MapRegisterBase,
MmGetSystemAddressForMdl(Mdl) + ((ULONG)CurrentVa - (ULONG)MmGetMdlVirtualAddress(Mdl)),
*Length );
}
return MmGetPhysicalAddress(MapRegisterBase);
}
DPRINT1("IoMapTransfer: Unsupported operation\n");
KEBUGCHECK(0);
return Address; return Address;
} }