-/* $Id: adapter.c,v 1.6 2003/10/20 06:03:28 vizzini Exp $
+/* $Id: adapter.c,v 1.7 2003/10/23 09:03:51 vizzini Exp $
*
* COPYRIGHT: See COPYING in the top level directory
* PROJECT: ReactOS kernel
/* NOTE: IoAllocateAdapterChannel in NTOSKRNL.EXE */
+\f
NTSTATUS STDCALL
HalAllocateAdapterChannel(PADAPTER_OBJECT AdapterObject,
PWAIT_CONTEXT_BLOCK WaitContextBlock,
* STATUS_SUCCESS in all other cases, including if the callbacak had
* to be queued for later delivery
* NOTES:
- * - Map registers don't exist on X86 so we can just call the callback
- * with a map register base of 0
* - the ADAPTER_OBJECT struct is undocumented; please make copious
- * notes here if anything is changed or improved since there is
- * no other documentation for this routine or its data structures
- * - The original implementation of this function allocated a contiguous
- * physical buffer the size of NumberOfMapRegisters * PAGE_SIZE, which
- * is unnecessary and very expensive (contiguous memory is rare). It
- * also leaked in some circumstances (drivers allocate and sometimes
- * don't free map registers)
+ * notes in hal.h if anything is changed or improved since there is
+ * no other documentation for this data structure
* BUGS:
- * - This routine should check whether or not map registers are needed
- * (rather than assuming they're not) and allocate them on platforms
- * that support them.
+ * - it's possible that some of this code is in the wrong place
+ * - there are many unhandled cases
*/
{
-#if 0
- KIRQL OldIrql;
- PVOID Buffer;
- int ret;
LARGE_INTEGER MaxAddress;
+ IO_ALLOCATION_ACTION Retval;
+ /* 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->DeviceQueue, (PKDEVICE_QUEUE_ENTRY)WaitContextBlock))
+ return STATUS_SUCCESS;
+
+ /* 24-bit max address due to 16-bit dma controllers */
MaxAddress.QuadPart = 0x1000000;
/* why 64K alignment? */
- Buffer = MmAllocateContiguousAlignedMemory( NumberOfMapRegisters * PAGE_SIZE,
- MaxAddress,
- 0x10000 );
- if( !Buffer )
+ /*
+ * 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.
+ */
+ AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory(
+ NumberOfMapRegisters * PAGE_SIZE, MaxAddress, 0x10000 );
+
+ if(!AdapterObject->MapRegisterBase)
return STATUS_INSUFFICIENT_RESOURCES;
- KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
- if( AdapterObject->Inuse )
+
+ AdapterObject->AllocatedMapRegisters = NumberOfMapRegisters;
+
+ /* call the client's AdapterControl callback with its map registers and context */
+ Retval = ExecutionRoutine(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
+ AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
+
+ /*
+ * 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( Retval == DeallocateObject )
+ IoFreeAdapterChannel(AdapterObject);
+ else if(Retval == DeallocateObjectKeepRegisters)
{
- // someone is already using it, we need to wait
- // create a wait block, and add it to the chain
- UNIMPLEMENTED;
+ /* don't free the allocated map registers - this is what IoFreeAdapterChannel checks */
+ AdapterObject->AllocatedMapRegisters = 0;
+ IoFreeAdapterChannel(AdapterObject);
}
- else {
- AdapterObject->Inuse = TRUE;
- KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
- ret = ExecutionRoutine( DeviceObject,
- NULL,
- Buffer,
- WaitContextBlock->DriverContext );
- KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
- if( ret == DeallocateObject )
- {
- MmFreeContiguousMemory( Buffer );
- AdapterObject->Inuse = FALSE;
- }
- else AdapterObject->Buffer = Buffer;
- }
- KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
-#endif
-
- AdapterObject->MapRegisterBase = 0;
- AdapterObject->AllocatedMapRegisters = 0;
-
- IO_ALLOCATION_ACTION Retval = ExecutionRoutine(WaitContextBlock->DeviceObject,
- WaitContextBlock->CurrentIrp, 0, WaitContextBlock->DeviceContext);
-
- if(Retval == DeallocateObject)
- IoFreeAdapterChannel(AdapterObject);
- else if(Retval == DeallocateObjectKeepRegisters)
- AdapterObject->AllocatedMapRegisters = 0;
+
+ /*
+ * if we don't call IoFreeAdapterChannel, the next device won't get de-queued,
+ * which is what we want.
+ */
return STATUS_SUCCESS;
}
-
+\f
BOOLEAN STDCALL
IoFlushAdapterBuffers (PADAPTER_OBJECT AdapterObject,
PMDL Mdl,
PVOID CurrentVa,
ULONG Length,
BOOLEAN WriteToDevice)
+/*
+ * FUNCTION: flush any data remaining in the dma controller's memory into the host memory
+ * ARGUMENTS:
+ * AdapterObject: the adapter object to flush
+ * Mdl: original MDL to flush data into
+ * MapRegisterBase: map register base that was just used by IoMapTransfer, etc
+ * CurrentVa: offset into Mdl to be flushed into, same as was passed to IoMapTransfer
+ * Length: length of the buffer to be flushed into
+ * WriteToDevice: True if it's a write, False if it's a read
+ * RETURNS:
+ * TRUE in all cases
+ * NOTES:
+ * - This copies data from the map register-backed buffer to the user's target buffer.
+ * Data is not in the user buffer until this is called.
+ * - This is only meaningful on a read operation. Return immediately for a write.
+ */
{
- // if this was a read from device, copy data back to caller buffer, otherwise, do nothing
- if( !WriteToDevice )
- memcpy( (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )), MapRegisterBase, Length );
+ /* FIXME we don't have ASSERT */
+ //ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
+
+ /* this can happen if the card supports scatter/gather */
+ if(!MapRegisterBase)
+ return TRUE;
+
+ if(WriteToDevice)
+ return TRUE;
+
+ memcpy(
+ (PVOID)((DWORD)MmGetSystemAddressForMdl( Mdl ) + (DWORD)CurrentVa - (DWORD)MmGetMdlVirtualAddress( Mdl )),
+ MapRegisterBase, Length );
+
return TRUE;
}
-
+\f
VOID STDCALL
IoFreeAdapterChannel (PADAPTER_OBJECT AdapterObject)
+/*
+ * FUNCTION: frees DMA resources allocated by IoAllocateAdapterChannel
+ * ARGUMENTS:
+ * AdapterObject: Adapter object with resources to free
+ * NOTES:
+ * - This function releases the DMA adapter and optionally the map registers
+ * - 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.
+ */
{
- KIRQL OldIrql;
-
- KeAcquireSpinLock( &AdapterObject->SpinLock, &OldIrql );
- if( AdapterObject->Inuse == FALSE )
- {
- DbgPrint( "Attempting to IoFreeAdapterChannel on a channel not in use\n" );
- KEBUGCHECK(0);
- }
- AdapterObject->Inuse = FALSE;
- if( AdapterObject->Buffer )
+ LARGE_INTEGER MaxAddress;
+ PWAIT_CONTEXT_BLOCK WaitContextBlock;
+ IO_ALLOCATION_ACTION Retval;
+
+ while(1)
{
- MmFreeContiguousMemory( AdapterObject->Buffer );
- AdapterObject->Buffer = 0;
+ /* To keep map registers, call here with the following set to 0 */
+ if(AdapterObject->AllocatedMapRegisters)
+ IoFreeMapRegisters(AdapterObject, AdapterObject->MapRegisterBase, AdapterObject->AllocatedMapRegisters);
+
+ if(!(WaitContextBlock = (PWAIT_CONTEXT_BLOCK)KeRemoveDeviceQueue(&AdapterObject->DeviceQueue)))
+ break;
+
+ /*
+ * the following should really be done elsewhere since this
+ * function really can't return an error code. FIXME.
+ */
+
+ /* 24-bit max address due to 16-bit dma controllers */
+ MaxAddress.QuadPart = 0x1000000;
+
+ AdapterObject->MapRegisterBase = MmAllocateContiguousAlignedMemory(
+ WaitContextBlock->NumberOfMapRegisters * PAGE_SIZE, MaxAddress, 0x10000 );
+
+ if(!AdapterObject->MapRegisterBase)
+ return;
+
+ /* call the adapter control routine */
+ Retval = ((PDRIVER_CONTROL)WaitContextBlock->DeviceRoutine)(WaitContextBlock->DeviceObject, WaitContextBlock->CurrentIrp,
+ AdapterObject->MapRegisterBase, WaitContextBlock->DeviceContext);
+
+ if(Retval == KeepObject)
+ {
+ /* we're done until the caller manually calls IoFreeAdapterChannel */
+ break;
+ }
+ else if(Retval == DeallocateObjectKeepRegisters)
+ {
+ /* hide the map registers so they aren't deallocated next time around */
+ AdapterObject->AllocatedMapRegisters = 0;
+ }
}
- KeReleaseSpinLock( &AdapterObject->SpinLock, OldIrql );
}
-
+\f
VOID STDCALL
IoFreeMapRegisters (PADAPTER_OBJECT AdapterObject,
PVOID MapRegisterBase,
ULONG NumberOfMapRegisters)
+/*
+ * FUNCTION: free map registers reserved by the system for a DMA
+ * ARGUMENTS:
+ * AdapterObject: dma adapter to free map registers on
+ * MapRegisterBase: hadle to map registers to free
+ * NumberOfRegisters: number of map registers to be freed
+ * NOTES:
+ * - XXX real windows has a funky interdependence between IoFreeMapRegisters
+ * and IoFreeAdapterChannel
+ * BUGS:
+ * - needs to be improved to use a real map register implementation
+ */
{
- UNIMPLEMENTED;
+ if( AdapterObject->AllocatedMapRegisters )
+ {
+ MmFreeContiguousMemory(AdapterObject->MapRegisterBase);
+ AdapterObject->MapRegisterBase = 0;
+ }
}
-
+\f
PHYSICAL_ADDRESS STDCALL
IoMapTransfer (PADAPTER_OBJECT AdapterObject,
PMDL Mdl,
PVOID CurrentVa,
PULONG Length,
BOOLEAN WriteToDevice)
+/*
+ * FUNCTION: map a dma for transfer and do the dma if it's a slave
+ * ARGUMENTS:
+ * AdapterObject: adapter object to do the dma on
+ * Mdl: locked-down user buffer to DMA in to or out of
+ * MapRegisterBase: handle to map registers to use for this dma
+ * CurrentVa: index into Mdl to transfer into/out of
+ * Length: length of transfer in/out
+ * WriteToDevice: TRUE if it's an output dma, FALSE otherwise
+ * RETURNS:
+ * If a busmaster: A logical address that can be used to program a dma controller
+ * Otherwise: nothing meaningful
+ * NOTES:
+ * - This function does a copyover to contiguous memory <16MB
+ * - If it's a slave transfer, this function actually performs it.
+ * BUGS:
+ * - If the controller supports scatter/gather, the copyover should not happen
+ */
{
PHYSICAL_ADDRESS Address;
// program up the dma controller, and return