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.
2387 lines
64 KiB
2387 lines
64 KiB
/*++
|
|
|
|
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();
|
|
}
|