NT4/private/ntos/nthals/halsgi/mips/sxhwsup.c
2020-09-30 17:12:29 +02:00

2388 lines
64 KiB
C
Raw Blame History

This file contains invisible Unicode characters

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

/*++
Copyright (c) 1990 Microsoft Corporation
Copyright (c) 1992 Silicon Graphics, Inc.
Module Name:
s3hwsup.c
Abstract:
This module contains the HalpXxx routines for the NT I/O system that
are hardware dependent.
Author:
Jeff Havens (jhavens ) 14-Feb-1990
Tom Bonola (o-tomb ) 28-Aug-1991
Kevin Meier (o-kevinm ) 14-Jan-1992
Environment:
Kernel mode, local to I/O system
Revision History:
--*/
#include "halp.h"
#include "stdio.h"
#define ENABLE_FIFOFULL
#undef DMADEBUG
#ifdef DMADEBUG
ULONG Hal2Debug = 1;
#endif // DMADEBUG
#undef IOMAPDEBUG
#ifdef IOMAPDEBUG
ULONG HalDebug = 0;
ULONG Hal3Debug = 1;
ULONG Hal4Debug = 1;
ULONG Hal5Debug = 0;
ULONG Hal6Debug = 1;
ULONG HalIoMapped = 0;
#endif // IOMAPDEBUG
//
// Define adapter object structure used for DMA transfers
// on SGI MIPS machines.
//
typedef struct _ADAPTER_OBJECT {
CSHORT Type;
CSHORT Size;
struct _ADAPTER_OBJECT *MasterAdapter;
ULONG MapRegistersPerChannel;
PVOID MapRegisterBase;
ULONG NumberOfMapRegisters;
struct _WAIT_CONTEXT_BLOCK *CurrentDevice;
KDEVICE_QUEUE ChannelWaitQueue;
LIST_ENTRY AdapterQueue;
KSPIN_LOCK SpinLock;
PRTL_BITMAP MapRegisters; // bit map used to keep track of free map regs
CHAR ChannelNumber; // (represents channel owner (SCSI, ENET, or IDE)
UCHAR AdapterNumber;
} ADAPTER_OBJECT;
//
// Define translation table entry structure.
//
extern POBJECT_TYPE IoAdapterObjectType;
//
// The DMA controller has a larger number of map registers which may
// be used by any adapter channel. 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.
//
PADAPTER_OBJECT MasterAdapterObject;
//
// The following are interrupt objects used for the 2nd level
// interrupt dispatch routines.
//
KINTERRUPT HalpLoc0Interrupt;
KINTERRUPT HalpLoc1Interrupt;
//
// The following spinlocks are used for the 2nd level interrupt
// dispatch routines.
//
KSPIN_LOCK HalpLoc0Spinlock;
KSPIN_LOCK HalpLoc1Spinlock;
//
// The following is an array of adapter object structures for the internal DMA
// channels.
//
PADAPTER_OBJECT HalpInternalAdapters[SGI_MAX_DMA_CHANNELS];
PADAPTER_OBJECT
HalpAllocateAdapter(
IN ULONG MapRegistersPerChannel,
IN ULONG AdapterChannel,
IN PVOID MapRegisterBase
);
PADAPTER_OBJECT
HalpAllocateMasterAdapter(
VOID
);
//
// This priority based index table contains offsets into
// the IDT for a device ISR to execute. This table corresponds
// to a left-to-right bit priority in the LOCAL0 register.
//
// The table works in the following way...
//
// The LOCAL0 masked status register is read.
// The value read from the masked status register will index
// into this table to obtain the offset into the IDT.
//
// Note the following example based on the table below...
//
// Assume the LOCAL0 masked status register has the value 9Ch.
// This bit pattern, 10011100, shows that the following interrupts
// are pending (from left to right):
//
// - VME0
// - Graphics DMA
// - Ethernet
// - SCSI
//
// Since we have established a left-to-right prioritization scheme,
// the VME0 interrupt will be vectored to based on our table
// below. Entry 9Ch corresponds to bit 7 (from FFINTR).
// Entry 7 in this table provides the interrupt vector for
// this interrupt.
//
UCHAR HalpVector0[8] = {
SGI_VECTOR_GIO0FIFOFULL, // 0
SGI_VECTOR_IDEDMA, // 1
SGI_VECTOR_SCSI, // 2
SGI_VECTOR_ETHERNET, // 3
SGI_VECTOR_GRAPHICSDMA, // 4
SGI_VECTOR_SGIDUART, // 5
SGI_VECTOR_GIO1GE, // 6
SGI_VECTOR_VME0, // 7
};
//
// This priority based index table contains offsets into
// the IDT for a device ISR to execute. This table corresponds
// to a left-to-right bit priority in the LOCAL1 register.
//
// This table works in the same way as the LOCAL1 vector table.
//
UCHAR HalpVector1[8] = {
0, // 0 reserved
0, // 1 reserved
0, // 2 reserved
SGI_VECTOR_VME1, // 3
SGI_VECTOR_DSP, // 4
SGI_VECTOR_ACFAIL, // 5
SGI_VECTOR_VIDEOOPTION, // 6
SGI_VECTOR_GIO2VERTRET, // 7
};
// Find first bit set in local interrupt status register
//
static UCHAR fftab_hi[16] = {0, 4, 5,5, 6,6,6,6, 7,7,7,7,7,7,7,7};
static UCHAR fftab_lo[16] = {0, 0, 1,1, 2,2,2,2, 3,3,3,3,3,3,3,3};
#define FFINTR(m) (((m)>>4)?fftab_hi[m>>4]:fftab_lo[m&0xf])
//
// Define the context structure for use by the interrupt routine.
//
typedef VOID (*PSECONDARY_DISPATCH)(
PVOID InterruptRoutine
);
//
// Define the secondary interrupt dispatch routines.
//
BOOLEAN
HalpLoc0Dispatch(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
);
BOOLEAN
HalpLoc1Dispatch(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
);
PADAPTER_OBJECT
HalGetAdapter(
IN PDEVICE_DESCRIPTION DeviceDescription,
IN OUT PULONG NumberOfMapRegisters
)
/*++
Routine Description:
This function returns the appropriate adapter object for the device defined
in the device description structure. The only bus type supported for the
Indigo is Internal.
Arguments:
DeviceDescription - Supplies a description of the deivce.
NumberOfMapRegisters - Returns the maximum number of map registers which
may be allocated by the device driver.
Return Value:
A pointer to the requested adpater object or NULL if an adapter could not
be created.
--*/
{
//
// Make sure this is the correct version.
//
if (DeviceDescription->Version > DEVICE_DESCRIPTION_VERSION1) {
return( NULL );
}
//
// Create the master adapter object if it is not already.
//
if( MasterAdapterObject == NULL ) {
if( !(MasterAdapterObject = HalpAllocateMasterAdapter()) ) {
return( NULL );
}
}
//
// Set the maximum number of map registers if requested.
//
if (NumberOfMapRegisters != NULL) {
//
// Return half the total number of map registers per channel.
//
*NumberOfMapRegisters =
MasterAdapterObject->MapRegistersPerChannel / 2;
}
//
// Make sure the DMA access is for the HPC devices (internal).
// The GIO devices on this machine are SCSI, ENET, and IDE.
//
if (DeviceDescription->InterfaceType == Internal) {
//
// Make sure the DMA channel range is valid. Only use channels 0-2.
//
if (DeviceDescription->DmaChannel > SGI_MAX_DMA_CHANNELS) {
return( NULL );
}
//
// If the adapter has not been allocated yet, allocate the
// adapter for this channel and return the adapter object.
//
// CAVEAT: Originally HalpAllocateAdapter was written to
// specify the VA of the DMA channel as the second
// parameter. However, this is not going to be used
// for the SGI machine since SGI MIPS machines
// can potentially support a number of DMA devices
// than can have more than 1 virtual address for
// DMA control per channel (i.e. ENET on the GIO
// bus has separate VAs for XMIT and RECV on one
// DMA/HPC channel). IoMapTransfer will keep track
// of the virtual addresses for the required DMA
// channel request. Another field has been added
// to the ADAPTER_OBJECT structure to indicate what
// DMA channel is being requested.
//
if (HalpInternalAdapters[DeviceDescription->DmaChannel] == NULL) {
HalpInternalAdapters[DeviceDescription->DmaChannel] =
HalpAllocateAdapter(
0, // map registers/channel
DeviceDescription->DmaChannel, // DMA channel number
NULL // map register base
);
}
return(HalpInternalAdapters[DeviceDescription->DmaChannel]);
} else {
return( NULL ); // This bus type is not supported.
}
}
BOOLEAN
HalTranslateBusAddress(
IN INTERFACE_TYPE InterfaceType,
IN ULONG BusNumber,
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:
InterfaceType - Supplies the type of bus which the address is for.
BusNumber - Supplies the bus number for the device.
BusAddress - Supplies the bus relative address.
AddressSpace - Supplies the address space number for the device: 0 for
memory and 1 for I/O space. Returns the address space on this system.
TranslatedAddress - Supplies a pointer to return the translated address
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
--*/
{
TranslatedAddress->LowPart = BusAddress.LowPart;
TranslatedAddress->HighPart = 0;
return(TRUE);
}
PADAPTER_OBJECT
HalpAllocateAdapter(
IN ULONG MapRegistersPerChannel,
IN ULONG AdapterChannel,
IN PVOID MapRegisterBase
)
/*++
Routine Description:
This routine allocates and initializes an adapter object to represent an
adapter or a DMA controller on the system.
Arguments:
MapRegistersPerChannel - Unused.
AdapterChannel - The DMA channel for this adapter.
MapRegisterBase - Unused.
Return Value:
The function value is a pointer to the allocate adapter object.
--*/
{
PADAPTER_OBJECT AdapterObject;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG Size;
HANDLE Handle;
NTSTATUS Status;
//
// Begin by initializing the object attributes structure to be used when
// creating the adapter object.
//
InitializeObjectAttributes( &ObjectAttributes,
NULL,
OBJ_PERMANENT,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
//
// Determine the size of the adapter object.
//
Size = sizeof( ADAPTER_OBJECT );
//
// Now create the adapter object.
//
Status = ObCreateObject( KernelMode,
*((POBJECT_TYPE *)IoAdapterObjectType),
&ObjectAttributes,
KernelMode,
(PVOID) NULL,
Size,
0,
0,
(PVOID *)&AdapterObject );
//
// Reference the object.
//
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByPointer(
AdapterObject,
FILE_READ_DATA | FILE_WRITE_DATA,
*((POBJECT_TYPE *)IoAdapterObjectType),
KernelMode
);
}
//
// If the adapter object was successfully created, then attempt to insert
// it into the the object table.
//
if (NT_SUCCESS( Status )) {
Status = ObInsertObject( AdapterObject,
NULL,
FILE_READ_DATA | FILE_WRITE_DATA,
0,
(PVOID *) NULL,
&Handle );
if (NT_SUCCESS( Status )) {
ZwClose( Handle );
//
// Initialize the adapter object itself.
//
AdapterObject->Type = IO_TYPE_ADAPTER;
AdapterObject->Size = (SHORT)Size;
AdapterObject->MapRegistersPerChannel =
DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY );
AdapterObject->ChannelNumber = (UCHAR)AdapterChannel;
AdapterObject->MasterAdapter = MasterAdapterObject;
AdapterObject->AdapterNumber = 1;
//
// Initialize the channel wait queue for this adapter.
//
KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue);
} else {
// An error was incurred for some reason. Set the return value
// to NULL.
//
return( NULL );
}
} else {
return( NULL );
}
return AdapterObject;
}
PADAPTER_OBJECT
HalpAllocateMasterAdapter(
VOID
)
/*++
Routine Description:
This routine allocates and initializes the master adapter object
used to pool DMA map registers in the system. A map register is
an arbitrary data structure defined by the HAL and used to
implement DMA transfers. For the SGI MIPS machine, the map
registers are structures to HPC descriptors.
Arguments:
none
Return Value:
The function value is a pointer to the allocated master adapter object.
--*/
{
PADAPTER_OBJECT AdapterObject;
OBJECT_ATTRIBUTES ObjectAttributes;
ULONG Size;
ULONG BitmapSize;
HANDLE Handle;
NTSTATUS Status;
//
// Begin by initializing the object attributes structure to be used when
// creating the adapter object.
//
InitializeObjectAttributes( &ObjectAttributes,
NULL,
OBJ_PERMANENT,
(HANDLE) NULL,
(PSECURITY_DESCRIPTOR) NULL
);
//
// Determine the size of the master adapter object and allocate space
// for the register bit map.
//
BitmapSize = (((sizeof( RTL_BITMAP ) +
((DMA_TRANSLATION_LIMIT / sizeof(TRANSLATION_ENTRY))+7>>3))+3)&~3);
Size = sizeof( ADAPTER_OBJECT ) + BitmapSize;
//
// Now create the adapter object.
//
Status = ObCreateObject( KernelMode,
*((POBJECT_TYPE *)IoAdapterObjectType),
&ObjectAttributes,
KernelMode,
(PVOID) NULL,
Size,
0,
0,
(PVOID *)&AdapterObject );
//
// Reference the object.
//
if (NT_SUCCESS(Status)) {
Status = ObReferenceObjectByPointer(
AdapterObject,
FILE_READ_DATA | FILE_WRITE_DATA,
*((POBJECT_TYPE *)IoAdapterObjectType),
KernelMode
);
}
//
// If the adapter object was successfully created, then attempt to insert
// it into the the object table.
//
if (NT_SUCCESS( Status )) {
Status = ObInsertObject( AdapterObject,
NULL,
FILE_READ_DATA | FILE_WRITE_DATA,
0,
(PVOID *) NULL,
&Handle );
if (NT_SUCCESS( Status )) {
ZwClose( Handle );
//
// Initialize the master adapter object.
//
AdapterObject->Type = IO_TYPE_ADAPTER;
AdapterObject->Size = (SHORT)Size;
AdapterObject->MapRegistersPerChannel =
DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY );
AdapterObject->ChannelNumber = -1;
AdapterObject->MasterAdapter = AdapterObject;
AdapterObject->AdapterNumber = 1;
AdapterObject->MapRegisters = (PVOID) ( AdapterObject + 1);
//
// Initialize the channel wait queue for this adapter.
//
KeInitializeDeviceQueue(&AdapterObject->ChannelWaitQueue);
//
// Initialize the register bit map, AdapterQueue, and spin lock.
//
KeInitializeSpinLock( &AdapterObject->SpinLock );
InitializeListHead( &AdapterObject->AdapterQueue );
RtlInitializeBitMap(
AdapterObject->MapRegisters,
(PULONG)(((PCHAR)(AdapterObject->MapRegisters)) +
sizeof( RTL_BITMAP )),
DMA_TRANSLATION_LIMIT / sizeof( TRANSLATION_ENTRY)
);
RtlClearAllBits( AdapterObject->MapRegisters );
//
// Allocate a page of memory to use as the map registers. This
// memory must be large enough to hold a all the registers and
// page aligned.
//
Size = DMA_TRANSLATION_LIMIT;
Size = ROUND_TO_PAGES( Size );
AdapterObject->MapRegisterBase = MmAllocateNonCachedMemory(Size);
#ifdef IOMAPDEBUG
#define PMASK ~0xfff
if (((unsigned)AdapterObject->MapRegisterBase & PMASK) !=
(((unsigned)AdapterObject->MapRegisterBase +
(DMA_TRANSLATION_LIMIT-1)) & PMASK)) {
DbgPrint("HalpAllocateMasterAdapter: "
"Map registers cross page boundary.\n");
DbgPrint("MapRegisterBase = 0x%x\n",
AdapterObject->MapRegisterBase);
DbgBreakPoint();
}
#endif // IOMAPDEBUG
if (AdapterObject->MapRegisterBase == NULL) {
ObDereferenceObject( AdapterObject );
return( NULL );
}
} else {
// An error was incurred for some reason. Set the return value
// to NULL.
//
return( NULL );
}
} else {
return( NULL );
}
return AdapterObject;
}
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_SUCCESS 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;
LONG MapRegisterNumber;
KIRQL Irql;
//
// Begin by obtaining a pointer to the master adapter associated with this
// request.
//
if (AdapterObject->MasterAdapter != NULL) {
MasterAdapter = AdapterObject->MasterAdapter;
} else {
MasterAdapter = AdapterObject;
}
//
// Initialize the device object's wait context block in case this device
// must wait before being able to allocate the adapter.
//
Wcb->DeviceRoutine = ExecutionRoutine;
Wcb->NumberOfMapRegisters = NumberOfMapRegisters;
//
// Allocate the adapter object for this particular device. If the
// adapter cannot be allocated because it has already been allocated
// to another device, then return to the caller now; otherwise,
// continue.
//
if (!KeInsertDeviceQueue( &AdapterObject->ChannelWaitQueue,
&Wcb->WaitQueueEntry )) {
//
// The adapter was not busy so it has been allocated. Now check
// to see whether this driver wishes to allocate any map registers.
// If so, then queue the device object to the master adapter queue
// to wait for them to become available. If the driver wants map
// registers, ensure that this adapter has enough total map registers
// to satisfy the request.
//
AdapterObject->CurrentDevice = Wcb;
AdapterObject->NumberOfMapRegisters =
Wcb->NumberOfMapRegisters;
if (NumberOfMapRegisters != 0) {
if (NumberOfMapRegisters > MasterAdapter->MapRegistersPerChannel) {
AdapterObject->NumberOfMapRegisters = 0;
IoFreeAdapterChannel(AdapterObject);
return(STATUS_INSUFFICIENT_RESOURCES);
}
//
// 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,
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 = 1;
} else {
AdapterObject->MapRegisterBase =
(PVOID)((PTRANSLATION_ENTRY)MasterAdapter->MapRegisterBase +
MapRegisterNumber);
}
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
}
//
// 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) {
Action = ExecutionRoutine( Wcb->DeviceObject,
Wcb->CurrentIrp,
AdapterObject->MapRegisterBase,
Wcb->DeviceContext );
//
// If the driver wishes to keep the map registers then set the
// number allocated to zero and set the action to deallocate
// object.
//
if (Action == DeallocateObjectKeepRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
Action = DeallocateObject;
}
//
// If the driver would like to have the adapter deallocated,
// then deallocate any map registers allocated and then release
// the adapter object.
//
if (Action == DeallocateObject) {
IoFreeAdapterChannel( AdapterObject );
}
}
}
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.
NumberOfMapRegisters - Number of map registers requested. If not all of
the register could be allocated, then this field is updated to show
how many were 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.
//
if (AdapterObject->MasterAdapter) {
MasterAdapter = AdapterObject->MasterAdapter;
} else {
MasterAdapter = AdapterObject;
}
//
// 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
);
//
// Ensure that any allocated map registers are valid for this adapter.
//
if (MapRegisterNumber == -1) {
//
// Make it appear as if there are no map registers.
//
RtlClearBits(
MasterAdapter->MapRegisters,
MapRegisterNumber,
*NumberOfMapRegisters
);
MapRegisterNumber = (ULONG)-1;
}
if (MapRegisterNumber == -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);
return AdapterObject->MapRegisterBase;
}
VOID
IoFreeMapRegisters(
PADAPTER_OBJECT AdapterObject,
PVOID MapRegisterBase,
ULONG NumberOfMapRegisters
)
/*++
Routine Description:
This routine deallocates the map registers for the adapter. If there are
any queued adapter waiting for an attempt is made to allocate the next
entry.
Arguments:
AdapterObject - The adapter object to where the map register should be
returned.
MapRegisterBase - The map register base of the registers to be deallocated.
NumberOfMapRegisters - The number of registers to be deallocated.
Return Value:
None
--+*/
{
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) {
MasterAdapter = AdapterObject->MasterAdapter;
} else {
MasterAdapter = AdapterObject;
}
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->CurrentDevice;
//
// Attempt to allocate map registers for this request. Use the previous
// register base as a hint.
//
MapRegisterNumber =
RtlFindClearBitsAndSet( MasterAdapter->MapRegisters,
AdapterObject->NumberOfMapRegisters,
MapRegisterNumber
);
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);
//
// Invoke the driver's execution routine now.
//
Action =
Wcb->DeviceRoutine( Wcb->DeviceObject,
Wcb->CurrentIrp,
AdapterObject->MapRegisterBase,
Wcb->DeviceContext );
//
// If the driver wishes to keep the map registers then set the number
// allocated to zero and set the action to deallocate object.
//
if (Action == DeallocateObjectKeepRegisters) {
AdapterObject->NumberOfMapRegisters = 0;
Action = DeallocateObject;
}
//
// If the driver would like to have the adapter deallocated,
// then deallocate any map registers allocated and then release
// the adapter object.
//
if (Action == DeallocateObject) {
//
// The map registers are deallocated here rather than in
// IoFreeAdapterChannel. This limits the number of times
// this routine can be called recursively possibly overflowing
// the stack. The worst case occurs if there is a pending
// request for the adapter that uses map registers and whos
// excution routine decallocates the adapter. In that case if
// there are no requests in the 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 );
}//END WHILE
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
}
VOID
IoFreeAdapterChannel(
IN PADAPTER_OBJECT AdapterObject
)
/*++
Routine Description:
This routine is invoked to deallocate the specified adapter object.
Any map registers that were allocated are also automatically deallocated.
No checks are made to ensure that the adapter is really allocated to
a device object. However, if it is not, then kernel will bugcheck.
If another device is waiting in the queue to allocate the adapter object
it will be pulled from the queue and its execution routine will be
invoked.
Arguments:
AdapterObject - Pointer to the adapter object to be deallocated.
Return Value:
None.
--*/
{
PKDEVICE_QUEUE_ENTRY Packet;
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.
//
if (AdapterObject->MasterAdapter != NULL) {
MasterAdapter = AdapterObject->MasterAdapter;
} else {
MasterAdapter = AdapterObject;
}
//
// 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->CurrentDevice = 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) {
if (Wcb->NumberOfMapRegisters >
MasterAdapter->MapRegistersPerChannel) {
KeBugCheck( INSUFFICIENT_SYSTEM_MAP_REGS );
}
//
// 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 = 1;
} else {
AdapterObject->MapRegisterBase =
(PVOID)((PTRANSLATION_ENTRY)MasterAdapter->MapRegisterBase +
MapRegisterNumber);
}
KeReleaseSpinLock( &MasterAdapter->SpinLock, Irql );
}
//
// 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->CurrentDevice = 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;
}
}
}
BOOLEAN
HalpCreateDmaStructures (
VOID
)
/*++
Routine Description:
This routine initializes the structures necessary for DMA operations
and connects the intermediate interrupt dispatchers.
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.
--*/
{
//
// Initialize the 2 2nd level dispatch interrupt routines.
//
KeInitializeSpinLock( &HalpLoc0Spinlock );
KeInitializeSpinLock( &HalpLoc1Spinlock );
//
// This routine is the 2nd level dispatch routine for the
// devices connected to the LOCAL0 interrupt controller
// register.
//
KeInitializeInterrupt( &HalpLoc0Interrupt,
HalpLoc0Dispatch,
(PVOID)0L,
&HalpLoc0Spinlock,
LOCAL0_LEVEL,
LOCAL0_LEVEL,
LOCAL0_LEVEL,
LevelSensitive,
FALSE,
0,
FALSE
);
if (!KeConnectInterrupt( &HalpLoc0Interrupt )) {
return(FALSE);
}
//
// This routine is the 2nd level dispatch routine for the
// devices connected to the LOCAL1 interrupt controller
// register.
//
KeInitializeInterrupt( &HalpLoc1Interrupt,
HalpLoc1Dispatch,
(PVOID)0L,
&HalpLoc1Spinlock,
LOCAL1_LEVEL,
LOCAL1_LEVEL,
LOCAL1_LEVEL,
LevelSensitive,
FALSE,
0,
FALSE
);
if (!KeConnectInterrupt( &HalpLoc1Interrupt )) {
return(FALSE);
}
#ifdef ENABLE_FIFOFULL
HalEnableSystemInterrupt (SGI_VECTOR_GIO0FIFOFULL, LOCAL0_LEVEL, Latched);
#endif
{
ULONG cpuctrl1 = *(volatile ULONG *)0xbfa00008;
cpuctrl1 = (cpuctrl1 & 0xfffffff0) | 0xd;
*(volatile ULONG *)0xbfa00008 = cpuctrl1;
}
return (TRUE);
}
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. This routine will start the DMA
transfer for the HPC device.
Scatter/Gather DMA transfers can be performed by building the scatter
gather list into the MDL. IoMapTransfer will handle mapping the
MDL list to the Scatter/Gather map registers defined by the DMA channel.
NB: This routine may need a spinlock to exclude access to DMA registers
that are programmed within this procedure. If no DMA registers are
programmed in this procedure, no spinlock is required.
This routine also needs to account for being called recursively
where CurrentVa is the buffer to work with from the StartVa in
the Mdl. This affects where to start in the Mdl and MapRegisterBase.
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. The assumption is
made that this address is equal to or some increment above
MDL->StartVa.
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.
--*/
{
PTRANSLATION_ENTRY DmaMapRegister = MapRegisterBase;
PULONG PageFrameNumber;
ULONG PhysMapRegister, NumberOfPages;
ULONG ByteCount, Offset, Len, i;
ULONG ShortPhysAddress;
PVOID Pa;
Offset = BYTE_OFFSET( (PCHAR) CurrentVa - (PCHAR) Mdl->StartVa );
//
// If an Adapter Object is not defined, then this request is from a
// master.
//
if( AdapterObject == NULL ) {
#ifdef IOMAPDEBUG
DbgPrint ("IoMapTransfer: called for master adapter object.\n");
DbgBreakPoint();
#endif // IOMAPDEBUG
return(RtlConvertUlongToLargeInteger(Offset));
}
#ifdef IOMAPDEBUG
if (HalIoMapped) {
DbgPrint ("IoMapTransfer: Io Mapped before flushed.\n");
DbgBreakPoint();
}
HalIoMapped = 1;
#endif // IOMAPDEBUG
Len = *Length;
//
// Get a pointer to the array of physical pages located just after
// the MDL header structure.
//
PageFrameNumber = (PULONG) (Mdl + 1);
PageFrameNumber += (((PCHAR) CurrentVa - (PCHAR) Mdl->StartVa) >> PAGE_SHIFT);
//
// Determine the maximum number of pages required to satisfy this request.
//
NumberOfPages = (Offset + *Length + PAGE_SIZE - 1) >> PAGE_SHIFT;
//
// Build the DMA descriptor list by mapping the MDL page(s) into the
// map registers. For the HPC DMA, there is a one-to-one mapping of
// MDL pages to HPC descriptors.
//
ByteCount = (NumberOfPages == 1 ? Len : PAGE_SIZE - Offset);
for( i = 0; i < NumberOfPages; i++, DmaMapRegister++ ) {
//
// If this is the first entry, add the byte offset to
// the first physical page address.
//
if( i == 0 )
Pa = (PVOID)((*PageFrameNumber++ << PAGE_SHIFT) + Offset);
else
Pa = (PVOID)(*PageFrameNumber++ << PAGE_SHIFT);
//
// Update the HPC descriptor fields.
//
ZERO_TRANSLATION_ENTRY( DmaMapRegister );
ASSERT (ByteCount <= (ULONG)0x1000);
SET_HPC_BC( DmaMapRegister, ByteCount );
SET_HPC_CBP( DmaMapRegister, Pa );
//
// If this is the last entry, indicate that this is the last
// HPC descriptor in the list, else point to the next one in
// the chain.
//
if( (i + 1) == NumberOfPages ) {
SET_HPC_EOD( DmaMapRegister );
} else {
ShortPhysAddress = MmGetPhysicalAddress(DmaMapRegister + 1).LowPart;
SET_HPC_NBP( DmaMapRegister, ShortPhysAddress );
}
//
// Compute the byte count for the next entry.
//
if( (i + 2) == NumberOfPages )
ByteCount = Len - ((PAGE_SIZE * (NumberOfPages-1)) - Offset);
else
ByteCount = PAGE_SIZE;
} //END FOR
//
// Setup the HPC for the DMA transfer and transfer the data.
//
ShortPhysAddress = MmGetPhysicalAddress(MapRegisterBase).LowPart;
PhysMapRegister = ShortPhysAddress;
switch( AdapterObject->ChannelNumber ) {
case SGI_SCSI_DMA_CHANNEL:
#ifdef IOMAPDEBUG
// Examine aux register on wd chip. If it's busy, then
// something's wrong.
if (Hal3Debug) {
volatile unsigned char aux = *(volatile unsigned char *)0xbfb80121;
unsigned long auxcount = 0;
#ifdef OLD
if (aux & 0x20) {
DbgPrint ("IoMapTransfer: wd chip already busy\n");
DbgBreakPoint();
}
#else // !OLD
while ((aux & 0x20) && (auxcount < 100000)) {
auxcount++;
aux = *(volatile unsigned char *)0xbfb80121;
}
if (auxcount) {
DbgPrint ("IoMapTransfer: wd chip already busy: %d\n",
auxcount);
if (Hal6Debug)
DbgBreakPoint();
}
#endif // !OLD
if (READ_REGISTER_ULONG(&SCSI0_HPCREG->ScsiCNTL) &
SGI_CNTL_SCSIDMASTART) {
DbgPrint ("IoMapTransfer: dma start bit still set\n");
DbgBreakPoint();
}
}
//
// This was the infamous delay bug.
// If HalDebug is set to 0x2000, everything works
//
if( HalDebug )
KeStallExecutionProcessor(HalDebug);
#endif // IOMAPDEBUG
#ifdef DMADEBUG
if (Hal2Debug) {
if (READ_REGISTER_ULONG(&SCSI0_HPCREG->ScsiCNTL) &
SGI_CNTL_SCSIDMASTART) {
DbgPrint ("IoMapTransfer: dma start bit still set\n");
DbgBreakPoint();
}
}
#endif // DMADEBUG
WRITE_REGISTER_ULONG( &SCSI0_HPCREG->ScsiNBP, PhysMapRegister );
WRITE_REGISTER_ULONG( &SCSI0_HPCREG->ScsiCNTL, (WriteToDevice ?
(SGI_CNTL_SCSIDMASTART) :
(SGI_CNTL_SCSIDMASTART | SGI_CNTL_SCSITOMEMORY)) );
break;
case SGI_ENET_DMA_CHANNEL:
case SGI_PARALLEL_DMA_CHANNEL:
default:
DbgPrint( "HAL: Dma Channel not supported %u\n",
AdapterObject->ChannelNumber );
DbgBreakPoint();
break;
}// END SWITCH
return(RtlConvertUlongToLargeInteger(Offset));
}
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.
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:
None
--*/
{
ULONG timeout; // in microseconds
//
// We will only flush slave DMA adapters.
//
if( AdapterObject == NULL )
return FALSE;
#ifdef IOMAPDEBUG
if (!HalIoMapped) {
DbgPrint ("HalFlushAdapter: Io Not Mapped.\n");
DbgBreakPoint();
}
HalIoMapped = 0;
#endif // IOMAPDEBUG
//
// If the last DMA transfer was a SCSI read, flush the DMA fifos.
//
if( AdapterObject->ChannelNumber == SGI_SCSI_DMA_CHANNEL ) {
#ifdef IOMAPDEBUG
// Examine aux register on wd chip. If it's busy, then
// something's wrong.
if (Hal4Debug) {
unsigned char aux = *(volatile unsigned char *)0xbfb80121;
if (aux & 0x20) {
int timeout = 1000;
if (Hal5Debug)
DbgPrint ("IoFlushAdapterBuffers: wd chip still busy\n");
while (timeout--) {
aux = *(volatile unsigned char *)0xbfb80121;
if (!(aux & 0x20))
break;
}
if (!timeout) {
DbgPrint("wd chip busy timed out\n");
DbgBreakPoint();
}
}
}
#endif // IOMAPDEBUG
// If the transfer was a read, set the HPC's flush bit to flush the
// fifos.
//
if( !WriteToDevice ) { // if read...
WRITE_REGISTER_ULONG(&SCSI0_HPCREG->ScsiCNTL,
(ULONG)(READ_REGISTER_ULONG( &SCSI0_HPCREG->ScsiCNTL ) |
SGI_CNTL_SCSIFLUSH));
//
// Make sure the fifo is flushed and the DMA transfer has
// completed for a read.
//
for( timeout = 1000000; timeout; timeout--)
if(!(READ_REGISTER_ULONG( &SCSI0_HPCREG->ScsiCNTL ) &
SGI_CNTL_SCSIDMASTART) )
break; // flush complete, get out of loop
//
// If a timeout occurred, then our DMA transfer did not flush
// for some reason and it is probably hosed.
//
if( !timeout ) {
DbgPrint("\nIoFlushAdapterBuffers: DMA not flushed.\n");
DbgPrint( " SCSI.CNTL = %08lx\n\n",
READ_REGISTER_ULONG( &SCSI0_HPCREG->ScsiCNTL ));
DbgBreakPoint();
}
}
// Turn off DMA start bit
//
WRITE_REGISTER_ULONG( &SCSI0_HPCREG->ScsiCNTL, 0 );
} else
DbgBreakPoint();
return TRUE;
}
BOOLEAN
HalpLoc0Dispatch(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
)
/*++
Routine Description:
This routine is entered as the result of an interrupt being generated
via the vector that is connected to an interrupt object that describes
the LOCAL0 device interrupts. Its function is to call the second
level interrupt dispatch routine.
This service routine should be connected as follows:
KeInitializeInterrupt( &HalpLoc0Interrupt,
HalpLoc0Dispatch,
(PVOID)0L,
&HalpLoc0Spinlock,
LOCAL0_LEVEL,
LOCAL0_LEVEL,
LevelSensitive,
FALSE,
0,
FALSE,
(PKINTERRUPT)NULL
);
KeConnectInterrupt( &HalpLoc0Interrupt );
Arguments:
Interrupt - Supplies a pointer to the interrupt object.
ServiceContext - Supplies a pointer to an arbitrary data structure.
Return Value:
Returns the value returned from the 2nd level routine.
--*/
{
UCHAR mask, stat;
//
// Since the mask registers ONLY disable the interrupt generating
// capability of the bits in the status register, we need to mask
// off any extraneous bits that we really want masked as to not
// process them.
//
stat = READ_REGISTER_UCHAR(SGI_LISTAT0_BASE);
mask = READ_REGISTER_UCHAR(SGI_LIMASK0_BASE);
// Fast (if you can call it that :) path for fifofull interrupt
//
if ((mask & L0_MASK_GIO0FIFOFULL) &&
((stat & L0_MASK_GIO0FIFOFULL) || !stat)) {
// Unlatch interrupt
//
mask &= ~L0_MASK_GIO0FIFOFULL;
WRITE_REGISTER_UCHAR(SGI_LIMASK0_BASE, mask);
mask |= L0_MASK_GIO0FIFOFULL;
WRITE_REGISTER_UCHAR(SGI_LIMASK0_BASE, mask);
if (stat) {
ULONG timeout;
#define FIFO_TIMEOUT 300000 // ~300 ms
// DbgPrint("FIFOFULL interrupt\n");
for (timeout = FIFO_TIMEOUT;
timeout > 0 && (stat & L0_MASK_GIO0FIFOFULL);
timeout--) {
stat = READ_REGISTER_UCHAR(SGI_LISTAT0_BASE);
KeStallExecutionProcessor(1);
}
if (!timeout)
DbgPrint("Fifo timeout\n");
}
// else
// DbgPrint("Stray FIFOFULL interrupt\n");
return TRUE;
}
mask &= stat;
//
// Dispatch to the secondary interrupt service routine for each
// interrupt asserted.
//
if (mask) do {
register UCHAR ffintr = FFINTR(mask);
register PKINTERRUPT_ROUTINE IntrFunc =
PCR->InterruptRoutine[HalpVector0[ffintr]];
// Kernel dispatch code actually restores the arguments
// for the second level service routine from dispatch
// function pointer.
//
((PSECONDARY_DISPATCH)IntrFunc)(IntrFunc);
mask &= ~(1<<ffintr);
} while (mask);
else {
DbgPrint ("Stray Local 0 Interrupt\n");
return FALSE;
}
return TRUE;
}
BOOLEAN
HalpLoc1Dispatch(
IN PKINTERRUPT Interrupt,
IN PVOID ServiceContext
)
/*++
Routine Description:
This routine is entered as the result of an interrupt being generated
via the vector that is connected to an interrupt object that describes
the LOCAL1 device interrupts. Its function is to call the second
level interrupt dispatch routine.
This service routine should be connected as follows:
KeInitializeInterrupt( &HalpLoc1Interrupt,
HalpLoc1Dispatch,
(PVOID)0L,
&HalpLoc1Spinlock,
LOCAL1_LEVEL,
LOCAL1_LEVEL,
LevelSensitive,
FALSE,
0,
FALSE,
(PKINTERRUPT)NULL
);
KeConnectInterrupt( &HalpLoc1Interrupt );
Arguments:
Interrupt - Supplies a pointer to the interrupt object.
ServiceContext - Supplies a pointer to an arbitrary data structure.
Return Value:
Returns the value returned from the 2nd level routine.
--*/
{
UCHAR mask;
//
// Since the mask registers ONLY disable the interrupt generating
// capability of the bits in the status register, we need to mask
// off any extraneous bits that we really want masked as to not
// process them.
//
mask = ( READ_REGISTER_UCHAR(SGI_LISTAT1_BASE) &
READ_REGISTER_UCHAR(SGI_LIMASK1_BASE) );
//
// Dispatch to the secondary interrupt service routine for each
// interrupt asserted.
//
if (mask) do {
register UCHAR ffintr = FFINTR(mask);
register PKINTERRUPT_ROUTINE IntrFunc =
PCR->InterruptRoutine[HalpVector1[ffintr]];
// Kernel dispatch code actually restores the arguments
// for the second level service routine from dispatch
// function pointer.
//
((PSECONDARY_DISPATCH)IntrFunc)(IntrFunc);
mask &= ~(1<<ffintr);
} while (mask);
else {
DbgPrint ("Stray Local 1 Interrupt\n");
return FALSE;
}
return TRUE;
}
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.
--*/
{
return(NULL);
}
BOOLEAN
HalFlushCommonBuffer(
IN PADAPTER_OBJECT AdapterObject,
IN ULONG Length,
IN PHYSICAL_ADDRESS LogicalAddress,
IN PVOID VirtualAddress
)
/*++
Routine Description:
This function is called to flush any hardware adapter buffers when the
driver needs to read data written by an I/O master device to a common
buffer.
Arguments:
AdapterObject - Supplies a pointer to the adapter object used by this
device.
Length - Supplies the length of the common buffer. This should be the same
value used for the allocation of the buffer.
LogicalAddress - Supplies the logical address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
VirtualAddress - Supplies the virtual address of the common buffer. This
must be the same value return by HalAllocateCommonBuffer.
Return Value:
Returns TRUE if no errors were detected; otherwise, FALSE is return.
--*/
{
return(TRUE);
}
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 memory is cached or not.
Return Value:
None
--*/
{
}
ULONG
HalGetBusDataByOffset(
IN BUS_DATA_TYPE BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
)
/*++
Routine Description:
The function returns the bus data for a slot or address.
Arguments:
BusDataType - Supplies the type of bus.
BusNumber - Indicates which bus.
Buffer - Supplies the space to store the data.
Offset - Offset in the BusData buffer
Length - Supplies a count in bytes of the maximum amount to return.
Return Value:
Returns the amount of data stored into the buffer.
--*/
{
return(0);
}
ULONG
HalGetBusData(
IN BUS_DATA_TYPE BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
)
/*++
Routine Description:
Subset of HalGetBusDataByOffset, just pass the request along.
--*/
{
return HalGetBusDataByOffset (
BusDataType,
BusNumber,
SlotNumber,
Buffer,
0,
Length
);
}
ULONG
HalSetBusDataByOffset(
IN BUS_DATA_TYPE BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Offset,
IN ULONG Length
)
/*++
Routine Description:
The function sets the bus data for a slot or address.
Arguments:
BusDataType - Supplies the type of bus.
BusNumber - Indicates which bus.
Buffer - Supplies the space to store the data.
Offset - Offset in the BusData buffer
Length - Supplies a count in bytes of the maximum amount to return.
Return Value:
Returns the amount of data stored into the buffer.
--*/
{
return(0);
}
ULONG
HalSetBusData(
IN BUS_DATA_TYPE BusDataType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN PVOID Buffer,
IN ULONG Length
)
/*++
Routine Description:
Subset of HalGetBusDataByOffset, just pass the request along.
--*/
{
return HalSetBusDataByOffset(
BusDataType,
BusNumber,
SlotNumber,
Buffer,
0,
Length
);
}
NTSTATUS
HalAssignSlotResources (
IN PUNICODE_STRING RegistryPath,
IN PUNICODE_STRING DriverClassName OPTIONAL,
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
IN INTERFACE_TYPE BusType,
IN ULONG BusNumber,
IN ULONG SlotNumber,
IN OUT PCM_RESOURCE_LIST *AllocatedResources
)
/*++
Routine Description:
Reads the targeted device to determine it's required resources.
Calls IoAssignResources to allocate them.
Sets the targeted device with it's assigned resoruces
and returns the assignments to the caller.
Arguments:
RegistryPath - Passed to IoAssignResources.
A device specific registry path in the current-control-set, used
to check for pre-assigned settings and to track various resource
assignment information for this device.
DriverClassName Used to report the assigned resources for the driver/device
DriverObject - Used to report the assigned resources for the driver/device
DeviceObject - Used to report the assigned resources for the driver/device
(ie, IoReportResoruceUsage)
BusType
BusNumber
SlotNumber - Together BusType,BusNumber,SlotNumber uniquely
indentify the device to be queried & set.
Return Value:
STATUS_SUCCESS or error
--*/
{
//
// This HAL doesn't support any buses which support
// HalAssignSlotResources
//
return STATUS_NOT_SUPPORTED;
}
NTSTATUS
HalAdjustResourceList (
IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList
)
/*++
Routine Description:
Takes the pResourceList and limits any requested resource to
it's corrisponding bus requirements.
Arguments:
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;
}
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.
--*/
{
return(0);
}
static char outbuf[64];
VOID
HalpBuserrInterrupt(void)
{
ULONG stat, addr;
HalDisplayString("Hal: bus error interrupt detected\n");
addr = *(volatile ULONG *)CPU_ERR_ADDR;
stat = *(volatile ULONG *)CPU_ERR_STAT;
sprintf (outbuf, "CPU: Stat = 0x%x, Addr = 0x%x\n", stat, addr);
HalDisplayString(outbuf);
addr = *(volatile ULONG *)GIO_ERR_ADDR;
stat = *(volatile ULONG *)GIO_ERR_STAT;
sprintf (outbuf, "GIO: Stat = 0x%x, Addr = 0x%x\n", stat, addr);
HalDisplayString(outbuf);
*(volatile ULONG *)CPU_ERR_ADDR = 0;
*(volatile ULONG *)CPU_ERR_STAT = 0;
*(volatile ULONG *)GIO_ERR_ADDR = 0;
*(volatile ULONG *)GIO_ERR_STAT = 0;
while (1)
DbgBreakPoint();
}
VOID
HalpSystemInit(void)
{
PCR->InterruptRoutine[SYSBUS_LEVEL] = HalpBuserrInterrupt;
HalpInitNvram();
}