/*++ Copyright (c) 1989 Microsoft Corporation Copyright (c) 1994 Digital Equipment Corporation Module Name: pcisup.c Abstract: Platform-independent PCI bus routines Author: Environment: Kernel mode Revision History: --*/ #include "halp.h" #include "snipci.h" #include "snipbus.h" #include "pci.h" #include "eisa.h" UCHAR HalpInterruptLine[10][32]; // // Define PCI slot validity // typedef enum _VALID_SLOT { InvalidBus = 0, InvalidSlot, ValidSlot } VALID_SLOT; // // Local prototypes for routines supporting HalpGet/SetPCIData // VOID HalpReadPCIConfig ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ); VOID HalpWritePCIConfig ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ); VALID_SLOT HalpValidPCISlot ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ); BOOLEAN HalpPCIConfigPartialRead( IN ULONG Offset, IN ULONG Length, IN PUCHAR Buffer ); VOID HalpPCIConfigPartialWrite( IN ULONG Offset, IN ULONG Length, IN PUCHAR Buffer ); PCI_CONFIGURATION_TYPES HalpPCIConfigCycleType ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ); VOID HalpIntLineUpdate( IN ULONG BusNumber, IN ULONG Slot, PPCI_COMMON_CONFIG PciData ); BOOLEAN HalpTowerTestConf( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ); // // Pragmas to assign functions to different kinds of pages. // #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,HalpInitializePCIBus) #endif // ALLOC_PRAGMA // // Globals // KSPIN_LOCK HalpPCIConfigLock; extern BOOLEAN HalPCIRegistryInitialized; ULONG PCIMaxBuses; ULONG PCIMaxLocalDevice; ULONG PCIMaxDevice; ULONG PCIMaxBusZeroDevice; PULONG HalpPciConfigAddr; PULONG HalpPciConfigData; UCHAR HalpIntAMax; UCHAR HalpIntBMax; UCHAR HalpIntCMax; UCHAR HalpIntDMax; // // Registry stuff // PCWSTR rgzMultiFunctionAdapter = L"\\Registry\\Machine\\Hardware\\Description\\System\\MultifunctionAdapter"; PCWSTR rgzConfigurationData = L"Configuration Data"; PCWSTR rgzIdentifier = L"Identifier"; PCWSTR rgzPCIIdentifier = L"PCI"; /*++ Routine Description: This function looks at the registry to find if the current machine has a PCI bus or not. The Arc firmware is responsible for building configuration information about the number of PCI buses on the system and nature (local vs. secondary - across a PCI-PCI bridge) of the each bus. This state is held in PCIRegInfo. Arguments: None. Return Value: None. --*/ VOID HalpRecurseLoaderBlock( IN PCONFIGURATION_COMPONENT_DATA CurrentEntry ) /*++ Routine Description: This routine parses the loader parameter block looking for the PCI node. Once found, used to determine if PCI parity checking should be enabled or disabled. Set the default to not disable checking. Arguments: CurrentEntry - Supplies a pointer to a loader configuration tree or subtree. Return Value: None. --*/ { PCONFIGURATION_COMPONENT Component; PPCI_REGISTRY_INFO PCIRegInfo = NULL; PCM_PARTIAL_RESOURCE_DESCRIPTOR HalpDesc; if (CurrentEntry) { Component = &CurrentEntry->ComponentEntry; if (Component->Class == AdapterClass && Component->Type == MultiFunctionAdapter) { if (strcmp(Component->Identifier, "PCI") == 0) { #if DBG HalDisplayString("PCI Machine detected\n"); #endif HalpDesc = ((PCM_PARTIAL_RESOURCE_LIST)(CurrentEntry->ConfigurationData))->PartialDescriptors; if (HalpDesc->Type == CmResourceTypeDeviceSpecific) { PCIRegInfo = (PPCI_REGISTRY_INFO) (HalpDesc+1); PCIMaxBuses = PCIRegInfo->NoBuses; HalPCIRegistryInitialized = TRUE; } return; } } // // Process all the Siblings of current entry // HalpRecurseLoaderBlock(CurrentEntry->Sibling); // // Process all the Childeren of current entry // HalpRecurseLoaderBlock(CurrentEntry->Child); } } VOID HalpParseLoaderBlock( IN PLOADER_PARAMETER_BLOCK LoaderBlock ) { if (LoaderBlock == NULL) { return; } HalpRecurseLoaderBlock( (PCONFIGURATION_COMPONENT_DATA) LoaderBlock->ConfigurationRoot); } /*++ Routine Description: The function intializes global PCI bus state from execpt those from the registry. The maximum virtual slot number on the local (type 0 config cycle) PCI bus is registered here, based on the machine dependent define PCI_MAX_LOCAL_DEVICE. This state is carried in PCIMaxLocalDevice. The maximum number of virtual slots on a secondary bus is fixed by the PCI Specification and is represented by PCI_MAX_DEVICES. This state is held in PCIMaxDevice. Arguments: None. Return Value: None. --*/ VOID HalpInitializePCIBus ( VOID ) { ULONG irqsel, iomemconf; PCIMaxLocalDevice = PCI_MAX_DEVICES - 1; PCIMaxDevice = PCI_MAX_DEVICES - 1; if (HalpIsTowerPci) { PCIMaxBusZeroDevice = PCI_MAX_DEVICES - 1; } else { PCIMaxBusZeroDevice = 7; } KeInitializeSpinLock (&HalpPCIConfigLock); // // Specific sni init to distinguish between desktop/minitower and tower ASIC // if (HalpIsTowerPci) HalpPciConfigAddr = (PULONG) (PCI_TOWER_CONF_ADDR_REGISTER); else HalpPciConfigAddr = (PULONG) (PCI_CONF_ADDR_REGISTER); HalpPciConfigData = (PULONG)(PCI_IO_BASE | 0xcfc); HalpIntAMax = INTA_VECTOR ; HalpIntBMax = INTB_VECTOR ; HalpIntCMax = INTC_VECTOR ; HalpIntDMax = INTD_VECTOR ; // enable PCI timeout interrupts if (!HalpIsTowerPci) { iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout + enable ECC detector-corrector WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf | PCI_IOMEMCONF_ENCHKECC); irqsel = READ_REGISTER_ULONG(PCI_IRQSEL_REGISTER); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , ((irqsel | PCI_IRQSEL_MASK ) & PCI_IRQSEL_INT)); } else { // init ECC single counter to autorize 0xffff single ECC errors before system crash WRITE_REGISTER_ULONG (PCI_TOWER_MEM_CONTROL_1, (READ_REGISTER_ULONG(PCI_TOWER_MEM_CONTROL_1) & ERROR_COUNTER_MASK) | ERROR_COUNTER_INITVALUE); } } /*++ Routine Description: The function returns the PCI bus data for a device. Arguments: BusNumber - Indicates which bus. VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word) 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. If this PCI slot has never been set, then the configuration information returned is zeroed. --*/ ULONG HalpGetPCIData ( IN ULONG BusNumber, IN ULONG Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) { PPCI_COMMON_CONFIG PciData; UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; ULONG Len; PCI_SLOT_NUMBER PciSlot; ULONG j; if (Length > sizeof (PCI_COMMON_CONFIG)) { Length = sizeof (PCI_COMMON_CONFIG); } Len = 0; PciData = (PPCI_COMMON_CONFIG) iBuffer; PciSlot = *((PPCI_SLOT_NUMBER) &Slot); if (Offset >= PCI_COMMON_HDR_LENGTH) { // // The user did not request any data from the common // header. Verify the PCI device exists, then continue // in the device specific area. // HalpReadPCIConfig (BusNumber, PciSlot, PciData, 0, sizeof(ULONG)); // // Check for non-existent bus // if (PciData->VendorID == 0x00) { return 0; // Requested bus does not exist. Return no data. } // // Check for invalid slot // if (PciData->VendorID == PCI_INVALID_VENDORID) { if ((Offset == 0) && (Length >=2)) *(PUSHORT)Buffer = PCI_INVALID_VENDORID; return 2; } } else { // // Caller requested at least some data within the // common header. Read the whole header, effect the // fields we need to and then copy the user's requested // bytes from the header // // // Read this PCI devices slot data // Len = PCI_COMMON_HDR_LENGTH; HalpReadPCIConfig (BusNumber, PciSlot, PciData, 0, Len); // // Check for non-existent bus // if (PciData->VendorID == 0x00) { return 0; // Requested bus does not exist. Return no data. } // // Check for invalid slot // if (PciData->VendorID == PCI_INVALID_VENDORID || PCI_CONFIG_TYPE (PciData) != 0) { if ((Offset == 0) && (Length >=2)) *(PUSHORT)Buffer = PCI_INVALID_VENDORID; return 2; // only return invalid id } // // Update interrupt line // HalpIntLineUpdate(BusNumber, Slot, PciData); // // Pb concerning especially SCSI : skip IO address when = 3bf0000 // for (j=0; j < PCI_TYPE0_ADDRESSES; j++) { if ( (((ULONG)(PciData->u.type0.BaseAddresses[j]) & ~0x3) | 0x01) == 0x3bf0001 ) PciData->u.type0.BaseAddresses[j] = 0; } // // Copy whatever data overlaps into the callers buffer // if (Len < Offset) { // no data at caller's buffer return 0; } Len -= Offset; if (Len > Length) { Len = Length; } RtlMoveMemory(Buffer, iBuffer + Offset, Len); Offset += Len; Buffer += Len; Length -= Len; } if (Length) { if (Offset >= PCI_COMMON_HDR_LENGTH) { // // The remaining Buffer comes from the Device Specific // area - put on the kitten gloves and read from it. // // Specific read/writes to the PCI device specific area // are guarenteed: // // Not to read/write any byte outside the area specified // by the caller. (this may cause WORD or BYTE references // to the area in order to read the non-dword aligned // ends of the request) // // To use a WORD access if the requested length is exactly // a WORD long. // // To use a BYTE access if the requested length is exactly // a BYTE long. // HalpReadPCIConfig (BusNumber, PciSlot, Buffer, Offset, Length); Len += Length; } } return Len; } /*++ Routine Description: The function returns the Pci bus data for a device. Arguments: VendorSpecificDevice - The VendorID (low Word) and DeviceID (High Word) 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. --*/ ULONG HalpSetPCIData ( IN ULONG BusNumber, IN ULONG Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) { PPCI_COMMON_CONFIG PciData, PciData2; UCHAR iBuffer[PCI_COMMON_HDR_LENGTH]; UCHAR iBuffer2[PCI_COMMON_HDR_LENGTH]; ULONG Len; PCI_SLOT_NUMBER PciSlot; if (Length > sizeof (PCI_COMMON_CONFIG)) { Length = sizeof (PCI_COMMON_CONFIG); } Len = 0; PciData = (PPCI_COMMON_CONFIG) iBuffer; PciData2 = (PPCI_COMMON_CONFIG) iBuffer2; PciSlot = *((PPCI_SLOT_NUMBER) &Slot); if (Offset >= PCI_COMMON_HDR_LENGTH) { // // The user did not request any data from the common // header. Verify the PCI device exists, then continue in // the device specific area. // HalpReadPCIConfig (BusNumber, PciSlot, PciData, 0, sizeof(ULONG)); if (PciData->VendorID == PCI_INVALID_VENDORID || PciData->VendorID == 0x00) { return 0; } } else { // // Caller requested to set at least some data within the // common header. // Len = PCI_COMMON_HDR_LENGTH; HalpReadPCIConfig (BusNumber, PciSlot, PciData, 0, Len); if (PciData->VendorID == PCI_INVALID_VENDORID || PciData->VendorID == 0x00 || PCI_CONFIG_TYPE (PciData) != 0) { // // no device, or header type unkown // return 0; } // // Copy COMMON_HDR values to buffer2, then overlay callers changes. // RtlMoveMemory (iBuffer2, iBuffer, Len); Len -= Offset; if (Len > Length) { Len = Length; } RtlMoveMemory (iBuffer2+Offset, Buffer, Len); // in case interrupt line or pin was editted //HalpPCILineToPin (BusNumber, PciSlot, PciData2, PciData); // // Set new PCI configuration // HalpWritePCIConfig (BusNumber, PciSlot, iBuffer2+Offset, Offset, Len); Offset += Len; Buffer += Len; Length -= Len; } if (Length) { if (Offset >= PCI_COMMON_HDR_LENGTH) { // // The remaining Buffer comes from the Device Specific // area - put on the kitten gloves and write it // // Specific read/writes to the PCI device specific area // are guarenteed: // // Not to read/write any byte outside the area specified // by the caller. (this may cause WORD or BYTE references // to the area in order to read the non-dword aligned // ends of the request) // // To use a WORD access if the requested length is exactly // a WORD long. // // To use a BYTE access if the requested length is exactly // a BYTE long. // HalpWritePCIConfig (BusNumber, PciSlot, Buffer, Offset, Length); Len += Length; } } return Len; } /*++ Routine Description: Arguments: Return Value: --*/ VOID HalpReadPCIConfig ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) { KIRQL OldIrql; PCI_CONFIG_ADDR PciConfigAddrData; PUCHAR BufferOrg; ULONG LengthOrg; ULONG PartialLength; ULONG irqsel, iomemconf; BOOLEAN result; // // Read the slot, if it's valid. // Otherwise, return an Invalid VendorId for a invalid slot on an existing bus // or a null (zero) buffer if we have a non-existant bus. // BufferOrg = Buffer;LengthOrg = Length; switch (HalpValidPCISlot (BusNumber, Slot)) { case ValidSlot: // // Acquire Spin Lock // KeAcquireSpinLock (&HalpPCIConfigLock, &OldIrql); // disable PCI timeout to avoid timeout interrupt when drivers attempt to access invalid slot if (!HalpIsTowerPci) { irqsel = READ_REGISTER_ULONG(PCI_IRQSEL_REGISTER); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , (irqsel & ~PCI_IRQSEL_TIMEOUTMASK)); } // // Program PciConfigAddr register // PciConfigAddrData.Type = (BusNumber ? PciConfigType1 : PciConfigType0); PciConfigAddrData.BusNumber = BusNumber; PciConfigAddrData.DeviceNumber = Slot.u.bits.DeviceNumber; PciConfigAddrData.FunctionNumber = 0; // PciConfigAddrData.DeviceNumber = (BusNumber ? Slot.u.bits.DeviceNumber : 0); // PciConfigAddrData.FunctionNumber = Slot.u.bits.FunctionNumber; PciConfigAddrData.Reserved = 0; PciConfigAddrData.Enable = 1; // // Issue PCI Configuration Cycles // if (Offset % 4) { PartialLength = (Length > (4 - (Offset % 4))) ? (4 - (Offset % 4)) : Length; PciConfigAddrData.RegisterNumber = (Offset - (Offset % 4)) >> 2; // ULONG frontier WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); if (HalpIsTowerPci &&( PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,MP_BUS_PCI_LOCK_REQ); result = HalpPCIConfigPartialRead((4 - (Offset % 4)),PartialLength, Buffer); if (HalpIsTowerPci &&( PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,0); if (!result){ RtlFillMemory (BufferOrg, LengthOrg, (UCHAR) -1); if (!HalpIsTowerPci) { iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf ); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , irqsel ); } KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql); return; } Offset += PartialLength; Length -= PartialLength; Buffer += PartialLength; } while (Length >= 4) { PciConfigAddrData.RegisterNumber = Offset >> 2; // ULONG frontier WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); if (HalpIsTowerPci &&( PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,MP_BUS_PCI_LOCK_REQ); * (PULONG) Buffer = READ_REGISTER_ULONG ((PULONG) HalpPciConfigData); if (HalpIsTowerPci && (PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,0); if (* (PULONG) Buffer == 0XFFFFFFFF) { RtlFillMemory (BufferOrg, LengthOrg, (UCHAR) -1); if (!HalpIsTowerPci) { // reenable timeout iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf ); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , irqsel ); } KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql); return; } Offset += 4; Buffer += 4; Length -= 4; } if ( Length > 0) { PciConfigAddrData.RegisterNumber = Offset >> 2; WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); if (HalpIsTowerPci &&( PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,MP_BUS_PCI_LOCK_REQ); result = HalpPCIConfigPartialRead(0,Length,Buffer); if (HalpIsTowerPci &&( PciConfigAddrData.BusNumber !=0 || PciConfigAddrData.DeviceNumber !=0)) WRITE_REGISTER_ULONG(PCI_TOWER_MP_BUS_PCI_LOCK,0); if (!result) { RtlFillMemory (BufferOrg, LengthOrg, (UCHAR) -1); if (!HalpIsTowerPci) { // reenable timeout iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf ); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , irqsel ); } KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql); return; } Offset += Length; Length -= Length; Buffer += Length; } // // Release Spin Lock // // reenable timeout if (!HalpIsTowerPci) { iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf ); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , irqsel ); } KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql); break; case InvalidSlot: // // Invalid SlotID return no data (Invalid Slot ID = 0xFFFF) // RtlFillMemory (Buffer, Length, (UCHAR) -1); break ; case InvalidBus: // // Invalid Bus, return return no data // RtlFillMemory (Buffer, Length, (UCHAR) 0); break ; } return; } /*++ Routine Description: Arguments: Return Value: --*/ VOID HalpWritePCIConfig ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot, IN PUCHAR Buffer, IN ULONG Offset, IN ULONG Length ) { KIRQL OldIrql; PCI_CONFIG_ADDR PciConfigAddrData; ULONG PartialLength; ULONG irqsel, iomemconf; if (HalpValidPCISlot (BusNumber, Slot) != ValidSlot) { // // Invalid SlotID do nothing // return ; } // // Acquire Spin Lock // KeAcquireSpinLock (&HalpPCIConfigLock, &OldIrql); // disable PCI timeout to avoid timeout interrupt when drivers attempt to access invalid slot if (!HalpIsTowerPci) { irqsel = READ_REGISTER_ULONG(PCI_IRQSEL_REGISTER); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , (irqsel & ~PCI_IRQSEL_TIMEOUTMASK)); } // // Program PciConfigAddr register // PciConfigAddrData.Type = (BusNumber ? PciConfigType1 : PciConfigType0); PciConfigAddrData.BusNumber = BusNumber; PciConfigAddrData.DeviceNumber = Slot.u.bits.DeviceNumber; PciConfigAddrData.FunctionNumber = 0; // PciConfigAddrData.DeviceNumber = (BusNumber ? Slot.u.bits.DeviceNumber : 0); // PciConfigAddrData.FunctionNumber = Slot.u.bits.FunctionNumber; PciConfigAddrData.Reserved = 0; PciConfigAddrData.Enable = 1; // // Issue PCI Configuration Cycles // if (Offset % 4) { PartialLength = (Length > (4 - (Offset % 4))) ? (4 - (Offset % 4)) : Length; PciConfigAddrData.RegisterNumber = (Offset - (Offset % 4)) >> 2; // ULONG frontier WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); HalpPCIConfigPartialWrite((4 - (Offset % 4)),PartialLength, Buffer); Offset += PartialLength; Length -= PartialLength; Buffer += PartialLength; } while (Length >= 4) { PciConfigAddrData.RegisterNumber = Offset >> 2; // ULONG frontier WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG)Buffer)); Offset += 4; Buffer += 4; Length -= 4; } if ( Length > 0) { PciConfigAddrData.RegisterNumber = Offset >> 2; WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); HalpPCIConfigPartialWrite(0,Length,Buffer); Offset += Length; Length -= Length; Buffer += Length; } // // Release Spin Lock // // reenable timeout if (!HalpIsTowerPci) { iomemconf = READ_REGISTER_ULONG(PCI_IOMEMCONF_REGISTER); WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , (iomemconf & (~PCI_IOMEMCONF_ENIOTMOUT))); // clear it // reenable timeout WRITE_REGISTER_ULONG( PCI_IOMEMCONF_REGISTER , iomemconf ); WRITE_REGISTER_ULONG( PCI_IRQSEL_REGISTER , irqsel ); } KeReleaseSpinLock (&HalpPCIConfigLock, OldIrql); } /*++ Routine Description: Arguments: Return Value: --*/ VALID_SLOT HalpValidPCISlot ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ) { PCI_SLOT_NUMBER Slot2; UCHAR HeaderType; ULONG i; PCI_CONFIGURATION_TYPES PciConfigType; // // Get the config cycle type for the proposed bus. // (PciConfigTypeInvalid indicates a non-existent bus.) // PciConfigType = HalpPCIConfigCycleType(BusNumber, Slot); // // The number of devices allowed on a local PCI bus may be different // than that across a PCI-PCI bridge. // switch(PciConfigType) { case PciConfigType0: if ((BusNumber == 0) && (Slot.u.bits.DeviceNumber > PCIMaxBusZeroDevice)) { return InvalidSlot; } if (Slot.u.bits.DeviceNumber > PCIMaxLocalDevice) { return InvalidSlot; } break; case PciConfigType1: if ((BusNumber == 0) && (Slot.u.bits.DeviceNumber > PCIMaxBusZeroDevice)) { return InvalidSlot; } if (Slot.u.bits.DeviceNumber > PCIMaxDevice) { return InvalidSlot; } break; case PciConfigTypeInvalid: return InvalidBus; break; } // // Check function number // if (Slot.u.bits.FunctionNumber == 0) { if (HalpTowerTestConf(BusNumber, Slot)) return ValidSlot;else return InvalidSlot; } // // Non zero function numbers are only supported if the // device has the PCI_MULTIFUNCTION bit set in it's header // i = Slot.u.bits.DeviceNumber; // // Read DeviceNumber, Function zero, to determine if the // PCI supports multifunction devices // Slot2 = Slot; Slot2.u.bits.FunctionNumber = 0; if ( ! HalpTowerTestConf(BusNumber, Slot2)) return InvalidSlot; HalpReadPCIConfig (BusNumber, Slot2, &HeaderType, FIELD_OFFSET (PCI_COMMON_CONFIG, HeaderType), sizeof(UCHAR) ); if (!(HeaderType & PCI_MULTIFUNCTION) || (HeaderType == 0xFF)) { // // this device doesn't exists or doesn't support MULTIFUNCTION types // return InvalidSlot; } return ValidSlot; } /*++ Routine Description: Partial write in the PCI config space ( less than one ULONG) Arguments: Return Value: --*/ VOID HalpPCIConfigPartialWrite( IN ULONG Offset, IN ULONG Length, IN PUCHAR Buffer ) { switch(Offset) { case 0: if (Length > 1) { WRITE_REGISTER_USHORT ((PULONG) HalpPciConfigData, *(PUSHORT)Buffer); Buffer +=2;Offset +=2; } if (Length !=2) { WRITE_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + Offset), *Buffer); ++Buffer; } break; case 1: WRITE_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 1), *Buffer); ++Buffer; if (Length == 2) WRITE_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 2), *Buffer); if (Length == 3) WRITE_REGISTER_USHORT ((PULONG) (((PUCHAR)HalpPciConfigData) + 2), *(PUSHORT)Buffer); break; case 2: if (Length < 2) { WRITE_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 2), *Buffer); } else { WRITE_REGISTER_USHORT ((PULONG) (((PUCHAR)HalpPciConfigData) + 2), *(PUSHORT)Buffer); } break; case 3: WRITE_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 1), *Buffer); break; } } /*++ Routine Description: Partial read in the PCI config space ( less than one ULONG) Arguments: Return Value: --*/ BOOLEAN HalpPCIConfigPartialRead( IN ULONG Offset, IN ULONG Length, IN PUCHAR Buffer ) { switch(Offset) { case 0: if (Length > 1) { * (PUSHORT) Buffer = READ_REGISTER_USHORT ((PULONG) HalpPciConfigData); if (* (PUSHORT) Buffer == 0XFFFF) return FALSE; Buffer +=2; Offset+=2; } if (Length !=2) { * Buffer = READ_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + Offset)); if (* Buffer == 0XFF) return FALSE; } break; case 1: * Buffer = READ_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 1)); if (* Buffer == 0XFF) return FALSE; ++Buffer; if (Length == 2) { * Buffer = READ_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 2)); if (* Buffer == 0XFF) return FALSE; } else { if (Length == 3) { * (PUSHORT) Buffer = READ_REGISTER_USHORT ((PULONG) (((PUCHAR)HalpPciConfigData) + 2)); if (* (PUSHORT) Buffer == 0XFFFF) return FALSE; } } break; case 2: if (Length < 2) { * Buffer = READ_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 2)); if (* Buffer == 0XFF) return FALSE; } else { * (PUSHORT) Buffer = READ_REGISTER_USHORT ((PULONG) (((PUCHAR)HalpPciConfigData) + 2)); if (* (PUSHORT) Buffer == 0XFFFF) return FALSE; } break; case 3: * Buffer = READ_REGISTER_UCHAR ((PULONG) (((PUCHAR)HalpPciConfigData) + 1)); ++Buffer; if (* Buffer == 0XFF) return FALSE; break; } return TRUE ; } /*++ Routine Description: Arguments: Return Value: --*/ PCI_CONFIGURATION_TYPES HalpPCIConfigCycleType ( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ) { // // Determine if Type0, Type1, or Invalid // if (BusNumber < 0 || BusNumber > (PCIMaxBuses - 1)) { return PciConfigTypeInvalid; } else if (BusNumber) { return PciConfigType1; } else { return PciConfigType0; } } /*++ Routine Description: Update interrupt line : the read value in the PCI config space is form 0 to 6. Each device needs a unique system interrupt number. This number is given by this routine. The array HalpInterruptLine keeps the given system value. Arguments: Return Value: --*/ VOID HalpIntLineUpdate( IN ULONG BusNumber, IN ULONG Slot, PPCI_COMMON_CONFIG PciData ) { if (HalpInterruptLine[BusNumber][Slot] == 0) { switch (PciData->u.type0.InterruptLine) { case 1: if (HalpIntAMax > INTA_VECTOR + 50) { DebugPrint(("More than 50 PCI Devices connected to INTA\n")); #if DBG DbgBreakPoint(); #endif } HalpInterruptLine[BusNumber][Slot] = HalpIntAMax; ++ HalpIntAMax; break; case 2: if (HalpIntBMax > INTB_VECTOR + 50) { DebugPrint(("More than 50 PCI Devices connected to INTB\n")); #if DBG DbgBreakPoint(); #endif } HalpInterruptLine[BusNumber][Slot] = HalpIntBMax; ++ HalpIntBMax; break; case 3: if (HalpIntCMax > INTC_VECTOR + 50) { DebugPrint(("More than 50 PCI Devices connected to INTC\n")); #if DBG DbgBreakPoint(); #endif } HalpInterruptLine[BusNumber][Slot] = HalpIntCMax; ++ HalpIntCMax; break; case 4: if (HalpIntDMax > INTD_VECTOR + 50) { DebugPrint(("More than 50 PCI Devices connected to INTD\n")); #if DBG DbgBreakPoint(); #endif } HalpInterruptLine[BusNumber][Slot] = HalpIntDMax; ++ HalpIntDMax; break; case 5: HalpInterruptLine[BusNumber][Slot] = SCSI_VECTOR; break; case 6: HalpInterruptLine[BusNumber][Slot] = NET_LEVEL; break; } } PciData->u.type0.InterruptLine = HalpInterruptLine[BusNumber][Slot]; } BOOLEAN HalpTowerTestConf( IN ULONG BusNumber, IN PCI_SLOT_NUMBER Slot ) /*++ Routine Description Only for tower. An access to an empty slot will do an exception if we don't care. Arguments None Return value True : slot ok False : slot not ok ++*/ { ULONG PciData,Buffer,SaveReg; PCI_CONFIG_ADDR PciConfigAddrData; if (!HalpIsTowerPci) return TRUE; PciData = PCI_TOWER_INTERRUPT_OFFSET | 0x80000000; WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciData)); // Save PCI interrupt register SaveReg = READ_REGISTER_ULONG ((PULONG) HalpPciConfigData); // reset MAUI PCI error, set flag for exception routine PciData = PI_RESET ; WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG) &PciData)); // write to vendor-id (ro) to see if exception appears. PciConfigAddrData.Type = (BusNumber ? PciConfigType1 : PciConfigType0); PciConfigAddrData.BusNumber = BusNumber; PciConfigAddrData.DeviceNumber = Slot.u.bits.DeviceNumber; PciConfigAddrData.FunctionNumber = 0; PciConfigAddrData.Reserved = 0; PciConfigAddrData.Enable = 1; PciConfigAddrData.RegisterNumber = 0; // ULONG frontier WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciConfigAddrData)); PciData = 0xffffffff; WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG) &PciData)); // read MAUI PCI error PciData = PCI_TOWER_INTERRUPT_OFFSET | 0x80000000; WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciData)); Buffer = READ_REGISTER_ULONG ((PULONG) HalpPciConfigData); if ( Buffer & PI_CPU_PCI_TIMO) { // reset MAUI PCI error, set flag for exception routine PciData = PCI_TOWER_INTERRUPT_OFFSET | 0x80000000; WRITE_REGISTER_ULONG(HalpPciConfigAddr, *((PULONG) &PciData)); PciData = PI_RESET ; WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG) &PciData)); // restore PCI interrupt PciData = SaveReg; WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG) &PciData)); return FALSE; } else { // restore PCI interrupt PciData = SaveReg; WRITE_REGISTER_ULONG ((PULONG) HalpPciConfigData, *((PULONG) &PciData)); return TRUE; } } /*++ 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: Return Value: STATUS_SUCCESS or error --*/ NTSTATUS HalpAssignPCISlotResources ( IN ULONG BusNumber, IN PUNICODE_STRING RegistryPath, IN PUNICODE_STRING DriverClassName OPTIONAL, IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN ULONG Slot, IN OUT PCM_RESOURCE_LIST *pAllocatedResources ) { NTSTATUS status; PUCHAR WorkingPool; PPCI_COMMON_CONFIG PciData; PCI_SLOT_NUMBER PciSlot; PCM_RESOURCE_LIST CmRes; PCM_PARTIAL_RESOURCE_DESCRIPTOR CmDescriptor; ULONG i, j, length, memtype, BaseAd, BaseAd2, Offset; ULONG BaseAddresses[10]; ULONG cnt, len; BOOLEAN conflict; *pAllocatedResources = NULL; PciSlot = *((PPCI_SLOT_NUMBER) &Slot); // // Allocate some pool for working space // i = sizeof (CM_RESOURCE_LIST) + sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR) * (PCI_TYPE0_ADDRESSES + 2) + PCI_COMMON_HDR_LENGTH * 2; WorkingPool = (PUCHAR) ExAllocatePool (PagedPool, i); if (!WorkingPool) { return STATUS_NO_MEMORY; } // // Zero initialize pool, and get pointers into memory // RtlZeroMemory (WorkingPool, i); CmRes = (PCM_RESOURCE_LIST) WorkingPool; PciData = (PPCI_COMMON_CONFIG) (WorkingPool + i - PCI_COMMON_HDR_LENGTH ); // // Read the PCI device's configuration // HalpReadPCIConfig (BusNumber, PciSlot, (PUCHAR) PciData, 0, PCI_COMMON_HDR_LENGTH); if (PciData->VendorID == PCI_INVALID_VENDORID || PciData->VendorID == 0x00) { ExFreePool (WorkingPool); return STATUS_NO_SUCH_DEVICE; } // // Update interrupt line // HalpIntLineUpdate(BusNumber, Slot, PciData); // // Build an CM_RESOURCE_LIST for the PCI device to report resources // to IoReportResourceUsage. // // This code does *not* use IoAssignResources, as the PCI // address space resources have been previously assigned by the ARC firmware // CmRes->Count = 1; CmRes->List[0].InterfaceType = PCIBus; CmRes->List[0].BusNumber = BusNumber; CmRes->List[0].PartialResourceList.Count = 0; // // Set current CM_RESOURCE_LIST version and revision // CmRes->List[0].PartialResourceList.Version = 0; CmRes->List[0].PartialResourceList.Revision = 0; CmDescriptor = CmRes->List[0].PartialResourceList.PartialDescriptors; // // If PCI device has an interrupt resource, add it // if (PciData->u.type0.InterruptPin) { CmDescriptor->Type = CmResourceTypeInterrupt; CmDescriptor->ShareDisposition = CmResourceShareShared; CmDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; CmDescriptor->u.Interrupt.Level = PciData->u.type0.InterruptLine; CmDescriptor->u.Interrupt.Vector = PciData->u.type0.InterruptLine; CmRes->List[0].PartialResourceList.Count++; CmDescriptor++; } // // Add a memory/port resource for each PCI resource // Offset = FIELD_OFFSET(PCI_COMMON_CONFIG,u.type0.BaseAddresses[0]); for (j=0; j < PCI_TYPE0_ADDRESSES; j++,Offset += sizeof(LONG)) { // // Pb concerning especially SCSI : skip IO address when = 3bf0000 // if ( (((ULONG)(PciData->u.type0.BaseAddresses[j]) & ~0x3) | 0x01) != 0x3bf0001 ) { BaseAddresses[j] = 0xFFFFFFFF; HalpWritePCIConfig (BusNumber, PciSlot,(PUCHAR)(&(BaseAddresses[j])), Offset, sizeof(LONG)); HalpReadPCIConfig (BusNumber, PciSlot, (PUCHAR)(&(BaseAddresses[j])), Offset, sizeof(LONG)); BaseAd = BaseAddresses[j]; if (BaseAd) { // // calculate the length necessary - // memory : the four less significant bits are only indicators // IO : the two less significant bits are indicators // length = 1 << ( BaseAd & PCI_ADDRESS_IO_SPACE ? 2 : 4); // mask the indicator bits while ( !( BaseAd & length ) && length ) { length <<= 1; } // now length => less significant bit set to 1. // scan for the most significant bit set to 1 if (BaseAd & PCI_ADDRESS_IO_SPACE) { memtype = 0; BaseAd2 = (ULONG)(PciData->u.type0.BaseAddresses[j]) & ~0x3; // BaseAd2 |= PCI_IO_BASE; CmDescriptor->Type = CmResourceTypePort; CmDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; CmDescriptor->Flags = CM_RESOURCE_PORT_IO; CmDescriptor->u.Port.Length = length; CmDescriptor->u.Port.Start.LowPart = BaseAd2; } else { memtype = BaseAd & PCI_ADDRESS_MEMORY_TYPE_MASK; BaseAd2 = (ULONG)(PciData->u.type0.BaseAddresses[j]) & ~0xf; CmDescriptor->Type = CmResourceTypeMemory; CmDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; CmDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE; CmDescriptor->u.Memory.Length = length; CmDescriptor->u.Memory.Start.LowPart = BaseAd2; } CmRes->List[0].PartialResourceList.Count++; CmDescriptor++; HalpWritePCIConfig (BusNumber, PciSlot, (PUCHAR)(&(PciData->u.type0.BaseAddresses[j])), Offset, sizeof(ULONG)); } } else { DebugPrint(("HalAssignResources : skip 0x3bf0001\n")); } } // // Setup the resource list. // Count only the acquired resources. // *pAllocatedResources = CmRes; cnt = CmRes->List[0].PartialResourceList.Count; len = sizeof (CM_RESOURCE_LIST) + cnt * sizeof (CM_PARTIAL_RESOURCE_DESCRIPTOR); #if DBG DbgPrint("HalAssignSlotResources: Acq. Resourses = %d (len %x list %x\n)", cnt, len, *pAllocatedResources); #endif // // Report the IO resource assignments // if (!DeviceObject) { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject *pAllocatedResources, // DriverList len, // DriverListSize DeviceObject, // DeviceObject NULL, // DeviceList 0, // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } else { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject NULL, // DriverList 0, // DriverListSize DeviceObject, *pAllocatedResources, // DeviceList len, // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } if (NT_SUCCESS(status) && conflict) { // // IopReportResourceUsage saw a conflict? // #if DBG DbgPrint("HalAssignSlotResources: IoAssignResources detected a conflict: %x\n", status); #endif status = STATUS_CONFLICTING_ADDRESSES; if (!DeviceObject) { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject (PCM_RESOURCE_LIST) &i, // DriverList sizeof (i), // DriverListSize DeviceObject, NULL, // DeviceList 0, // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } else { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject NULL, // DriverList 0, // DriverListSize DeviceObject, (PCM_RESOURCE_LIST) &i, // DeviceList sizeof (i), // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } ExFreePool (*pAllocatedResources); *pAllocatedResources = NULL; } if (!NT_SUCCESS(status)) { #if DBG DbgPrint("HalAssignSlotResources: IoAssignResources failed: %x\n", status); #endif if (!DeviceObject) { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject (PCM_RESOURCE_LIST) &i, // DriverList sizeof (i), // DriverListSize DeviceObject, NULL, // DeviceList 0, // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } else { status = IoReportResourceUsage ( DriverClassName, DriverObject, // DriverObject NULL, // DriverList 0, // DriverListSize DeviceObject, (PCM_RESOURCE_LIST) &i, // DeviceList sizeof (i), // DeviceListSize FALSE, // override conflict &conflict // conflicted detected ); } ExFreePool (*pAllocatedResources); *pAllocatedResources = NULL; } // ExFreePool (WorkingPool); return status; } /*++ Routine Description: Arguments: Return Value: --*/ NTSTATUS HalpAdjustPCIResourceList ( IN ULONG BusNumber, IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList ) { UCHAR buffer[PCI_COMMON_HDR_LENGTH]; PCI_SLOT_NUMBER PciSlot; PPCI_COMMON_CONFIG PciData; LARGE_INTEGER liIo, liMem; PIO_RESOURCE_REQUIREMENTS_LIST CompleteList; PIO_RESOURCE_LIST ResourceList; PIO_RESOURCE_DESCRIPTOR Descriptor; ULONG alt, cnt; return STATUS_SUCCESS; liIo = RtlConvertUlongToLargeInteger (PCI_MAX_IO_ADDRESS); liMem = RtlConvertUlongToLargeInteger (PCI_MAX_MEMORY_ADDRESS); // // First, shrink to limits // HalpAdjustResourceListUpperLimits (pResourceList, liIo, // IO Maximum Address liMem, // Memory Maximum Address PCI_MAX_INTERRUPT_VECTOR, // irq 0xffff); // dma // // Fix any requested IRQs for this device to be the // support value for this device. // PciSlot = *((PPCI_SLOT_NUMBER) &(*pResourceList)->SlotNumber), PciData = (PPCI_COMMON_CONFIG) buffer; HalGetBusData ( PCIConfiguration, BusNumber, PciSlot.u.AsULONG, PciData, PCI_COMMON_HDR_LENGTH); if (PciData->VendorID == PCI_INVALID_VENDORID || PCI_CONFIG_TYPE (PciData) != 0) { return STATUS_UNSUCCESSFUL; } CompleteList = *pResourceList; ResourceList = CompleteList->List; for (alt=0; alt < CompleteList->AlternativeLists; alt++) { Descriptor = ResourceList->Descriptors; for (cnt = ResourceList->Count; cnt; cnt--) { switch (Descriptor->Type) { case CmResourceTypeInterrupt: // // Interrupt lines on a PCI device can not move. // Make sure the request fits within the PCI device's // requirements. // if (Descriptor->u.Interrupt.MinimumVector > PciData->u.type0.InterruptLine || Descriptor->u.Interrupt.MaximumVector < PciData->u.type0.InterruptLine) { // // descriptor doesn't fit requirements // return STATUS_UNSUCCESSFUL; } // // Fix the interrupt at the HAL programed routing // Descriptor->u.Interrupt.MinimumVector = PciData->u.type0.InterruptLine; Descriptor->u.Interrupt.MaximumVector = PciData->u.type0.InterruptLine; break; case CmResourceTypePort: break; case CmResourceTypeMemory: // // Check for prefetchable memory // if ( Descriptor->Flags & CM_RESOURCE_MEMORY_PREFETCHABLE) { // Set upper limit to max dense space address Descriptor->u.Memory.MinimumAddress.HighPart = 0; Descriptor->u.Memory.MinimumAddress.LowPart = PCI_MIN_DENSE_MEMORY_ADDRESS; Descriptor->u.Memory.MaximumAddress.HighPart = 0; Descriptor->u.Memory.MaximumAddress.LowPart = PCI_MAX_DENSE_MEMORY_ADDRESS; } break; default: return STATUS_UNSUCCESSFUL; } // // Next descriptor // Descriptor++; } // // Next Resource List // ResourceList = (PIO_RESOURCE_LIST) Descriptor; } return STATUS_SUCCESS; } /*++ Routine Description: --*/ VOID HalpAdjustResourceListUpperLimits ( IN OUT PIO_RESOURCE_REQUIREMENTS_LIST *pResourceList, IN LARGE_INTEGER MaximumPortAddress, IN LARGE_INTEGER MaximumMemoryAddress, IN ULONG MaximumInterruptVector, IN ULONG MaximumDmaChannel ) { PIO_RESOURCE_REQUIREMENTS_LIST CompleteList; PIO_RESOURCE_LIST ResourceList; PIO_RESOURCE_DESCRIPTOR Descriptor; ULONG alt, cnt; // // Walk each ResourceList and shrink any values to system limits // CompleteList = *pResourceList; ResourceList = CompleteList->List; for (alt=0; alt < CompleteList->AlternativeLists; alt++) { Descriptor = ResourceList->Descriptors; for (cnt = ResourceList->Count; cnt; cnt--) { // // Make sure descriptor limits fall within the // CompleteList->InterfaceType & CompleteList->BusNumber. // // switch (Descriptor->Type) { case CmResourceTypePort: if (Descriptor->u.Port.MaximumAddress.QuadPart > MaximumPortAddress.QuadPart) { Descriptor->u.Port.MaximumAddress = MaximumPortAddress; } break; case CmResourceTypeInterrupt: if (Descriptor->u.Interrupt.MaximumVector > MaximumInterruptVector ) { Descriptor->u.Interrupt.MaximumVector = MaximumInterruptVector; } break; case CmResourceTypeMemory: if (Descriptor->u.Memory.MaximumAddress.QuadPart > MaximumMemoryAddress.QuadPart) { Descriptor->u.Memory.MaximumAddress = MaximumMemoryAddress; } break; case CmResourceTypeDma: if (Descriptor->u.Dma.MaximumChannel > MaximumDmaChannel ) { Descriptor->u.Dma.MaximumChannel = MaximumDmaChannel; } break; } // // Next descriptor // Descriptor++; } // // Next Resource List // ResourceList = (PIO_RESOURCE_LIST) Descriptor; } }