NT4/private/ntos/nthals/halalpha/ebsgdma.c
2020-09-30 17:12:29 +02:00

2308 lines
61 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1993 Digital Equipment Corporation
Module Name:
ebsgdma.c
Abstract:
This module contains the hardware dependent routines to support
Io Adapters, Map Registers, and Common buffers for Scatter/Gather
Eisa/Isa bus Alpha AXP systems. The systems supported must include
support for 2 scatter/gather windows. Originally, this module will
support APECS- and LCA-based systems.
Author:
Joe Notarangelo 11-Oct-1993
Environment:
Kernel mode
Revision History:
Dick Bissen (DEC) 01-Nov-1993
Forced scatter/gather tables to be aligned with table size
Joe Notarangelo 24-Nov-1993
Do not program DMA controllers for ISA masters in IoMapTransfer and
IoFlushAdapterBuffers. Previous code did so if the device was an
Isa device without regard to whether or not it was a master device.
Joe Notarangelo 02-Feb-1994
Various bug fixes. Don't adjust mapRegister in IoMapTransfer and
IoFlushAdapterBuffers. Fix alignment adjustment code for Isa
machines. Initialize map registers to zero. Initialize bitmap
for map allocations by calling RtlClearAllBits. Add debugging
prints to fit new module haldebug.c
--*/
#include "halp.h"
#include "pci.h"
#include "pcip.h"
//
// There are 2 map register adapters that are created to control access
// to each of the 2 mapping windows that exist for APECS and LCA.
//
// The first adapter (IsaMapAdapter) controls access to the first mapping
// windows which maps 8MB : 16MB-1 in bus space. The window is chosen
// to be as large as possible and must be below 16MB to support ISA
// bus masters and the standard EISA/ISA dma controllers.
//
// The second adapter (MasterMapAdapter) controls access to the second
// mapping windows which maps a large region in bus space that may
// begin above 16MB. This window is used for bus masters that are not
// constrained by the ISA 24-bit limit.
//
PMAP_REGISTER_ADAPTER HalpIsaMapAdapter = NULL;
PMAP_REGISTER_ADAPTER HalpMasterMapAdapter = NULL;
//
// Pointer to superpage address memory for map registers.
//
PTRANSLATION_ENTRY HalpIsaMapRegisterBase = NULL;
PTRANSLATION_ENTRY HalpMasterMapRegisterBase = NULL;
//
// Control structures for each of the map register windows.
//
WINDOW_CONTROL_REGISTERS HalpIsaWindowControl;
WINDOW_CONTROL_REGISTERS HalpMasterWindowControl;
//
// Local function prototypes.
//
IO_ALLOCATION_ACTION
HalpAllocationRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
);
PMAP_REGISTER_ADAPTER
HalpAllocateMapAdapter(
IN PWINDOW_CONTROL_REGISTERS WindowRegisters,
IN HAL_ADAPTER_TYPE AdapterType,
IN PTRANSLATION_ENTRY MapRegisterBase
);
PADAPTER_OBJECT
HalpAllocateAdapter(
VOID
);
BOOLEAN
HalpAllocateMapRegisters(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG NumberOfMapRegisters,
IN BOOLEAN MapAdapterLocked
);
BOOLEAN
HalpCreateDmaStructures (
PLOADER_PARAMETER_BLOCK LoaderBlock
)
/*++
Routine Description:
This routine initializes the structures necessary for DMA operations.
Specifically, this routine allocates the physical pages to be used
to contain the scatter/gather entries for all DMA.
Arguments:
None.
Return Value:
TRUE is returned.
--*/
{
ULONG Allocated;
ULONG ByteSize;
ULONG MaxPhysicalAddress;
//
// Initialize the window control structures for each of the 2
// DMA windows.
//
INITIALIZE_ISA_DMA_CONTROL( &HalpIsaWindowControl );
INITIALIZE_MASTER_DMA_CONTROL( &HalpMasterWindowControl );
//
// Insure that the maximum address allocated will guarantee that the
// entirety of each allocation can be accessed via the 32-bit superpage.
//
MaxPhysicalAddress = __1GB - 1;
//
// Allocate the pages to contain the scatter/gather entries for the
// ISA DMA region (logical address range 8MB: 16MB-1).
//
ByteSize = ((HalpIsaWindowControl.WindowSize / PAGE_SIZE) *
sizeof(TRANSLATION_ENTRY)) + PAGE_SIZE-1;
//
// Memory allocation for the Isa scatter/gather table will always
// align on a 8K boundry.
//
Allocated = HalpAllocPhysicalMemory( LoaderBlock,
MaxPhysicalAddress,
ByteSize >> PAGE_SHIFT,
FALSE );
ASSERT( Allocated != 0 );
HalpIsaMapRegisterBase = (PTRANSLATION_ENTRY)(Allocated | KSEG0_BASE);
RtlZeroMemory( HalpIsaMapRegisterBase,
(ByteSize >> PAGE_SHIFT) << PAGE_SHIFT );
//
// Allocate the pages to contain the scatter/gather entries for the
// bus master DMA region. Allocation of scatter/gather tables MUST
// be aligned based on the size of the scatter/gather table (16k).
//
ByteSize = ((HalpMasterWindowControl.WindowSize / PAGE_SIZE) *
sizeof(TRANSLATION_ENTRY)) + PAGE_SIZE-1;
//
// Allocated on an aligned 64k boundry will ensure table alignment
// on a 16K boundry for a 16MB window size.
//
Allocated = HalpAllocPhysicalMemory( LoaderBlock,
MaxPhysicalAddress,
ByteSize >> PAGE_SHIFT,
TRUE );
ASSERT( Allocated != 0 );
HalpMasterMapRegisterBase = (PTRANSLATION_ENTRY)(Allocated | KSEG0_BASE);
RtlZeroMemory( HalpMasterMapRegisterBase,
(ByteSize >> PAGE_SHIFT) << PAGE_SHIFT );
//
// Perform any Eisa/Isa initialization.
//
HalpEisaInitializeDma();
//
// Program the DMA windows to reflect the translations.
//
INITIALIZE_DMA_WINDOW( &HalpMasterWindowControl,
(PVOID)( (ULONG)HalpMasterMapRegisterBase &
~KSEG0_BASE ) );
INITIALIZE_DMA_WINDOW( &HalpIsaWindowControl,
(PVOID)( (ULONG)HalpIsaMapRegisterBase &
~KSEG0_BASE ) );
return TRUE;
}
PADAPTER_OBJECT
HalGetAdapter(
IN PDEVICE_DESCRIPTION DeviceDescription,
IN OUT PULONG NumberOfMapRegisters
)
/*++
Routine Description:
This function returns the appropriate adapter object for the device defined
in the device description structure. Eisa/Isa bus types and all master
devices are supported for the system.
Arguments:
DeviceDescription - Supplies a description of the deivce.
NumberOfMapRegisters - Returns the maximum number of map registers which
may be allocated by the device driver.
Return Value:
A pointer to the requested adapter object or NULL if an adapter could not
be created.
--*/
{
ULONG MaximumMapRegistersPerChannel;
PADAPTER_OBJECT adapterObject;
PBUS_HANDLER BusHandler;
PPCIPBUSDATA PciBusData;
//
// Make sure this is the correct version.
//
if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION1) {
return(NULL);
}
//
// If the device is not a bus master, then it must be an ISA, EISA
// or PCI device on hardware bus 0. PCI devices on hardware busses
// other than 0 cannot support slave DMA devices because the DMAC
// needed to support slave DMA is part of the ISA/EISA bridge, (which
// is located on h/w bus 0).
//
if( DeviceDescription->Master != TRUE ){
//
// This device requires slave DMA h/w support. Determine which
// type of device it is.
//
switch( DeviceDescription->InterfaceType ){
case Isa:
case Eisa:
//
// The ISA/EISA bridge implements the DMA controller logic
// needed to support slave DMA.
//
break;
case PCIBus:
//
// Get the bus handler for the PCI bus.
//
BusHandler = HaliHandlerForBus(
PCIBus,
DeviceDescription->BusNumber
);
//
// If a bus handler does not exist, then there is a s/w bug
// somewhere. Just return failure.
//
if( BusHandler == NULL ){
return NULL;
}
//
// Get a pointer to the PCI private bus data for this bus.
// The h/w bus number is located therein.
//
PciBusData = (PPCIPBUSDATA)BusHandler->BusData;
//
// The DMA controller we use to support slave DMA is located
// on the ISA/EISA bridge in h/w bus 0. If this PCI bus is
// not located on h/w bus 0, return failure.
//
if( PciBusData->HwBusNumber != 0 ){
return NULL;
}
break;
default:
//
// We only support ISA, EISA and PCI slave DMA.
//
return NULL;
}
}
//
// Create an EISA adapter if this device is an ISA device
// or is not a master device.
//
if( (DeviceDescription->Master != TRUE) ||
(DeviceDescription->InterfaceType == Isa ) ){
//
// Allocate the Isa Map Register Adapter if it has not
// already been allocated.
//
if( HalpIsaMapAdapter == NULL ){
HalpIsaMapAdapter = HalpAllocateMapAdapter(
&HalpIsaWindowControl,
IsaAdapter,
HalpIsaMapRegisterBase );
if( HalpIsaMapAdapter == NULL ){
return NULL;
}
}
adapterObject = HalpAllocateEisaAdapter(
DeviceDescription,
NumberOfMapRegisters );
if( adapterObject == NULL ){
return NULL;
}
adapterObject->Type = IsaAdapter;
adapterObject->MapAdapter = HalpIsaMapAdapter;
adapterObject->MapRegisterBase = NULL;
adapterObject->NumberOfMapRegisters = 0;
} else {
//
// Allocate the master map register adapter if it has not
// already been allocated.
//
if( HalpMasterMapAdapter == NULL ){
HalpMasterMapAdapter = HalpAllocateMapAdapter(
&HalpMasterWindowControl,
BusMasterAdapter,
HalpMasterMapRegisterBase );
if( HalpMasterMapAdapter == NULL ){
return NULL;
}
}
//
// Allocate an adapter for this master device.
//
adapterObject = HalpAllocateAdapter();
if( adapterObject == NULL ){
return NULL;
}
//
// Initialize the adapter object.
//
adapterObject->Type = BusMasterAdapter;
adapterObject->MasterDevice = TRUE;
adapterObject->MapAdapter = HalpMasterMapAdapter;
adapterObject->MapRegisterBase = NULL;
adapterObject->NumberOfMapRegisters = 0;
//
// Calculate maximum number of map registers for this adapter.
//
if (NumberOfMapRegisters != NULL) {
//
// Return number of map registers requested based on the maximum
// transfer length.
//
*NumberOfMapRegisters = BYTES_TO_PAGES(
DeviceDescription->MaximumLength ) + 1;
//
// Limit the number of map registers to no more than 1/4 of all
// of the map registers available for this DMA window.
//
MaximumMapRegistersPerChannel =
(HalpMasterMapAdapter->WindowSize >> PAGE_SHIFT) / 4;
if( *NumberOfMapRegisters > MaximumMapRegistersPerChannel ){
*NumberOfMapRegisters = MaximumMapRegistersPerChannel;
}
adapterObject->MapRegistersPerChannel = *NumberOfMapRegisters;
} else {
adapterObject->MapRegistersPerChannel = 0;
}
}
return(adapterObject);
}
PMAP_REGISTER_ADAPTER
HalpAllocateMapAdapter(
IN PWINDOW_CONTROL_REGISTERS WindowRegisters,
IN HAL_ADAPTER_TYPE AdapterType,
IN PTRANSLATION_ENTRY MapRegisterBase
)
/*++
Routine Description:
This routine allocates and initializes the structure for the bus
master map register adapter.
Arguments:
WindowRegisters - Supplies a pointer to the software window control
registers that describes the DMA window associated
with this map adapter.
AdapterType - Supplies the type of the adapter.
MapRegisterBase - Supplies the starting virtual address of the map
registers for this adapter.
Return Value:
Returns the pointer to the allocated and initialized map
adapter if allocation was successful, NULL otherwise.
--*/
{
ULONG NumberMapRegisters;
ULONG Size;
PMAP_REGISTER_ADAPTER mapAdapter;
Size = sizeof(MAP_REGISTER_ADAPTER);
NumberMapRegisters = WindowRegisters->WindowSize / PAGE_SIZE;
//
// Add size of bitmap. Size of bitmap is the number of bytes required,
// computed by dividing map registers by 8 (>>3) and then rounding up
// to the nearest value divisible by 4.
//
Size += sizeof(RTL_BITMAP) + (( ((NumberMapRegisters+7) >> 3) + 3) & ~3);
//
// Allocate the map register adapter.
//
mapAdapter = ExAllocatePool( NonPagedPool, Size );
if( mapAdapter == NULL ){
return NULL;
}
//
// Initialize the fields within the map adapter structure.
//
mapAdapter->Type = AdapterType;
KeInitializeSpinLock( &mapAdapter->SpinLock );
InitializeListHead( &mapAdapter->RegisterWaitQueue );
mapAdapter->MapRegisterBase = MapRegisterBase;
mapAdapter->NumberOfMapRegisters = NumberMapRegisters;
mapAdapter->MapRegisterAllocation = (PRTL_BITMAP)(mapAdapter + 1);
RtlInitializeBitMap( mapAdapter->MapRegisterAllocation,
(PULONG)((PCHAR)(mapAdapter->MapRegisterAllocation) +
sizeof(RTL_BITMAP)),
NumberMapRegisters );
RtlClearAllBits( mapAdapter->MapRegisterAllocation );
mapAdapter->WindowBase = WindowRegisters->WindowBase;
mapAdapter->WindowSize = WindowRegisters->WindowSize;
mapAdapter->WindowControl = WindowRegisters;
return mapAdapter;
}
NTSTATUS
HalAllocateAdapterChannel(
IN PADAPTER_OBJECT AdapterObject,
IN PWAIT_CONTEXT_BLOCK Wcb,
IN ULONG NumberOfMapRegisters,
IN PDRIVER_CONTROL ExecutionRoutine
)
/*++
Routine Description:
This routine allocates the adapter channel specified by the adapter object.
This is accomplished by placing the device object of the driver that wants
to allocate the adapter on the adapter's queue. If the queue is already
"busy", then the adapter has already been allocated, so the device object
is simply placed onto the queue and waits until the adapter becomes free.
Once the adapter becomes free (or if it already is), then the driver's
execution routine is invoked.
Also, a number of map registers may be allocated to the driver by specifying
a non-zero value for NumberOfMapRegisters. Then the map register must be
allocated from the master adapter. Once there are a sufficient number of
map registers available, then the execution routine is called and the
base address of the allocated map registers in the adapter is also passed
to the driver's execution routine.
Arguments:
AdapterObject - Pointer to the adapter control object to allocate to the
driver.
Wcb - Supplies a wait context block for saving the allocation parameters.
The DeviceObject, CurrentIrp and DeviceContext should be initalized.
NumberOfMapRegisters - The number of map registers that are to be allocated
from the channel, if any.
ExecutionRoutine - The address of the driver's execution routine that is
invoked once the adapter channel (and possibly map registers) have been
allocated.
Return Value:
Returns STATUS_SUCESS unless too many map registers are requested.
Notes:
Note that this routine MUST be invoked at DISPATCH_LEVEL or above.
--*/
{
IO_ALLOCATION_ACTION Action;
BOOLEAN Busy = FALSE;
PMAP_REGISTER_ADAPTER MapAdapter;
//
// Begin by obtaining a pointer to the map register adapter associated
// with this request.
//
MapAdapter = AdapterObject->MapAdapter;
DebugPrint( (HALDBG_MAPREG,
"\nHalAllocateAdapter, Adapter=%x, MapA=%x, Maps=%x\n",
AdapterObject, MapAdapter, NumberOfMapRegisters) );
//
// Initialize the device object's wait context block in case this device
// must wait before being able to allocate the adapter.
//
Wcb->DeviceRoutine = ExecutionRoutine;
Wcb->NumberOfMapRegisters = NumberOfMapRegisters;
//
// Allocate the adapter object for this particular device. If the
// adapter cannot be allocated because it has already been allocated
// to another device, then return to the caller now; otherwise,
// continue.
//
if (!KeInsertDeviceQueue( &AdapterObject->ChannelWaitQueue,
&Wcb->WaitQueueEntry )) {
//
// The adapter was not busy so it has been allocated. Now check
// to see whether this driver wishes to allocate any map registers.
// If so, then queue the device object to the master adapter queue
// to wait for them to become available. If the driver wants map
// registers, ensure that this adapter has enough total map registers
// to satisfy the request.
//
AdapterObject->CurrentWcb = Wcb;
AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters;
if (NumberOfMapRegisters != 0) {
//
// Validate that the requested number of map registers is
// within the maximum limit.
//
if (NumberOfMapRegisters > MapAdapter->NumberOfMapRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
IoFreeAdapterChannel(AdapterObject);
return(STATUS_INSUFFICIENT_RESOURCES);
}
Busy = HalpAllocateMapRegisters( AdapterObject,
NumberOfMapRegisters,
FALSE );
}
//
// If there were either enough map registers available or no map
// registers needed to be allocated, invoke the driver's execution
// routine now.
//
if (Busy == FALSE) {
Action = ExecutionRoutine( Wcb->DeviceObject,
Wcb->CurrentIrp,
AdapterObject->MapRegisterBase,
Wcb->DeviceContext
);
//
// If the driver wishes to keep the map registers then set the
// number allocated to zero and set the action to deallocate
// object.
//
if (Action == DeallocateObjectKeepRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
Action = DeallocateObject;
}
//
// If the driver would like to have the adapter deallocated,
// then deallocate any map registers allocated and then release
// the adapter object.
//
if (Action == DeallocateObject) {
IoFreeAdapterChannel( AdapterObject );
}
} else {
DebugPrint( (HALDBG_MAPREG,
"No map registers available, Adapter= %x, Maps= %x\n",
AdapterObject, NumberOfMapRegisters) );
}
} else {
DebugPrint( (HALDBG_MAPREG,
"Device Queue is busy, AdapterObject = %x\n",
AdapterObject) );
}
return(STATUS_SUCCESS);
}
BOOLEAN
HalpAllocateMapRegisters(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG NumberOfMapRegisters,
IN BOOLEAN MapAdapterLocked
)
/*++
Routine Description:
Allocate the requested number of contiguous map registers from
the Map adapter.
Arguments:
AdapterObject - Supplies a pointer to the adapter object for which the
map registers are to be allocated.
NumberOfMapRegisters - Supplies the number of map registers to allocate.
MapAdapterLocked - Supplies a boolean which indicates if the map adapter
for the AdapterObject is already locked.
Return Value:
The value returned indicates if the map registers are busy.
The value FALSE is returned if the map registers were allocated.
Otherwise, the Adapter is put on the register wait queue for its
associated map adapter and TRUE is returned.
--*/
{
ULONG AllocationMask;
BOOLEAN Busy = FALSE;
ULONG ExtentBegin;
ULONG HintIndex;
KIRQL Irql;
ULONG MapRegisterIndex;
PMAP_REGISTER_ADAPTER mapAdapter;
//
// Some devices do DMA prefetch. This is bad since it will cause certain
// chipsets to generate a PFN error because a map register has not been
// allocated and validated. To fix this, we'll put in a hack. We'll
// allocate one extra map register and map it to some junk page to avoid
// this nasty problem.
//
NumberOfMapRegisters += 1;
//
// Acquire a pointer to the map adapter that contains the map registers
// for the adapter.
//
mapAdapter = AdapterObject->MapAdapter;
//
// Lock the map register bit map and the adapter queue in the
// master adapter object.
//
if( MapAdapterLocked == FALSE ){
KeAcquireSpinLock( &mapAdapter->SpinLock, &Irql );
}
MapRegisterIndex = MAXULONG;
if (IsListEmpty( &mapAdapter->RegisterWaitQueue)) {
//
// If this is an Isa machine and the requested DMA is for an
// Isa device then we must be careful that the DMA does not cross
// a 64K boundary on the bus.
//
if( (HalpBusType == MACHINE_TYPE_ISA) &&
(mapAdapter->Type == IsaAdapter) ){
ASSERT( (NumberOfMapRegisters * PAGE_SIZE) <= __64K );
//
// This is an Isa allocation, guarantee that the allocation
// of map registers will not span a 64K boundary. We do this by
// looking for a contiguous allocation of:
// NumberOfMapRegisters * 2 - 1
// Any allocation of this size will guarantee that:
// (a) The allocation fits before the next 64K boundary or
// (b) The allocation can be made on the next 64K boundary.
//
// N.B. - This algorithm depends on RtlFindClear* to find
// the first available extent of cleared bits.
//
ExtentBegin = RtlFindClearBits(
mapAdapter->MapRegisterAllocation,
NumberOfMapRegisters + 7,
0 );
if( ExtentBegin != -1 ){
//
// Compute the hint index. If ExtentBegin + NumberOfMaps
// does not cross a 64K boundary then ExtentBegin will be
// the hint index. Otherwise, align the hint to the next
// 64K boundary above ExtentBegin.
//
AllocationMask = (__64K >> PAGE_SHIFT) - 1;
HintIndex = (ExtentBegin+AllocationMask) & ~AllocationMask;
MapRegisterIndex = RtlFindClearBitsAndSet(
mapAdapter->MapRegisterAllocation,
NumberOfMapRegisters,
HintIndex );
}
} else {
//
// This allocation is not subject to the Isa 64K restriction.
//
ExtentBegin = RtlFindClearBits(
mapAdapter->MapRegisterAllocation,
NumberOfMapRegisters + 7,
0 );
AllocationMask = (__64K >> PAGE_SHIFT) - 1;
HintIndex = (ExtentBegin + AllocationMask) & ~AllocationMask;
MapRegisterIndex = RtlFindClearBitsAndSet(
mapAdapter->MapRegisterAllocation,
NumberOfMapRegisters,
HintIndex );
} //endif HalpBusType == MACHINE_TYPE_ISA
} //endif IsListEmpty
if (MapRegisterIndex == MAXULONG) {
//
// There were not enough free map registers. Queue this request
// on the map adapter where it will wait until some registers
// are deallocated.
//
InsertTailList( &mapAdapter->RegisterWaitQueue,
&AdapterObject->AdapterQueue );
Busy = TRUE;
}
//
// Unlock the map adapter (unless locked by the caller).
//
if( MapAdapterLocked == FALSE ){
KeReleaseSpinLock( &mapAdapter->SpinLock, Irql );
}
//
// If map registers were allocated, return the index of the first
// map register in the contiguous extent.
//
if( Busy == FALSE ){
AdapterObject->MapRegisterBase =
(PVOID) ((PTRANSLATION_ENTRY) mapAdapter->MapRegisterBase
+ MapRegisterIndex);
}
return Busy;
}
PADAPTER_OBJECT
HalpAllocateAdapter(
VOID
)
/*++
Routine Description:
This routine allocates and initializes an adapter object to represent an
adapter or a DMA controller on the system.
Arguments:
None.
Return Value:
The function value is a pointer to the allocated adapter object.
--*/
{
PADAPTER_OBJECT AdapterObject;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG Size;
HANDLE Handle;
NTSTATUS Status;
//
// Begin by initializing the object attributes structure to be used when
// creating the adapter object.
//
InitializeObjectAttributes( &ObjectAttributes,
NULL,
OBJ_PERMANENT,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
Size = sizeof( ADAPTER_OBJECT );
//
// Now create the adapter object.
//
Status = ObCreateObject( KernelMode,
*((POBJECT_TYPE *)IoAdapterObjectType),
&ObjectAttributes,
KernelMode,
(PVOID) NULL,
Size,
0,
0,
(PVOID *)&AdapterObject );
//
// Reference the object.
//
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByPointer(
AdapterObject,
FILE_READ_DATA | FILE_WRITE_DATA,
*IoAdapterObjectType,
KernelMode
);
}
//
// If the adapter object was successfully created, then attempt to insert
// it into the the object table.
//
if (NT_SUCCESS( Status )) {
Status = ObInsertObject( AdapterObject,
NULL,
FILE_READ_DATA | FILE_WRITE_DATA,
0,
(PVOID *) NULL,
&Handle );
if (NT_SUCCESS( Status )) {
ZwClose(Handle);
//
// Initialize the adapter object itself.
//
AdapterObject->Type = IO_TYPE_ADAPTER;
AdapterObject->Size = (USHORT) Size;
//
// Initialize the channel wait queue for this
// adapter.
//
KeInitializeDeviceQueue( &AdapterObject->ChannelWaitQueue );
} else {
//
// An error was incurred for some reason. Set the return value
// to NULL.
//
AdapterObject = (PADAPTER_OBJECT) NULL;
}
} else {
AdapterObject = (PADAPTER_OBJECT) NULL;
}
return AdapterObject;
}
PVOID
HalAllocateCrashDumpRegisters(
IN PADAPTER_OBJECT AdapterObject,
IN PULONG NumberOfMapRegisters
)
/*++
Routine Description:
This routine is called during the crash dump disk driver's initialization
to allocate a number map registers permanently.
Arguments:
AdapterObject - Pointer to the adapter control object to allocate to the
driver.
NumberOfMapRegisters - Number of map registers requested and updated to
show number actually allocated.
Return Value:
Returns a pointer to the allocated map register base.
--*/
{
ULONG AllocationMask;
PMAP_REGISTER_ADAPTER MapAdapter;
ULONG HintIndex;
ULONG MapRegisterIndex;
ULONG ExtentBegin;
//
// Begin by obtaining a pointer to the map adapter associated with this
// request.
//
MapAdapter = AdapterObject->MapAdapter;
//
// Ensure that this adapter has enough total map registers to satisfy
// the request.
//
if (*NumberOfMapRegisters > MapAdapter->NumberOfMapRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
return NULL;
}
MapRegisterIndex = (ULONG)-1;
//
// If this is an Isa machine and the requested DMA is for an
// Isa device then we must be areful that the DMA does not cross
// a 64K boundary on the bus.
//
if( (HalpBusType == MACHINE_TYPE_ISA) &&
(MapAdapter->Type == IsaAdapter) ){
//
// This is an Isa allocation, guarantee that the allocation
// of map registers will not span a 64K boundary. We do this by
// looking for a consiguous allocation of:
// NumberOfMapRegisters * 2 - 1
// Any allocation of this size will guarantee that:
// (a) The allocation fitst before the next 64K boundary or
// (b) The allocation can be made on the next 64K boundary.
//
// N.B. - This algorithm depends on RtlFindClear* to find
// the first available extent of cleared bits.
//
ExtentBegin = RtlFindClearBits(
MapAdapter->MapRegisterAllocation,
(*NumberOfMapRegisters * 2) - 1,
0 );
if( ExtentBegin != -1){
//
// Compute the hint index. If ExtentBegin + NumberOfMaps
// does not cross a 64K boundary then ExtentBegin will be
// the hint index. Otherwise, align the hint to the next
// 64K boundary above ExtentBegin.
//
AllocationMask = (__64K >> PAGE_SHIFT) - 1;
HintIndex = ExtentBegin;
if( (ExtentBegin + *NumberOfMapRegisters) >
((ExtentBegin + AllocationMask) & ~AllocationMask) ){
//
// Allocation would have spanned a 64K boundary.
// Round up to next 64K boundary.
//
HintIndex = (ExtentBegin+AllocationMask) & ~AllocationMask;
}
MapRegisterIndex = RtlFindClearBitsAndSet(
MapAdapter->MapRegisterAllocation,
*NumberOfMapRegisters,
HintIndex );
}
} else {
//
// This allocation is not subject to the Isa 64K restriction.
//
HintIndex = 0;
MapRegisterIndex = RtlFindClearBitsAndSet(
MapAdapter->MapRegisterAllocation,
*NumberOfMapRegisters,
0 );
}
if (MapRegisterIndex == (ULONG)-1) {
//
// Not enough free map registers were found, so they were busy
// being used by the system when it crashed. Force the appropriate
// number to be "allocated" at the base by simply overjamming the
// bits and return the base map register as the start.
//
RtlSetBits(
MapAdapter->MapRegisterAllocation,
HintIndex,
*NumberOfMapRegisters
);
MapRegisterIndex = HintIndex;
}
//
// Calculate the map register base from the allocated map
// register and base of the master adapter object.
//
AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY) MapAdapter->MapRegisterBase + MapRegisterIndex);
return AdapterObject->MapRegisterBase;
}
VOID
IoFreeMapRegisters(
PADAPTER_OBJECT AdapterObject,
PVOID MapRegisterBase,
ULONG NumberOfMapRegisters
)
/*++
Routine Description:
This routine deallocates the map registers for the adapter. If there are
any queued adapter waiting for an attempt is made to allocate the next
entry.
Arguments:
AdapterObject - The adapter object to where the map register should be
returned.
MapRegisterBase - The map register base of the registers to be deallocated.
NumberOfMapRegisters - The number of registers to be deallocated.
Return Value:
None
--+*/
{
IO_ALLOCATION_ACTION Action;
BOOLEAN Busy = FALSE;
KIRQL Irql;
LONG MapRegisterIndex;
PLIST_ENTRY Packet;
PWAIT_CONTEXT_BLOCK Wcb;
PMAP_REGISTER_ADAPTER mapAdapter;
//
// Deallocate the extra map register that we originally allocated to fix
// the DMA prefetch problem.
//
NumberOfMapRegisters += 1;
//
// Begin by getting the address of the map register adapter.
//
mapAdapter = AdapterObject->MapAdapter;
DebugPrint( (HALDBG_MAPREG,
"IoFreeMapRegisters, Adapter=%x, MapA=%x, Maps=%x\n",
AdapterObject, mapAdapter, NumberOfMapRegisters) );
MapRegisterIndex = (PTRANSLATION_ENTRY) MapRegisterBase -
(PTRANSLATION_ENTRY) mapAdapter->MapRegisterBase;
//
// Acquire the map adapter spinlock which locks the adapter queue and the
// bit map for the map registers.
//
KeAcquireSpinLock(&mapAdapter->SpinLock, &Irql);
//
// Return the registers to the bit map.
//
RtlClearBits( mapAdapter->MapRegisterAllocation,
MapRegisterIndex,
NumberOfMapRegisters
);
//
// Process any requests waiting for map registers in the adapter queue.
// Requests are processed until a request cannot be satisfied or until
// there are no more requests in the queue.
//
while(TRUE) {
if ( IsListEmpty(&mapAdapter->RegisterWaitQueue) ){
break;
}
Packet = RemoveHeadList( &mapAdapter->RegisterWaitQueue );
AdapterObject = CONTAINING_RECORD( Packet,
ADAPTER_OBJECT,
AdapterQueue
);
DebugPrint( (HALDBG_MAPREG,
"IoFreeMaps, waking Adapter=%x\n", AdapterObject) );
Wcb = AdapterObject->CurrentWcb;
//
// Attempt to allocate the map registers.
//
Busy = HalpAllocateMapRegisters( AdapterObject,
Wcb->NumberOfMapRegisters,
TRUE );
if( Busy == TRUE ){
DebugPrint( (HALDBG_MAPREG,
"IoFreeMaps, Not enough maps, Adapter=%x, Maps=%x\n",
AdapterObject, Wcb->NumberOfMapRegisters) );
break;
}
KeReleaseSpinLock( &mapAdapter->SpinLock, Irql );
//
// Invoke the driver's execution routine now.
//
Action = Wcb->DeviceRoutine( Wcb->DeviceObject,
Wcb->CurrentIrp,
AdapterObject->MapRegisterBase,
Wcb->DeviceContext );
//
// If the driver wishes to keep the map registers then set the number
// allocated to zero and set the action to deallocate object.
//
if (Action == DeallocateObjectKeepRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
Action = DeallocateObject;
}
//
// If the driver would like to have the adapter deallocated,
// then deallocate any map registers allocated and then release
// the adapter object.
//
if (Action == DeallocateObject) {
//
// The map registers are deallocated here rather than in
// IoFreeAdapterChannel. This limits the number of times
// this routine can be called recursively possibly overflowing
// the stack. The worst case occurs if there is a pending
// request for the adapter that uses map registers and whos
// excution routine decallocates the adapter. In that case if
// there are no requests in the map adapter queue, then
// IoFreeMapRegisters will get called again.
//
if (AdapterObject->NumberOfMapRegisters != 0) {
//
// Deallocate the map registers and clear the count so that
// IoFreeAdapterChannel will not deallocate them again.
//
KeAcquireSpinLock( &mapAdapter->SpinLock, &Irql );
MapRegisterIndex =
(PTRANSLATION_ENTRY)AdapterObject->MapRegisterBase -
(PTRANSLATION_ENTRY)mapAdapter->MapRegisterBase;
RtlClearBits( mapAdapter->MapRegisterAllocation,
MapRegisterIndex,
AdapterObject->NumberOfMapRegisters
);
AdapterObject->NumberOfMapRegisters = 0;
KeReleaseSpinLock( &mapAdapter->SpinLock, Irql );
}
IoFreeAdapterChannel( AdapterObject );
}
KeAcquireSpinLock( &mapAdapter->SpinLock, &Irql );
}
KeReleaseSpinLock( &mapAdapter->SpinLock, Irql );
}
VOID
IoFreeAdapterChannel(
IN PADAPTER_OBJECT AdapterObject
)
/*++
Routine Description:
This routine is invoked to deallocate the specified adapter object.
Any map registers that were allocated are also automatically deallocated.
No checks are made to ensure that the adapter is really allocated to
a device object. However, if it is not, then kernel will bugcheck.
If another device is waiting in the queue to allocate the adapter object
it will be pulled from the queue and its execution routine will be
invoked.
Arguments:
AdapterObject - Pointer to the adapter object to be deallocated.
Return Value:
None.
--*/
{
PKDEVICE_QUEUE_ENTRY Packet;
PMAP_REGISTER_ADAPTER mapAdapter;
BOOLEAN Busy = FALSE;
IO_ALLOCATION_ACTION Action;
PWAIT_CONTEXT_BLOCK Wcb;
KIRQL Irql;
LONG MapRegisterNumber;
//
// Begin by getting the address of the map register adapter.
//
mapAdapter = AdapterObject->MapAdapter;
DebugPrint( (HALDBG_MAPREG,
"IoFreeChannel, Adapter=%x, MapAdapter=%x\n",
AdapterObject, mapAdapter) );
//
// Pull requests of the adapter's device wait queue as long as the
// adapter is free and there are sufficient map registers available.
//
while( TRUE ){
//
// Begin by checking to see whether there are any map registers that
// need to be deallocated. If so, then deallocate them now.
//
if (AdapterObject->NumberOfMapRegisters != 0) {
IoFreeMapRegisters( AdapterObject,
AdapterObject->MapRegisterBase,
AdapterObject->NumberOfMapRegisters
);
}
//
// Simply remove the next entry from the adapter's device wait queue.
// If one was successfully removed, allocate any map registers that it
// requires and invoke its execution routine.
//
Packet = KeRemoveDeviceQueue( &AdapterObject->ChannelWaitQueue );
if (Packet == NULL) {
//
// There are no more requests break out of the loop.
//
break;
}
Wcb = CONTAINING_RECORD( Packet,
WAIT_CONTEXT_BLOCK,
WaitQueueEntry );
AdapterObject->CurrentWcb = Wcb;
AdapterObject->NumberOfMapRegisters = Wcb->NumberOfMapRegisters;
DebugPrint( (HALDBG_MAPREG,
"IoFreeChannel, waking for Maps=%x\n",
Wcb->NumberOfMapRegisters) );
//
// Check to see whether this driver wishes to allocate any map
// registers. If so, then queue the device object to the master
// adapter queue to wait for them to become available. If the driver
// wants map registers, ensure that this adapter has enough total
// map registers to satisfy the request.
//
if (Wcb->NumberOfMapRegisters != 0) {
Busy = HalpAllocateMapRegisters( AdapterObject,
Wcb->NumberOfMapRegisters,
FALSE );
}
//
// If there were either enough map registers available or no map
// registers needed to be allocated, invoke the driver's execution
// routine now.
//
if (Busy == FALSE) {
AdapterObject->CurrentWcb = Wcb;
Action = Wcb->DeviceRoutine( Wcb->DeviceObject,
Wcb->CurrentIrp,
AdapterObject->MapRegisterBase,
Wcb->DeviceContext );
//
// If the execution routine would like to have the adapter
// deallocated, then release the adapter object.
//
if (Action == KeepObject) {
//
// This request wants to keep the channel a while so break
// out of the loop.
//
break;
}
//
// If the driver wants to keep the map registers then set the
// number allocated to 0. This keeps the deallocation routine
// from deallocating them.
//
if (Action == DeallocateObjectKeepRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
}
} else {
//
// This request did not get the requested number of map registers so
// out of the loop.
//
DebugPrint( (HALDBG_MAPREG,
"IoFreeChannel, not enough maps, Adapter=%x, Maps=%x\n",
AdapterObject, Wcb->NumberOfMapRegisters) );
break;
}
}
}
PHYSICAL_ADDRESS
IoMapTransfer(
IN PADAPTER_OBJECT AdapterObject,
IN PMDL Mdl,
IN PVOID MapRegisterBase,
IN PVOID CurrentVa,
IN OUT PULONG Length,
IN BOOLEAN WriteToDevice
)
/*++
Routine Description:
This routine is invoked to set up the map registers in the DMA controller
to allow a transfer to or from a device.
Arguments:
AdapterObject - Pointer to the adapter object representing the DMA
controller channel that has been allocated.
Mdl - Pointer to the MDL that describes the pages of memory that are
being read or written.
MapRegisterBase - The address of the base map register that has been
allocated to the device driver for use in mapping the transfer.
CurrentVa - Current virtual address in the buffer described by the MDL
that the transfer is being done to or from.
Length - Supplies the length of the transfer. This determines the
number of map registers that need to be written to map the transfer.
Returns the length of the transfer which was actually mapped.
WriteToDevice - Boolean value that indicates whether this is a write
to the device from memory (TRUE), or vice versa.
Return Value:
Returns the logical address to be used by bus masters.
N.B. - The MapRegisterBase must point to the mapping intended for
the start virtual address of the Mdl.
--*/
{
ULONG NumberOfPages;
ULONG Offset;
PULONG PageFrameNumber;
ULONG i;
PMAP_REGISTER_ADAPTER mapAdapter;
PTRANSLATION_ENTRY mapRegister;
PHYSICAL_ADDRESS ReturnAddress;
DebugPrint( (HALDBG_IOMT,
"\nIoMT: CurrentVA = %x, Length = %x, WriteToDevice = %x\n",
CurrentVa, *Length, WriteToDevice ) );
//
// Determine the Map Register Adapter.
//
mapAdapter = NULL;
if( AdapterObject == NULL ){
//
// The caller did not supply the adapter object, we will determine
// the map adapter by matching the MapRegisterBase to the ranges
// allocated for each map adapter.
//
if( (HalpIsaMapAdapter != NULL) &&
(MapRegisterBase >= HalpIsaMapAdapter->MapRegisterBase) &&
((PTRANSLATION_ENTRY)MapRegisterBase <
(PTRANSLATION_ENTRY)HalpIsaMapAdapter->MapRegisterBase +
HalpIsaMapAdapter->NumberOfMapRegisters ) ){
mapAdapter = HalpIsaMapAdapter;
}
if( (HalpMasterMapAdapter != NULL) &&
(MapRegisterBase >= HalpMasterMapAdapter->MapRegisterBase) &&
((PTRANSLATION_ENTRY)MapRegisterBase <
(PTRANSLATION_ENTRY)HalpMasterMapAdapter->MapRegisterBase +
HalpMasterMapAdapter->NumberOfMapRegisters ) ){
mapAdapter = HalpMasterMapAdapter;
}
} else {
//
// The adapter object has been provided and will always have
// a pointer to the map adapter.
//
mapAdapter = AdapterObject->MapAdapter;
}
ASSERT( mapAdapter != NULL );
//
// Begin by determining where in the buffer this portion of the operation
// is taking place.
//
Offset = BYTE_OFFSET( (PCHAR)CurrentVa - (PCHAR)Mdl->StartVa );
DebugPrint( (HALDBG_IOMT, "Offset (1) = %x\n", Offset ) );
//
// Compute number of pages that this transfer spans.
//
NumberOfPages = (Offset + *Length + PAGE_SIZE - 1) >> PAGE_SHIFT;
DebugPrint( (HALDBG_IOMT, "NumberOfPages = %x\n", NumberOfPages ) );
//
// Compute a pointer to the page frame of the starting page of the transfer.
//
PageFrameNumber = (PULONG) (Mdl + 1);
PageFrameNumber += ( ((PCHAR) CurrentVa - (PCHAR) Mdl->StartVa)
>> PAGE_SHIFT );
//
// Compute a pointer to the map register that maps the starting page of
// the transfer.
//
mapRegister = MapRegisterBase;
//
// For each page, establish the mapping in the scatter/gather tables.
//
for (i = 0; i < NumberOfPages; i++) {
HAL_MAKE_VALID_TRANSLATION( mapRegister, *PageFrameNumber );
DebugPrint( (HALDBG_IOMT,
"Validate: *PageFrameNumber = %x, mapRegister = %x\n",
*PageFrameNumber, mapRegister ) );
PageFrameNumber += 1;
mapRegister += 1;
}
//
// If the operation is a write to device (transfer from memory to device),
// we will validate the extra map register so we don't generate a PFN
// error due to DMA prefetch by some devices.
//
if (WriteToDevice) {
PageFrameNumber -= 1;
HAL_MAKE_VALID_TRANSLATION( mapRegister, *PageFrameNumber );
}
//
// Synchronize the scatter/gather entry writes with any subsequent writes
// to the device.
//
HalpMb(); //jnfix - create HalpWmb();
//
// Invalidate any cached translations in the DMA window.
//
INVALIDATE_DMA_TRANSLATIONS( mapAdapter->WindowControl );
//
// Set the offset to point to the map register plus the offset.
//
Offset += ((PTRANSLATION_ENTRY) MapRegisterBase -
(PTRANSLATION_ENTRY) mapAdapter->MapRegisterBase) << PAGE_SHIFT;
Offset += (ULONG)mapAdapter->WindowBase;
DebugPrint( (HALDBG_IOMT, "Offset(3) = %x\n", Offset ) );
if( (AdapterObject != NULL) &&
(AdapterObject->Type == IsaAdapter) &&
(AdapterObject->MasterDevice != TRUE) ){
//
// Start the EISA DMA controller.
//
HalpMapEisaTransfer(
AdapterObject,
Offset,
*Length,
WriteToDevice
);
}
ReturnAddress.QuadPart = Offset;
return(ReturnAddress);
}
BOOLEAN
IoFlushAdapterBuffers(
IN PADAPTER_OBJECT AdapterObject,
IN PMDL Mdl,
IN PVOID MapRegisterBase,
IN PVOID CurrentVa,
IN ULONG Length,
IN BOOLEAN WriteToDevice
)
/*++
Routine Description:
This routine flushes the DMA adapter object buffers and clears the
enable flag which aborts the dma.
Arguments:
AdapterObject - Pointer to the adapter object representing the DMA
controller channel.
Mdl - A pointer to a Memory Descriptor List (MDL) that maps the locked-down
buffer to/from which the I/O occured.
MapRegisterBase - A pointer to the base of the map registers in the adapter
or DMA controller.
CurrentVa - The current virtual address in the buffer described the the Mdl
where the I/O operation occurred.
Length - Supplies the length of the transfer.
WriteToDevice - Supplies a BOOLEAN value that indicates the direction of
the data transfer was to the device.
Return Value:
TRUE - If the transfer was successful.
FALSE - If there was an error in the transfer.
--*/
{
ULONG NumberOfPages;
ULONG Offset;
BOOLEAN Result;
ULONG i;
PMAP_REGISTER_ADAPTER mapAdapter;
PTRANSLATION_ENTRY mapRegister;
DebugPrint( (HALDBG_IOMT,
"\nIoFlush: CurrentVA = %x, Length = %x, WriteToDevice = %x\n",
CurrentVa, Length, WriteToDevice ) );
//
// Determine the Map Register Adapter.
//
mapAdapter = NULL;
if( AdapterObject == NULL ){
//
// The caller did not supply the adapter object, we will determine
// the map adapter by matching the MapRegisterBase to the ranges
// allocated for each map adapter.
//
if( (HalpIsaMapAdapter != NULL) &&
(MapRegisterBase >= HalpIsaMapAdapter->MapRegisterBase) &&
((PTRANSLATION_ENTRY)MapRegisterBase <
(PTRANSLATION_ENTRY)HalpIsaMapAdapter->MapRegisterBase +
HalpIsaMapAdapter->NumberOfMapRegisters ) ){
mapAdapter = HalpIsaMapAdapter;
}
if( (HalpMasterMapAdapter != NULL) &&
(MapRegisterBase >= HalpMasterMapAdapter->MapRegisterBase) &&
((PTRANSLATION_ENTRY)MapRegisterBase <
(PTRANSLATION_ENTRY)HalpMasterMapAdapter->MapRegisterBase +
HalpMasterMapAdapter->NumberOfMapRegisters ) ){
mapAdapter = HalpMasterMapAdapter;
}
} else {
//
// The adapter object has been provided and will always have
// a pointer to the map adapter.
//
mapAdapter = AdapterObject->MapAdapter;
}
//
// Set the result of the flush to success.
//
Result = TRUE;
//
// If this is an Isa compatiable adapter or an adapter that uses
// the ISA/EISA Dma controllers then use the standard routines
// to clear the Dma controller.
//
if( (AdapterObject != NULL) &&
(AdapterObject->Type == IsaAdapter) &&
(AdapterObject->MasterDevice != TRUE) ){
Result = HalpFlushEisaAdapter( AdapterObject,
Mdl,
MapRegisterBase,
CurrentVa,
Length,
WriteToDevice );
}
//
// The Mdl->StartVa must point to a page boundary.
//
ASSERT( ( (ULONG)Mdl->StartVa & (PAGE_SIZE-1) ) == 0 );
//
// Compute the starting offset of the transfer.
//
Offset = BYTE_OFFSET( (PCHAR)CurrentVa - (PCHAR)Mdl->StartVa );
//
// Compute the number of pages that this transfer spanned.
//
NumberOfPages = (Offset + Length + PAGE_SIZE-1) >> PAGE_SHIFT;
//
// Compute a pointer to the first translation entry that mapped this
// transfer.
//
mapRegister = (PTRANSLATION_ENTRY)MapRegisterBase;
//
// Mark each translation as invalid.
//
for( i=0; i < NumberOfPages; i++ ){
HAL_INVALIDATE_TRANSLATION( mapRegister );
DebugPrint( (HALDBG_IOMT,
"Invalidate mapRegister = %x, PageFrame=%x\n",
mapRegister, (PTRANSLATION_ENTRY)mapRegister->Pfn) );
mapRegister += 1;
}
if( WriteToDevice ){
HAL_INVALIDATE_TRANSLATION( mapRegister );
}
//
// Invalidate any cached translations in the DMA window.
//
INVALIDATE_DMA_TRANSLATIONS( mapAdapter->WindowControl );
//
// Synchronize the updated translations with any subsequent device
// accesses.
// Also, synchronize any reads of the newly written DMA data by
// ensuring this processors view of memory is coherent.
// jnfix - actually this second task must be handled by HalFlushIoBuffers
//
HalpMb();
return Result;
}
ULONG
HalReadDmaCounter(
IN PADAPTER_OBJECT AdapterObject
)
/*++
Routine Description:
This function reads the DMA counter and returns the number of bytes left
to be transfered.
Arguments:
AdapterObject - Supplies a pointer to the adapter object to be read.
Return Value:
Returns the number of bytes still be be transfered.
--*/
{
//
// If this is an Isa compatiable adapter or an adapter that uses
// the ISA/EISA Dma controllers then use the standard routines
// to return the Dma count.
//
if( AdapterObject->Type == IsaAdapter ){
return HalpReadEisaDmaCounter( AdapterObject );
}
return 0;
}
PVOID
HalAllocateCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG Length,
OUT PPHYSICAL_ADDRESS LogicalAddress,
IN BOOLEAN CacheEnabled
)
/*++
Routine Description:
This function allocates the memory for a common buffer and maps so that it
can be accessed by a master device and the CPU.
Arguments:
AdapterObject - Supplies a pointer to the adapter object used by this
device.
Length - Supplies the length of the common buffer to be allocated.
LogicalAddress - Returns the logical address of the common buffer.
CacheEnable - Indicates whether the memeory is cached or not.
Return Value:
Returns the virtual address of the common buffer. If the buffer cannot be
allocated then NULL is returned.
--*/
{
PVOID virtualAddress;
PVOID mapRegisterBase;
ULONG numberOfMapRegisters;
ULONG mappedLength;
WAIT_CONTEXT_BLOCK wcb;
KEVENT allocationEvent;
NTSTATUS status;
PMDL mdl;
KIRQL irql;
PHYSICAL_ADDRESS MaxPhysicalAddress;
numberOfMapRegisters = BYTES_TO_PAGES(Length);
//
// Allocate the actual buffer and limit its physical address
// below 1GB. The 1GB limitation guarantees that the buffer will
// be accessible via 32-bit superpage.
//
MaxPhysicalAddress.HighPart = 0;
MaxPhysicalAddress.LowPart = __1GB - 1;
virtualAddress = MmAllocateContiguousMemory( Length, MaxPhysicalAddress );
if (virtualAddress == NULL) {
return(virtualAddress);
}
//
// Initialize an event.
//
KeInitializeEvent( &allocationEvent, NotificationEvent, FALSE);
//
// Initialize the wait context block. Use the device object to indicate
// where the map register base should be stored.
//
wcb.DeviceObject = &mapRegisterBase;
wcb.CurrentIrp = NULL;
wcb.DeviceContext = &allocationEvent;
//
// Allocate the adapter and the map registers.
//
KeRaiseIrql(DISPATCH_LEVEL, &irql);
status = HalAllocateAdapterChannel(
AdapterObject,
&wcb,
numberOfMapRegisters,
HalpAllocationRoutine
);
KeLowerIrql(irql);
if (!NT_SUCCESS(status)) {
//
// Cleanup and return NULL.
//
MmFreeContiguousMemory( virtualAddress );
return(NULL);
}
//
// Wait for the map registers to be allocated.
//
status = KeWaitForSingleObject(
&allocationEvent,
Executive,
KernelMode,
FALSE,
NULL
);
if (!NT_SUCCESS(status)) {
//
// Cleanup and return NULL.
//
MmFreeContiguousMemory( virtualAddress );
return(NULL);
}
//
// Create an mdl to use with call to I/O map transfer.
//
mdl = IoAllocateMdl(
virtualAddress,
Length,
FALSE,
FALSE,
NULL
);
MmBuildMdlForNonPagedPool(mdl);
//
// Map the transfer so that the controller can access the memory.
//
mappedLength = Length;
*LogicalAddress = IoMapTransfer(
NULL,
mdl,
mapRegisterBase,
virtualAddress,
&mappedLength,
TRUE
);
IoFreeMdl(mdl);
if (mappedLength < Length) {
//
// Cleanup and indicate that the allocation failed.
//
HalFreeCommonBuffer(
AdapterObject,
Length,
*LogicalAddress,
virtualAddress,
FALSE
);
return(NULL);
}
//
// The allocation completed successfully.
//
return(virtualAddress);
}
VOID
HalFreeCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG Length,
IN PHYSICAL_ADDRESS LogicalAddress,
IN PVOID VirtualAddress,
IN BOOLEAN CacheEnabled
)
/*++
Routine Description:
This function frees a common buffer and all of the resouces it uses.
Arguments:
AdapterObject - Supplies a pointer to the adapter object used by this
device.
Length - Supplies the length of the common buffer. This should be the same
value used for the allocation of the buffer.
LogicalAddress - Supplies the logical address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
VirtualAddress - Supplies the virtual address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
CacheEnable - Indicates whether the memeory is cached or not.
Return Value:
None
--*/
{
PMAP_REGISTER_ADAPTER mapAdapter;
PTRANSLATION_ENTRY mapRegisterBase;
ULONG mapRegisterIndex;
ULONG numberOfMapRegisters;
mapAdapter = AdapterObject->MapAdapter;
//
// Calculate the number of map registers, the map register index and
// the map register base.
//
numberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES( VirtualAddress,
Length );
mapRegisterIndex = (LogicalAddress.LowPart - (ULONG)mapAdapter->WindowBase)
>> PAGE_SHIFT;
mapRegisterBase = (PTRANSLATION_ENTRY) mapAdapter->MapRegisterBase
+ mapRegisterIndex;
//
// Free the map registers.
//
IoFreeMapRegisters(
AdapterObject,
(PVOID) mapRegisterBase,
numberOfMapRegisters
);
//
// Free the memory for the common buffer.
//
MmFreeContiguousMemory( VirtualAddress );
return;
}
BOOLEAN
HalFlushCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG Length,
IN PHYSICAL_ADDRESS LogicalAddress,
IN PVOID VirtualAddress
)
/*++
Routine Description:
This function is called to flush any hardware adapter buffers when the
driver needs to read data written by an I/O master device to a common
buffer.
Arguments:
AdapterObject - Supplies a pointer to the adapter object used by this
device.
Length - Supplies the length of the common buffer. This should be the same
value used for the allocation of the buffer.
LogicalAddress - Supplies the logical address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
VirtualAddress - Supplies the virtual address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
Return Value:
Returns TRUE if no errors were detected; otherwise, FALSE is return.
--*/
{
return(TRUE);
}
IO_ALLOCATION_ACTION
HalpAllocationRoutine (
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID MapRegisterBase,
IN PVOID Context
)
/*++
Routine Description:
This function is called by HalAllocateAdapterChannel when sufficent resources
are available to the driver. This routine saves the MapRegisterBase,
and set the event pointed to by the context parameter.
Arguments:
DeviceObject - Supplies a pointer where the map register base should be
stored.
Irp - Unused.
MapRegisterBase - Supplied by the Io subsystem for use in IoMapTransfer.
Context - Supplies a pointer to an event which is set to indicate the
AdapterObject has been allocated.
Return Value:
DeallocateObjectKeepRegisters - Indicates the adapter should be freed
and mapregisters should remain allocated after return.
--*/
{
UNREFERENCED_PARAMETER(Irp);
*((PVOID *) DeviceObject) = MapRegisterBase;
(VOID) KeSetEvent( (PKEVENT) Context, 0L, FALSE );
return(DeallocateObjectKeepRegisters);
}