/*++ Copyright (c) 1995 Microsoft Corporation Module Name: isolate.c Abstract: Author: Shie-Lin Tzong (shielint) July-10-1995 Environment: Kernel mode only. Revision History: --*/ #include "busp.h" #include "pbios.h" #include "pnpisa.h" BOOLEAN PipFindIrqInformation ( IN ULONG IrqLevel, IN PUCHAR BiosRequirements, OUT PUCHAR Information ); BOOLEAN PipFindMemoryInformation ( IN ULONG Base, IN ULONG Limit, IN PUCHAR BiosRequirements, OUT PUCHAR NameTag, OUT PUCHAR Information ); BOOLEAN PipFindIoPortInformation ( IN ULONG BaseAddress, IN PUCHAR BiosRequirements, OUT PUCHAR Information, OUT PUCHAR Alignment, OUT PUCHAR RangeLength ); VOID PipReadCardResourceDataBytes ( IN USHORT BytesToRead, IN PUCHAR Buffer ); // // Internal type definitions // typedef struct _MEMORY_DESC_{ ULONG Base; ULONG Length; BOOLEAN Memory32; } MEMORY_DESC, *PMEMORY_DESC; typedef struct _IRQ_DESC_{ UCHAR Level; ULONG Type; }IRQ_DESC, *PIRQ_DESC; #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,PipFindIrqInformation) #pragma alloc_text(INIT,PipFindMemoryInformation) #pragma alloc_text(INIT,PipFindIoPortInformation) #pragma alloc_text(INIT,PipReadCardResourceData) #pragma alloc_text(INIT,PipReadDeviceBootResourceData) #pragma alloc_text(INIT,PipWriteDeviceBootResourceData) #pragma alloc_text(INIT,PipLFSRInitiation) #pragma alloc_text(INIT,PipIsolateCards) #pragma alloc_text(INIT,PipFindNextLogicalDeviceTag) #pragma alloc_text(INIT,PipSelectLogicalDevice) #pragma alloc_text(INIT,PipReadCardResourceDataBytes) #endif BOOLEAN PipFindIrqInformation ( IN ULONG IrqLevel, IN PUCHAR BiosRequirements, OUT PUCHAR Information ) /*++ Routine Description: This routine searches the Bios resource requirement lists for the corresponding Irq descriptor information. The search stops when we encounter another logical device id tag or the END tag. On input, the BiosRequirements points to current logical id tag. Arguments: IrqLevel - Supplies the irq level. BiosRequirements - Supplies a pointer to the bios resource requirement lists. This parameter must point to the logical device Id tag. Information - supplies a pointer to a UCHAR to receive the port information/flags. Return Value: TRUE - if memory information found. Else False. --*/ { UCHAR tag; ULONG increment; USHORT irqMask; PPNP_IRQ_DESCRIPTOR biosDesc; // // Skip current logical id tag // tag = *BiosRequirements; ASSERT((tag & SMALL_TAG_MASK) == TAG_LOGICAL_ID); BiosRequirements += (tag & SMALL_TAG_SIZE_MASK) + 1; // // Search the possible resource list to get the information // for the Irq. // irqMask = 1 << IrqLevel; tag = *BiosRequirements; while ((tag != TAG_COMPLETE_END) && ((tag & SMALL_TAG_MASK) != TAG_LOGICAL_ID)) { if ((tag & SMALL_TAG_MASK) == TAG_IRQ) { biosDesc = (PPNP_IRQ_DESCRIPTOR)BiosRequirements; if (biosDesc->IrqMask & irqMask) { if ((tag & SMALL_TAG_SIZE_MASK) == 2) { // // if no irq info is available, a value of zero is returned. // (o is not a valid irq information.) // *Information = 0; } else { *Information = biosDesc->Information; } return TRUE; } } increment = (tag & SMALL_TAG_SIZE_MASK) + 1; BiosRequirements += increment; tag = *BiosRequirements; } return FALSE; } BOOLEAN PipFindMemoryInformation ( IN ULONG BaseAddress, IN ULONG Limit, IN PUCHAR BiosRequirements, OUT PUCHAR NameTag, OUT PUCHAR Information ) /*++ Routine Description: This routine searches the Bios resource requirement lists for the corresponding memory descriptor information. The search stops when we encounter another logical device id tag or the END tag. Note, the memory range specified by Base and Limit must be within a single Pnp ISA memory descriptor. Arguments: BaseAddress - Supplies the base address of the memory range. Limit - Supplies the upper limit of the memory range. BiosRequirements - Supplies a pointer to the bios resource requirement lists. This parameter must point to the logical device Id tag. NameTag - Supplies a variable to receive the Tag of the memory descriptor which describes the memory information. Information - supplies a pointer to a UCHAR to receive the memory information for the specified memory range. Return Value: TRUE - if memory information found. Else False. --*/ { UCHAR tag; BOOLEAN found = FALSE; ULONG minAddr, length, maxAddr, alignment; USHORT increment; // // Skip current logical id tag. // tag = *BiosRequirements; ASSERT((tag & SMALL_TAG_MASK) == TAG_LOGICAL_ID); BiosRequirements += (tag & SMALL_TAG_SIZE_MASK) + 1; // // Search the possible resource list to get the information // for the memory range described by Base and Limit. // tag = *BiosRequirements; while ((tag != TAG_COMPLETE_END) && ((tag & SMALL_TAG_MASK) != TAG_LOGICAL_ID)) { switch (tag) { case TAG_MEMORY: minAddr = ((ULONG)(((PPNP_MEMORY_DESCRIPTOR)BiosRequirements)->MinimumAddress)) << 8; length = ((ULONG)(((PPNP_MEMORY_DESCRIPTOR)BiosRequirements)->MemorySize)) << 8; maxAddr = (((ULONG)(((PPNP_MEMORY_DESCRIPTOR)BiosRequirements)->MaximumAddress)) << 8) + length - 1; break; case TAG_MEMORY32: length = ((PPNP_MEMORY32_DESCRIPTOR)BiosRequirements)->MemorySize; minAddr = ((PPNP_MEMORY32_DESCRIPTOR)BiosRequirements)->MinimumAddress; maxAddr = ((PPNP_MEMORY32_DESCRIPTOR)BiosRequirements)->MaximumAddress + length - 1; break; case TAG_MEMORY32_FIXED: length = ((PPNP_FIXED_MEMORY32_DESCRIPTOR)BiosRequirements)->MemorySize; minAddr = ((PPNP_FIXED_MEMORY32_DESCRIPTOR)BiosRequirements)->BaseAddress; maxAddr = minAddr + length - 1; break; } if (minAddr <= BaseAddress && maxAddr >= Limit) { *Information = ((PPNP_MEMORY32_DESCRIPTOR)BiosRequirements)->Information; *NameTag = tag; found = TRUE; break; } // // Advance to next tag // if (tag & LARGE_RESOURCE_TAG) { increment = *(PUCHAR)(BiosRequirements + 1); increment += 3; // length of large tag } else { increment = tag & SMALL_TAG_SIZE_MASK; increment += 1; // length of small tag } BiosRequirements += increment; tag = *BiosRequirements; } return found; } BOOLEAN PipFindIoPortInformation ( IN ULONG BaseAddress, IN PUCHAR BiosRequirements, OUT PUCHAR Information, OUT PUCHAR Alignment, OUT PUCHAR RangeLength ) /*++ Routine Description: This routine searches the Bios resource requirement lists for the corresponding Io port descriptor information. The search stops when we encounter another logical device id tag or the END tag. Arguments: BaseAddress - Supplies the base address of the Io port range. BiosRequirements - Supplies a pointer to the bios resource requirement lists. This parameter must point to the logical device Id tag. Information - supplies a pointer to a UCHAR to receive the port information/flags. Alignment - supplies a pointer to a UCHAR to receive the port alignment information. RangeLength - supplies a pointer to a UCHAR to receive the port range length information. Return Value: TRUE - if memory information found. Else False. --*/ { UCHAR tag; BOOLEAN found = FALSE; ULONG minAddr, length, maxAddr, alignment; USHORT increment; PPNP_PORT_DESCRIPTOR portDesc; PPNP_FIXED_PORT_DESCRIPTOR fixedPortDesc; tag = *BiosRequirements; ASSERT((tag & SMALL_TAG_MASK) == TAG_LOGICAL_ID); BiosRequirements += (tag & SMALL_TAG_SIZE_MASK) + 1; // // Search the possible resource list to get the information // for the io port range described by Base. // tag = *BiosRequirements; while ((tag != TAG_COMPLETE_END) && ((tag & SMALL_TAG_MASK) != TAG_LOGICAL_ID)) { switch (tag & SMALL_TAG_MASK) { case TAG_IO: portDesc = (PPNP_PORT_DESCRIPTOR)BiosRequirements; minAddr = portDesc->MinimumAddress; maxAddr = portDesc->MaximumAddress; if (minAddr <= BaseAddress && maxAddr >= BaseAddress) { *Information = portDesc->Information; *Alignment = portDesc->Alignment; *RangeLength = portDesc->Length; found = TRUE; } break; case TAG_IO_FIXED: fixedPortDesc = (PPNP_FIXED_PORT_DESCRIPTOR)BiosRequirements; minAddr = fixedPortDesc->MinimumAddress; if (BaseAddress == minAddr) { *Information = 0; // 10 bit decode *Alignment = 1; *RangeLength = fixedPortDesc->Length; found = TRUE; } break; } if (found) { break; } // // Advance to next tag // if (tag & LARGE_RESOURCE_TAG) { increment = *(PUCHAR)(BiosRequirements + 1); increment += 3; // length of large tag } else { increment = tag & SMALL_TAG_SIZE_MASK; increment += 1; // length of small tag } BiosRequirements += increment; tag = *BiosRequirements; } return found; } NTSTATUS PipReadCardResourceData ( IN ULONG Csn, OUT PULONG NumberLogicalDevices, IN PUCHAR *ResourceData, OUT PULONG ResourceDataLength ) /*++ Routine Description: This routine reads resources data from a specified PnP ISA card. It is caller's responsibility to release the memory. Before calling this routine, the Pnp ISA card should be in sleep state (i.e. Initiation Key was sent.) After exiting this routine, the card will be left in Config state. Arguments: Csn - Specifies the CardSelectNumber to indicate which PNP ISA card. NumberLogicalDevices - supplies a variable to receive the number of logical devices associated with the Pnp Isa card. ResourceData - Supplies a variable to receive the pointer to the resource data. ResourceDataLength - Supplies a variable to receive the length of the ResourceData. Return Value: NT STATUS code. --*/ { PUCHAR buffer, p; LONG sizeToRead, limit, i; USHORT size; UCHAR tag; ULONG noDevices; BOOLEAN failed; // // Allocate memory to store the resource data. // N.B. The buffer size should cover 99.999% of the machines. // sizeToRead = 4096; tryAgain: noDevices = 0; buffer = (PUCHAR)ExAllocatePoolWithTag(PagedPool, sizeToRead, 'iPnP'); if (!buffer) { return STATUS_INSUFFICIENT_RESOURCES; } // // Send card from sleep state to configuration state // Note, by doing this the resource data includes 9 bytes Id. // PipWriteAddress (WAKE_CSN_PORT); PipWriteData((UCHAR)Csn); // // Read card id bytes // p = buffer; PipReadCardResourceDataBytes(NUMBER_CARD_ID_BYTES, p); i = NUMBER_CARD_ID_BYTES; p += NUMBER_CARD_ID_BYTES; // // read all the tag descriptors of the card resource data // failed = FALSE; limit = sizeToRead - 4 - NUMBER_CARD_ID_BYTES;; while (TRUE) { // // Read tag byte. Make sure it's a valid tag and determine // the size of the descriptor. // PipReadCardResourceDataBytes(1, p); tag = *p; i++; p++; if (tag == TAG_COMPLETE_END) { PipReadCardResourceDataBytes(1, p); p++; i++; break; } if (tag & LARGE_RESOURCE_TAG) { if (tag & 0x70) { failed = TRUE; break; } else { PipReadCardResourceDataBytes(2, p); size = *((PUSHORT)p); p += 2; i += 2; } } else { if ((tag & 0x70) == 0x50 || (tag & 0x70) == 0x60 || (((tag & 0x70) == 0) && (tag != 0xa))) { failed = TRUE; break; } else { if ((tag & SMALL_TAG_MASK) == TAG_LOGICAL_ID) { noDevices++; } size = (USHORT)(tag & SMALL_TAG_SIZE_MASK); } } // // read 'size' number of bytes for the current descriptor // i += size; if (i < limit) { PipReadCardResourceDataBytes(size, p); p += size; } else { ExFreePool(buffer); sizeToRead <<= 1; // double the buffer // // If we can find the END tag with 32K byte, assume the resource // requirement list is bad. // if (sizeToRead > 0x80000) { return STATUS_INVALID_PARAMETER; } else { goto tryAgain; } } } if (failed) { ExFreePool(buffer); return STATUS_UNSUCCESSFUL; } // // Determine the real size of the buffer required and // resize the buffer. // size = p - buffer; // i p = (PUCHAR)ExAllocatePoolWithTag(PagedPool, size, 'iPnP'); if (p) { RtlMoveMemory(p, buffer, size); ExFreePool(buffer); } else { // // Fail to resize the buffer. Simply leave it alone. // p = buffer; } // // Should we leave card in config state??? // *ResourceData = p; *NumberLogicalDevices = noDevices; *ResourceDataLength = size; return STATUS_SUCCESS; } NTSTATUS PipReadDeviceBootResourceData ( IN ULONG BusNumber, IN PUCHAR BiosRequirements, OUT PCM_RESOURCE_LIST *ResourceData, OUT PULONG Length ) /*++ Routine Description: This routine reads boot resource data from an enabled logical device of a PNP ISA card. Caller must put the card into configuration state and select the logical device before calling this function. It is caller's responsibility to release the memory. ( The boot resource data is the resources that a card assigned during boot.) Arguments: BusNumber - specifies the bus number of the device whose resource data to be read. BiosRequirements - Supplies a pointer to the resource requirement list for the logical device. This parameter must point to the logical device Id tag. ResourceData - Supplies a variable to receive the pointer to the resource data. Length - Supplies a variable to recieve the length of the resource data. Return Value: NT STATUS code. --*/ { UCHAR c, junk1, junk2; PUCHAR base; ULONG l, resourceCount; BOOLEAN limit; LONG i, j, noMemoryDesc = 0, noIoDesc = 0, noDmaDesc =0, noIrqDesc = 0; MEMORY_DESC memoryDesc[NUMBER_MEMORY_DESCRIPTORS + NUMBER_32_MEMORY_DESCRIPTORS]; IRQ_DESC irqDesc[NUMBER_IRQ_DESCRIPTORS]; UCHAR dmaDesc[NUMBER_DMA_DESCRIPTORS]; USHORT ioDesc[NUMBER_IO_DESCRIPTORS]; PCM_RESOURCE_LIST cmResource; PCM_PARTIAL_RESOURCE_LIST partialResList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDesc; #if 0 PipWriteAddress(ACTIVATE_PORT); if (!(PipReadData() & 1)) { DebugPrint((DEBUG_MESSAGE, "PnpIsa-ReadDeviceBootResourceData:The logical device has not been activated\n")); } #endif // DBG // // First make sure the specified BiosRequirements is valid and at the right tag. // if (!BiosRequirements && ((*BiosRequirements & SMALL_TAG_MASK) != TAG_LOGICAL_ID)) { return STATUS_INVALID_PARAMETER; } // // Read memory configuration // base = (PUCHAR)ADDRESS_MEMORY_BASE; for (i = 0; i < NUMBER_MEMORY_DESCRIPTORS; i++) { // // Read memory base address // PipWriteAddress(base + ADDRESS_MEMORY_HI); c = PipReadData(); l = c; l <<= 8; PipWriteAddress(base + ADDRESS_MEMORY_LO); c = PipReadData(); l |= c; l <<= 8; // l = memory base address if (l == 0) { break; } memoryDesc[noMemoryDesc].Base = l; // // Read memory control byte // PipWriteAddress(base + ADDRESS_MEMORY_CTL); c= PipReadData(); limit = c & 1; // // Read memory upper limit address or range length // PipWriteAddress(base + ADDRESS_MEMORY_UPPER_HI); c = PipReadData(); l = c; l <<= 8; PipWriteAddress(base + ADDRESS_MEMORY_UPPER_LO); c = PipReadData(); l |= c; l <<= 8; if (limit == ADDRESS_MEMORY_CTL_LIMIT) { l = l - memoryDesc[noMemoryDesc].Base; } memoryDesc[noMemoryDesc].Length = l; memoryDesc[noMemoryDesc].Memory32 = FALSE; noMemoryDesc++; base += ADDRESS_MEMORY_INCR; } // // Read memory 32 configuration // for (i = 0; i < NUMBER_32_MEMORY_DESCRIPTORS; i++) { base = ADDRESS_32_MEMORY_BASE(i); // // Read memory base address // l = 0; for (j = ADDRESS_32_MEMORY_B3; j <= ADDRESS_32_MEMORY_B0; j++) { PipWriteAddress(base + j); c = PipReadData(); l <<= 8; l |= c; } if (l == 0) { break; } memoryDesc[noMemoryDesc].Base = l; // // Read memory control byte // PipWriteAddress(base + ADDRESS_32_MEMORY_CTL); c= PipReadData(); limit = c & 1; // // Read memory upper limit address or range length // l = 0; for (j = ADDRESS_32_MEMORY_E3; j <= ADDRESS_32_MEMORY_E0; j++) { PipWriteAddress(base + j); c = PipReadData(); l <<= 8; l |= c; } if (limit == ADDRESS_MEMORY_CTL_LIMIT) { l = l - memoryDesc[noMemoryDesc].Base; } memoryDesc[noMemoryDesc].Length = l; memoryDesc[noMemoryDesc].Memory32 = TRUE; noMemoryDesc++; } // // Read Io Port Configuration // base = (PUCHAR)ADDRESS_IO_BASE; for (i = 0; i < NUMBER_IO_DESCRIPTORS; i++) { PipWriteAddress(base + ADDRESS_IO_BASE_HI); c = PipReadData(); l = c; PipWriteAddress(base + ADDRESS_IO_BASE_LO); c = PipReadData(); l <<= 8; l |= c; if (l == 0) { break; } ioDesc[noIoDesc++] = (USHORT)l; base += ADDRESS_IO_INCR; } // // Read Interrupt configuration // base = (PUCHAR)ADDRESS_IRQ_BASE; for (i = 0; i < NUMBER_IRQ_DESCRIPTORS; i++) { PipWriteAddress(base + ADDRESS_IRQ_VALUE); c = PipReadData() & 0xf; if (c == 0) { break; } irqDesc[noIrqDesc].Level = c; PipWriteAddress(base + ADDRESS_IRQ_TYPE); c = PipReadData(); irqDesc[noIrqDesc++].Type = c; base += ADDRESS_IRQ_INCR; } // // Read DMA configuration // base = (PUCHAR)ADDRESS_DMA_BASE; for (i = 0; i < NUMBER_DMA_DESCRIPTORS; i++) { PipWriteAddress(base + ADDRESS_DMA_VALUE); c = PipReadData() & 0x7; if (c == 4) { break; } dmaDesc[noDmaDesc++] = c; base += ADDRESS_DMA_INCR; } // // Construct CM_RESOURCE_LIST structure based on the resource data // we collect so far. // resourceCount = noMemoryDesc + noIoDesc + noDmaDesc + noIrqDesc; // // if empty bios resources, simply return. // if (resourceCount == 0) { *ResourceData = NULL; *Length = 0; return STATUS_SUCCESS; } l = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * ( resourceCount - 1); cmResource = ExAllocatePoolWithTag(PagedPool, l, 'iPnP'); if (!cmResource) { return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(cmResource, l); *Length = l; // Set returned resource data length cmResource->Count = 1; cmResource->List[0].InterfaceType = Isa; cmResource->List[0].BusNumber = BusNumber; partialResList = (PCM_PARTIAL_RESOURCE_LIST)&cmResource->List[0].PartialResourceList; partialResList->Version = 0; partialResList->Revision = 0x3000; partialResList->Count = resourceCount; partialDesc = (PCM_PARTIAL_RESOURCE_DESCRIPTOR)&partialResList->PartialDescriptors[0]; // // Set up all the CM memory descriptors // for (i = 0; i < noMemoryDesc; i++) { partialDesc->Type = CmResourceTypeMemory; partialDesc->ShareDisposition = CmResourceShareDeviceExclusive; partialDesc->u.Memory.Length = memoryDesc[i].Length; partialDesc->u.Memory.Start.HighPart = 0; partialDesc->u.Memory.Start.LowPart = memoryDesc[i].Base; // // Need to consult configuration data for the Flags // l = memoryDesc[i].Base + memoryDesc[i].Length - 1; if (PipFindMemoryInformation (memoryDesc[i].Base, l, BiosRequirements, &junk1, &c)) { if (c & PNP_MEMORY_WRITE_STATUS_MASK) { partialDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE; } else { partialDesc->Flags = CM_RESOURCE_MEMORY_READ_ONLY; } } else { DebugPrint((DEBUG_BREAK, "PnpIsa-ReadDeviceBootResourceData:No matched memory information in config data\n")); partialDesc->Flags = CM_RESOURCE_MEMORY_READ_WRITE; } if (memoryDesc[i].Memory32 == FALSE) { partialDesc->Flags |= CM_RESOURCE_MEMORY_24; } partialDesc++; } // // Set up all the CM io/port descriptors // for (i = 0; i < noIoDesc; i++) { partialDesc->Type = CmResourceTypePort; partialDesc->ShareDisposition = CmResourceShareDeviceExclusive; partialDesc->Flags = CM_RESOURCE_PORT_IO; partialDesc->u.Port.Start.LowPart = ioDesc[i]; // // Need to consult configuration data for the Port length // if (PipFindIoPortInformation (ioDesc[i], BiosRequirements, &junk1, &junk2, &c)) { partialDesc->u.Port.Length = c; partialDesc++; } else { DebugPrint((DEBUG_BREAK, "PnpIsa-ReadDeviceBootResourceData:No matched port length in config data\n")); ExFreePool(cmResource); *ResourceData = NULL; return STATUS_UNSUCCESSFUL; } } // // Set up all the CM DMA descriptors // for (i = 0; i < noDmaDesc; i++) { partialDesc->Type = CmResourceTypeDma; partialDesc->ShareDisposition = CmResourceShareDeviceExclusive; partialDesc->Flags = 0; // no flags for DMA descriptor partialDesc->u.Dma.Channel = (ULONG) dmaDesc[i]; partialDesc->u.Dma.Port = 0; partialDesc->u.Dma.Reserved1 = 0; partialDesc++; } // // Set up all the CM interrupt descriptors // for (i = 0; i < noIrqDesc; i++) { partialDesc->Type = CmResourceTypeInterrupt; partialDesc->ShareDisposition = CmResourceShareDeviceExclusive; if (irqDesc[i].Type & 1) { partialDesc->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; } else { partialDesc->Flags = CM_RESOURCE_INTERRUPT_LATCHED; } partialDesc->u.Interrupt.Vector = partialDesc->u.Interrupt.Level = irqDesc[i].Level; partialDesc->u.Interrupt.Affinity = (ULONG)-1; partialDesc++; } *ResourceData = cmResource; return STATUS_SUCCESS; } NTSTATUS PipWriteDeviceBootResourceData ( IN PUCHAR BiosRequirements, IN PCM_RESOURCE_LIST CmResources ) /*++ Routine Description: This routine writes boot resource data to an enabled logical device of a Pnp ISA card. Caller must put the card into configuration state and select the logical device before calling this function. Arguments: BiosRequirements - Supplies a pointer to the possible resources for the logical device. This parameter must point to the logical device Id tag. ResourceData - Supplies a pointer to the cm resource data. Return Value: NT STATUS code. --*/ { UCHAR c, information; ULONG count, i, j, pass, base, limit; PCM_PARTIAL_RESOURCE_DESCRIPTOR cmDesc; ULONG noIrq =0, noIo = 0, noDma = 0, noMemory = 0, no32Memory = 0; PUCHAR memoryBase, irqBase, dmaBase, ioBase, tmp; ULONG memory32Base; #if 0 PipWriteAddress(ACTIVATE_PORT); if (!(PipReadData() & 1)) { DbgPrint("PnpIsa-WriteDeviceBootResourceData:The logical device has not been activated\n"); } #endif // DBG // // First make sure the specified BiosRequirements is valid and at the right tag. // if ((*BiosRequirements & SMALL_TAG_MASK) != TAG_LOGICAL_ID) { return STATUS_INVALID_PARAMETER; } count = CmResources->List[0].PartialResourceList.Count; memoryBase = (PUCHAR)ADDRESS_MEMORY_BASE; memory32Base = 0; ioBase = (PUCHAR)ADDRESS_IO_BASE; irqBase = (PUCHAR)ADDRESS_IRQ_BASE; dmaBase = (PUCHAR)ADDRESS_DMA_BASE; for (pass = 1; pass <= 2; pass++) { // // First pass we make sure the resources to be set is acceptable. // Second pass we actually write the resources to the logical device's // configuration space. // cmDesc = CmResources->List[0].PartialResourceList.PartialDescriptors; for (i = 0; i < count; i++) { switch (cmDesc->Type) { case CmResourceTypePort: if (pass == 1) { noIo++; if (noIo > NUMBER_IO_DESCRIPTORS || cmDesc->u.Port.Start.HighPart != 0 || cmDesc->u.Port.Start.LowPart & 0xffff0000 || cmDesc->u.Port.Length & 0xffffff00) { return STATUS_INVALID_PARAMETER; } } else { // // Set the Io port base address to logical device configuration space // c = (UCHAR)cmDesc->u.Port.Start.LowPart; PipWriteAddress(ioBase + ADDRESS_IO_BASE_LO); PipWriteData(c); c = (UCHAR)(cmDesc->u.Port.Start.LowPart >> 8); PipWriteAddress(ioBase + ADDRESS_IO_BASE_HI); PipWriteData(c); ioBase += ADDRESS_IO_INCR; } break; case CmResourceTypeInterrupt: if (pass == 1) { noIrq++; if (noIrq > NUMBER_IRQ_DESCRIPTORS || (cmDesc->u.Interrupt.Level & 0xfffffff0)) { return STATUS_INVALID_PARAMETER; } // // See if we can get the interrupt information from possible resource // data. We need it to set the configuration register. // if (!PipFindIrqInformation(cmDesc->u.Interrupt.Level, BiosRequirements, &information)) { return STATUS_INVALID_PARAMETER; } } else { // // Set the Irq to logical device configuration space // c = (UCHAR)cmDesc->u.Interrupt.Level; PipWriteAddress(irqBase + ADDRESS_IRQ_VALUE); PipWriteData(c); PipFindIrqInformation(cmDesc->u.Interrupt.Level, BiosRequirements, &information); if (information != 0) { switch (information & 0xf) { case 1: // High true edge sensitive c = 2; break; case 2: // Low true edge sensitive c = 0; break; case 4: // High true level sensitive c = 3; break; case 8: // Low true level sensitive c = 1; break; } PipWriteAddress(irqBase + ADDRESS_IRQ_TYPE); PipWriteData(c); } irqBase += ADDRESS_IRQ_INCR; } break; case CmResourceTypeDma: if (pass == 1) { noDma++; if (noDma > NUMBER_IRQ_DESCRIPTORS || (cmDesc->u.Dma.Channel & 0xfffffff8)) { return STATUS_INVALID_PARAMETER; } } else { // // Set the Dma channel to logical device configuration space // c = (UCHAR)cmDesc->u.Dma.Channel; PipWriteAddress(dmaBase + ADDRESS_DMA_VALUE); PipWriteData(c); dmaBase += ADDRESS_DMA_INCR; } break; case CmResourceTypeMemory: if (pass == 1) { base = cmDesc->u.Memory.Start.LowPart; limit = base + cmDesc->u.Memory.Length - 1; if (!PipFindMemoryInformation(base, limit, BiosRequirements, &c, &information)) { return STATUS_INVALID_PARAMETER; } else { if (cmDesc->Flags & CM_RESOURCE_MEMORY_24) { noMemory++; // // Make sure the lower 8 bits of the base address are zero. // if (noMemory > NUMBER_MEMORY_DESCRIPTORS || base & 0xff) { return STATUS_INVALID_PARAMETER; } } else { no32Memory++; if (no32Memory > NUMBER_32_MEMORY_DESCRIPTORS) { return STATUS_INVALID_PARAMETER; } } } } else { // // Find information in BiosRequirements to help determine how to write // the memory configuration space. // base = cmDesc->u.Memory.Start.LowPart; limit = base + cmDesc->u.Memory.Length - 1; PipFindMemoryInformation(base, limit, BiosRequirements, &c, &information); if (cmDesc->Flags & CM_RESOURCE_MEMORY_24) { PipWriteAddress(memoryBase + ADDRESS_MEMORY_LO); base >>= 8; PipWriteData(base); PipWriteAddress(memoryBase + ADDRESS_MEMORY_HI); base >>= 8; PipWriteData(base); PipWriteAddress(memoryBase + ADDRESS_MEMORY_CTL); c = 2 + ADDRESS_MEMORY_CTL_LIMIT; // assume 16 bit memory if (information & 0x18 == 0) { // 8 bit memory only c = 0 + ADDRESS_MEMORY_CTL_LIMIT; } PipWriteData(c); limit >>= 8; PipWriteAddress(memoryBase + ADDRESS_MEMORY_UPPER_LO); PipWriteData((UCHAR)limit); limit >>= 8; PipWriteAddress(memoryBase + ADDRESS_MEMORY_UPPER_HI); PipWriteData((UCHAR)limit); memoryBase += ADDRESS_MEMORY_INCR; } else { tmp = ADDRESS_32_MEMORY_BASE(memory32Base); PipWriteAddress(tmp + ADDRESS_32_MEMORY_B0); PipWriteData(base); PipWriteAddress(tmp + ADDRESS_32_MEMORY_B1); base >>= 8; PipWriteData(base); PipWriteAddress(tmp + ADDRESS_32_MEMORY_B2); base >>= 8; PipWriteData(base); PipWriteAddress(tmp + ADDRESS_32_MEMORY_B3); base >>= 8; PipWriteData(base); switch (information & 0x18) { case 0: // 8 bit only c = ADDRESS_MEMORY_CTL_LIMIT; case 8: // 16 bit only case 0x10: // 8 and 16 bit supported c = 2 + ADDRESS_MEMORY_CTL_LIMIT; break; case 0x18: // 32 bit only c = 6 + ADDRESS_MEMORY_CTL_LIMIT; break; } PipWriteAddress(ADDRESS_32_MEMORY_CTL); PipWriteData(c); PipWriteAddress(tmp + ADDRESS_32_MEMORY_E0); PipWriteData(limit); limit >>= 8; PipWriteAddress(tmp + ADDRESS_32_MEMORY_E1); PipWriteData(limit); limit >>= 8; PipWriteAddress(tmp + ADDRESS_32_MEMORY_E2); PipWriteData(limit); limit >>= 8; PipWriteAddress(tmp + ADDRESS_32_MEMORY_E3); PipWriteData(limit); memory32Base++; } } } cmDesc++; } } // // Finally, mark all the unused descriptors as disabled. // for (i = noMemory; i < NUMBER_MEMORY_DESCRIPTORS; i++) { for (j = 0; j < 5; j++) { PipWriteAddress(memoryBase + j); PipWriteData(0); } memoryBase += ADDRESS_MEMORY_INCR; } for (i = no32Memory; i < NUMBER_32_MEMORY_DESCRIPTORS; i++) { tmp = ADDRESS_32_MEMORY_BASE(memory32Base); for (j = 0; j < 9; j++) { PipWriteAddress(tmp + j); PipWriteData(0); } memory32Base++; } for (i = noIo; i < NUMBER_IO_DESCRIPTORS; i++) { for (j = 0; j < 2; j++) { PipWriteAddress(ioBase + j); PipWriteData(0); } ioBase += ADDRESS_IO_INCR; } for (i = noIrq; i < NUMBER_IRQ_DESCRIPTORS; i++) { for (j = 0; j < 2; j++) { PipWriteAddress(irqBase + j); PipWriteData(0); } irqBase += ADDRESS_IRQ_INCR; } for (i = noDma; i < NUMBER_DMA_DESCRIPTORS; i++) { PipWriteAddress(dmaBase); PipWriteData(4); dmaBase += ADDRESS_DMA_INCR; } return STATUS_SUCCESS; } VOID PipLFSRInitiation ( VOID ) /*++ Routine Description: This routine insures the LFSR (linear feedback shift register) is in its initial state and then performs 32 writes to the ADDRESS port to initiation LFSR function. Pnp software sends the initiation key to all the Pnp ISA cards to place them into configuration mode. The software then ready to perform isolation protocol. Arguments: None. Return Value: None. --*/ { UCHAR seed, bit7; ULONG i; // // First perform two writes of value zero to insure the LFSR is in the // initial state. // PipWriteAddress (0); PipWriteAddress (0); // // Perform the initiation key. // seed = LFSR_SEED; // initial value of 0x6a for (i = 0; i < 32; i++) { PipWriteAddress (seed); bit7=(((seed & 2) >> 1) ^ (seed & 1)) << 7; seed =(seed >> 1) | bit7; } } VOID PipIsolateCards ( OUT PULONG NumberCSNs ) /*++ Routine Description: This routine performs PnP ISA cards isolation sequence. Arguments: NumberCSNs - supplies the addr of a variable to receive the number of Pnp Isa cards isolated. ReadDataPort - Supplies the address of a variable to supply ReadData port address. Return Value: None. --*/ { USHORT j, i; UCHAR cardId[NUMBER_CARD_ID_BYTES]; UCHAR bit, bit7, checksum, byte1, byte2; ULONG csn = 0; *NumberCSNs = 0; // // First send Initiation Key to all the PNP ISA cards to enable PnP auto-config // ports and put all cards in configuration mode. // PipLFSRInitiation (); // // Reset all Pnp ISA cards' CSN to 0 and return to wait-for-key state // PipWriteAddress (CONFIG_CONTROL_PORT); PipWriteData (CONTROL_WAIT_FOR_KEY | CONTROL_RESET_CSN); // // Delay 2 msec for cards to load initial configuration state. // KeStallExecutionProcessor(20000); // delay 2 msec // // Put cards into configuration mode to ready isolation process. // The hardware on each PnP Isa card expects 72 pairs of I/O read // access to the read data port. // PipLFSRInitiation (); // // Starting Pnp Isa card isolation process. // // // Send WAKE[CSN=0] to force all cards without CSN into isolation // state to set READ DATA PORT. // PipWriteAddress(WAKE_CSN_PORT); PipWriteData(0); // // Set read data port to current testing value. // PipWriteAddress(SET_READ_DATA_PORT); PipWriteData((UCHAR)((ULONG)PipReadDataPort >> 2)); // // Isolate one PnP ISA card until fail // while (TRUE) { // // Read serial isolation port to cause PnP cards in the isolation // state to compare one bit of the boards ID. // PipWriteAddress(SERIAL_ISOLATION_PORT); // // We need to delay 1 msec prior to starting the first pair of isolation // reads and must wait 250usec between each subsequent pair of isolation // reads. This delay gives the ISA cards time to access information from // possible very slow storage device. // KeStallExecutionProcessor(10000); // delay 1 msec RtlZeroMemory(cardId, NUMBER_CARD_ID_BYTES); checksum = LFSR_SEED; for (j = 0; j < NUMBER_CARD_ID_BITS; j++) { // // Read card id bit by bit // byte1 = PipReadData(); byte2 = PipReadData(); bit = (byte1 == ISOLATION_TEST_BYTE_1) && (byte2 == ISOLATION_TEST_BYTE_2); cardId[j / 8] |= bit << (j % 8); if (j < CHECKSUMED_BITS) { // // Calculate checksum and only do it for the first 64 bits // bit7 = (((checksum & 2) >> 1) ^ (checksum & 1) ^ (bit)) << 7; checksum = (checksum >> 1) | bit7; } KeStallExecutionProcessor(2500); // delay 250 usec } // // Verify the card id we read is legitimate // First make sure checksum is valid. Note zero checksum is considered valid. // if (cardId[8] == 0 || checksum == cardId[8]) { // // Next make sure cardId is not zero // byte1 = 0; for (j = 0; j < NUMBER_CARD_ID_BYTES; j++) { byte1 |= cardId[j]; } if (byte1 != 0) { // // We found a valid Pnp Isa card, assign it a CSN number // PipWriteAddress(SET_CSN_PORT); PipWriteData(++csn); // // Do Wake[CSN] command to put the newly isolated card to // sleep state and other un-isolated cards to isolation // state. // PipWriteAddress(WAKE_CSN_PORT); PipWriteData(0); continue; // ... to isolate more cards ... } } break; // could not isolate more cards ... } // // Finaly put all cards into wait for key state. // PipWriteAddress(CONFIG_CONTROL_PORT); PipWriteData(CONTROL_WAIT_FOR_KEY); *NumberCSNs = csn; } ULONG PipFindNextLogicalDeviceTag ( IN OUT PUCHAR *CardData, IN OUT LONG *Limit ) /*++ Routine Description: This function searches the Pnp Isa card data for the Next logical device tag encountered. The input *CardData should point to an logical device id tag, which is the current logical device tag. If the *CardData does not point to a logical device id tag (but, it must point to some kind of tag), it will be moved to next logical device id tag. Arguments: CardData - a variable to supply a pointer to the pnp Isa resource descriptors and to receive next logical device tag pointer. Limit - a variable to supply the maximum length of the search and to receive the new lemit after the search. Return Value: Length of the data between current and next logical device tags, ie the data length of the current logical device. In case there is no 'next' logical device tag, the returned *CardData = NULL, *Limit = zero and the data length of current logical tag is returned as function returned value. --*/ { UCHAR tag; USHORT size; LONG l; ULONG retLength; PUCHAR p; BOOLEAN atIdTag = FALSE;; p = *CardData; l = *Limit; tag = *p; retLength = 0; while (tag != TAG_COMPLETE_END && l > 0) { // // Determine the size of the BIOS resource descriptor // if (!(tag & LARGE_RESOURCE_TAG)) { size = (USHORT)(tag & SMALL_TAG_SIZE_MASK); size += 1; // length of small tag } else { size = *((PUSHORT)(p + 1)); size += 3; // length of large tag } p += size; retLength += size; l -= size; tag = *p; if ((tag & SMALL_TAG_MASK) == TAG_LOGICAL_ID) { *CardData = p; *Limit = l; return retLength; } } *CardData = NULL; *Limit = 0; if (tag == TAG_COMPLETE_END) { return (retLength + 2); // add 2 for the length of end tag descriptor } else { return 0; } } VOID PipSelectLogicalDevice ( IN USHORT Csn, IN USHORT LogicalDeviceNumber, IN BOOLEAN Activate ) /*++ Routine Description: This function selects the logical logical device in the specified device. Arguments: Csn - Supplies a CardSelectNumber to select the card. LogicalDeviceNumber - supplies a logical device number to select the logical device. Activate - supplies a BOOLEAN variable to specify if the logical device should be enabled. Return Value: None --*/ { UCHAR tmp; // // Put cards into configuration mode to ready isolation process. // PipLFSRInitiation (); // // Send WAKE[CSN] to force the card into config state. // PipWriteAddress(WAKE_CSN_PORT); PipWriteData(Csn); // // Select the logical device. // PipWriteAddress(LOGICAL_DEVICE_PORT); PipWriteData(LogicalDeviceNumber); if (Activate) { // // If we need to activate the logical device, disable its io range check and // then enable the device. // PipWriteAddress(IO_RANGE_CHECK_PORT); tmp = PipReadData(); tmp &= ~2; PipWriteAddress(IO_RANGE_CHECK_PORT); PipWriteData(tmp); PipWriteAddress(ACTIVATE_PORT); PipWriteData(1); } } VOID PipReadCardResourceDataBytes ( IN USHORT BytesToRead, IN PUCHAR Buffer ) /*++ Routine Description: This function reads specified number of bytes of card resource data . Arguments: BytesToRead - supplies number of bytes to read. Buffer - supplies a pointer to a buffer to receive the read bytes. Return Value: None --*/ { USHORT i; PUCHAR p; for (i = 0, p = Buffer; i < BytesToRead; i++, p++) { PipWriteAddress(CONFIG_DATA_STATUS_PORT); // // Waiting for data ready status bit // while ((PipReadData() & 1) != 1) { } // // Read the data ... // PipWriteAddress(CONFIG_DATA_PORT); *p = PipReadData(); } }