mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4290 lines
108 KiB
4290 lines
108 KiB
#if defined(JENSEN)
|
|
|
|
/*++
|
|
|
|
Copyright (c) 1990 Microsoft Corporation
|
|
Copyright (c) 1992 Digital Equipment Corporation
|
|
|
|
Module Name:
|
|
|
|
jxhwsup.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the HalpXxx routines for the NT I/O system that
|
|
are hardware dependent. Were these routines not hardware dependent,
|
|
they would normally reside in the internal.c module.
|
|
|
|
Author:
|
|
|
|
Jeff Havens (jhavens) 14-Feb-1990
|
|
Miche Baker-Harvey (miche) 22-May-1992
|
|
Jeff McLeman (mcleman) 27-May-1992
|
|
|
|
Environment:
|
|
|
|
Kernel mode, local to I/O system
|
|
|
|
Revision History:
|
|
|
|
|
|
--*/
|
|
|
|
#include "halp.h"
|
|
#include "jnsndef.h"
|
|
#include "jnsnint.h"
|
|
#include "jnsndma.h"
|
|
#include "eisa.h"
|
|
#include "jxisa.h"
|
|
#include "string.h"
|
|
|
|
|
|
|
|
#define HAL_32MB 0x2000000
|
|
|
|
#define HALF_MEGABYTE 0x80000
|
|
|
|
#define NUMBER_OF_SPECIAL_REGIONS 6
|
|
|
|
#define SPECIAL_BUFFER_SIZE NUMBER_OF_SPECIAL_REGIONS*sizeof(MEMORY_REGION)
|
|
|
|
PVOID HalpEisaControlBase;
|
|
|
|
//
|
|
// We have one fixed special memory region at half a megabyte.
|
|
//
|
|
|
|
MEMORY_REGION HalpHalfMeg;
|
|
|
|
//
|
|
// Pointer to special memory regions that must be checked on every I/O.
|
|
// Use to check if PFN is contained in a special memory region.
|
|
//
|
|
|
|
PMEMORY_REGION HalpSpecialRegions = NULL;
|
|
|
|
//
|
|
// Buffers used for MEMORY_REGION descriptors. We cannot allocate non-paged
|
|
// pool when we are building the MEMORY_REGION descriptors. So we will have
|
|
// our own little pool.
|
|
//
|
|
|
|
UCHAR HalpMemoryRegionFree[SPECIAL_BUFFER_SIZE];
|
|
|
|
//
|
|
// We have one fixed pool to allocate MEMORY_REGIONS from.
|
|
//
|
|
|
|
PVOID HalpMemoryRegionBuffers = HalpMemoryRegionFree;
|
|
ULONG HalpMemoryRegionSize = SPECIAL_BUFFER_SIZE;
|
|
|
|
//
|
|
// The following is the interrupt object used by the DMA controller dispatch
|
|
// routine to provide synchronization to the DMA controller. It is initialized
|
|
// by the I/O system during system initialization.
|
|
//
|
|
|
|
KINTERRUPT HalpDmaInterrupt;
|
|
|
|
//
|
|
// The HaeIndex, used in creating QVAs for EISA memory space.
|
|
//
|
|
ULONG HaeIndex;
|
|
|
|
//
|
|
// This is the HAE table. The first table entry is used to map the lowest
|
|
// 32MB in a Jensen system. The second entry is used to map the next 32MB
|
|
// entry so that graphics cards, etc., will work.
|
|
//
|
|
|
|
CHAR HalpHaeTable[4] = { 0, 1, 0, 0 };
|
|
|
|
//
|
|
// The following is an array of adapter object structures for the Eisa DMA
|
|
// channels.
|
|
//
|
|
|
|
//
|
|
// Define the area for the Eisa objects
|
|
//
|
|
|
|
PADAPTER_OBJECT HalpEisaAdapter[8];
|
|
|
|
//
|
|
// Some devices require a phyicially contiguous data buffers for DMA transfers.
|
|
// Map registers are used give the appearance that all data buffers are
|
|
// contiguous. In order to pool all of the map registers a master
|
|
// adapter object is used. This object is allocated and saved internal to this
|
|
// file. It contains a bit map for allocation of the registers and a queue
|
|
// for requests which are waiting for more map registers. This object is
|
|
// allocated during the first request to allocate an adapter which requires
|
|
// map registers.
|
|
//
|
|
|
|
PADAPTER_OBJECT MasterAdapterObject;
|
|
|
|
BOOLEAN LessThan16Mb;
|
|
BOOLEAN HalpEisaDma;
|
|
|
|
IO_ALLOCATION_ACTION
|
|
HalpAllocationRoutine (
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID MapRegisterBase,
|
|
IN PVOID Context
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpGrowMapBuffers(
|
|
PADAPTER_OBJECT AdapterObject,
|
|
ULONG Amount
|
|
);
|
|
|
|
|
|
PADAPTER_OBJECT
|
|
HalpAllocateAdapter(
|
|
IN ULONG MapRegistersPerChannel,
|
|
IN PVOID AdapterBaseVa,
|
|
IN PVOID ChannelNumber
|
|
);
|
|
|
|
VOID
|
|
HalpCopyBufferMap(
|
|
IN PMDL Mdl,
|
|
IN PTRANSLATION_ENTRY translationEntry,
|
|
IN PVOID CurrentVa,
|
|
IN ULONG Length,
|
|
IN BOOLEAN WriteToDevice
|
|
);
|
|
|
|
PVOID
|
|
HalCreateQva(
|
|
IN PHYSICAL_ADDRESS PA,
|
|
IN PVOID VA
|
|
);
|
|
|
|
ULONG
|
|
HalpGetEisaData(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
ULONG
|
|
HalpNoBusData (
|
|
IN PVOID BusHandler,
|
|
IN PVOID RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpSpecialMemory(
|
|
IN ULONG PFN
|
|
);
|
|
|
|
BOOLEAN
|
|
HalpAnySpecialMemory(
|
|
IN PMDL Mdl,
|
|
IN ULONG Length,
|
|
IN ULONG Offset
|
|
);
|
|
|
|
|
|
VOID
|
|
HalpCopyBufferMap(
|
|
IN PMDL Mdl,
|
|
IN PTRANSLATION_ENTRY translationEntry,
|
|
IN PVOID CurrentVa,
|
|
IN ULONG Length,
|
|
IN BOOLEAN WriteToDevice
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine copies the specified data between the user buffer and the
|
|
map register buffer. First, the user buffer is mapped, if need be then
|
|
the data is copied. Finally, the user buffer will be unmapped, if need be.
|
|
|
|
Arguments:
|
|
|
|
Mdl - Pointer to the Mdl that describes the pages of memory that are
|
|
being read or written.
|
|
|
|
translationEntry - The address of the base map register that has been
|
|
allocated to the device driver for use in mapping the xfer.
|
|
|
|
CurrentVa - Current Virtual Address in the buffer described by the Mdl
|
|
that the transfer is being done to or from.
|
|
|
|
Length - The length of the transfer. This determines the number of map
|
|
registers that need to be written to map the transfer.
|
|
|
|
WriteToDevice - A Boolean value that indicates whether this is a write
|
|
to the device from memory of vise-versa.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PCCHAR bufferAddress;
|
|
PCCHAR mapAddress;
|
|
|
|
//
|
|
// Get the system address of the MDL.
|
|
//
|
|
|
|
bufferAddress = MmGetSystemAddressForMdl(Mdl);
|
|
|
|
//
|
|
// Calculate the actual start of the buffer based on the system VA and
|
|
// the current VA.
|
|
//
|
|
|
|
bufferAddress += (PCCHAR) CurrentVa - (PCCHAR) MmGetMdlVirtualAddress(Mdl);
|
|
|
|
mapAddress = (PCCHAR) translationEntry->VirtualAddress +
|
|
BYTE_OFFSET(CurrentVa);
|
|
|
|
//
|
|
// Copy the data between the user buffer and the map buffer.
|
|
//
|
|
|
|
if (WriteToDevice) {
|
|
|
|
RtlMoveMemory( mapAddress, bufferAddress, Length);
|
|
|
|
} else {
|
|
|
|
RtlMoveMemory ( bufferAddress, mapAddress, Length);
|
|
|
|
}
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
|
|
PADAPTER_OBJECT MasterAdapter;
|
|
BOOLEAN Busy = FALSE;
|
|
IO_ALLOCATION_ACTION Action;
|
|
KIRQL Irql;
|
|
ULONG MapRegisterNumber;
|
|
|
|
//
|
|
// Begin by obtaining a pointer to the master adapter associated with this
|
|
// request.
|
|
//
|
|
|
|
MasterAdapter = AdapterObject->MasterAdapter;
|
|
|
|
//
|
|
// 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 )) {
|
|
|
|
//
|
|
// Save the parameters in case there are not enough map registers.
|
|
//
|
|
|
|
AdapterObject->NumberOfMapRegisters = NumberOfMapRegisters;
|
|
AdapterObject->CurrentWcb = Wcb;
|
|
|
|
//
|
|
// The adapter was not busy so it has been allocated. Now check
|
|
// to see whether this driver wishes to allocate any map registers.
|
|
// Ensure that this adapter has enough total map registers
|
|
// to satisfy the request.
|
|
//
|
|
|
|
if (NumberOfMapRegisters != 0 && AdapterObject->NeedsMapRegisters) {
|
|
|
|
//
|
|
// Lock the map register bit map and the adapter queue in the
|
|
// master adapter object. The channel structure offset is used as
|
|
// a hint for the register search.
|
|
//
|
|
|
|
if (NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) {
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
IoFreeAdapterChannel(AdapterObject);
|
|
return(STATUS_INSUFFICIENT_RESOURCES);
|
|
}
|
|
|
|
KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql );
|
|
|
|
MapRegisterNumber = -1;
|
|
|
|
if (IsListEmpty( &MasterAdapter->AdapterQueue)) {
|
|
|
|
MapRegisterNumber = RtlFindClearBitsAndSet(
|
|
MasterAdapter->MapRegisters,
|
|
NumberOfMapRegisters,
|
|
0
|
|
);
|
|
}
|
|
|
|
if (MapRegisterNumber == -1) {
|
|
|
|
//
|
|
// There were not enough free map registers. Queue this request
|
|
// on the master adapter where is will wait until some registers
|
|
// are deallocated.
|
|
//
|
|
|
|
InsertTailList( &MasterAdapter->AdapterQueue,
|
|
&AdapterObject->AdapterQueue
|
|
);
|
|
Busy = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Calculate the map register base from the allocated map
|
|
// register and base of the master adapter object.
|
|
//
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY)
|
|
MasterAdapter->MapRegisterBase + MapRegisterNumber);
|
|
|
|
//
|
|
// Set the no scatter/gather flag if scatter/gather not
|
|
// supported.
|
|
//
|
|
|
|
if (!AdapterObject->ScatterGather) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER);
|
|
}
|
|
|
|
if (AdapterObject->EisaAdapter) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | EISA_ADAPTER);
|
|
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
|
|
|
|
} else {
|
|
|
|
AdapterObject->MapRegisterBase = NULL;
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
AdapterObject->CurrentWcb = Wcb;
|
|
Action = ExecutionRoutine( Wcb->DeviceObject,
|
|
Wcb->CurrentIrp,
|
|
AdapterObject->MapRegisterBase,
|
|
Wcb->DeviceContext );
|
|
|
|
//
|
|
// If the driver would like to have the adapter deallocated,
|
|
// then release the adapter object.
|
|
//
|
|
|
|
if (Action == DeallocateObject) {
|
|
|
|
IoFreeAdapterChannel( AdapterObject );
|
|
|
|
} else if (Action == DeallocateObjectKeepRegisters) {
|
|
|
|
//
|
|
// Set the NumberOfMapRegisters = 0 in the adapter object.
|
|
// This will keep IoFreeAdapterChannel from freeing the
|
|
// registers. After this it is the driver's responsiblity to
|
|
// keep track of the number of map registers.
|
|
//
|
|
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
IoFreeAdapterChannel(AdapterObject);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
|
|
}
|
|
|
|
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.
|
|
NumerOfMapRegisters - Number of map registers required. Updated to show
|
|
actual number of registers allocated.
|
|
|
|
Return Value:
|
|
|
|
Returns STATUS_SUCESS if map registers allocated.
|
|
|
|
--*/
|
|
{
|
|
PADAPTER_OBJECT MasterAdapter;
|
|
ULONG MapRegisterNumber;
|
|
|
|
//
|
|
// Begin by obtaining a pointer to the master adapter associated with this
|
|
// request.
|
|
//
|
|
|
|
MasterAdapter = AdapterObject->MasterAdapter;
|
|
|
|
if ( MasterAdapter == NULL ) {
|
|
if ( MasterAdapterObject == NULL ) {
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
return NULL;
|
|
} else {
|
|
MasterAdapter = MasterAdapterObject;
|
|
AdapterObject->MapRegistersPerChannel = 16;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check to see whether this driver needs to allocate any map registers.
|
|
//
|
|
|
|
//
|
|
// Ensure that this adapter has enough total map registers to satisfy
|
|
// the request.
|
|
//
|
|
|
|
if (*NumberOfMapRegisters > AdapterObject->MapRegistersPerChannel) {
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Attempt to allocate the required number of map registers w/o
|
|
// affecting those registers that were allocated when the system
|
|
// crashed.
|
|
//
|
|
|
|
MapRegisterNumber = (ULONG)-1;
|
|
|
|
MapRegisterNumber = RtlFindClearBitsAndSet(
|
|
MasterAdapter->MapRegisters,
|
|
*NumberOfMapRegisters,
|
|
0
|
|
);
|
|
|
|
if (MapRegisterNumber == (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(
|
|
MasterAdapter->MapRegisters,
|
|
0,
|
|
*NumberOfMapRegisters
|
|
);
|
|
MapRegisterNumber = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// Calculate the map register base from the allocated map
|
|
// register and base of the master adapter object.
|
|
//
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)((PTRANSLATION_ENTRY)
|
|
MasterAdapter->MapRegisterBase + MapRegisterNumber);
|
|
|
|
//
|
|
// Set the no scatter/gather flag if scatter/gather not
|
|
// supported.
|
|
//
|
|
|
|
if (!AdapterObject->ScatterGather) {
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER);
|
|
}
|
|
|
|
if (AdapterObject->EisaAdapter) {
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | EISA_ADAPTER);
|
|
}
|
|
|
|
return AdapterObject->MapRegisterBase;
|
|
}
|
|
|
|
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 memory 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;
|
|
PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
UNREFERENCED_PARAMETER(AdapterObject);
|
|
UNREFERENCED_PARAMETER(CacheEnabled);
|
|
|
|
//
|
|
// Assume below 16M to support ISA devices
|
|
//
|
|
|
|
physicalAddress.LowPart = MAXIMUM_ISA_PHYSICAL_ADDRESS-1;
|
|
physicalAddress.HighPart = 0;
|
|
|
|
//
|
|
// If the caller supports 32bit addresses, and it's a master, let
|
|
// it have any memory below 1G
|
|
//
|
|
|
|
if (AdapterObject->Dma32BitAddresses && AdapterObject->MasterDevice) {
|
|
physicalAddress.LowPart = 0xFFFFFFFF;
|
|
}
|
|
|
|
//
|
|
// Allocate the actual buffer.
|
|
//
|
|
|
|
virtualAddress = MmAllocateContiguousMemory(
|
|
Length,
|
|
physicalAddress
|
|
);
|
|
|
|
if (!HALP_IS_PHYSICAL_ADDRESS(virtualAddress)) {
|
|
|
|
*LogicalAddress = MmGetPhysicalAddress(virtualAddress);
|
|
|
|
} else {
|
|
|
|
LogicalAddress->QuadPart = (ULONG)virtualAddress & (~KSEG0_BASE);
|
|
|
|
}
|
|
return(virtualAddress);
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(AdapterObject);
|
|
UNREFERENCED_PARAMETER(Length);
|
|
UNREFERENCED_PARAMETER(LogicalAddress);
|
|
UNREFERENCED_PARAMETER(VirtualAddress);
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
UNREFERENCED_PARAMETER(AdapterObject);
|
|
UNREFERENCED_PARAMETER(Length);
|
|
UNREFERENCED_PARAMETER(LogicalAddress);
|
|
UNREFERENCED_PARAMETER(CacheEnabled);
|
|
|
|
MmFreeContiguousMemory(VirtualAddress);
|
|
|
|
|
|
}
|
|
|
|
PADAPTER_OBJECT
|
|
HalGetAdapter(
|
|
IN PDEVICE_DESCRIPTION DeviceDescriptor,
|
|
OUT PULONG NumberOfMapRegisters
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the appropriate adapter object for the device defined
|
|
in the device description structure. This code works for Isa and Eisa
|
|
systems.
|
|
|
|
Arguments:
|
|
|
|
DeviceDescriptor - 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.
|
|
|
|
--*/
|
|
|
|
{
|
|
PADAPTER_OBJECT adapterObject;
|
|
PVOID adapterBaseVa;
|
|
ULONG channelNumber;
|
|
ULONG controllerNumber;
|
|
DMA_EXTENDED_MODE extendedMode;
|
|
UCHAR adapterMode;
|
|
ULONG numberOfMapRegisters;
|
|
BOOLEAN useChannel;
|
|
ULONG maximumLength;
|
|
UCHAR DataByte;
|
|
|
|
//
|
|
// Determine if the the channel number is important. Master cards on
|
|
// Eisa and Mca do not use a channel number.
|
|
//
|
|
|
|
if (DeviceDescriptor->InterfaceType != Isa &&
|
|
DeviceDescriptor->Master) {
|
|
|
|
useChannel = FALSE;
|
|
} else {
|
|
|
|
useChannel = TRUE;
|
|
}
|
|
|
|
//
|
|
// Support for ISA local bus machines:
|
|
// If the driver is a Master but really does not want a channel since it
|
|
// is using the local bus DMA, just don't use an ISA channel.
|
|
//
|
|
|
|
if (DeviceDescriptor->InterfaceType == Isa &&
|
|
DeviceDescriptor->DmaChannel > 7) {
|
|
|
|
useChannel = FALSE;
|
|
}
|
|
|
|
//
|
|
// Determine if Eisa DMA is supported.
|
|
//
|
|
|
|
if (HalpBusType == MACHINE_TYPE_EISA) {
|
|
|
|
HalpEisaDma = FALSE;
|
|
|
|
WRITE_PORT_UCHAR(&((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort.Channel2, 0x55);
|
|
DataByte = READ_PORT_UCHAR(&((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort.Channel2);
|
|
|
|
if (DataByte == 0x55) {
|
|
HalpEisaDma = TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Limit the maximum length to 2 GB this is done so that the BYTES_TO_PAGES
|
|
// macro works correctly.
|
|
//
|
|
|
|
maximumLength = DeviceDescriptor->MaximumLength & 0x7fffffff;
|
|
|
|
//
|
|
// Channel 4 cannot be used since it is used for chaining. Return null if
|
|
// it is requested.
|
|
//
|
|
|
|
if (DeviceDescriptor->DmaChannel == 4 && useChannel) {
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Determine the number of map registers for this device.
|
|
//
|
|
|
|
if (DeviceDescriptor->ScatterGather && (LessThan16Mb ||
|
|
DeviceDescriptor->InterfaceType == Eisa)) {
|
|
|
|
//
|
|
// Since the device support scatter/Gather then map registers are not
|
|
// required.
|
|
//
|
|
|
|
numberOfMapRegisters = 0;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Determine the number of map registers required based on the maximum
|
|
// transfer length, up to a maximum number.
|
|
//
|
|
|
|
numberOfMapRegisters = BYTES_TO_PAGES(maximumLength)
|
|
+ 1;
|
|
numberOfMapRegisters = numberOfMapRegisters > MAXIMUM_ISA_MAP_REGISTER ?
|
|
MAXIMUM_ISA_MAP_REGISTER : numberOfMapRegisters;
|
|
|
|
//
|
|
// Make sure there where enough registers allocated initalize to support
|
|
// this size relaibly. This implies there must be to chunks equal to
|
|
// the allocatd size. This is only a problem on Isa systems where the
|
|
// map buffers cannot cross 64KB boundtires.
|
|
//
|
|
|
|
if (!HalpEisaDma &&
|
|
numberOfMapRegisters > HalpMapBufferSize / (PAGE_SIZE * 2)) {
|
|
|
|
numberOfMapRegisters = (HalpMapBufferSize / (PAGE_SIZE * 2));
|
|
}
|
|
|
|
//
|
|
// If the device is not a master and does scatter/gather then
|
|
// it only needs one map register.
|
|
//
|
|
|
|
if (DeviceDescriptor->ScatterGather && !DeviceDescriptor->Master) {
|
|
|
|
numberOfMapRegisters = 1;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set the channel number.
|
|
//
|
|
|
|
channelNumber = DeviceDescriptor->DmaChannel & 0x03;
|
|
|
|
//
|
|
// Set the adapter base address to the Base address register and controller
|
|
// number.
|
|
//
|
|
|
|
if (!(DeviceDescriptor->DmaChannel & 0x04)) {
|
|
|
|
controllerNumber = 1;
|
|
adapterBaseVa = (PVOID) &((PEISA_CONTROL) HalpEisaControlBase)->Dma1BasePort;
|
|
|
|
} else {
|
|
|
|
controllerNumber = 2;
|
|
adapterBaseVa = &((PEISA_CONTROL) HalpEisaControlBase)->Dma2BasePort;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine if a new adapter object is necessary. If so then allocate it.
|
|
//
|
|
|
|
if (useChannel && HalpEisaAdapter[DeviceDescriptor->DmaChannel] != NULL) {
|
|
|
|
adapterObject = HalpEisaAdapter[DeviceDescriptor->DmaChannel];
|
|
|
|
if (adapterObject->NeedsMapRegisters) {
|
|
|
|
if (numberOfMapRegisters > adapterObject->MapRegistersPerChannel) {
|
|
|
|
adapterObject->MapRegistersPerChannel = numberOfMapRegisters;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
|
|
//
|
|
// Allocate an adapter object.
|
|
//
|
|
|
|
adapterObject = (PADAPTER_OBJECT) HalpAllocateAdapter(
|
|
numberOfMapRegisters,
|
|
adapterBaseVa,
|
|
NULL
|
|
);
|
|
|
|
if (adapterObject == NULL) {
|
|
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
if (useChannel) {
|
|
|
|
HalpEisaAdapter[DeviceDescriptor->DmaChannel] = adapterObject;
|
|
|
|
}
|
|
|
|
//
|
|
// Set the maximum number of map registers for this channel bus to
|
|
// the number requested and the type of device.
|
|
//
|
|
|
|
if (numberOfMapRegisters) {
|
|
|
|
//
|
|
// The speicified number of registers are actually allowed to be
|
|
// allocated.
|
|
//
|
|
|
|
adapterObject->MapRegistersPerChannel = numberOfMapRegisters;
|
|
|
|
//
|
|
// Increase the commitment for the map registers.
|
|
//
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
//
|
|
// Master I/O devices use several sets of map registers double
|
|
// their commitment.
|
|
//
|
|
|
|
MasterAdapterObject->CommittedMapRegisters +=
|
|
numberOfMapRegisters * 2;
|
|
|
|
} else {
|
|
|
|
MasterAdapterObject->CommittedMapRegisters +=
|
|
numberOfMapRegisters;
|
|
|
|
}
|
|
|
|
//
|
|
// If the committed map registers is signicantly greater than the
|
|
// number allocated then grow the map buffer.
|
|
//
|
|
|
|
if (MasterAdapterObject->CommittedMapRegisters >
|
|
MasterAdapterObject->NumberOfMapRegisters &&
|
|
MasterAdapterObject->CommittedMapRegisters -
|
|
MasterAdapterObject->NumberOfMapRegisters >
|
|
MAXIMUM_ISA_MAP_REGISTER ) {
|
|
|
|
HalpGrowMapBuffers(
|
|
MasterAdapterObject,
|
|
INCREMENT_MAP_BUFFER_SIZE
|
|
);
|
|
}
|
|
|
|
adapterObject->NeedsMapRegisters = TRUE;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No real map registers were allocated. If this is a master
|
|
// device, then the device can have as may registers as it wants.
|
|
//
|
|
|
|
adapterObject->NeedsMapRegisters = FALSE;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
adapterObject->MapRegistersPerChannel = BYTES_TO_PAGES(
|
|
maximumLength
|
|
)
|
|
+ 1;
|
|
|
|
} else {
|
|
|
|
//
|
|
// The device only gets one register. It must call
|
|
// IoMapTransfer repeatedly to do a large transfer.
|
|
//
|
|
|
|
adapterObject->MapRegistersPerChannel = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
adapterObject->Dma32BitAddresses = DeviceDescriptor->Dma32BitAddresses;
|
|
adapterObject->ScatterGather = DeviceDescriptor->ScatterGather;
|
|
*NumberOfMapRegisters = adapterObject->MapRegistersPerChannel;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
adapterObject->MasterDevice = TRUE;
|
|
|
|
} else {
|
|
|
|
adapterObject->MasterDevice = FALSE;
|
|
|
|
}
|
|
|
|
//
|
|
// Indicate whether the device is an Eisa adapter.
|
|
//
|
|
|
|
if ( DeviceDescriptor->InterfaceType == Eisa ) {
|
|
adapterObject->EisaAdapter = TRUE;
|
|
} else {
|
|
adapterObject->EisaAdapter = FALSE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the channel number is not used then we are finished. The rest of
|
|
// the work deals with channels.
|
|
//
|
|
|
|
if (!useChannel) {
|
|
return(adapterObject);
|
|
}
|
|
|
|
//
|
|
// Setup the pointers to all the random registers.
|
|
//
|
|
|
|
adapterObject->ChannelNumber = (UCHAR) channelNumber;
|
|
|
|
if (controllerNumber == 1) {
|
|
|
|
switch ((UCHAR)channelNumber) {
|
|
|
|
case 0:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel0;
|
|
break;
|
|
|
|
case 1:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel1;
|
|
break;
|
|
|
|
case 2:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel2;
|
|
break;
|
|
|
|
case 3:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel3;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the adapter number.
|
|
//
|
|
|
|
adapterObject->AdapterNumber = 1;
|
|
|
|
//
|
|
// Save the extended mode register address.
|
|
//
|
|
|
|
adapterBaseVa =
|
|
&((PEISA_CONTROL) HalpEisaControlBase)->Dma1ExtendedModePort;
|
|
|
|
} else {
|
|
|
|
switch (channelNumber) {
|
|
case 1:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel5;
|
|
break;
|
|
|
|
case 2:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel6;
|
|
break;
|
|
|
|
case 3:
|
|
adapterObject->PagePort = (PUCHAR) &((PDMA_PAGE) 0)->Channel7;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Set the adapter number.
|
|
//
|
|
|
|
adapterObject->AdapterNumber = 2;
|
|
|
|
//
|
|
// Save the extended mode register address.
|
|
//
|
|
adapterBaseVa =
|
|
&((PEISA_CONTROL) HalpEisaControlBase)->Dma2ExtendedModePort;
|
|
|
|
}
|
|
|
|
|
|
adapterObject->Width16Bits = FALSE;
|
|
|
|
if (HalpEisaDma) {
|
|
|
|
//
|
|
// Initialzie the extended mode port.
|
|
//
|
|
|
|
*((PUCHAR) &extendedMode) = 0;
|
|
extendedMode.ChannelNumber = channelNumber;
|
|
|
|
switch (DeviceDescriptor->DmaSpeed) {
|
|
case Compatible:
|
|
extendedMode.TimingMode = COMPATIBLITY_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:
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
switch (DeviceDescriptor->DmaWidth) {
|
|
case Width8Bits:
|
|
extendedMode.TransferSize = BY_BYTE_8_BITS;
|
|
break;
|
|
|
|
case Width16Bits:
|
|
extendedMode.TransferSize = BY_BYTE_16_BITS;
|
|
|
|
//
|
|
// Note Width16bits should not be set here because there is no need
|
|
// to shift the address and the transfer count.
|
|
//
|
|
|
|
break;
|
|
|
|
case Width32Bits:
|
|
extendedMode.TransferSize = BY_BYTE_32_BITS;
|
|
break;
|
|
|
|
default:
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
WRITE_PORT_UCHAR( adapterBaseVa, *((PUCHAR) &extendedMode));
|
|
|
|
} else if (!DeviceDescriptor->Master) {
|
|
|
|
|
|
switch (DeviceDescriptor->DmaWidth) {
|
|
case Width8Bits:
|
|
|
|
//
|
|
// The channel must use controller 1.
|
|
//
|
|
|
|
if (controllerNumber != 1) {
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
}
|
|
|
|
break;
|
|
|
|
case Width16Bits:
|
|
|
|
//
|
|
// The channel must use controller 2.
|
|
//
|
|
|
|
if (controllerNumber != 2) {
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
}
|
|
|
|
adapterObject->Width16Bits = TRUE;
|
|
break;
|
|
|
|
default:
|
|
ObDereferenceObject( adapterObject );
|
|
return(NULL);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the adapter mode register value to the correct parameters,
|
|
// and save them in the adapter object.
|
|
//
|
|
|
|
adapterMode = 0;
|
|
((PDMA_EISA_MODE) &adapterMode)->Channel = adapterObject->ChannelNumber;
|
|
|
|
if (DeviceDescriptor->Master) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = CASCADE_REQUEST_MODE;
|
|
|
|
//
|
|
// Set the mode, and enable the request.
|
|
//
|
|
|
|
if (adapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = adapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = adapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
//
|
|
// Unmask the DMA channel.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | adapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
|
|
} else if (DeviceDescriptor->DemandMode) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = DEMAND_REQUEST_MODE;
|
|
|
|
} else {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->RequestMode = SINGLE_REQUEST_MODE;
|
|
|
|
}
|
|
|
|
if (DeviceDescriptor->AutoInitialize) {
|
|
|
|
((PDMA_EISA_MODE) &adapterMode)->AutoInitialize = 1;
|
|
|
|
}
|
|
|
|
adapterObject->AdapterMode = adapterMode;
|
|
|
|
return(adapterObject);
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpTranslateSystemBusAddress(
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN PHYSICAL_ADDRESS BusAddress,
|
|
IN OUT PULONG AddressSpace,
|
|
OUT PPHYSICAL_ADDRESS TranslatedAddress
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns the system physical address for a specified I/O bus
|
|
address. The return value is suitable for use in a subsequent call to
|
|
MmMapIoSpace.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Registered BUSHANDLER for the target configuration space
|
|
Supplies the bus handler (bus no, interface type).
|
|
|
|
RootHandler - Registered BUSHANDLER for the orginating
|
|
HalTranslateBusAddress request.
|
|
|
|
BusAddress - Supplies the bus relative address.
|
|
|
|
AddressSpace - Supplies the address space number for the device: 0 for
|
|
memory and 1 for I/O space. If the desired access mode is user mode,
|
|
then bit 1 must be TRUE.
|
|
|
|
TranslatedAddress - Supplies a pointer to return the translated address
|
|
|
|
|
|
Notes:
|
|
|
|
This is a variation of what began in the MIPS code. The intel code often
|
|
assumes that if an address is in I/O space, the bottom 32 bits of the
|
|
physical address can be used "like" a virtual address, and are returned
|
|
to the user. This doesn't work on MIPs machines where physical
|
|
addresses can be larger than 32 bits.
|
|
|
|
Since we are using superpage addresses for I/O on Alpha, we can do
|
|
almost what is done on intel. If AddressSpace is equal to 0 or 1, then
|
|
we assume the user is doing kernel I/O and we call
|
|
HalCreateQva to build a Quasi Virtual Address and return
|
|
that to the caller. We then set AddressSpace to a 1, so that the caller
|
|
will not call MmMapIoSpace. The Caller will use the low 32 bits of the
|
|
physical address we return as the VA. (Which we built a QVA in).
|
|
If the caller wants to access EISA I/O or Memory through user mode, then
|
|
the caller must set bit 1 in AddressSpace to a 1 (AddressSpace=2 or 3,
|
|
depending on whether EISA I/O or Memory), then the caller is returned the
|
|
34 bit Physical address. The caller will then call MmMapIoSpace, or
|
|
ZwMapViewOfSection which in turn calls HalCreateQva to build a QVA out
|
|
of a VA mapped through the page tables.
|
|
|
|
**** Note ****
|
|
|
|
The QVA in user mode can only be accessed with the routines WRITE/READ_
|
|
REGISTER_UCHAR/USHORT/ULONG, and they must be the ones in module
|
|
JXIOUSER.C. The user CANNOT call the above routines in the HAL from
|
|
usermode. (Which is pointless, since the HAL is superpage access
|
|
only).
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
A return value of TRUE indicates that a system physical address
|
|
corresponding to the supplied bus relative address and bus address
|
|
number has been returned in TranslatedAddress.
|
|
|
|
A return value of FALSE occurs if the translation for the address was
|
|
not possible
|
|
|
|
--*/
|
|
|
|
{
|
|
INTERFACE_TYPE InterfaceType = BusHandler->InterfaceType;
|
|
ULONG BusNumber = BusHandler->BusNumber;
|
|
|
|
PVOID va = 0; // note, this is used for a placeholder
|
|
|
|
HaeIndex = 0;
|
|
|
|
//
|
|
// If this is for the internal bus then the device is on the combo chip.
|
|
// BusAddress.LowPart should contains the port of the device.
|
|
//
|
|
|
|
if (InterfaceType == Internal) {
|
|
|
|
//
|
|
// Return the passed parameters.
|
|
//
|
|
|
|
TranslatedAddress->HighPart = 1;
|
|
TranslatedAddress->LowPart = 0xC0000000 + (BusAddress.LowPart << COMBO_BIT_SHIFT);
|
|
|
|
//
|
|
// Now call HalCreateQva. This will create a QVA
|
|
// that we'll return to the caller. Then we will implicitly set
|
|
// AddressSpace to a 1. The caller then will not call MmMapIoSpace
|
|
// and will use the address we return as a VA.
|
|
|
|
TranslatedAddress->LowPart = (ULONG) HalCreateQva(
|
|
*TranslatedAddress, va);
|
|
|
|
TranslatedAddress->HighPart = 0; // clear high longword for QVA
|
|
|
|
*AddressSpace = 1; // Make sure user doesn't call
|
|
// MmMapIoSpace.
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
if (InterfaceType != Isa && InterfaceType != Eisa) {
|
|
|
|
//
|
|
// Not on this system return nothing.
|
|
//
|
|
|
|
*AddressSpace = 0;
|
|
TranslatedAddress->LowPart = 0;
|
|
return(FALSE);
|
|
}
|
|
//
|
|
// Jensen only has one I/O bus which is an EISA, so the bus number is unused.
|
|
//
|
|
// Determine the address based on whether the bus address is in I/O space
|
|
// or bus memory space.
|
|
//
|
|
|
|
switch (*AddressSpace) {
|
|
|
|
case 0 : {
|
|
|
|
//
|
|
// The address is in EISA memory space, kernel mode.
|
|
//
|
|
|
|
//
|
|
// If the address cannot be mapped into the predefined low 32MB
|
|
// 'default' region, then find a free or matching region in the HAE
|
|
// Table. NB: slot zero is predefined to the lowest 32MB and cannot
|
|
// be overwritten.
|
|
//
|
|
if ( BusAddress.LowPart >= HAL_32MB ) {
|
|
ULONG HaeValue;
|
|
|
|
HaeValue = (BusAddress.LowPart >> 25);
|
|
|
|
for ( HaeIndex = 1; HaeIndex < 4; HaeIndex++ ) {
|
|
if ( HalpHaeTable[HaeIndex] == 0 ||
|
|
HalpHaeTable[HaeIndex] == HaeValue ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check if no HAE slots were available, if so return error.
|
|
|
|
if ( HaeIndex == 4 ) {
|
|
*AddressSpace = 0;
|
|
TranslatedAddress->LowPart = 0;
|
|
return(FALSE);
|
|
} else {
|
|
HalpHaeTable[HaeIndex] = HaeValue;
|
|
}
|
|
|
|
}
|
|
|
|
TranslatedAddress->HighPart = 0x2;
|
|
|
|
//
|
|
// There is no component of the bus address in the low part
|
|
//
|
|
TranslatedAddress->LowPart = (BusAddress.LowPart << EISA_BIT_SHIFT);
|
|
|
|
//
|
|
// Now call HalCreateQva. This will create a QVA
|
|
// that we'll return to the caller. Then we will implicitly set
|
|
// AddressSpace to a 1. The caller then will not call MmMapIoSpace
|
|
// and will use the address we return as a VA.
|
|
|
|
TranslatedAddress->LowPart = (ULONG) HalCreateQva(
|
|
*TranslatedAddress, va);
|
|
|
|
TranslatedAddress->HighPart = 0; // clear high longword for QVA
|
|
|
|
*AddressSpace = 1; // don't let the user call MmMapIoSpace
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
case 1 : {
|
|
//
|
|
// The address is in EISA I/O space, kernel mode.
|
|
//
|
|
|
|
TranslatedAddress->HighPart = 0x3;
|
|
//
|
|
// There is no component of the bus address in the low part
|
|
//
|
|
TranslatedAddress->LowPart = (BusAddress.LowPart << EISA_BIT_SHIFT);
|
|
|
|
//
|
|
// Now call HalCreateQva. This will create a QVA
|
|
// that we'll return to the caller. Then we will implicitly set
|
|
// AddressSpace to a 1. The caller then will not call MmMapIoSpace
|
|
// and will use the address we return as a VA.
|
|
|
|
TranslatedAddress->LowPart = (ULONG) HalCreateQva(
|
|
*TranslatedAddress, va);
|
|
|
|
TranslatedAddress->HighPart = 0; // clear high longword for QVA
|
|
|
|
*AddressSpace = 1; // Make sure user doesn't call
|
|
// MmMapIoSpace.
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
case 2 : {
|
|
|
|
//
|
|
// The address is in EISA memory space, user mode.
|
|
//
|
|
|
|
|
|
TranslatedAddress->HighPart = 0x2;
|
|
|
|
|
|
//
|
|
// There is no component of the bus address in the low part
|
|
//
|
|
TranslatedAddress->LowPart = (BusAddress.LowPart << EISA_BIT_SHIFT);
|
|
|
|
|
|
*AddressSpace = 0; // Let the user call MmMapIoSpace
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
case 3 : {
|
|
//
|
|
// The address is in EISA I/O space, user mode.
|
|
//
|
|
|
|
TranslatedAddress->HighPart = 0x3;
|
|
//
|
|
// There is no component of the bus address in the low part
|
|
//
|
|
TranslatedAddress->LowPart = (BusAddress.LowPart << EISA_BIT_SHIFT);
|
|
|
|
|
|
*AddressSpace = 0; // Make sure user can call
|
|
// MmMapIoSpace.
|
|
|
|
return(TRUE);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
PVOID
|
|
HalCreateQva(
|
|
IN PHYSICAL_ADDRESS PA,
|
|
IN PVOID VA
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is called two ways. First, from HalTranslateBusAddress,
|
|
if the caller is going to run in kernel mode and use superpages.
|
|
The second way is if the user is going to access in user mode.
|
|
MmMapIoSpace or ZwViewMapOfSection will call this.
|
|
|
|
If the input parameter VA is zero, then we assume super page and build
|
|
a QUASI virtual address that is only usable by calling the hal I/O
|
|
access routines.
|
|
|
|
if the input parameter VA is non-zero, we assume the user has either
|
|
called MmMapIoSpace or ZwMapViewOfSection and will use the access
|
|
routines in JXIOUSER.C
|
|
|
|
If the PA is not an I/O space address (Combo chip, Eisa I/O, Eisa
|
|
memory), then return the VA as the QVA.
|
|
|
|
Arguments:
|
|
|
|
PA - the physical address generated by HalTranslateBusAddress
|
|
|
|
VA - the virtual address returned by MmMapIoSpace
|
|
|
|
Return Value:
|
|
|
|
The returned value is a quasi virtual address in that it can be
|
|
added to and subtracted from, but it cannot be used to access the
|
|
bus directly. The top bits are set so that we can trap invalid
|
|
accesses in the memory management subsystem. All access should be
|
|
done through the Hal Access Routines in *ioacc.s if it was a superpage
|
|
kernel mode access. If it is usermode, then JXIOUSER.C should be built
|
|
into the users code.
|
|
|
|
--*/
|
|
{
|
|
|
|
PVOID qva;
|
|
|
|
if (PA.HighPart == 2) {
|
|
|
|
//
|
|
// in EISA MEMORY space
|
|
//
|
|
|
|
if (VA == 0) {
|
|
|
|
//
|
|
// Remember, the PA.LowPart has already been shifted up 7 bits. We
|
|
// must first make room at bits <31:30> to insert the HaeIndex.
|
|
//
|
|
PA.LowPart = PA.LowPart >> 2;
|
|
PA.LowPart |= (HaeIndex << 30);
|
|
qva = (PVOID)(PA.QuadPart >> EISA_BIT_SHIFT-2);
|
|
|
|
} else {
|
|
|
|
qva = (PVOID)((ULONG)VA >> EISA_BIT_SHIFT);
|
|
}
|
|
|
|
qva = (PVOID)((ULONG)qva | EISA_QVA);
|
|
|
|
return(qva);
|
|
}
|
|
|
|
if (PA.HighPart == 3) {
|
|
|
|
//
|
|
// in EISA IO space
|
|
//
|
|
|
|
if (VA == 0) {
|
|
|
|
PA.LowPart = PA.LowPart >> 2;
|
|
qva = (PVOID)(PA.QuadPart >> EISA_BIT_SHIFT-2);
|
|
|
|
} else {
|
|
|
|
qva = (PVOID)((ULONG)VA >> EISA_BIT_SHIFT);
|
|
|
|
}
|
|
|
|
qva = (PVOID)((ULONG)qva | EISA_QVA);
|
|
|
|
return(qva);
|
|
}
|
|
|
|
if (PA.HighPart == 1) {
|
|
|
|
//
|
|
// on the combo chip (82C106)
|
|
//
|
|
|
|
if (VA == 0) {
|
|
|
|
qva = (PVOID)(PA.QuadPart >> COMBO_BIT_SHIFT);
|
|
|
|
} else {
|
|
|
|
qva = (PVOID)((ULONG)VA >> COMBO_BIT_SHIFT);
|
|
}
|
|
|
|
qva = (PVOID)((ULONG)qva | COMBO_QVA);
|
|
|
|
return(qva);
|
|
}
|
|
|
|
//
|
|
// It is not an I/O space address, return the VA as the QVA
|
|
//
|
|
|
|
return(VA);
|
|
|
|
}
|
|
|
|
PVOID
|
|
HalDereferenceQva(
|
|
PVOID Qva,
|
|
INTERFACE_TYPE InterfaceType,
|
|
ULONG BusNumber
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function performs the inverse of the HalCreateQva for I/O addresses
|
|
that are memory-mapped (i.e. the quasi-virtual address was created from
|
|
a virtual address rather than a physical address).
|
|
|
|
Arguments:
|
|
|
|
Qva - Supplies the quasi-virtual address to be converted back to a
|
|
virtual address.
|
|
|
|
InterfaceType - Supplies the interface type of the bus to which the
|
|
Qva pertains.
|
|
|
|
BusNumber - Supplies the bus number of the bus to which the Qva pertains.
|
|
|
|
Return Value:
|
|
|
|
The Virtual Address from which the quasi-address was originally created
|
|
is returned.
|
|
|
|
--*/
|
|
{
|
|
|
|
|
|
//
|
|
// For Jensen we have only 2 buses:
|
|
//
|
|
// Internal(0)
|
|
// Eisa(0)
|
|
//
|
|
// We will allow Isa as an alias for Eisa. All other values not named
|
|
// above will be considered bogus. Bus Number must be zero.
|
|
//
|
|
|
|
if( BusNumber != 0 ){
|
|
return NULL;
|
|
}
|
|
|
|
switch (InterfaceType ){
|
|
|
|
case Internal:
|
|
|
|
return( (PVOID)( (ULONG)Qva << COMBO_BIT_SHIFT ) );
|
|
|
|
case Isa:
|
|
case Eisa:
|
|
|
|
return( (PVOID)( (ULONG)Qva << EISA_BIT_SHIFT ) );
|
|
|
|
|
|
default:
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpGrowMapBuffers(
|
|
PADAPTER_OBJECT AdapterObject,
|
|
ULONG Amount
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to allocate additional map buffers for use by I/O
|
|
devices. The map register table is updated to indicate the additional
|
|
buffers.
|
|
|
|
Arguments:
|
|
|
|
AdapterObject - Supplies the adapter object for which the buffers are to be
|
|
allocated.
|
|
|
|
Amount - Indicates the size of the map buffers which should be allocated.
|
|
|
|
Return Value:
|
|
|
|
TRUE is returned if the memory could be allocated.
|
|
|
|
FALSE is returned if the memory could not be allocated.
|
|
|
|
--*/
|
|
{
|
|
ULONG MapBufferPhysicalAddress;
|
|
PVOID MapBufferVirtualAddress;
|
|
PTRANSLATION_ENTRY TranslationEntry;
|
|
LONG NumberOfPages;
|
|
LONG i;
|
|
KIRQL Irql;
|
|
PHYSICAL_ADDRESS physicalAddress;
|
|
|
|
KeAcquireSpinLock( &AdapterObject->SpinLock, &Irql );
|
|
|
|
NumberOfPages = BYTES_TO_PAGES(Amount);
|
|
|
|
//
|
|
// Make sure there is room for the addition pages. The maximum number of
|
|
// slots needed is equal to NumberOfPages + Amount / 64K + 1.
|
|
//
|
|
|
|
i = BYTES_TO_PAGES(MAXIMUM_MAP_BUFFER_SIZE) - (NumberOfPages +
|
|
(NumberOfPages * PAGE_SIZE) / 0x10000 + 1 +
|
|
AdapterObject->NumberOfMapRegisters);
|
|
|
|
if (i < 0) {
|
|
|
|
//
|
|
// Reduce the allocatation amount to so it will fit.
|
|
//
|
|
|
|
NumberOfPages += i;
|
|
}
|
|
|
|
if (NumberOfPages <= 0) {
|
|
//
|
|
// No more memory can be allocated.
|
|
//
|
|
|
|
KeReleaseSpinLock( &AdapterObject->SpinLock, Irql );
|
|
return(FALSE);
|
|
|
|
}
|
|
|
|
|
|
if (AdapterObject->NumberOfMapRegisters == 0 && HalpMapBufferSize) {
|
|
|
|
NumberOfPages = BYTES_TO_PAGES(HalpMapBufferSize);
|
|
|
|
//
|
|
// Since this is the initial allocation, use the buffer allocated by
|
|
// HalInitSystem rather than allocating a new one.
|
|
//
|
|
|
|
MapBufferPhysicalAddress = HalpMapBufferPhysicalAddress.LowPart;
|
|
|
|
//
|
|
// Map the buffer for access thru KSEG0, since we don't want to
|
|
// use translation entries.
|
|
//
|
|
|
|
MapBufferVirtualAddress =
|
|
(PVOID)(HalpMapBufferPhysicalAddress.LowPart |
|
|
(ULONG)KSEG0_BASE);
|
|
|
|
} else {
|
|
|
|
//
|
|
// Allocate the map buffers.
|
|
//
|
|
physicalAddress.LowPart = MAXIMUM_ISA_PHYSICAL_ADDRESS - 1;
|
|
physicalAddress.HighPart = 0;
|
|
MapBufferVirtualAddress = MmAllocateContiguousMemory(
|
|
NumberOfPages * PAGE_SIZE,
|
|
physicalAddress
|
|
);
|
|
|
|
if (MapBufferVirtualAddress == NULL) {
|
|
|
|
KeReleaseSpinLock( &AdapterObject->SpinLock, Irql );
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Get the physical address of the map base.
|
|
//
|
|
|
|
if (!HALP_IS_PHYSICAL_ADDRESS(MapBufferVirtualAddress)) {
|
|
|
|
MapBufferPhysicalAddress = MmGetPhysicalAddress(
|
|
MapBufferVirtualAddress
|
|
).LowPart;
|
|
|
|
} else {
|
|
|
|
MapBufferPhysicalAddress = (ULONG)MapBufferVirtualAddress &
|
|
(~KSEG0_BASE);
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Initailize the map registers where memory has been allocated.
|
|
//
|
|
|
|
TranslationEntry = ((PTRANSLATION_ENTRY) AdapterObject->MapRegisterBase) +
|
|
AdapterObject->NumberOfMapRegisters;
|
|
|
|
for (i = 0; (ULONG) i < NumberOfPages; i++) {
|
|
|
|
//
|
|
// Make sure the perivous entry is physically contiguous with the next
|
|
// entry and that a 64K physical bountry is not crossed unless this
|
|
// is an Eisa system.
|
|
//
|
|
|
|
if (TranslationEntry != AdapterObject->MapRegisterBase &&
|
|
(((TranslationEntry - 1)->PhysicalAddress + PAGE_SIZE) !=
|
|
MapBufferPhysicalAddress || (!HalpEisaDma &&
|
|
((TranslationEntry - 1)->PhysicalAddress & ~0x0ffff) !=
|
|
(MapBufferPhysicalAddress & ~0x0ffff)))) {
|
|
|
|
//
|
|
// An entry needs to be skipped in the table. This entry will
|
|
// remain marked as allocated so that no allocation of map
|
|
// registers will cross this bountry.
|
|
//
|
|
|
|
TranslationEntry++;
|
|
AdapterObject->NumberOfMapRegisters++;
|
|
}
|
|
|
|
//
|
|
// Clear the bits where the memory has been allocated.
|
|
//
|
|
|
|
RtlClearBits(
|
|
AdapterObject->MapRegisters,
|
|
TranslationEntry - (PTRANSLATION_ENTRY)
|
|
AdapterObject->MapRegisterBase,
|
|
1
|
|
);
|
|
|
|
TranslationEntry->VirtualAddress = MapBufferVirtualAddress;
|
|
TranslationEntry->PhysicalAddress = MapBufferPhysicalAddress;
|
|
TranslationEntry++;
|
|
(PCCHAR) MapBufferVirtualAddress += PAGE_SIZE;
|
|
MapBufferPhysicalAddress += PAGE_SIZE;
|
|
|
|
}
|
|
|
|
//
|
|
// Remember the number of pages that where allocated.
|
|
//
|
|
|
|
AdapterObject->NumberOfMapRegisters += NumberOfPages;
|
|
|
|
KeReleaseSpinLock( &AdapterObject->SpinLock, Irql );
|
|
return(TRUE);
|
|
}
|
|
|
|
PADAPTER_OBJECT
|
|
HalpAllocateAdapter(
|
|
IN ULONG MapRegistersPerChannel,
|
|
IN PVOID AdapterBaseVa,
|
|
IN PVOID ChannelNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates and initializes an adapter object to represent an
|
|
adapter or a DMA controller on the system. If no map registers are required
|
|
then a standalone adapter object is allocated with no master adapter.
|
|
|
|
If map registers are required, then a master adapter object is used to
|
|
allocate the map registers. For Isa systems these registers are really
|
|
phyically contiguous memory pages.
|
|
|
|
Arguments:
|
|
|
|
MapRegistersPerChannel - Specifies the number of map registers that each
|
|
channel provides for I/O memory mapping.
|
|
|
|
AdapterBaseVa - Address of the the DMA controller.
|
|
|
|
ChannelNumber - Unused.
|
|
|
|
Return Value:
|
|
|
|
The function value is a pointer to the allocate adapter object.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
PADAPTER_OBJECT AdapterObject;
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
ULONG Size;
|
|
ULONG BitmapSize;
|
|
HANDLE Handle;
|
|
NTSTATUS Status;
|
|
|
|
UNREFERENCED_PARAMETER(ChannelNumber);
|
|
|
|
//
|
|
// Initalize the master adapter if necessary.
|
|
//
|
|
if (MasterAdapterObject == NULL && AdapterBaseVa != (PVOID) -1 &&
|
|
MapRegistersPerChannel) {
|
|
|
|
MasterAdapterObject = HalpAllocateAdapter(
|
|
MapRegistersPerChannel,
|
|
(PVOID) -1,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If we could not allocate the master adapter then give up.
|
|
//
|
|
if (MasterAdapterObject == NULL) {
|
|
return(NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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
|
|
);
|
|
|
|
//
|
|
// Determine the size of the adapter object. If this is the master object
|
|
// then allocate space for the register bit map; otherwise, just allocate
|
|
// an adapter object.
|
|
//
|
|
if (AdapterBaseVa == (PVOID) -1) {
|
|
|
|
//
|
|
// Allocate a bit map large enough MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE
|
|
// of map register buffers.
|
|
//
|
|
|
|
BitmapSize = (((sizeof( RTL_BITMAP ) +
|
|
(( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE ) + 7 >> 3)) + 3) & ~3);
|
|
|
|
Size = sizeof( ADAPTER_OBJECT ) + BitmapSize;
|
|
|
|
} else {
|
|
|
|
Size = sizeof( ADAPTER_OBJECT );
|
|
|
|
}
|
|
|
|
//
|
|
// Now create the adapter object.
|
|
//
|
|
|
|
Status = ObCreateObject( KernelMode,
|
|
*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 )) {
|
|
|
|
RtlZeroMemory (AdapterObject, sizeof (ADAPTER_OBJECT));
|
|
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;
|
|
AdapterObject->MapRegistersPerChannel = 1;
|
|
AdapterObject->AdapterBaseVa = AdapterBaseVa;
|
|
|
|
if (MapRegistersPerChannel) {
|
|
|
|
AdapterObject->MasterAdapter = MasterAdapterObject;
|
|
|
|
} else {
|
|
|
|
AdapterObject->MasterAdapter = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// Initialize the channel wait queue for this
|
|
// adapter.
|
|
//
|
|
|
|
KeInitializeDeviceQueue( &AdapterObject->ChannelWaitQueue );
|
|
|
|
//
|
|
// If this is the MasterAdatper then initialize the register bit map,
|
|
// AdapterQueue and the spin lock.
|
|
//
|
|
|
|
if ( AdapterBaseVa == (PVOID) -1 ) {
|
|
|
|
KeInitializeSpinLock( &AdapterObject->SpinLock );
|
|
|
|
InitializeListHead( &AdapterObject->AdapterQueue );
|
|
|
|
AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1);
|
|
|
|
RtlInitializeBitMap( AdapterObject->MapRegisters,
|
|
(PULONG) (((PCHAR) (AdapterObject->MapRegisters)) + sizeof( RTL_BITMAP )),
|
|
( MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE )
|
|
);
|
|
//
|
|
// Set all the bits in the memory to indicate that memory
|
|
// has not been allocated for the map buffers
|
|
//
|
|
|
|
RtlSetAllBits( AdapterObject->MapRegisters );
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
AdapterObject->CommittedMapRegisters = 0;
|
|
|
|
//
|
|
// ALlocate the memory map registers.
|
|
//
|
|
|
|
AdapterObject->MapRegisterBase = ExAllocatePool(
|
|
NonPagedPool,
|
|
(MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) *
|
|
sizeof(TRANSLATION_ENTRY)
|
|
);
|
|
|
|
if (AdapterObject->MapRegisterBase == NULL) {
|
|
|
|
ObDereferenceObject( AdapterObject );
|
|
AdapterObject = NULL;
|
|
return(NULL);
|
|
|
|
}
|
|
|
|
//
|
|
// Zero the map registers.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
AdapterObject->MapRegisterBase,
|
|
(MAXIMUM_MAP_BUFFER_SIZE / PAGE_SIZE) *
|
|
sizeof(TRANSLATION_ENTRY)
|
|
);
|
|
|
|
if (!HalpGrowMapBuffers(AdapterObject, INITIAL_MAP_BUFFER_SMALL_SIZE))
|
|
{
|
|
|
|
//
|
|
// If no map registers could be allocated then free the
|
|
// object.
|
|
//
|
|
|
|
ObDereferenceObject( AdapterObject );
|
|
AdapterObject = NULL;
|
|
return(NULL);
|
|
|
|
}
|
|
}
|
|
|
|
} 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;
|
|
|
|
}
|
|
|
|
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;
|
|
PWAIT_CONTEXT_BLOCK Wcb;
|
|
PADAPTER_OBJECT MasterAdapter;
|
|
BOOLEAN Busy = FALSE;
|
|
IO_ALLOCATION_ACTION Action;
|
|
KIRQL Irql;
|
|
LONG MapRegisterNumber;
|
|
|
|
//
|
|
// Begin by getting the address of the master adapter.
|
|
//
|
|
|
|
MasterAdapter = AdapterObject->MasterAdapter;
|
|
|
|
//
|
|
// 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;
|
|
|
|
//
|
|
// 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 &&
|
|
AdapterObject->MasterAdapter != NULL) {
|
|
|
|
//
|
|
// Lock the map register bit map and the adapter queue in the
|
|
// master adapter object. The channel structure offset is used as
|
|
// a hint for the register search.
|
|
//
|
|
|
|
KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql );
|
|
|
|
MapRegisterNumber = -1;
|
|
|
|
if (IsListEmpty( &MasterAdapter->AdapterQueue)) {
|
|
MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters,
|
|
Wcb->NumberOfMapRegisters,
|
|
0
|
|
);
|
|
}
|
|
if (MapRegisterNumber == -1) {
|
|
|
|
//
|
|
// There were not enough free map registers. Queue this request
|
|
// on the master adapter where is will wait until some registers
|
|
// are deallocated.
|
|
//
|
|
|
|
InsertTailList( &MasterAdapter->AdapterQueue,
|
|
&AdapterObject->AdapterQueue
|
|
);
|
|
Busy = TRUE;
|
|
|
|
} else {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY)
|
|
MasterAdapter->MapRegisterBase + MapRegisterNumber);
|
|
|
|
//
|
|
// Set the no scatter/gather flag if scatter/gather not
|
|
// supported.
|
|
//
|
|
|
|
if (!AdapterObject->ScatterGather) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER);
|
|
|
|
}
|
|
|
|
if (AdapterObject->EisaAdapter) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | EISA_ADAPTER);
|
|
|
|
}
|
|
}
|
|
|
|
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
|
|
|
|
} else {
|
|
|
|
AdapterObject->MapRegisterBase = NULL;
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
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.
|
|
//
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
--+*/
|
|
{
|
|
PADAPTER_OBJECT MasterAdapter;
|
|
LONG MapRegisterNumber;
|
|
PWAIT_CONTEXT_BLOCK Wcb;
|
|
PLIST_ENTRY Packet;
|
|
IO_ALLOCATION_ACTION Action;
|
|
KIRQL Irql;
|
|
|
|
|
|
//
|
|
// Begin by getting the address of the master adapter.
|
|
//
|
|
|
|
if (AdapterObject->MasterAdapter != NULL && MapRegisterBase != NULL) {
|
|
|
|
MasterAdapter = AdapterObject->MasterAdapter;
|
|
|
|
} else {
|
|
|
|
//
|
|
// There are no map registers to return.
|
|
//
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Strip no scatter/gather flag.
|
|
//
|
|
|
|
MapRegisterBase = (PVOID) ((ULONG) MapRegisterBase &
|
|
~(NO_SCATTER_GATHER | EISA_ADAPTER));
|
|
|
|
MapRegisterNumber = (PTRANSLATION_ENTRY) MapRegisterBase -
|
|
(PTRANSLATION_ENTRY) MasterAdapter->MapRegisterBase;
|
|
|
|
//
|
|
// Acquire the master adapter spinlock which locks the adapter queue and the
|
|
// bit map for the map registers.
|
|
//
|
|
|
|
KeAcquireSpinLock(&MasterAdapter->SpinLock, &Irql);
|
|
|
|
//
|
|
// Return the registers to the bit map.
|
|
//
|
|
|
|
RtlClearBits( MasterAdapter->MapRegisters,
|
|
MapRegisterNumber,
|
|
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(&MasterAdapter->AdapterQueue) ){
|
|
break;
|
|
}
|
|
|
|
Packet = RemoveHeadList( &MasterAdapter->AdapterQueue );
|
|
AdapterObject = CONTAINING_RECORD( Packet,
|
|
ADAPTER_OBJECT,
|
|
AdapterQueue
|
|
);
|
|
Wcb = AdapterObject->CurrentWcb;
|
|
|
|
//
|
|
// Attempt to allocate map registers for this request. Use the previous
|
|
// register base as a hint.
|
|
//
|
|
|
|
MapRegisterNumber = RtlFindClearBitsAndSet( MasterAdapter->MapRegisters,
|
|
AdapterObject->NumberOfMapRegisters,
|
|
MasterAdapter->NumberOfMapRegisters
|
|
);
|
|
|
|
if (MapRegisterNumber == -1) {
|
|
|
|
//
|
|
// There were not enough free map registers. Put this request back on
|
|
// the adapter queue where is came from.
|
|
//
|
|
|
|
InsertHeadList( &MasterAdapter->AdapterQueue,
|
|
&AdapterObject->AdapterQueue
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID) ((PTRANSLATION_ENTRY)
|
|
MasterAdapter->MapRegisterBase + MapRegisterNumber);
|
|
|
|
//
|
|
// Set the no scatter/gather flag if scatter/gather not
|
|
// supported.
|
|
//
|
|
|
|
if (!AdapterObject->ScatterGather) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | NO_SCATTER_GATHER);
|
|
|
|
}
|
|
|
|
if (AdapterObject->EisaAdapter) {
|
|
|
|
AdapterObject->MapRegisterBase = (PVOID)
|
|
((ULONG) AdapterObject->MapRegisterBase | EISA_ADAPTER);
|
|
|
|
}
|
|
//
|
|
// 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 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 master 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( &MasterAdapter->SpinLock, &Irql );
|
|
|
|
RtlClearBits( MasterAdapter->MapRegisters,
|
|
MapRegisterNumber,
|
|
AdapterObject->NumberOfMapRegisters
|
|
);
|
|
|
|
AdapterObject->NumberOfMapRegisters = 0;
|
|
|
|
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
|
|
}
|
|
|
|
IoFreeAdapterChannel( AdapterObject );
|
|
}
|
|
|
|
KeAcquireSpinLock( &MasterAdapter->SpinLock, &Irql );
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
|
|
}
|
|
|
|
BOOLEAN
|
|
HalpCreateDmaStructures (
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the structures necessary for DMA operations
|
|
and connects the intermediate interrupt dispatcher.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
If the second level interrupt dispatcher is connected, then a value of
|
|
TRUE is returned. Otherwise, a value of FALSE is returned.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
|
|
//
|
|
// Init the Eisa interrupts
|
|
//
|
|
|
|
return HalpCreateEisaStructures ();
|
|
}
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOLEAN useBuffer;
|
|
ULONG transferLength;
|
|
ULONG logicalAddress;
|
|
PHYSICAL_ADDRESS returnAddress;
|
|
ULONG index;
|
|
PULONG pageFrame;
|
|
PUCHAR bytePointer;
|
|
UCHAR adapterMode;
|
|
UCHAR dataByte;
|
|
PTRANSLATION_ENTRY translationEntry;
|
|
ULONG pageOffset;
|
|
KIRQL Irql;
|
|
BOOLEAN specialMemory;
|
|
|
|
pageOffset = BYTE_OFFSET(CurrentVa);
|
|
|
|
if ( MapRegisterBase != NULL ) {
|
|
specialMemory = HalpAnySpecialMemory( Mdl, *Length, pageOffset);
|
|
} else {
|
|
specialMemory = FALSE;
|
|
}
|
|
|
|
//
|
|
// Calculate how much of the transfer is contiguous.
|
|
//
|
|
|
|
transferLength = PAGE_SIZE - pageOffset;
|
|
pageFrame = (PULONG)(Mdl+1);
|
|
pageFrame += ((ULONG) CurrentVa - (ULONG) Mdl->StartVa) >> PAGE_SHIFT;
|
|
logicalAddress = (*pageFrame << PAGE_SHIFT) + pageOffset;
|
|
|
|
//
|
|
// If the buffer is contigous and does not cross a 64 K bountry then
|
|
// just extend the buffer. The 64 K bountry restriction does not apply
|
|
// to Eisa systems.
|
|
//
|
|
//
|
|
|
|
if ( !specialMemory ) {
|
|
if (HalpEisaDma) {
|
|
|
|
while( transferLength < *Length ) {
|
|
|
|
if (*pageFrame + 1 != *(pageFrame + 1)) {
|
|
break;
|
|
}
|
|
|
|
transferLength += PAGE_SIZE;
|
|
pageFrame++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
while( transferLength < *Length ) {
|
|
|
|
if (*pageFrame + 1 != *(pageFrame + 1) ||
|
|
(*pageFrame & ~0x07) != (*(pageFrame + 1) & ~0x07)) {
|
|
break;
|
|
}
|
|
|
|
transferLength += PAGE_SIZE;
|
|
pageFrame++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Limit the transferLength to the requested Length.
|
|
//
|
|
|
|
transferLength = transferLength > *Length ? *Length : transferLength;
|
|
|
|
}
|
|
|
|
//
|
|
// Determine if the data transfer needs to use the map buffer.
|
|
//
|
|
|
|
if (MapRegisterBase != NULL) {
|
|
|
|
//
|
|
// Strip no scatter/gather flag.
|
|
//
|
|
|
|
translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase &
|
|
~(NO_SCATTER_GATHER | EISA_ADAPTER));
|
|
|
|
|
|
if ( specialMemory ) {
|
|
|
|
logicalAddress = translationEntry->PhysicalAddress + pageOffset;
|
|
translationEntry->Index = HOLE_BUFFER;
|
|
transferLength = *Length;
|
|
|
|
//
|
|
// Copy the data.
|
|
//
|
|
|
|
if (WriteToDevice) {
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry,
|
|
CurrentVa,
|
|
*Length,
|
|
WriteToDevice
|
|
);
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
if (((ULONG) MapRegisterBase & NO_SCATTER_GATHER) ||
|
|
!((ULONG)MapRegisterBase & EISA_ADAPTER)) {
|
|
|
|
if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER
|
|
&& transferLength < *Length) {
|
|
|
|
logicalAddress = translationEntry->PhysicalAddress + pageOffset;
|
|
translationEntry->Index = COPY_BUFFER;
|
|
index = 0;
|
|
transferLength = *Length;
|
|
useBuffer = TRUE;
|
|
|
|
} else {
|
|
|
|
useBuffer = FALSE;
|
|
index = translationEntry->Index;
|
|
translationEntry->Index += ADDRESS_AND_SIZE_TO_SPAN_PAGES(
|
|
CurrentVa,
|
|
transferLength
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// For devices with no scatter/gather or non-Eisa devices:
|
|
// It must require memory to be at less than 16 MB. If the logical
|
|
// address is greater than 16MB then map registers must be used.
|
|
//
|
|
|
|
if (logicalAddress+transferLength > MAXIMUM_ISA_PHYSICAL_ADDRESS) {
|
|
|
|
logicalAddress = (translationEntry + index)->PhysicalAddress
|
|
+ pageOffset;
|
|
useBuffer = TRUE;
|
|
|
|
if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER) {
|
|
|
|
translationEntry->Index = COPY_BUFFER;
|
|
index = 0;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the data if necessary.
|
|
//
|
|
|
|
if (useBuffer && WriteToDevice) {
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry + index,
|
|
CurrentVa,
|
|
*Length,
|
|
WriteToDevice
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
translationEntry->Index = SKIP_BUFFER;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Return the length.
|
|
//
|
|
|
|
*Length = transferLength;
|
|
|
|
//
|
|
// We only support 32 bits, but the return is 64. Just
|
|
// zero extend
|
|
//
|
|
|
|
returnAddress.LowPart = logicalAddress;
|
|
returnAddress.HighPart = 0;
|
|
|
|
//
|
|
// If no adapter was specificed then there is no more work to do so
|
|
// return.
|
|
//
|
|
|
|
if (AdapterObject == NULL || AdapterObject->MasterDevice) {
|
|
return(returnAddress);
|
|
}
|
|
|
|
//
|
|
// Determine the mode based on the transfer direction.
|
|
//
|
|
|
|
adapterMode = AdapterObject->AdapterMode;
|
|
((PDMA_EISA_MODE) &adapterMode)->TransferType = (UCHAR) (WriteToDevice ?
|
|
WRITE_TRANSFER : READ_TRANSFER);
|
|
|
|
bytePointer = (PUCHAR) &logicalAddress;
|
|
|
|
if (AdapterObject->Width16Bits) {
|
|
|
|
//
|
|
// If this is a 16 bit transfer then adjust the length and the address
|
|
// for the 16 bit DMA mode.
|
|
//
|
|
|
|
transferLength >>= 1;
|
|
|
|
//
|
|
// In 16 bit DMA mode the low 16 bits are shifted right one and the
|
|
// page register value is unchanged. So save the page register value
|
|
// and shift the logical address then restore the page value.
|
|
//
|
|
|
|
dataByte = bytePointer[2];
|
|
logicalAddress >>= 1;
|
|
bytePointer[2] = dataByte;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// grab the spinlock for the system DMA controller
|
|
//
|
|
|
|
KeAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock, &Irql );
|
|
|
|
//
|
|
// Determine the controller number based on the Adapter number.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
bytePointer[0]
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
bytePointer[1]
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageLowPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
bytePointer[2]
|
|
);
|
|
|
|
if (HalpEisaDma) {
|
|
|
|
//
|
|
// Write the high page register with zero value. This enable a special mode
|
|
// which allows ties the page register and base count into a single 24 bit
|
|
// address register.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// Notify DMA chip of the length to transfer.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((transferLength - 1) & 0xff)
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((transferLength - 1) >> 8)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the DMA chip to read or write mode; and unmask it.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 2
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->Mode, adapterMode );
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
bytePointer[0]
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseAddress,
|
|
bytePointer[1]
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageLowPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
bytePointer[2]
|
|
);
|
|
|
|
if (HalpEisaDma) {
|
|
|
|
//
|
|
// Write the high page register with zero value. This enable a special mode
|
|
// which allows ties the page register and base count into a single 24 bit
|
|
// address register.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
((PUCHAR) &((PEISA_CONTROL) HalpEisaControlBase)->DmaPageHighPort) +
|
|
(ULONG)AdapterObject->PagePort,
|
|
0
|
|
);
|
|
}
|
|
|
|
//
|
|
// Notify DMA chip of the length to transfer.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((transferLength - 1) & 0xff)
|
|
);
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount,
|
|
(UCHAR) ((transferLength - 1) >> 8)
|
|
);
|
|
|
|
|
|
//
|
|
// Set the DMA chip to read or write mode; and unmask it.
|
|
//
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_CLEARMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
KeReleaseSpinLock (&AdapterObject->MasterAdapter->SpinLock, Irql);
|
|
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. For the Jensen system
|
|
its 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.
|
|
|
|
--*/
|
|
{
|
|
|
|
PTRANSLATION_ENTRY translationEntry;
|
|
PULONG pageFrame;
|
|
ULONG transferLength;
|
|
ULONG partialLength;
|
|
BOOLEAN masterDevice;
|
|
ULONG index;
|
|
|
|
masterDevice = AdapterObject == NULL || AdapterObject->MasterDevice ?
|
|
TRUE : FALSE;
|
|
|
|
//
|
|
// If this is a slave device, then stop the DMA controller.
|
|
//
|
|
|
|
if (!masterDevice) {
|
|
|
|
//
|
|
// Mask the DMA request line so that DMA requests cannot occur.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 2
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR(
|
|
&dmaControl->SingleMask,
|
|
(UCHAR) (DMA_SETMASK | AdapterObject->ChannelNumber)
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (MapRegisterBase == NULL) {
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// Determine if the data needs to be copied to the orginal buffer.
|
|
// This only occurs if the data tranfer is from the device, the
|
|
// MapReisterBase is not NULL and the transfer spans a page.
|
|
//
|
|
|
|
//
|
|
// Strip no scatter/gather flag.
|
|
//
|
|
|
|
translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase &
|
|
~(NO_SCATTER_GATHER | EISA_ADAPTER));
|
|
|
|
if ( translationEntry->Index == SKIP_BUFFER ) {
|
|
translationEntry->Index = 0;
|
|
return(TRUE);
|
|
}
|
|
|
|
if (!WriteToDevice) {
|
|
|
|
//
|
|
// If this is not a master device, then just transfer the buffer.
|
|
//
|
|
|
|
if (translationEntry->Index == HOLE_BUFFER) {
|
|
|
|
if (!masterDevice) {
|
|
|
|
//
|
|
// Copy only the bytes that have actually been transfered.
|
|
//
|
|
|
|
Length -= HalReadDmaCounter(AdapterObject);
|
|
|
|
}
|
|
|
|
//
|
|
// The adapter does not support scatter/gather copy the buffer.
|
|
//
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry,
|
|
CurrentVa,
|
|
Length,
|
|
WriteToDevice
|
|
);
|
|
|
|
} else if ((ULONG) MapRegisterBase & NO_SCATTER_GATHER) {
|
|
|
|
if (translationEntry->Index == COPY_BUFFER) {
|
|
|
|
if (!masterDevice) {
|
|
|
|
//
|
|
// Copy only the bytes that have actually been transfered.
|
|
//
|
|
|
|
Length -= HalReadDmaCounter(AdapterObject);
|
|
|
|
}
|
|
|
|
//
|
|
// The adapter does not support scatter/gather copy the buffer.
|
|
//
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry,
|
|
CurrentVa,
|
|
Length,
|
|
WriteToDevice
|
|
);
|
|
}
|
|
|
|
} else if (!((ULONG) MapRegisterBase & NO_SCATTER_GATHER) &&
|
|
!((ULONG) MapRegisterBase & EISA_ADAPTER)) {
|
|
|
|
//
|
|
// Cycle through the pages of the transfer to determine if there
|
|
// are any which need to be copied back.
|
|
//
|
|
|
|
transferLength = PAGE_SIZE - BYTE_OFFSET(CurrentVa);
|
|
partialLength = transferLength;
|
|
pageFrame = (PULONG)(Mdl+1);
|
|
pageFrame += ((ULONG) CurrentVa - (ULONG) Mdl->StartVa) >> PAGE_SHIFT;
|
|
|
|
while( transferLength <= Length ){
|
|
|
|
if (*pageFrame >= BYTES_TO_PAGES(MAXIMUM_ISA_PHYSICAL_ADDRESS)) {
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry,
|
|
CurrentVa,
|
|
partialLength,
|
|
WriteToDevice
|
|
);
|
|
|
|
}
|
|
|
|
(PCCHAR) CurrentVa += partialLength;
|
|
partialLength = PAGE_SIZE;
|
|
|
|
//
|
|
// Note that transferLength indicates the amount which will
|
|
// be transfered after the next loop; thus, it is updated
|
|
// with the new partial length.
|
|
//
|
|
|
|
transferLength += partialLength;
|
|
pageFrame++;
|
|
translationEntry++;
|
|
}
|
|
|
|
//
|
|
// Process any remaining residue.
|
|
//
|
|
|
|
partialLength = Length - transferLength + partialLength;
|
|
if (partialLength && (*pageFrame >= BYTES_TO_PAGES(MAXIMUM_ISA_PHYSICAL_ADDRESS))) {
|
|
|
|
HalpCopyBufferMap(
|
|
Mdl,
|
|
translationEntry,
|
|
CurrentVa,
|
|
partialLength,
|
|
WriteToDevice
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Strip no scatter/gather flag.
|
|
//
|
|
|
|
translationEntry = (PTRANSLATION_ENTRY) ((ULONG) MapRegisterBase &
|
|
~(NO_SCATTER_GATHER | EISA_ADAPTER));
|
|
|
|
//
|
|
// Clear index in map register.
|
|
//
|
|
|
|
translationEntry->Index = 0;
|
|
|
|
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);
|
|
}
|
|
|
|
ULONG
|
|
HalpGetEisaData (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN ULONG SlotNumber,
|
|
IN PVOID Buffer,
|
|
IN ULONG Offset,
|
|
IN ULONG Length
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The function returns the Eisa bus data for a slot or address.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Registered BUSHANDLER for the target configuration space
|
|
|
|
RootHandler - Registered BUSHANDLER for the orginating HalGetBusData
|
|
request.
|
|
|
|
Buffer - Supplies the space to store the data.
|
|
|
|
Length - Supplies a count in bytes of the maximum amount to return.
|
|
|
|
Return Value:
|
|
|
|
Returns the amount of data stored into the buffer.
|
|
|
|
--*/
|
|
|
|
{
|
|
OBJECT_ATTRIBUTES ObjectAttributes;
|
|
OBJECT_ATTRIBUTES BusObjectAttributes;
|
|
PWSTR EisaPath = L"\\Registry\\Machine\\Hardware\\Description\\System\\EisaAdapter";
|
|
PWSTR ConfigData = L"Configuration Data";
|
|
ANSI_STRING TmpString;
|
|
ULONG BusNumber;
|
|
UCHAR BusString[] = "00";
|
|
UNICODE_STRING RootName, BusName;
|
|
UNICODE_STRING ConfigDataName;
|
|
NTSTATUS NtStatus;
|
|
PKEY_VALUE_FULL_INFORMATION ValueInformation;
|
|
PCM_FULL_RESOURCE_DESCRIPTOR Descriptor;
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResource;
|
|
PCM_EISA_SLOT_INFORMATION SlotInformation;
|
|
ULONG PartialCount;
|
|
ULONG TotalDataSize, SlotDataSize;
|
|
HANDLE EisaHandle, BusHandle;
|
|
ULONG BytesWritten, BytesNeeded;
|
|
PUCHAR KeyValueBuffer;
|
|
ULONG i;
|
|
ULONG DataLength = 0;
|
|
PUCHAR DataBuffer = Buffer;
|
|
BOOLEAN Found = FALSE;
|
|
|
|
|
|
UNREFERENCED_PARAMETER( RootHandler );
|
|
|
|
RtlInitUnicodeString(
|
|
&RootName,
|
|
EisaPath
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&RootName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
(HANDLE)NULL,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the EISA root
|
|
//
|
|
|
|
NtStatus = ZwOpenKey(
|
|
&EisaHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
#if DBG
|
|
DbgPrint("HAL: Open Status = %x\n",NtStatus);
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// Init bus number path
|
|
//
|
|
|
|
BusNumber = BusHandler->BusNumber;
|
|
|
|
if (BusNumber > 99) {
|
|
return (0);
|
|
}
|
|
|
|
if (BusNumber > 9) {
|
|
BusString[0] += (UCHAR) (BusNumber/10);
|
|
BusString[1] += (UCHAR) (BusNumber % 10);
|
|
} else {
|
|
BusString[0] += (UCHAR) BusNumber;
|
|
BusString[1] = '\0';
|
|
}
|
|
|
|
RtlInitAnsiString(
|
|
&TmpString,
|
|
BusString
|
|
);
|
|
|
|
RtlAnsiStringToUnicodeString(
|
|
&BusName,
|
|
&TmpString,
|
|
TRUE
|
|
);
|
|
|
|
|
|
InitializeObjectAttributes(
|
|
&BusObjectAttributes,
|
|
&BusName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
(HANDLE)EisaHandle,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Open the EISA root + Bus Number
|
|
//
|
|
|
|
NtStatus = ZwOpenKey(
|
|
&BusHandle,
|
|
KEY_READ,
|
|
&BusObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
#if DBG
|
|
DbgPrint("HAL: Opening Bus Number: Status = %x\n",NtStatus);
|
|
#endif
|
|
return(0);
|
|
}
|
|
|
|
//
|
|
// opening the configuration data. This first call tells us how
|
|
// much memory we need to allocate
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&ConfigDataName,
|
|
ConfigData
|
|
);
|
|
|
|
//
|
|
// This should fail. We need to make this call so we can
|
|
// get the actual size of the buffer to allocate.
|
|
//
|
|
|
|
ValueInformation = (PKEY_VALUE_FULL_INFORMATION) &i;
|
|
|
|
NtStatus = ZwQueryValueKey(
|
|
BusHandle,
|
|
&ConfigDataName,
|
|
KeyValueFullInformation,
|
|
ValueInformation,
|
|
0,
|
|
&BytesNeeded
|
|
);
|
|
|
|
KeyValueBuffer = ExAllocatePool(
|
|
NonPagedPool,
|
|
BytesNeeded
|
|
);
|
|
|
|
if (KeyValueBuffer == NULL) {
|
|
#if DBG
|
|
DbgPrint("HAL: Cannot allocate Key Value Buffer\n");
|
|
#endif
|
|
ZwClose(BusHandle);
|
|
return(0);
|
|
}
|
|
|
|
ValueInformation = (PKEY_VALUE_FULL_INFORMATION)KeyValueBuffer;
|
|
|
|
NtStatus = ZwQueryValueKey(
|
|
BusHandle,
|
|
&ConfigDataName,
|
|
KeyValueFullInformation,
|
|
ValueInformation,
|
|
BytesNeeded,
|
|
&BytesWritten
|
|
);
|
|
|
|
|
|
ZwClose(BusHandle);
|
|
|
|
if (!NT_SUCCESS(NtStatus) || ValueInformation->DataLength == 0) {
|
|
#if DBG
|
|
DbgPrint("HAL: Query Config Data: Status = %x\n",NtStatus);
|
|
#endif
|
|
ExFreePool(KeyValueBuffer);
|
|
return(0);
|
|
}
|
|
|
|
|
|
//
|
|
// We get back a Full Resource Descriptor List
|
|
//
|
|
|
|
Descriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)ValueInformation +
|
|
ValueInformation->DataOffset);
|
|
|
|
PartialResource = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)
|
|
&(Descriptor->PartialResourceList.PartialDescriptors);
|
|
PartialCount = Descriptor->PartialResourceList.Count;
|
|
|
|
for (i = 0; i < PartialCount; i++) {
|
|
|
|
//
|
|
// Do each partial Resource
|
|
//
|
|
|
|
switch (PartialResource->Type) {
|
|
case CmResourceTypeNull:
|
|
case CmResourceTypePort:
|
|
case CmResourceTypeInterrupt:
|
|
case CmResourceTypeMemory:
|
|
case CmResourceTypeDma:
|
|
|
|
//
|
|
// We dont care about these.
|
|
//
|
|
|
|
PartialResource++;
|
|
|
|
break;
|
|
|
|
case CmResourceTypeDeviceSpecific:
|
|
|
|
//
|
|
// Bingo!
|
|
//
|
|
|
|
TotalDataSize = PartialResource->u.DeviceSpecificData.DataSize;
|
|
|
|
SlotInformation = (PCM_EISA_SLOT_INFORMATION)
|
|
((PUCHAR)PartialResource +
|
|
sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
|
|
|
|
while (((LONG)TotalDataSize) > 0) {
|
|
|
|
if (SlotInformation->ReturnCode == EISA_EMPTY_SLOT) {
|
|
|
|
SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION);
|
|
|
|
} else {
|
|
|
|
SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION) +
|
|
SlotInformation->NumberFunctions *
|
|
sizeof(CM_EISA_FUNCTION_INFORMATION);
|
|
}
|
|
|
|
if (SlotDataSize > TotalDataSize) {
|
|
|
|
//
|
|
// Something is wrong again
|
|
//
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
return(0);
|
|
|
|
}
|
|
|
|
if (SlotNumber != 0) {
|
|
|
|
SlotNumber--;
|
|
|
|
SlotInformation = (PCM_EISA_SLOT_INFORMATION)
|
|
((PUCHAR)SlotInformation + SlotDataSize);
|
|
|
|
TotalDataSize -= SlotDataSize;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
//
|
|
// This is our slot
|
|
//
|
|
|
|
Found = TRUE;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// End loop
|
|
//
|
|
|
|
i = PartialCount;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
#if DBG
|
|
DbgPrint("Bad Data in registry!\n");
|
|
#endif
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
return(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (Found) {
|
|
|
|
i = Length + Offset;
|
|
if (i > SlotDataSize) {
|
|
i = SlotDataSize;
|
|
}
|
|
|
|
DataLength = i - Offset;
|
|
RtlMoveMemory (Buffer, ((PUCHAR)SlotInformation + Offset), DataLength);
|
|
|
|
|
|
}
|
|
|
|
ExFreePool(KeyValueBuffer);
|
|
return DataLength;
|
|
}
|
|
|
|
|
|
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.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG count;
|
|
ULONG high;
|
|
KIRQL Irql;
|
|
|
|
//
|
|
// Grab the spinlock for the system DMA controller.
|
|
//
|
|
|
|
KeAcquireSpinLock( &AdapterObject->MasterAdapter->SpinLock, &Irql );
|
|
|
|
//
|
|
// Determine the controller number based on the Adapter number.
|
|
//
|
|
|
|
if (AdapterObject->AdapterNumber == 1) {
|
|
|
|
//
|
|
// This request is for DMA controller 1
|
|
//
|
|
|
|
PDMA1_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
|
|
//
|
|
// Initialize count to a value which will not match.
|
|
//
|
|
|
|
count = 0xFFFF00;
|
|
|
|
//
|
|
// Loop until the same high byte is read twice.
|
|
//
|
|
|
|
do {
|
|
|
|
high = count;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
//
|
|
// Read the current DMA count.
|
|
//
|
|
|
|
count = READ_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
);
|
|
|
|
count |= READ_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
) << 8;
|
|
|
|
} while ((count & 0xFFFF00) != (high & 0xFFFF00));
|
|
|
|
} else {
|
|
|
|
//
|
|
// This request is for DMA controller 2
|
|
//
|
|
|
|
PDMA2_CONTROL dmaControl;
|
|
|
|
dmaControl = AdapterObject->AdapterBaseVa;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
//
|
|
// Initialize count to a value which will not match.
|
|
//
|
|
|
|
count = 0xFFFF00;
|
|
|
|
//
|
|
// Loop until the same high byte is read twice.
|
|
//
|
|
|
|
do {
|
|
|
|
high = count;
|
|
|
|
WRITE_PORT_UCHAR( &dmaControl->ClearBytePointer, 0 );
|
|
|
|
//
|
|
// Read the current DMA count.
|
|
//
|
|
|
|
count = READ_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
);
|
|
|
|
count |= READ_PORT_UCHAR(
|
|
&dmaControl->DmaAddressCount[AdapterObject->ChannelNumber]
|
|
.DmaBaseCount
|
|
) << 8;
|
|
|
|
} while ((count & 0xFFFF00) != (high & 0xFFFF00));
|
|
|
|
|
|
}
|
|
|
|
//
|
|
// Release the spinlock for the system DMA controller.
|
|
//
|
|
|
|
KeReleaseSpinLock( &AdapterObject->MasterAdapter->SpinLock, Irql );
|
|
|
|
//
|
|
// The DMA counter has a bias of one and can only be 16 bit long.
|
|
//
|
|
|
|
count = (count + 1) & 0xFFFF;
|
|
|
|
//
|
|
// If this is a 16 bit dma the multiply the count by 2.
|
|
//
|
|
|
|
if (AdapterObject->Width16Bits) {
|
|
|
|
count *= 2;
|
|
|
|
}
|
|
|
|
return(count);
|
|
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpSpecialMemory(
|
|
IN ULONG PFN
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks if the supplied PFN is contained within a section
|
|
of special memory.
|
|
|
|
Arguments:
|
|
|
|
PFN - Page Frame Number of the page in question.
|
|
|
|
Return Value:
|
|
|
|
Returns TRUE if the specified page is part of special memory.
|
|
|
|
--*/
|
|
|
|
{
|
|
PMEMORY_REGION specialMemoryRegion = HalpSpecialRegions;
|
|
|
|
while ( specialMemoryRegion != NULL ) {
|
|
if ( PFN >= specialMemoryRegion->PfnBase &&
|
|
PFN < specialMemoryRegion->PfnBase + specialMemoryRegion->PfnCount ) {
|
|
return TRUE;
|
|
}
|
|
specialMemoryRegion = specialMemoryRegion->Next;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpInitializeSpecialMemory(
|
|
IN PLOADER_PARAMETER_BLOCK LoaderBlock
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function initializes the special memory regions on Jensen.
|
|
|
|
Arguments:
|
|
|
|
LoaderBlock - pointer to the Loader Parameter Block.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
PLIST_ENTRY NextMd;
|
|
PMEMORY_ALLOCATION_DESCRIPTOR Descriptor;
|
|
PMEMORY_REGION MemoryRegion;
|
|
|
|
//
|
|
// Initialize the fixed half-meg region, length is half-meg also.
|
|
//
|
|
|
|
HalpHalfMeg.PfnBase = HALF_MEGABYTE / PAGE_SIZE;
|
|
HalpHalfMeg.PfnCount = HALF_MEGABYTE / PAGE_SIZE;
|
|
|
|
//
|
|
// Link the half-meg region into the special regions list
|
|
//
|
|
|
|
HalpHalfMeg.Next = HalpSpecialRegions;
|
|
HalpSpecialRegions = &HalpHalfMeg;
|
|
|
|
return;
|
|
|
|
#if 0
|
|
//
|
|
// Scan through all memory descriptors looking for special memory regions
|
|
//
|
|
|
|
NextMd = LoaderBlock->MemoryDescriptorListHead.Flink;
|
|
|
|
while ( NextMd != &LoaderBlock->MemoryDescriptorListHead ) {
|
|
|
|
Descriptor = CONTAINING_RECORD(NextMd,
|
|
MEMORY_ALLOCATION_DESCRIPTOR,
|
|
ListEntry);
|
|
if ( Descriptor->MemoryType == LoaderSpecialMemory ) {
|
|
|
|
//
|
|
// Allocate pool and copy info into MemoryRegion and insert in list
|
|
//
|
|
|
|
if ( sizeof(MEMORY_REGION) > HalpMemoryRegionSize ) {
|
|
return;
|
|
}
|
|
|
|
HalpMemoryRegionSize -= sizeof(MEMORY_REGION);
|
|
MemoryRegion = (PMEMORY_REGION) HalpMemoryRegionBuffers;
|
|
HalpMemoryRegionBuffers = (PVOID) (MemoryRegion + 1);
|
|
|
|
MemoryRegion->PfnBase = Descriptor->BasePage;
|
|
MemoryRegion->PfnCount = Descriptor->PageCount;
|
|
MemoryRegion->Next = HalpSpecialRegions;
|
|
HalpSpecialRegions = MemoryRegion;
|
|
Descriptor->MemoryType = LoaderFree;
|
|
|
|
}
|
|
|
|
NextMd = NextMd->Flink;
|
|
}
|
|
|
|
return;
|
|
|
|
#endif // 0
|
|
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
HalpAnySpecialMemory(
|
|
IN PMDL Mdl,
|
|
IN ULONG Length,
|
|
IN ULONG Offset
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function checks an MDL to see if any pages contained in the MDL
|
|
are from 'special memory'.
|
|
|
|
Arguments:
|
|
|
|
Mdl - pointer to an MDL.
|
|
|
|
Length - length of requested transfer.
|
|
|
|
Offset - offset of first byte within the first page for this transfer.
|
|
|
|
Return Value:
|
|
|
|
Reutrns TRUE if any of the pages in the MDL are in 'special memory',
|
|
otherwise returns FALSE.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
PULONG pageFrame;
|
|
ULONG numRegs;
|
|
|
|
pageFrame = (PULONG)(Mdl + 1);
|
|
|
|
// Calculate number of PFNs to scan
|
|
|
|
numRegs = (Length + Offset - 1) >> PAGE_SHIFT;
|
|
|
|
for ( i = 0; i <= numRegs; i++ ) {
|
|
if ( HalpSpecialMemory(*pageFrame) ) {
|
|
return TRUE;
|
|
}
|
|
pageFrame++;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
HalpRegisterInternalBusHandlers (
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function registers the bushandlers for buses on the system
|
|
that will always be present on the system.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PBUS_HANDLER Bus;
|
|
|
|
//
|
|
// Initalize BusHandler data before registering any handlers
|
|
//
|
|
|
|
HalpInitBusHandler ();
|
|
|
|
//
|
|
// Build the processor internal bus 0
|
|
//
|
|
|
|
HaliRegisterBusHandler (ProcessorInternal, // Bus Type
|
|
-1, // No config space
|
|
0, // Bus Number
|
|
-1, // No parent bus type
|
|
0, // No parent bus number
|
|
0, // No extension data
|
|
NULL, // No install handler
|
|
&Bus); // Bushandler return
|
|
|
|
Bus->GetInterruptVector = HalpGetSystemInterruptVector;
|
|
|
|
//
|
|
// Build internal-bus 0, or system level bus
|
|
//
|
|
|
|
HaliRegisterBusHandler (Internal, // Bus Type
|
|
-1, // No config space
|
|
0, // Bus Number
|
|
-1, // No parent bus type
|
|
0, // No parent bus number
|
|
0, // No extension data
|
|
NULL, // No install handler
|
|
&Bus); // Bushandler return
|
|
|
|
Bus->GetInterruptVector = HalpGetSystemInterruptVector;
|
|
Bus->TranslateBusAddress = HalpTranslateSystemBusAddress;
|
|
|
|
//
|
|
// Build Isa/Eisa bus #0
|
|
//
|
|
|
|
HaliRegisterBusHandler (Eisa, // Bus Type
|
|
EisaConfiguration, // Config space type
|
|
0, // Internal bus #0
|
|
Internal, // Parent bus type
|
|
0, // Parent bus number
|
|
0, // No extension data
|
|
NULL, // No install handler
|
|
&Bus); // Bushandler return
|
|
|
|
Bus->GetBusData = HalpGetEisaData;
|
|
Bus->AdjustResourceList = HalpAdjustEisaResourceList;
|
|
|
|
HaliRegisterBusHandler (Isa, // Bus Type
|
|
-1, // No config space
|
|
0, // Internal bus #0
|
|
Eisa, // Parent bus type
|
|
0, // Parent bus number
|
|
0, // No extension data
|
|
NULL, // No install handler
|
|
&Bus); // Bushandler returne
|
|
|
|
Bus->GetBusData = HalpNoBusData;
|
|
Bus->AdjustResourceList = HalpAdjustIsaResourceList;
|
|
}
|
|
|
|
NTSTATUS
|
|
HalpAdjustIsaResourceList (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes the pResourceList and limits any requested resource to
|
|
it's corrisponding bus requirements.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Registered BUSHANDLER for the target configuration space
|
|
|
|
RootHandler - Register BUSHANDLER for the orginating HalAdjustResourceList request.
|
|
|
|
pResourceList - The resource list to adjust.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or error
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// BUGBUG: This function should verify that the resoruces fit
|
|
// the bus requirements - for now we will assume that the bus
|
|
// can support anything the device may ask for.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
HalpAdjustEisaResourceList (
|
|
IN PBUS_HANDLER BusHandler,
|
|
IN PBUS_HANDLER RootHandler,
|
|
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Takes the pResourceList and limits any requested resource to
|
|
it's corrisponding bus requirements.
|
|
|
|
Arguments:
|
|
|
|
BusHandler - Registered BUSHANDLER for the target configuration space
|
|
|
|
RootHandler - Register BUSHANDLER for the orginating HalAdjustResourceList request.
|
|
|
|
pResourceList - The resource list to adjust.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS or error
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// BUGBUG: This function should verify that the resoruces fit
|
|
// the bus requirements - for now we will assume that the bus
|
|
// can support anything the device may ask for.
|
|
//
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
#endif
|