|
|
/*++
Copyright (c) 1989 Microsoft Corporation
Module Name:
ixisabus.c
Abstract:
Author:
Environment:
Revision History:
--*/
#include "halp.h"
ULONG HalpGetEisaInterruptVector( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN ULONG BusInterruptLevel, IN ULONG BusInterruptVector, OUT PKIRQL Irql, OUT PKAFFINITY Affinity );
BOOLEAN HalpTranslateIsaBusAddress ( IN PVOID BusHandler, IN PVOID RootHandler, IN PHYSICAL_ADDRESS BusAddress, IN OUT PULONG AddressSpace, OUT PPHYSICAL_ADDRESS TranslatedAddress );
BOOLEAN HalpTranslateEisaBusAddress ( IN PVOID BusHandler, IN PVOID RootHandler, IN PHYSICAL_ADDRESS BusAddress, IN OUT PULONG AddressSpace, OUT PPHYSICAL_ADDRESS TranslatedAddress );
NTSTATUS HalpAdjustEisaResourceList ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList );
HalpGetEisaData ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length );
extern USHORT HalpEisaIrqMask; extern USHORT HalpEisaIrqIgnore;
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,HalpGetEisaInterruptVector)
#pragma alloc_text(PAGE,HalpAdjustEisaResourceList)
#pragma alloc_text(PAGE,HalpGetEisaData)
#pragma alloc_text(PAGE,HalIrqTranslateResourceRequirementsIsa)
#pragma alloc_text(PAGE,HalIrqTranslateResourcesIsa)
#pragma alloc_text(PAGE,HalpRecordEisaInterruptVectors)
#endif
#ifndef ACPI_HAL
ULONG HalpGetEisaInterruptVector( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN ULONG BusInterruptLevel, IN ULONG BusInterruptVector, OUT PKIRQL Irql, OUT PKAFFINITY Affinity )
/*++
Routine Description:
This function returns the system interrupt vector and IRQL level corresponding to the specified bus interrupt level and/or vector. The system interrupt vector and IRQL are suitable for use in a subsequent call to KeInitializeInterrupt.
Arguments:
BusHandle - Per bus specific structure
Irql - Returns the system request priority.
Affinity - Returns the system wide irq affinity.
Return Value:
Returns the system interrupt vector corresponding to the specified device.
--*/ { UNREFERENCED_PARAMETER( BusInterruptVector );
//
// On standard PCs, IRQ 2 is the cascaded interrupt, and it really shows
// up on IRQ 9.
//
#if defined(NEC_98)
if (BusInterruptLevel == 7) { BusInterruptLevel = 8; } #else // defined(NEC_98)
if (BusInterruptLevel == 2) { BusInterruptLevel = 9; } #endif // defined(NEC_98)
if (BusInterruptLevel > 15) { return 0; }
//
// Get parent's translation from here..
//
return BusHandler->ParentHandler->GetInterruptVector ( BusHandler->ParentHandler, RootHandler, BusInterruptLevel, BusInterruptVector, Irql, Affinity ); }
NTSTATUS HalpAdjustEisaResourceList ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList ) { SUPPORTED_RANGE InterruptRange;
RtlZeroMemory (&InterruptRange, sizeof InterruptRange); InterruptRange.Base = 0; InterruptRange.Limit = 15;
return HaliAdjustResourceListRange ( BusHandler->BusAddresses, &InterruptRange, pResourceList ); }
BOOLEAN HalpTranslateIsaBusAddress( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN PHYSICAL_ADDRESS BusAddress, IN OUT PULONG AddressSpace, OUT PPHYSICAL_ADDRESS TranslatedAddress )
/*++
Routine Description:
This function translates a bus-relative address space and address into a system physical address.
Arguments:
BusAddress - Supplies the bus-relative address
AddressSpace - Supplies the address space number. Returns the host address space number.
AddressSpace == 0 => memory space AddressSpace == 1 => I/O space
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
--*/
{ BOOLEAN Status;
//
// Translated normally
//
Status = HalpTranslateSystemBusAddress ( BusHandler, RootHandler, BusAddress, AddressSpace, TranslatedAddress );
//
// If it could not be translated, and it's memory space
// then we allow the translation as it would occur on it's
// corrisponding EISA bus. We're allowing this because
// many VLBus drivers are claiming to be ISA devices.
// (yes, they should claim to be VLBus devices, but VLBus is
// run by video cards and like everything else about video
// there's no hope of fixing it. (At least according to
// Andre))
//
if (Status == FALSE && *AddressSpace == 0) { Status = HalTranslateBusAddress ( Eisa, BusHandler->BusNumber, BusAddress, AddressSpace, TranslatedAddress ); }
return Status; }
BOOLEAN HalpTranslateEisaBusAddress( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN PHYSICAL_ADDRESS BusAddress, IN OUT PULONG AddressSpace, OUT PPHYSICAL_ADDRESS TranslatedAddress )
/*++
Routine Description:
This function translates a bus-relative address space and address into a system physical address.
Arguments:
BusAddress - Supplies the bus-relative address
AddressSpace - Supplies the address space number. Returns the host address space number.
AddressSpace == 0 => memory space AddressSpace == 1 => I/O space
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
--*/
{ BOOLEAN Status;
//
// Translated normally
//
Status = HalpTranslateSystemBusAddress ( BusHandler, RootHandler, BusAddress, AddressSpace, TranslatedAddress );
//
// If it could not be translated, and it's in the 640k - 1m
// range then (for compatibility) try translating it on the
// Internal bus for
//
if (Status == FALSE && *AddressSpace == 0 && BusAddress.HighPart == 0 && BusAddress.LowPart >= 0xA0000 && BusAddress.LowPart < 0xFFFFF) {
Status = HalTranslateBusAddress ( Internal, 0, BusAddress, AddressSpace, TranslatedAddress ); }
return Status; } #endif
HalpGetEisaData ( IN PBUS_HANDLER BusHandler, IN PBUS_HANDLER RootHandler, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) /*++
Routine Description:
The function returns the Eisa bus data for a slot or address.
Arguments:
Buffer - Supplies the space to store the data.
Length - Supplies a count in bytes of the maximum amount to return.
Return Value:
Returns the amount of data stored into the buffer.
--*/
{ OBJECT_ATTRIBUTES ObjectAttributes; OBJECT_ATTRIBUTES BusObjectAttributes; PWSTR EisaPath = L"\\Registry\\Machine\\Hardware\\Description\\System\\EisaAdapter"; PWSTR ConfigData = L"Configuration Data"; ULONG BusNumber; WCHAR BusString[] = L"00"; UNICODE_STRING RootName, BusName = {0}; UNICODE_STRING ConfigDataName; NTSTATUS NtStatus; PKEY_VALUE_FULL_INFORMATION ValueInformation; PCM_FULL_RESOURCE_DESCRIPTOR Descriptor; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialResource; PCM_EISA_SLOT_INFORMATION SlotInformation; ULONG PartialCount; ULONG TotalDataSize, SlotDataSize; HANDLE EisaHandle = INVALID_HANDLE; HANDLE BusHandle = INVALID_HANDLE; ULONG BytesWritten, BytesNeeded; PUCHAR KeyValueBuffer = NULL; ULONG i; ULONG DataLength = 0; PUCHAR DataBuffer = Buffer; BOOLEAN Found = FALSE;
PAGED_CODE ();
RtlInitUnicodeString( &RootName, EisaPath );
InitializeObjectAttributes( &ObjectAttributes, &RootName, OBJ_CASE_INSENSITIVE, (HANDLE)NULL, NULL );
//
// Open the EISA root
//
NtStatus = ZwOpenKey( &EisaHandle, KEY_READ, &ObjectAttributes );
if (!NT_SUCCESS(NtStatus)) { DataLength = 0; goto HalpGetEisaDataExit; }
//
// Init bus number path
//
BusNumber = BusHandler->BusNumber; if (BusNumber > 99) { DataLength = 0; goto HalpGetEisaDataExit; }
if (BusNumber > 9) { BusString[0] += (WCHAR) (BusNumber/10); BusString[1] += (WCHAR) (BusNumber % 10); } else { BusString[0] += (WCHAR) BusNumber; BusString[1] = '\0'; }
RtlInitUnicodeString( &BusName, BusString );
InitializeObjectAttributes( &BusObjectAttributes, &BusName, OBJ_CASE_INSENSITIVE, (HANDLE)EisaHandle, NULL );
//
// Open the EISA root + Bus Number
//
NtStatus = ZwOpenKey( &BusHandle, KEY_READ, &BusObjectAttributes );
// Done with Eisa Handle
ZwClose(EisaHandle); EisaHandle = INVALID_HANDLE;
if (!NT_SUCCESS(NtStatus)) { DbgPrint("HAL: Opening Bus Number: Status = %x\n",NtStatus); DataLength = 0; goto HalpGetEisaDataExit; }
//
// opening the configuration data. This first call tells us how
// much memory we need to allocate
//
RtlInitUnicodeString( &ConfigDataName, ConfigData );
//
// This should fail. We need to make this call so we can
// get the actual size of the buffer to allocate.
//
ValueInformation = (PKEY_VALUE_FULL_INFORMATION) &i; NtStatus = ZwQueryValueKey( BusHandle, &ConfigDataName, KeyValueFullInformation, ValueInformation, 0, &BytesNeeded );
KeyValueBuffer = ExAllocatePoolWithTag( NonPagedPool, BytesNeeded, HAL_POOL_TAG );
if (KeyValueBuffer == NULL) { #if DBG
DbgPrint("HAL: Cannot allocate Key Value Buffer\n"); #endif
ZwClose(BusHandle); DataLength = 0; goto HalpGetEisaDataExit; }
ValueInformation = (PKEY_VALUE_FULL_INFORMATION)KeyValueBuffer;
NtStatus = ZwQueryValueKey( BusHandle, &ConfigDataName, KeyValueFullInformation, ValueInformation, BytesNeeded, &BytesWritten );
ZwClose(BusHandle);
if (!NT_SUCCESS(NtStatus)) { #if DBG
DbgPrint("HAL: Query Config Data: Status = %x\n",NtStatus); #endif
DataLength = 0; goto HalpGetEisaDataExit; }
//
// We get back a Full Resource Descriptor List
//
Descriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)((PUCHAR)ValueInformation + ValueInformation->DataOffset);
PartialResource = (PCM_PARTIAL_RESOURCE_DESCRIPTOR) &(Descriptor->PartialResourceList.PartialDescriptors); PartialCount = Descriptor->PartialResourceList.Count;
for (i = 0; i < PartialCount; i++) {
//
// Do each partial Resource
//
switch (PartialResource->Type) { case CmResourceTypeNull: case CmResourceTypePort: case CmResourceTypeInterrupt: case CmResourceTypeMemory: case CmResourceTypeDma:
//
// We dont care about these.
//
PartialResource++;
break;
case CmResourceTypeDeviceSpecific:
//
// Bingo!
//
TotalDataSize = PartialResource->u.DeviceSpecificData.DataSize;
SlotInformation = (PCM_EISA_SLOT_INFORMATION) ((PUCHAR)PartialResource + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
while (((LONG)TotalDataSize) > 0) {
if (SlotInformation->ReturnCode == EISA_EMPTY_SLOT) {
SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION);
} else {
SlotDataSize = sizeof(CM_EISA_SLOT_INFORMATION) + SlotInformation->NumberFunctions * sizeof(CM_EISA_FUNCTION_INFORMATION); }
if (SlotDataSize > TotalDataSize) {
//
// Something is wrong again
//
DataLength = 0; goto HalpGetEisaDataExit; }
if (SlotNumber != 0) {
SlotNumber--;
SlotInformation = (PCM_EISA_SLOT_INFORMATION) ((PUCHAR)SlotInformation + SlotDataSize);
TotalDataSize -= SlotDataSize;
continue;
}
//
// This is our slot
//
Found = TRUE; break;
}
//
// End loop
//
i = PartialCount;
break;
default:
#if DBG
DbgPrint("Bad Data in registry!\n"); #endif
DataLength = 0; goto HalpGetEisaDataExit; } }
if (Found) { i = Length + Offset; if (i > SlotDataSize) { i = SlotDataSize; }
DataLength = i - Offset; RtlMoveMemory (Buffer, ((PUCHAR)SlotInformation + Offset), DataLength); }
HalpGetEisaDataExit:
if (EisaHandle != INVALID_HANDLE) { ZwClose(EisaHandle); }
if (KeyValueBuffer) ExFreePool(KeyValueBuffer);
return DataLength; }
NTSTATUS HalIrqTranslateResourceRequirementsIsa( IN PVOID Context, IN PIO_RESOURCE_DESCRIPTOR Source, IN PDEVICE_OBJECT PhysicalDeviceObject, OUT PULONG TargetCount, OUT PIO_RESOURCE_DESCRIPTOR *Target ) /*++
Routine Description:
This function is basically a wrapper for HalIrqTranslateResourceRequirementsRoot that understands the weirdnesses of the ISA bus.
Arguments:
Return Value:
status
--*/ { PIO_RESOURCE_DESCRIPTOR modSource, target, rootTarget; NTSTATUS status; BOOLEAN picSlaveDeleted = FALSE; BOOLEAN deleteResource; ULONG sourceCount = 0; ULONG targetCount = 0; ULONG resource; ULONG rootCount; ULONG invalidIrq; BOOLEAN pciIsaConflict = FALSE;
PAGED_CODE(); ASSERT(Source->Type == CmResourceTypeInterrupt);
modSource = ExAllocatePoolWithTag(PagedPool, // we will have at most nine ranges when we are done
sizeof(IO_RESOURCE_DESCRIPTOR) * 9, HAL_POOL_TAG );
if (!modSource) { return STATUS_INSUFFICIENT_RESOURCES; }
RtlZeroMemory(modSource, sizeof(IO_RESOURCE_DESCRIPTOR) * 9);
//
// Is the PIC_SLAVE_IRQ in this resource?
//
if ((Source->u.Interrupt.MinimumVector <= PIC_SLAVE_IRQ) && (Source->u.Interrupt.MaximumVector >= PIC_SLAVE_IRQ)) {
//
// Clip the maximum
//
if (Source->u.Interrupt.MinimumVector < PIC_SLAVE_IRQ) {
modSource[sourceCount] = *Source;
modSource[sourceCount].u.Interrupt.MinimumVector = Source->u.Interrupt.MinimumVector;
modSource[sourceCount].u.Interrupt.MaximumVector = PIC_SLAVE_IRQ - 1;
sourceCount++; }
//
// Clip the minimum
//
if (Source->u.Interrupt.MaximumVector > PIC_SLAVE_IRQ) {
modSource[sourceCount] = *Source;
modSource[sourceCount].u.Interrupt.MaximumVector = Source->u.Interrupt.MaximumVector;
modSource[sourceCount].u.Interrupt.MinimumVector = PIC_SLAVE_IRQ + 1;
sourceCount++; }
//
// In ISA machines, the PIC_SLAVE_IRQ is rerouted
// to PIC_SLAVE_REDIRECT. So find out if PIC_SLAVE_REDIRECT
// is within this list. If it isn't we need to add it.
//
if (!((Source->u.Interrupt.MinimumVector <= PIC_SLAVE_REDIRECT) && (Source->u.Interrupt.MaximumVector >= PIC_SLAVE_REDIRECT))) {
modSource[sourceCount] = *Source;
modSource[sourceCount].u.Interrupt.MinimumVector = PIC_SLAVE_REDIRECT; modSource[sourceCount].u.Interrupt.MaximumVector = PIC_SLAVE_REDIRECT;
sourceCount++; }
} else {
*modSource = *Source; sourceCount = 1; }
//
// Now that the PIC_SLAVE_IRQ has been handled, we have
// to take into account IRQs that may have been steered
// away to the PCI bus.
//
// N.B. The algorithm used below may produce resources
// with minimums greater than maximums. Those will
// be stripped out later.
//
for (invalidIrq = 0; invalidIrq < PIC_VECTORS; invalidIrq++) {
//
// Look through all the resources, possibly removing
// this IRQ from them.
//
for (resource = 0; resource < sourceCount; resource++) {
deleteResource = FALSE;
if (HalpPciIrqMask & (1 << invalidIrq)) {
//
// This IRQ belongs to the PCI bus.
//
if (!((HalpBusType == MACHINE_TYPE_EISA) && ((modSource[resource].Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE)))) {
//
// And this resource is not an EISA-style,
// level-triggered interrupt.
//
// N.B. Only the system BIOS truely knows
// whether an IRQ on a PCI bus can be
// shared with an IRQ on an ISA bus.
// This code assumes that, in the case
// that the BIOS set an EISA device to
// the same interrupt as a PCI device,
// the machine can actually function.
//
deleteResource = TRUE; } }
#ifndef MCA
if ((HalpBusType == MACHINE_TYPE_EISA) && !(HalpEisaIrqIgnore & (1 << invalidIrq))) {
if (modSource[resource].Flags != HalpGetIsaIrqState(invalidIrq)) {
//
// This driver has requested a level-triggered interrupt
// and this particular interrupt is set to be edge, or
// vice-versa.
//
deleteResource = TRUE; pciIsaConflict = TRUE; } } #endif
if (deleteResource) {
if (modSource[resource].u.Interrupt.MinimumVector == invalidIrq) {
modSource[resource].u.Interrupt.MinimumVector++;
} else if (modSource[resource].u.Interrupt.MaximumVector == invalidIrq) {
modSource[resource].u.Interrupt.MaximumVector--;
} else if ((modSource[resource].u.Interrupt.MinimumVector < invalidIrq) && (modSource[resource].u.Interrupt.MaximumVector > invalidIrq)) {
//
// Copy the current resource into a new resource.
//
modSource[sourceCount] = modSource[resource];
//
// Clip the current resource to a range below invalidIrq.
//
modSource[resource].u.Interrupt.MaximumVector = invalidIrq - 1;
//
// Clip the new resource to a range above invalidIrq.
//
modSource[sourceCount].u.Interrupt.MinimumVector = invalidIrq + 1;
sourceCount++; } } } }
target = ExAllocatePoolWithTag(PagedPool, sizeof(IO_RESOURCE_DESCRIPTOR) * sourceCount, HAL_POOL_TAG );
if (!target) { ExFreePool(modSource); return STATUS_INSUFFICIENT_RESOURCES; }
//
// Now send each of these ranges through
// HalIrqTranslateResourceRequirementsRoot.
//
for (resource = 0; resource < sourceCount; resource++) {
//
// Skip over resources that we have previously
// clobbered (while deleting PCI IRQs.)
//
if (modSource[resource].u.Interrupt.MinimumVector > modSource[resource].u.Interrupt.MaximumVector) {
continue; }
status = HalIrqTranslateResourceRequirementsRoot( Context, &modSource[resource], PhysicalDeviceObject, &rootCount, &rootTarget );
if (!NT_SUCCESS(status)) { ExFreePool(target); goto HalIrqTranslateResourceRequirementsIsaExit; }
//
// HalIrqTranslateResourceRequirementsRoot should return
// either one resource or, occasionally, zero.
//
ASSERT(rootCount <= 1);
if (rootCount == 1) {
target[targetCount] = *rootTarget; targetCount++; ExFreePool(rootTarget); } }
status = STATUS_TRANSLATION_COMPLETE; *TargetCount = targetCount;
if (targetCount > 0) {
*Target = target;
} else {
ExFreePool(target); if (pciIsaConflict == TRUE) { status = STATUS_PNP_IRQ_TRANSLATION_FAILED; } }
HalIrqTranslateResourceRequirementsIsaExit:
ExFreePool(modSource); return status; }
NTSTATUS HalIrqTranslateResourcesIsa( IN PVOID Context, IN PCM_PARTIAL_RESOURCE_DESCRIPTOR Source, IN RESOURCE_TRANSLATION_DIRECTION Direction, IN ULONG AlternativesCount, OPTIONAL IN IO_RESOURCE_DESCRIPTOR Alternatives[], OPTIONAL IN PDEVICE_OBJECT PhysicalDeviceObject, OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Target ) /*++
Routine Description:
This function is basically a wrapper for HalIrqTranslateResourcesRoot that understands the weirdnesses of the ISA bus.
Arguments:
Return Value:
status
--*/ { CM_PARTIAL_RESOURCE_DESCRIPTOR modSource; NTSTATUS status; BOOLEAN usePicSlave = FALSE; ULONG i;
modSource = *Source;
if (Direction == TranslateChildToParent) {
if (Source->u.Interrupt.Vector == PIC_SLAVE_IRQ) { modSource.u.Interrupt.Vector = PIC_SLAVE_REDIRECT; modSource.u.Interrupt.Level = PIC_SLAVE_REDIRECT; } }
status = HalIrqTranslateResourcesRoot( Context, &modSource, Direction, AlternativesCount, Alternatives, PhysicalDeviceObject, Target);
if (!NT_SUCCESS(status)) { return status; }
if (Direction == TranslateParentToChild) {
//
// Because the ISA interrupt controller is
// cascaded, there is one case where there is
// a two-to-one mapping for interrupt sources.
// (On a PC, both 2 and 9 trigger vector 9.)
//
// We need to account for this and deliver the
// right value back to the driver.
//
if (Target->u.Interrupt.Level == PIC_SLAVE_REDIRECT) {
//
// Search the Alternatives list. If it contains
// PIC_SLAVE_IRQ but not PIC_SLAVE_REDIRECT,
// we should return PIC_SLAVE_IRQ.
//
for (i = 0; i < AlternativesCount; i++) {
if ((Alternatives[i].u.Interrupt.MinimumVector >= PIC_SLAVE_REDIRECT) && (Alternatives[i].u.Interrupt.MaximumVector <= PIC_SLAVE_REDIRECT)) {
//
// The list contains, PIC_SLAVE_REDIRECT. Stop
// looking.
//
usePicSlave = FALSE; break; }
if ((Alternatives[i].u.Interrupt.MinimumVector >= PIC_SLAVE_IRQ) && (Alternatives[i].u.Interrupt.MaximumVector <= PIC_SLAVE_IRQ)) {
//
// The list contains, PIC_SLAVE_IRQ. Use it
// unless we find PIC_SLAVE_REDIRECT later.
//
usePicSlave = TRUE; } }
if (usePicSlave) {
Target->u.Interrupt.Level = PIC_SLAVE_IRQ; Target->u.Interrupt.Vector = PIC_SLAVE_IRQ; } } }
return status; }
VOID HalpRecordEisaInterruptVectors( VOID ) { HalpEisaIrqMask = READ_PORT_UCHAR((PUCHAR)EISA_EDGE_LEVEL0) & 0xff; HalpEisaIrqMask |= READ_PORT_UCHAR((PUCHAR)EISA_EDGE_LEVEL1) << 8;
if ((HalpEisaIrqMask == 0xffff) || (HalpEisaIrqMask == 0x0000)) {
HalpEisaIrqIgnore = 0xffff; } }
|