/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: tuples.c Abstract: This module contains the code that parses and processes the configuration tuples of the PC Cards in the PCMCIA sockets Author: Bob Rinne (BobRi) 3-Aug-1994 Jeff McLeman 12-Apr-1994 Ravisankar Pudipeddi (ravisp) 1-Nov-1996 Neil Sandlin (neilsa) 1-Jun-1999 Revision History: Lotsa cleaning up. Support for PnP. orthogonalized tuple processing. Support links etc. - Ravisankar Pudipeddi (ravisp) 1-Dec-1996 Environment: Kernel mode Revision History : --*/ #include "pch.h" #define MAX_MISSED_TUPLES 256 // how many bad tuples will we tolerate? #define MAX_TUPLE_DATA_LENGTH 128 // Enough for the longest tuple BOOLEAN CheckLinkTarget( IN PTUPLE_PACKET TuplePacket ); UCHAR ConvertVoltage( UCHAR MantissaExponentByte, UCHAR ExtensionByte ); VOID PcmciaProcessPower( IN PTUPLE_PACKET TuplePacket, UCHAR FeatureByte ); VOID PcmciaProcessIoSpace( IN PTUPLE_PACKET TuplePacket, PCONFIG_ENTRY ConfigEntry ); VOID PcmciaProcessIrq( IN PTUPLE_PACKET TuplePacket, PCONFIG_ENTRY ConfigEntry ); VOID PcmciaProcessTiming( IN PTUPLE_PACKET TuplePacket, IN PCONFIG_ENTRY ConfigEntry ); VOID PcmciaProcessMemSpace( IN PTUPLE_PACKET TuplePacket, IN PCONFIG_ENTRY ConfigEntry, IN UCHAR MemSpace ); VOID PcmciaMiscFeatures( IN PTUPLE_PACKET TuplePacket ); PCONFIG_ENTRY PcmciaProcessConfigTable( IN PTUPLE_PACKET TuplePacket ); VOID ProcessConfig( IN PTUPLE_PACKET TuplePacket ); VOID ProcessConfigCB( IN PTUPLE_PACKET TuplePacket ); NTSTATUS InitializeTuplePacket( IN PTUPLE_PACKET TuplePacket ); NTSTATUS GetFirstTuple( IN PTUPLE_PACKET TuplePacket ); BOOLEAN TupleMatches( IN PTUPLE_PACKET TuplePacket ); NTSTATUS GetNextTuple( IN PTUPLE_PACKET TuplePacket ); NTSTATUS NextTupleInChain( IN PTUPLE_PACKET TuplePacket ); NTSTATUS GetAnyTuple( IN PTUPLE_PACKET TuplePacket ); NTSTATUS FollowLink( IN PTUPLE_PACKET TuplePacket ); NTSTATUS NextLink( IN PTUPLE_PACKET TuplePacket ); NTSTATUS GetTupleData( IN PTUPLE_PACKET TuplePacket ); UCHAR GetTupleChar( IN PTUPLE_PACKET TuplePacket ); NTSTATUS ProcessLinkTuple( IN PTUPLE_PACKET TuplePacket ); BOOLEAN PcmciaQuickVerifyTupleChain( IN PUCHAR Buffer, IN ULONG Length ); NTSTATUS PcmciaMemoryCardHack( IN PSOCKET Socket, PSOCKET_DATA SocketData ); VOID PcmciaCheckForRecognizedDevice( IN PSOCKET Socket, IN OUT PSOCKET_DATA SocketData ); // // Some useful macros // // VOID // PcmciaCopyIrqConfig( // IN CONFIG_ENTRY DestConfig, // IN CONFIG_ENTRY SourceConfig // ) // Routine Description: // Copies the IRQ information from SourceConfig to DestConfig // // Arguments: // // DestConfig - a pointer to the destination configuration entry // SourceConfig - a pointer to the source configuration entry // // Return Values: // None // #define PcmciaCopyIrqConfig(DestConfig, SourceConfig) \ { \ DestConfig->IrqMask = SourceConfig->IrqMask; \ DestConfig->LevelIrq = SourceConfig->LevelIrq; \ DestConfig->ShareDisposition = \ SourceConfig->ShareDisposition; \ } // // VOID // PcmciaCopyIoConfig( // IN CONFIG_ENTRY DestConfig, // IN CONFIG_ENTRY SourceConfig // ) // Routine Description: // Copies the Io space information from SourceConfig to DestConfig // // Arguments: // // DestConfig - a pointer to the destination configuration entry // SourceConfig - a pointer to the source configuration entry // // Return Values: // None // #define PcmciaCopyIoConfig(DestConfig, SourceConfig) \ { \ DestConfig->NumberOfIoPortRanges = \ SourceConfig->NumberOfIoPortRanges; \ DestConfig->Io16BitAccess = \ SourceConfig->Io16BitAccess; \ DestConfig->Io8BitAccess = \ SourceConfig->Io8BitAccess; \ RtlCopyMemory(DestConfig->IoPortBase, \ SourceConfig->IoPortBase, \ sizeof(SourceConfig->IoPortBase[0])* \ SourceConfig->NumberOfIoPortRanges); \ RtlCopyMemory(DestConfig->IoPortLength, \ SourceConfig->IoPortLength, \ sizeof(SourceConfig->IoPortLength[0])* \ SourceConfig->NumberOfIoPortRanges); \ RtlCopyMemory(DestConfig->IoPortAlignment, \ SourceConfig->IoPortAlignment, \ sizeof(SourceConfig->IoPortAlignment[0])* \ SourceConfig->NumberOfIoPortRanges); \ } // // VOID // PcmciaCopyMemConfig( // IN CONFIG_ENTRY DestConfig, // IN CONFIG_ENTRY SourceConfig // ) // Routine Description: // Copies the Memory space information from SourceConfig to DestConfig // // Arguments: // // DestConfig - a pointer to the destination configuration entry // SourceConfig - a pointer to the source configuration entry // // Return Values: // None // #define PcmciaCopyMemConfig(DestConfig,SourceConfig) \ { \ DestConfig->NumberOfMemoryRanges = \ SourceConfig->NumberOfMemoryRanges; \ RtlCopyMemory(DestConfig->MemoryHostBase, \ SourceConfig->MemoryHostBase, \ sizeof(SourceConfig->MemoryHostBase[0])* \ SourceConfig->NumberOfMemoryRanges); \ RtlCopyMemory(DestConfig->MemoryCardBase, \ SourceConfig->MemoryCardBase, \ sizeof(SourceConfig->MemoryCardBase[0])* \ SourceConfig->NumberOfMemoryRanges); \ RtlCopyMemory(DestConfig->MemoryLength, \ SourceConfig->MemoryLength, \ sizeof(SourceConfig->MemoryLength[0])* \ SourceConfig->NumberOfMemoryRanges); \ } USHORT VoltageConversionTable[16] = { 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90 }; UCHAR TplList[] = { CISTPL_DEVICE, CISTPL_VERS_1, CISTPL_CONFIG, CISTPL_CFTABLE_ENTRY, CISTPL_MANFID, CISTPL_END }; static unsigned short crc16a[] = { 0000000, 0140301, 0140601, 0000500, 0141401, 0001700, 0001200, 0141101, 0143001, 0003300, 0003600, 0143501, 0002400, 0142701, 0142201, 0002100, }; static unsigned short crc16b[] = { 0000000, 0146001, 0154001, 0012000, 0170001, 0036000, 0024000, 0162001, 0120001, 0066000, 0074000, 0132001, 0050000, 0116001, 0104001, 0043000, }; UCHAR GetCISChar( IN PTUPLE_PACKET TuplePacket, IN ULONG Offset ) /*++ Routine Description: Returns the contents of the CIS memory of the PC-Card in the given socket, at the specified offset Arguments: TuplePacket - Pointer to the initialized tuple packet Offset - Offset at which the CIS memory contents need to be read This offset is added to the current offset position of the CIS being read as indicated through the TuplePacket to obtain the actual offset Return Value: The byte at the specified offset of the CIS --*/ { PPDO_EXTENSION pdoExtension = TuplePacket->SocketData->PdoExtension; MEMORY_SPACE MemorySpace; if (Is16BitCardInSocket(pdoExtension->Socket)) { if (TuplePacket->Flags & TPLF_COMMON) { MemorySpace = (TuplePacket->Flags & TPLF_INDIRECT) ? PCCARD_COMMON_MEMORY_INDIRECT : PCCARD_COMMON_MEMORY; } else { MemorySpace = (TuplePacket->Flags & TPLF_INDIRECT) ? PCCARD_ATTRIBUTE_MEMORY_INDIRECT : PCCARD_ATTRIBUTE_MEMORY; } } else { switch((TuplePacket->Flags & TPLF_ASI) >> TPLF_ASI_SHIFT) { case 0: MemorySpace = PCCARD_PCI_CONFIGURATION_SPACE; break; case 1: MemorySpace = PCCARD_CARDBUS_BAR0; break; case 2: MemorySpace = PCCARD_CARDBUS_BAR1; break; case 3: MemorySpace = PCCARD_CARDBUS_BAR2; break; case 4: MemorySpace = PCCARD_CARDBUS_BAR3; break; case 5: MemorySpace = PCCARD_CARDBUS_BAR4; break; case 6: MemorySpace = PCCARD_CARDBUS_BAR5; break; case 7: MemorySpace = PCCARD_CARDBUS_ROM; break; } } return PcmciaReadCISChar(pdoExtension, MemorySpace, TuplePacket->CISOffset + Offset); } UCHAR ConvertVoltage( UCHAR MantissaExponentByte, UCHAR ExtensionByte ) /*++ Routine Description: Convert the voltage requirements for the PCCARD based on the mantissa and extension byte. Arguments: MantissaExponentByte ExtensionByte Return Value: The voltage specified in tenths of a volt. --*/ { SHORT power; USHORT value; value = (USHORT) VoltageConversionTable[(MantissaExponentByte >> 3) & 0x0f]; power = 1; if ((MantissaExponentByte & EXTENSION_BYTE_FOLLOWS) && (((value / 10) * 10) == value) && (ExtensionByte < 100)) { value = (10 * value + (ExtensionByte & 0x7f)); power += 1; } power = (MantissaExponentByte & 0x07) - 4 - power; while (power > 0) { value *= 10; power--; } while (power < 0) { value /= 10; power++; } return (UCHAR) value; } VOID PcmciaProcessPower( IN PTUPLE_PACKET TuplePacket, UCHAR FeatureByte ) /*++ Routine Description: Process power information from CIS. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet FeatureByte - the feature byte from the tuple containing power information. Return Value: none --*/ { PSOCKET_DATA SocketData = TuplePacket->SocketData; UCHAR powerSelect; UCHAR bit; UCHAR item; UCHAR rawItem; UCHAR extensionByte; UCHAR index = 0; UCHAR count = FeatureByte; UCHAR skipByte; ASSERT(count <= 3); while (index < count) { powerSelect = GetTupleChar(TuplePacket); for (bit = 0; bit < 7; bit++) { if (powerSelect & (1 << bit)) { rawItem = GetTupleChar(TuplePacket); if (rawItem & EXTENSION_BYTE_FOLLOWS) { extensionByte = GetTupleChar(TuplePacket); // // Skip the rest // skipByte = extensionByte; while (skipByte & EXTENSION_BYTE_FOLLOWS) { skipByte = GetTupleChar(TuplePacket); } } else { extensionByte = (UCHAR) 0; } if (bit == 0) { // // Convert nominal power for output. // item = ConvertVoltage(rawItem, extensionByte); switch (index) { case 0: SocketData->Vcc = item; break; case 1: SocketData->Vpp2 = SocketData->Vpp1 = item; break; case 2: SocketData->Vpp2 = item; break; } } } } index++; } } VOID PcmciaProcessIoSpace( IN PTUPLE_PACKET TuplePacket, PCONFIG_ENTRY ConfigEntry ) /*++ Routine Description: Process I/O space information from CIS. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet ConfigEntry - a config entry structure in which to store the information. Return Value: none --*/ { ULONG address = 0; ULONG index=0, i; UCHAR item = GetTupleChar(TuplePacket); UCHAR ioAddrLines = (item & IO_ADDRESS_LINES_MASK); UCHAR ranges=0; UCHAR addressSize=0; UCHAR lengthSize=0; ConfigEntry->Io16BitAccess = Is16BitAccess(item); ConfigEntry->Io8BitAccess = Is8BitAccess(item); ranges = HasRanges(item); if ((!ranges) && (!ioAddrLines)) { // // The IBM token ring card has a slightly different interpretation // of the tuple data here. It isn't clear it is incorrect. // ranges = 0xFF; } if (ranges) { // // Specific ranges listed in the tuple. // if (ranges == 0xFF) { // // Special processing for IBM token ring IoSpace layout. // addressSize = 2; lengthSize = 1; ranges = 1; } else { item = GetTupleChar(TuplePacket); ranges = item & RANGE_MASK; ranges++; addressSize = GetAddressSize(item); lengthSize = GetLengthSize(item); } index = 0; while (ranges) { address = 0; if (addressSize >= 1) { address = (ULONG) GetTupleChar(TuplePacket); } if (addressSize >= 2) { address |= (GetTupleChar(TuplePacket)) << 8; } if (addressSize >= 3) { address |= (GetTupleChar(TuplePacket)) << 16; address |= (GetTupleChar(TuplePacket)) << 24; } ConfigEntry->IoPortBase[index] = (USHORT) address; address = 0; if (lengthSize >= 1) { address = (ULONG) GetTupleChar(TuplePacket); } if (lengthSize >= 2) { address |= (GetTupleChar(TuplePacket)) << 8; } if (lengthSize >= 3) { address |= (GetTupleChar(TuplePacket)) << 16; address |= (GetTupleChar(TuplePacket)) << 24; } ConfigEntry->IoPortLength[index] = (USHORT) address; ConfigEntry->IoPortAlignment[index] = 1; index++; if (index == MAX_NUMBER_OF_IO_RANGES) { break; } ranges--; } ConfigEntry->NumberOfIoPortRanges = (USHORT) index; } // // Handle all combinations as specified in table on p. 80 // (Basic compatibility Layer 1, i/o space encoding guidelines) of // PC Card standard Metaformat Specification, March 1997 (PCMCIA/JEIDA) // if (ioAddrLines) { // // A modulo base specified // if (addressSize == 0) { // // No I/O Base address specified // if (lengthSize == 0) { // // No ranges specified. This is a pure modulo base case // ConfigEntry->NumberOfIoPortRanges = 1; ConfigEntry->IoPortBase[0] = 0; ConfigEntry->IoPortLength[0] = (1 << ioAddrLines)-1; ConfigEntry->IoPortAlignment[0] = (1 << ioAddrLines); } else { // // Length specified. Modulo base is used only to specify alignment. // for (i=0; i < ConfigEntry->NumberOfIoPortRanges; i++) { ConfigEntry->IoPortBase[i] = 0; ConfigEntry->IoPortAlignment[i] = (1 << ioAddrLines); } } } else { // // Alignment specified.. // This fix is for Xircom CE3 card // for (i=0; i < ConfigEntry->NumberOfIoPortRanges; i++) { if (ConfigEntry->IoPortBase[i] != 0) { // // Fixed base address supplied.... // Don't specify alignment! // continue; } ConfigEntry->IoPortAlignment[i] = (1 << ioAddrLines); } } } else { // // No Modulo Base. So specific ranges should've been specified // if (lengthSize == 0) { // // Error! Length HAS to be specified // DebugPrint((PCMCIA_DEBUG_FAIL, "PcmciaProcessIoSpace: Length not specified in TPCE_IO descriptor for PC Card\n")); } else if (addressSize == 0) { for (i = 0; i < ConfigEntry->NumberOfIoPortRanges; i++) { ConfigEntry->IoPortBase[i] = 0x0; ConfigEntry->IoPortAlignment[i] = 2; } } else { // // Proper ranges specified // Don't change anything } } } VOID PcmciaProcessIrq( IN PTUPLE_PACKET TuplePacket, PCONFIG_ENTRY ConfigEntry ) /*++ Routine Description: Process IRQ from CIS. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet ConfigEntry - a place to store the IRQ. Return Value: none --*/ { USHORT mask; UCHAR level = GetTupleChar(TuplePacket); if (!level) { // // NOTE: It looks like Future Domain messed up on this // and puts an extra zero byte into the structure. // skip it for now. // level = GetTupleChar(TuplePacket); } if (level & 0x20) { ConfigEntry->LevelIrq = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; } else { ConfigEntry->LevelIrq = CM_RESOURCE_INTERRUPT_LATCHED; } if (level & 0x80) { ConfigEntry->ShareDisposition = CmResourceShareShared; } else { ConfigEntry->ShareDisposition = CmResourceShareDeviceExclusive; } mask = level & 0x10; // // 3COM 3C589-75D5 has a peculiar problem // where mask bit is 0, but should have been 1. // Handle this case here // if ((mask==0) && ((level & 0xf) == 0)) { mask = 1; } if (mask) { // // Each bit set in the mask indicates the corresponding IRQ // (from 0-15) may be assigned to this card's interrupt req. pin // mask = (USHORT) GetTupleChar(TuplePacket); mask |= ((USHORT) GetTupleChar(TuplePacket)) << 8; ConfigEntry->IrqMask = mask; } else { ConfigEntry->IrqMask = 1 << (level & 0x0f); } } VOID PcmciaProcessTiming( IN PTUPLE_PACKET TuplePacket, IN PCONFIG_ENTRY ConfigEntry ) /*++ Routine Description: Move the data pointer around the timing information structure. No processing of this data occurs at this time. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet ConfigEntry - currently unused. Return Value: none --*/ { UCHAR item = GetTupleChar(TuplePacket); UCHAR reservedScale = (item & 0xe0) >> 5; UCHAR readyBusyScale = (item & 0x1c) >> 2; UCHAR waitScale = (item & 0x03); // // NOTE: It looks like the processing of extension bytes is not // coded correctly in this routine. // if (waitScale != 3) { item = GetTupleChar(TuplePacket); while (item & EXTENSION_BYTE_FOLLOWS) { item = GetTupleChar(TuplePacket); } } if (readyBusyScale != 7) { item = GetTupleChar(TuplePacket); while (item & EXTENSION_BYTE_FOLLOWS) { item = GetTupleChar(TuplePacket); } } if (reservedScale != 7) { item = GetTupleChar(TuplePacket); while (item & EXTENSION_BYTE_FOLLOWS) { item = GetTupleChar(TuplePacket); } } } VOID PcmciaProcessMemSpace( IN PTUPLE_PACKET TuplePacket, IN PCONFIG_ENTRY ConfigEntry, IN UCHAR MemSpace ) /*++ Routine Description: Process memory space requirements from CIS. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet ConfigEntry - the socket configuration structure. MemSpace - the memory space enumerator from the config table entry structure. Return Value: none --*/ { ULONG longValue; ULONG index; UCHAR lengthSize; UCHAR addrSize; UCHAR number; UCHAR hasHostAddress; switch (MemSpace) { case 1: { // // Only length is specified // longValue = (ULONG) GetTupleChar(TuplePacket); longValue |= ((ULONG) GetTupleChar(TuplePacket)) << 8; ConfigEntry->MemoryLength[0] = longValue * 256; ConfigEntry->NumberOfMemoryRanges++; break; } case 2: { longValue = (ULONG) GetTupleChar(TuplePacket); longValue |= ((ULONG) GetTupleChar(TuplePacket)) << 8; ConfigEntry->MemoryLength[0] = longValue * 256; longValue = (ULONG) GetTupleChar(TuplePacket); longValue |= ((ULONG) GetTupleChar(TuplePacket)) << 8; ConfigEntry->MemoryCardBase[0] = ConfigEntry->MemoryHostBase[0] = longValue * 256; ConfigEntry->NumberOfMemoryRanges++; break; } case 3: { UCHAR item = GetTupleChar(TuplePacket); lengthSize = (item & 0x18) >> 3; addrSize = (item & 0x60) >> 5; number = (item & 0x07) + 1; hasHostAddress = item & 0x80; if (number > MAX_NUMBER_OF_MEMORY_RANGES) { number = MAX_NUMBER_OF_MEMORY_RANGES; } for (index = 0; index < (ULONG) number; index++) { longValue = 0; if (lengthSize >= 1) { longValue = (ULONG) GetTupleChar(TuplePacket); } if (lengthSize >= 2) { longValue |= (GetTupleChar(TuplePacket)) << 8; } if (lengthSize == 3) { longValue |= (GetTupleChar(TuplePacket)) << 16; } ConfigEntry->MemoryLength[index] = longValue * 256; longValue = 0; if (addrSize >= 1) { longValue = (ULONG) GetTupleChar(TuplePacket); } if (addrSize >= 2) { longValue |= (GetTupleChar(TuplePacket)) << 8; } if (addrSize == 3) { longValue |= (GetTupleChar(TuplePacket)) << 16; } ConfigEntry->MemoryCardBase[index] = longValue * 256; if (hasHostAddress) { longValue = 0; if (addrSize >= 1) { longValue = (ULONG) GetTupleChar(TuplePacket); } if (addrSize >= 2) { longValue |= (GetTupleChar(TuplePacket)) << 8; } if (addrSize == 3) { longValue |= (GetTupleChar(TuplePacket)) << 16; } ConfigEntry->MemoryHostBase[index] = longValue * 256; } } ConfigEntry->NumberOfMemoryRanges = (USHORT) number; break; } } } PCONFIG_ENTRY PcmciaProcessConfigTable( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: A pointer to a config entry structure if one is created. --*/ { PSOCKET_DATA SocketData = TuplePacket->SocketData; PCONFIG_ENTRY configEntry; UCHAR item; UCHAR defaultBit; UCHAR memSpace; UCHAR power; UCHAR misc; configEntry = ExAllocatePool(NonPagedPool, sizeof(CONFIG_ENTRY)); if (!configEntry) { return NULL; } RtlZeroMemory(configEntry, sizeof(CONFIG_ENTRY)); item = GetTupleChar(TuplePacket); defaultBit = Default(item); configEntry->IndexForThisConfiguration = ConfigEntryNumber(item); if (IntFace(item)) { // // This byte indicates type of interface in tuple (i.e. io or memory) // This could be processed, but for now is just skipped. // item = GetTupleChar(TuplePacket); } item = GetTupleChar(TuplePacket); memSpace = MemSpaceInformation(item); power = PowerInformation(item); misc = MiscInformation(item); if (power) { PcmciaProcessPower(TuplePacket, power); } if (TimingInformation(item)) { PcmciaProcessTiming(TuplePacket, configEntry); } if (IoSpaceInformation(item)) { PcmciaProcessIoSpace(TuplePacket, configEntry); } else if (!defaultBit && (SocketData->DefaultConfiguration != NULL)) { PcmciaCopyIoConfig(configEntry, SocketData->DefaultConfiguration); } if (IRQInformation(item)) { PcmciaProcessIrq(TuplePacket, configEntry); } else if (!defaultBit && (SocketData->DefaultConfiguration != NULL)) { PcmciaCopyIrqConfig(configEntry,SocketData->DefaultConfiguration); } if (memSpace) { PcmciaProcessMemSpace(TuplePacket, configEntry, memSpace); } else if (!defaultBit && (SocketData->DefaultConfiguration != NULL)) { PcmciaCopyMemConfig(configEntry,SocketData->DefaultConfiguration); } if (misc) { PcmciaMiscFeatures(TuplePacket); } // need default bit processing here too if (defaultBit) { // // Save this config as the default config for this pc-card (which // may be accessed by subsequent tuples) // SocketData->DefaultConfiguration = configEntry; } // // One more configuration // SocketData->NumberOfConfigEntries++; DebugPrint((PCMCIA_DEBUG_TUPLES, "config entry %08x idx %x ccr %x\n", configEntry, configEntry->IndexForThisConfiguration, SocketData->ConfigRegisterBase )); return configEntry; } VOID PcmciaMiscFeatures( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Parse the miscellaneous features field and look for audio supported bit. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: none --*/ { PSOCKET_DATA SocketData = TuplePacket->SocketData; UCHAR item = GetTupleChar(TuplePacket); DebugPrint((PCMCIA_DEBUG_TUPLES, "TPCE_MS (%lx) is present in CISTPL_CFTABLE_ENTRY \n", item)); // // If the audio bit is set, remember this in the socket information // structure. // if (item & 0x8) { DebugPrint((PCMCIA_DEBUG_TUPLES, "Audio bit set in TPCE_MS \n")); SocketData->Audio = TRUE; } // // Step around the miscellaneous features and its extension bytes. // while (item & EXTENSION_BYTE_FOLLOWS) { item = GetTupleChar(TuplePacket); } } VOID ProcessConfig( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Parse the CISTPL_CONFIG to extract the last index value and the configuration register base for the PCCARD. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: None --*/ { PSOCKET_DATA SocketData = TuplePacket->SocketData; PUCHAR TupleData = TuplePacket->TupleData; ULONG base = 0; UCHAR widthOfBaseAddress; UCHAR widthOfRegPresentMask; UCHAR widthOfReservedArea; UCHAR widthOfInterfaceId; UCHAR index; UCHAR subtupleCount = 0; ULONG InterfaceId; widthOfBaseAddress = TpccRasz(TupleData[0]) + 1; widthOfRegPresentMask = TpccRmsz(TupleData[0]) + 1; widthOfReservedArea = TpccRfsz(TupleData[0]); ASSERT (widthOfReservedArea == 0); ASSERT (widthOfRegPresentMask <= 16); SocketData->LastEntryInCardConfig = TupleData[1]; switch (widthOfBaseAddress) { case 4: base = ((ULONG)TupleData[5] << 24); case 3: base |= ((ULONG)TupleData[4] << 16); case 2: base |= ((ULONG)TupleData[3] << 8); case 1: base |= TupleData[2]; break; default: DebugPrint((PCMCIA_DEBUG_FAIL, "ProcessConfig - bad number of bytes %d\n", widthOfBaseAddress)); break; } SocketData->ConfigRegisterBase = base; DebugPrint((PCMCIA_DEBUG_TUPLES, "ConfigRegisterBase in attribute memory is 0x%x\n", SocketData->ConfigRegisterBase)); // // Copy the register presence mask // for (index = 0; index < widthOfRegPresentMask; index++) { SocketData->RegistersPresentMask[index] = TupleData[2 + widthOfBaseAddress + index]; } DebugPrint((PCMCIA_DEBUG_TUPLES, "First Byte in RegPresentMask=%x, width is %d\n", SocketData->RegistersPresentMask[0], widthOfRegPresentMask)); // // Look for subtuples // index = 2 + widthOfBaseAddress + widthOfRegPresentMask + widthOfReservedArea; while (((index+5) < TuplePacket->TupleDataMaxLength) && (++subtupleCount <= 4) && (TupleData[index] == CCST_CIF)) { widthOfInterfaceId = ((TupleData[index+2] & 0xC0) >> 6) + 1 ; InterfaceId = 0; switch (widthOfInterfaceId) { case 4: InterfaceId = ((ULONG)TupleData[index+5] << 24); case 3: InterfaceId |= ((ULONG)TupleData[index+4] << 16); case 2: InterfaceId |= ((ULONG)TupleData[index+3] << 8); case 1: InterfaceId |= TupleData[index+2]; break; default: DebugPrint((PCMCIA_DEBUG_FAIL, "ProcessConfig - bad number of bytes %d in subtuple\n", widthOfInterfaceId)); break; } DebugPrint((PCMCIA_DEBUG_TUPLES, "Custom Interface ID %8x\n", InterfaceId)); // // Currently don't have generic code for recording sub-tuple information, // all we look for is Zoom Video. // if (InterfaceId == 0x141) { SocketData->Flags |= SDF_ZV; } index += (TupleData[index+1] + 2); } } VOID ProcessConfigCB( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Parse the CISTPL_CONFIG_CB to extract the last index value and the configuration register base for the PCCARD. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: None --*/ { PSOCKET_DATA SocketData = TuplePacket->SocketData; PUCHAR TupleData = TuplePacket->TupleData; ULONG base = 0; UCHAR widthOfFields; widthOfFields = TupleData[0]; if (widthOfFields != 3) { DebugPrint((PCMCIA_DEBUG_FAIL, "ProcessConfigCB - bad width of fields %d\n", widthOfFields)); return; } SocketData->LastEntryInCardConfig = TupleData[1]; base = ((ULONG)TupleData[5] << 24); base |= ((ULONG)TupleData[4] << 16); base |= ((ULONG)TupleData[3] << 8); base |= TupleData[2]; SocketData->ConfigRegisterBase = base; DebugPrint((PCMCIA_DEBUG_TUPLES, "ConfigRegisterBase = %08x\n", SocketData->ConfigRegisterBase)); } VOID PcmciaSubstituteUnderscore( IN OUT PUCHAR Str ) /*++ Routine description Substitutes underscores ('_' character) for invalid device id characters such as spaces & commas in the supplied string Parameters Str - The string for which the substitution is to take place in situ Return Value None --*/ { if (Str == NULL) { return; } while (*Str) { if (*Str == ' ' || *Str == ',' ) { *Str = '_'; } Str++; } } /*-------------- Tuple API starts here ----------------------------------------*/ NTSTATUS InitializeTuplePacket( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Initializes the supplied tuple packet Arguments: TuplePacket - Pointer the caller supplied tuple packet Return Value: Status --*/ { TuplePacket->Flags = TPLF_IMPLIED_LINK; TuplePacket->LinkOffset = 0; TuplePacket->CISOffset = 0; if (IsCardBusCardInSocket(TuplePacket->Socket)) { PPDO_EXTENSION pdoExtension = TuplePacket->SocketData->PdoExtension; GetPciConfigSpace(pdoExtension, CBCFG_CISPTR, &TuplePacket->CISOffset, sizeof(TuplePacket->CISOffset)); DebugPrint((PCMCIA_DEBUG_TUPLES, "CardBus CISPTR = %08x\n", TuplePacket->CISOffset)); TuplePacket->Flags = TPLF_COMMON | TPLF_IMPLIED_LINK; TuplePacket->Flags |= (TuplePacket->CISOffset & 7) << TPLF_ASI_SHIFT; TuplePacket->CISOffset &= 0x0ffffff8; TuplePacket->LinkOffset = TuplePacket->CISOffset; } return STATUS_SUCCESS; } NTSTATUS GetFirstTuple( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Retrieves the very first tuple off the pc-card Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if tuple was retrieved STATUS_NO_MORE_ENTRIES - if no tuples were found this is possible if this is a flash memory card --*/ { NTSTATUS status; status=InitializeTuplePacket(TuplePacket); if (!NT_SUCCESS(status)) { return status; } TuplePacket->TupleCode = GetCISChar(TuplePacket, 0); TuplePacket->TupleLink = GetCISChar(TuplePacket, 1); if (TuplePacket->TupleCode == CISTPL_LINKTARGET) { if ((status=FollowLink(TuplePacket)) == STATUS_NO_MORE_ENTRIES) { return status; } } else if (IsCardBusCardInSocket(TuplePacket->Socket)) { // // First tuple on Cardbus cards must be link target // return STATUS_NO_MORE_ENTRIES; } if (!NT_SUCCESS(status) || TupleMatches(TuplePacket)) { return status; } return GetNextTuple(TuplePacket); } BOOLEAN TupleMatches( PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Checks if the retrieved tuple matches what the caller requested Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: TRUE - if the tuple matches FALSE - if not --*/ { if (TuplePacket->TupleCode == TuplePacket->DesiredTuple) { return TRUE; } if (TuplePacket->DesiredTuple != 0xFF) { return FALSE; } // // Requested any tuple , but might not want link tuples // if (TuplePacket->Attributes & TPLA_RET_LINKS) { return TRUE; } return ((TuplePacket->TupleCode != CISTPL_LONGLINK_CB) && (TuplePacket->TupleCode != CISTPL_INDIRECT) && (TuplePacket->TupleCode != CISTPL_LONGLINK_MFC) && (TuplePacket->TupleCode != CISTPL_LONGLINK_A) && (TuplePacket->TupleCode != CISTPL_LONGLINK_C) && (TuplePacket->TupleCode != CISTPL_NO_LINK) && (TuplePacket->TupleCode != CISTPL_LINKTARGET)); } NTSTATUS GetNextTuple( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Retrieves the next unprocessed tuple that matches the caller requested tuple code off the pc-card Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if tuple was retrieved STATUS_NO_MORE_ENTRIES - if no more tuples were found --*/ { ULONG missCount; NTSTATUS status; for (missCount = 0; missCount < MAX_MISSED_TUPLES; missCount++) { if (((status = GetAnyTuple(TuplePacket)) != STATUS_SUCCESS) || TupleMatches(TuplePacket)) { break; } status = STATUS_NO_MORE_ENTRIES; } return status; } NTSTATUS NextTupleInChain( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Retrieves the immediately next unprocessed tuple on the pc-card Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: status --*/ { NTSTATUS status; ULONG i; UCHAR link; status = STATUS_SUCCESS; switch (GetCISChar(TuplePacket, 0)) { case CISTPL_END:{ status = STATUS_NO_MORE_ENTRIES; break; } case CISTPL_NULL: { for (i = 0; i < MAX_MISSED_TUPLES; i++) { TuplePacket->CISOffset++; if (GetCISChar(TuplePacket, 0) != CISTPL_NULL) { break; } } if (i >= MAX_MISSED_TUPLES) { status = STATUS_DEVICE_NOT_READY; } break; } default: { link = GetCISChar(TuplePacket, 1); if (link == 0xFF) { status = STATUS_NO_MORE_ENTRIES; } else { TuplePacket->CISOffset += link+2; } break; } } return (status); } NTSTATUS GetAnyTuple( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Retrieves the next tuple - regardless of tuple code- off the pc-card. If the end of chain is reached on the current tuple chain, any links are followed to obtain the next tuple. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if tuple was retrieved STATUS_NO_MORE_ENTRIES - if no tuples were found --*/ { NTSTATUS status; if (!NT_SUCCESS((status = NextTupleInChain(TuplePacket)))) { /* End of this CIS. Follow a link if it exists */ if (status == STATUS_DEVICE_NOT_READY) { return status; } if ((status = FollowLink(TuplePacket)) != STATUS_SUCCESS) { return status; } } TuplePacket->TupleCode = GetCISChar(TuplePacket, 0); TuplePacket->TupleLink = GetCISChar(TuplePacket, 1); return (ProcessLinkTuple(TuplePacket)); } NTSTATUS FollowLink( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Called when the end of tuple chain is encountered: this follows links, if any are present Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if a link is present STATUS_NO_MORE_ENTRIES - if not --*/ { if (NextLink(TuplePacket) == STATUS_SUCCESS) { return STATUS_SUCCESS; } // There is no implied or explicit link to follow. If an indirect link // has been specified, indirect attribute memory is processed with an // implied link to common memory. if ((TuplePacket->Flags & TPLF_IND_LINK) && !(TuplePacket->Flags & TPLF_INDIRECT)) { // Link to indirect attribute memory at offset 0. TuplePacket->Flags &= ~(TPLF_COMMON | TPLF_IND_LINK | TPLF_LINK_MASK); TuplePacket->Flags |= TPLF_INDIRECT; TuplePacket->CISOffset = 0; if (CheckLinkTarget(TuplePacket)) { return STATUS_SUCCESS; } return(NextLink(TuplePacket)); } return STATUS_NO_MORE_ENTRIES; } BOOLEAN CheckLinkTarget( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Ensures that the target of a link has the signature 'CIS' which indicates it is a valid target, as documented in the PC-Card standard Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if valid target STATUS_NO_MORE_ENTRIES - if not --*/ { return (GetCISChar(TuplePacket, 0) == CISTPL_LINKTARGET && GetCISChar(TuplePacket, 1) >= 3 && GetCISChar(TuplePacket, 2) == 'C' && GetCISChar(TuplePacket, 3) == 'I' && GetCISChar(TuplePacket, 4) == 'S'); } NTSTATUS NextLink( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Fetches the next link off the pc-card tuple chain if any are present Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if a link was present STATUS_NO_MORE_ENTRIES - if no links --*/ { switch (TuplePacket->Flags & TPLF_LINK_MASK) { case TPLF_IMPLIED_LINK: case TPLF_LINK_TO_C: { TuplePacket->Flags |= TPLF_COMMON; TuplePacket->CISOffset = TuplePacket->LinkOffset; break; } case TPLF_LINK_TO_A:{ TuplePacket->Flags &= ~TPLF_COMMON; TuplePacket->CISOffset = TuplePacket->LinkOffset; break; } case TPLF_LINK_TO_CB: { // // Needs work! We have to switch to the appropriate // address space (BARs/Expansion Rom/Config space) // depending on the link offset // TuplePacket->Flags &= ~TPLF_ASI; TuplePacket->Flags |= (TuplePacket->LinkOffset & 7) << TPLF_ASI_SHIFT; TuplePacket->CISOffset = TuplePacket->LinkOffset & ~7 ; break; } case TPLF_NO_LINK: default: { return STATUS_NO_MORE_ENTRIES; } } // Validate the link target if (!CheckLinkTarget (TuplePacket)) { if (TuplePacket->Flags & (TPLF_COMMON | TPLF_INDIRECT)) { return(STATUS_NO_MORE_ENTRIES); } // The R2 PCMCIA spec was not clear on how the link off // memory was defined. As a result the offset is often // by 2 as defined in the later specs. Therefore if th // not found at the proper offset, the offset is divide // proper link target is checked for at that offset. TuplePacket->CISOffset >>= 1; // Divide by 2 if (!CheckLinkTarget(TuplePacket)) { return(STATUS_NO_MORE_ENTRIES); } return STATUS_NO_MORE_ENTRIES; } TuplePacket->Flags &= ~TPLF_LINK_MASK; return STATUS_SUCCESS; } NTSTATUS ProcessLinkTuple( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Processes an encountered link while traversing the tuple chain by storing it for future use - when the link has to be followed after end of chain is encountered Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS --*/ { ULONG k; switch (TuplePacket->TupleCode) { case CISTPL_LONGLINK_CB: { // needs to be filled in if (TuplePacket->TupleLink < 4) { return (STATUS_NO_MORE_ENTRIES); } TuplePacket->Flags = (TuplePacket->Flags & ~TPLF_LINK_MASK) | TPLF_LINK_TO_CB; TuplePacket->LinkOffset = GetCISChar(TuplePacket, TPLL_ADDR) + (GetCISChar(TuplePacket, TPLL_ADDR + 1)<<8) + (GetCISChar(TuplePacket, TPLL_ADDR + 2)<<16) + (GetCISChar(TuplePacket, TPLL_ADDR + 3)<<24); break; } case CISTPL_INDIRECT: { TuplePacket->Flags |= TPLF_IND_LINK; TuplePacket->LinkOffset = 0; // Don't set link offset for indirect SetPdoFlag(TuplePacket->SocketData->PdoExtension, PCMCIA_PDO_INDIRECT_CIS); break; } case CISTPL_LONGLINK_A: case CISTPL_LONGLINK_C: { if (TuplePacket->TupleLink < 4) { return STATUS_NO_MORE_ENTRIES; } TuplePacket->Flags = ((TuplePacket->Flags & ~TPLF_LINK_MASK) | (TuplePacket->TupleCode == CISTPL_LONGLINK_A ? TPLF_LINK_TO_A: TPLF_LINK_TO_C)); TuplePacket->LinkOffset = GetCISChar(TuplePacket, TPLL_ADDR) + (GetCISChar(TuplePacket, TPLL_ADDR+1) << 8) + (GetCISChar(TuplePacket, TPLL_ADDR+2) << 16) + (GetCISChar(TuplePacket, TPLL_ADDR+3) << 24) ; break; } case CISTPL_LONGLINK_MFC:{ k = TPLMFC_NUM; TuplePacket->Socket->NumberOfFunctions = GetCISChar(TuplePacket, TPLMFC_NUM); if ((TuplePacket->TupleLink < (TuplePacket->Function*5 + 6)) || (GetCISChar(TuplePacket, k) <= TuplePacket->Function)) { return STATUS_NO_MORE_ENTRIES; } k += TuplePacket->Function*5 + 1; TuplePacket->Flags = (TuplePacket->Flags & ~TPLF_LINK_MASK) | (GetCISChar(TuplePacket, k) == 0?TPLF_LINK_TO_A: TPLF_LINK_TO_C); k++; TuplePacket->LinkOffset = GetCISChar(TuplePacket, k) + (GetCISChar(TuplePacket, k+1) << 8) + (GetCISChar(TuplePacket, k+2) << 16) + (GetCISChar(TuplePacket, k+3) << 24); break; } case CISTPL_NO_LINK:{ TuplePacket->Flags = (TuplePacket->Flags & ~TPLF_LINK_MASK) | TPLF_NO_LINK; break; } } return STATUS_SUCCESS; } NTSTATUS GetTupleData( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Retrieves the tuple body for the currently requested tuple. NOTE: This function assumes that the caller provided a big enough buffer in the TuplePacket to hold the tuple data. No attempt is made to trap exceptions etc. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: STATUS_SUCCESS if tuple data was retrieved STATUS_NO_MORE_ENTRIES - otherwise --*/ { PUCHAR bufferPointer; USHORT xferLength; USHORT tupleOffset; TuplePacket->TupleDataIndex = 0; xferLength = TuplePacket->TupleDataLength = GetCISChar(TuplePacket, 1); if ((tupleOffset = TuplePacket->TupleOffset) > xferLength) { return STATUS_NO_MORE_ENTRIES; } xferLength = MIN((xferLength - tupleOffset), TuplePacket->TupleDataMaxLength); for (bufferPointer = TuplePacket->TupleData; xferLength; tupleOffset++, bufferPointer++, xferLength--) { *bufferPointer = GetCISChar(TuplePacket, tupleOffset + 2); } return STATUS_SUCCESS; } UCHAR GetTupleChar( IN PTUPLE_PACKET TuplePacket ) /*++ Routine Description: Returns the next byte in the current set of tuple data. Arguments: TuplePacket - Pointer the caller supplied, initialized tuple packet Return Value: tuple data byte --*/ { UCHAR tupleChar = 0; if (TuplePacket->TupleDataIndex < TuplePacket->TupleDataMaxLength) { tupleChar = TuplePacket->TupleData[TuplePacket->TupleDataIndex++]; } return tupleChar; } /*------------- End of Tuple API -----------------------*/ USHORT GetCRC( IN PSOCKET_DATA SocketData ) /*++ Routine Description: Using the same algorithm as Windows 95, calculate the CRC value to be appended with the manufacturer name and device name to obtain the unique identifier for the PCCARD. Arguments: Socket - Pointer to the socket which contains the device Function - function number of device Return Value: A USHORT CRC value. --*/ { PSOCKET Socket = SocketData->Socket; TUPLE_PACKET tuplePacket; PUCHAR tupleData; PUCHAR cp; PUCHAR cpEnd; PUCHAR tplBuffer; NTSTATUS status; USHORT crc = 0; USHORT index; USHORT length; UCHAR tupleCode; UCHAR tmp; RtlZeroMemory(&tuplePacket, sizeof(TUPLE_PACKET)); tuplePacket.DesiredTuple = 0xFF; tuplePacket.TupleData = ExAllocatePool(NonPagedPool, MAX_TUPLE_DATA_LENGTH); if (tuplePacket.TupleData == NULL) { return 0; } tuplePacket.Socket = Socket; tuplePacket.SocketData = SocketData; tuplePacket.TupleDataMaxLength = MAX_TUPLE_DATA_LENGTH; tuplePacket.TupleOffset = 0; tuplePacket.Function = SocketData->Function; try{ status = GetFirstTuple(&tuplePacket); // // Calculate CRC // while (NT_SUCCESS(status)) { tupleCode = tuplePacket.TupleCode; for (index = 0; TplList[index] != CISTPL_END; index++) { if (tupleCode == TplList[index]) { status = GetTupleData(&tuplePacket); if (!NT_SUCCESS(status)) { // // Bail... // crc = 0; leave; }; tupleData = tuplePacket.TupleData; length = tuplePacket.TupleDataLength; // // This one is included in the CRC calculation // if (tupleCode == CISTPL_VERS_1) { cp = tupleData + 2; cpEnd = tupleData + MAX_TUPLE_DATA_LENGTH; // // Include all of the manufacturer name. // while ((cp < cpEnd) && *cp) { cp++; } // // Include the product string // cp++; while ((cp < cpEnd) && *cp) { cp++; } cp++; length = (USHORT)(cp - tupleData); } if (length >= MAX_TUPLE_DATA_LENGTH) { crc = 0; leave; } for (cp = tupleData; length; length--, cp++) { tmp = *cp ^ (UCHAR)crc; crc = (crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4]; } break; } } status = GetNextTuple(&tuplePacket); } } finally { if (tuplePacket.TupleData) { ExFreePool(tuplePacket.TupleData); } } DebugPrint((PCMCIA_DEBUG_TUPLES, "Checksum=%x\n", crc)); return crc; } NTSTATUS PcmciaParseFunctionData( IN PSOCKET Socket, IN PSOCKET_DATA SocketData ) /*++ Routine Description Parses the tuple data for the supplied function (SocketPtr->Function) Arguments: Socket - Pointer to the socket which contains the device SocketData - Pointer to the socket data structure for the function which will be filled with the parsed information Return Value: Status --*/ { PCONFIG_ENTRY configEntry, prevEntry = NULL; TUPLE_PACKET tuplePacket; NTSTATUS status; if (SocketData->Function >= Socket->NumberOfFunctions) { return STATUS_NO_MORE_ENTRIES; } if (Is16BitCardInSocket(Socket)) { // // Get the CIS checksum // SocketData->CisCrc = GetCRC(SocketData); } RtlZeroMemory(&tuplePacket, sizeof(TUPLE_PACKET)); tuplePacket.DesiredTuple = 0xFF; tuplePacket.TupleData = ExAllocatePool(PagedPool, MAX_TUPLE_DATA_LENGTH); if (tuplePacket.TupleData == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } tuplePacket.Socket = Socket; tuplePacket.SocketData = SocketData; tuplePacket.TupleDataMaxLength = MAX_TUPLE_DATA_LENGTH; tuplePacket.TupleOffset = 0; tuplePacket.Function = SocketData->Function; status = GetFirstTuple(&tuplePacket); if (!NT_SUCCESS(status) || (tuplePacket.TupleCode == CISTPL_END)) { if (IsCardBusCardInSocket(Socket)) { // // Couldn't get CIS of cardbus card, no big deal // status = STATUS_SUCCESS; } else if (status != STATUS_DEVICE_NOT_READY) { // // No CIS, munge it to look like a memory card // status = PcmciaMemoryCardHack(Socket, SocketData); } if (tuplePacket.TupleData) { ExFreePool(tuplePacket.TupleData); } return status; } while (NT_SUCCESS(status)) { status = GetTupleData(&tuplePacket); ASSERT (NT_SUCCESS(status)); DebugPrint((PCMCIA_DEBUG_TUPLES, "%04x TUPLE %02x %s\n", tuplePacket.CISOffset, tuplePacket.TupleCode, TUPLE_STRING(tuplePacket.TupleCode))); switch (tuplePacket.TupleCode) { case CISTPL_VERS_1: { ULONG byteCount; PUCHAR pStart, pCurrent; // // Extract manufacturer name and card name. // pStart = pCurrent = tuplePacket.TupleData+2; // To string fields byteCount = 0; while ((*pCurrent != '\0') && (*pCurrent != (UCHAR)0xff)) { if ((byteCount >= MAX_MANFID_LENGTH-1) || (byteCount >= MAX_TUPLE_DATA_LENGTH)) { status = STATUS_DEVICE_NOT_READY; break; } byteCount++; pCurrent++; } if (!NT_SUCCESS(status)) { break; } RtlCopyMemory((PUCHAR)SocketData->Mfg, pStart, byteCount); // // Null terminate SocketData->Mfg[byteCount] = '\0'; DebugPrint((PCMCIA_DEBUG_TUPLES, "Manufacturer: %s\n", SocketData->Mfg)); PcmciaSubstituteUnderscore(SocketData->Mfg); pCurrent++; pStart = pCurrent; byteCount = 0; while ((*pCurrent != '\0') && (*pCurrent != (UCHAR)0xff)) { if ((byteCount >= MAX_IDENT_LENGTH-1) || (byteCount >= MAX_TUPLE_DATA_LENGTH)) { status = STATUS_DEVICE_NOT_READY; break; } byteCount++; pCurrent++; } if (!NT_SUCCESS(status)) { break; } RtlCopyMemory((PUCHAR)SocketData->Ident, pStart, byteCount); // // Null terminate SocketData->Ident[byteCount] = '\0'; DebugPrint((PCMCIA_DEBUG_TUPLES, "Identifier: %s\n", SocketData->Ident)); PcmciaSubstituteUnderscore(SocketData->Ident); break; } // // get the device configuration base // case CISTPL_CONFIG: { ProcessConfig(&tuplePacket); break; } case CISTPL_CONFIG_CB: { ProcessConfigCB(&tuplePacket); break; } case CISTPL_CFTABLE_ENTRY_CB: case CISTPL_CFTABLE_ENTRY: { // // construct a possible configuration entry for this device // configEntry = PcmciaProcessConfigTable(&tuplePacket); if (configEntry) { // // Link configurations at the end of the list. // configEntry->NextEntry = NULL; if (prevEntry) { prevEntry->NextEntry = configEntry; } else { SocketData->ConfigEntryChain = configEntry; } prevEntry = configEntry; } break; } case CISTPL_FUNCID: { // Mark device type.. SocketData->DeviceType = * (tuplePacket.TupleData); DebugPrint((PCMCIA_DEBUG_TUPLES, "DeviceType: %x\n", SocketData->DeviceType)); break; } case CISTPL_MANFID: { // PUCHAR localBufferPointer = tuplePacket.TupleData; SocketData->ManufacturerCode = *(localBufferPointer+1) << 8 | *localBufferPointer; SocketData->ManufacturerInfo = *(localBufferPointer+3)<<8 | *(localBufferPointer+2); DebugPrint((PCMCIA_DEBUG_TUPLES, "Code: %x, Info: %x\n", SocketData->ManufacturerCode, SocketData->ManufacturerInfo)); break; } } // end switch on Tuple code // // Skip to the next tuple // status = GetNextTuple(&tuplePacket); } if (tuplePacket.TupleData) { ExFreePool(tuplePacket.TupleData); } if (status == STATUS_DEVICE_NOT_READY) { return status; } // // Serial/modem/ATA devices recognized and appropriate // fixes for tuples applied here // PcmciaCheckForRecognizedDevice(Socket,SocketData); DebugPrint((PCMCIA_DEBUG_SOCKET, "skt %08x ParseFunctionData: Final PcCard type %x\n", Socket, SocketData->DeviceType)); return STATUS_SUCCESS; } NTSTATUS PcmciaParseFunctionDataForID( IN PSOCKET_DATA SocketData ) /*++ Routine Description Parses the tuple data for the supplied function (SocketPtr->Function) Arguments: Socket - Pointer to the socket which contains the device SocketData - Pointer to the socket data structure for the function which will be filled with the parsed information Return Value: Status --*/ { PSOCKET Socket = SocketData->Socket; TUPLE_PACKET tuplePacket; PUCHAR localBufferPointer; NTSTATUS status; USHORT ManufacturerCode = 0; USHORT ManufacturerInfo = 0; USHORT CisCrc; DebugPrint((PCMCIA_DEBUG_TUPLES, "Parsing function %d for ID...\n", SocketData->Function)); RtlZeroMemory(&tuplePacket, sizeof(TUPLE_PACKET)); tuplePacket.DesiredTuple = 0xFF; tuplePacket.TupleData = ExAllocatePool(NonPagedPool, MAX_TUPLE_DATA_LENGTH); if (tuplePacket.TupleData == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } tuplePacket.Socket = Socket; tuplePacket.SocketData = SocketData; tuplePacket.TupleDataMaxLength = MAX_TUPLE_DATA_LENGTH; tuplePacket.TupleOffset = 0; tuplePacket.Function = SocketData->Function; status = GetFirstTuple(&tuplePacket); if (!NT_SUCCESS(status) || (tuplePacket.TupleCode == CISTPL_END)) { if (status != STATUS_DEVICE_NOT_READY) { if (IsSocketFlagSet(Socket, SOCKET_CARD_MEMORY)) { status = STATUS_SUCCESS; } status = STATUS_NO_MORE_ENTRIES; } if (tuplePacket.TupleData) { ExFreePool(tuplePacket.TupleData); } return status; } while (NT_SUCCESS(status)) { status = GetTupleData(&tuplePacket); ASSERT (NT_SUCCESS(status)); switch (tuplePacket.TupleCode) { case CISTPL_MANFID: { localBufferPointer = tuplePacket.TupleData; ManufacturerCode = *(localBufferPointer+1) << 8 | *localBufferPointer; ManufacturerInfo = *(localBufferPointer+3)<<8 | *(localBufferPointer+2); break; } } // end switch on Tuple code // // Skip to the next tuple // status = GetNextTuple(&tuplePacket); } if (tuplePacket.TupleData) { ExFreePool(tuplePacket.TupleData); } if (SocketData->ManufacturerCode != ManufacturerCode) { DebugPrint((PCMCIA_DEBUG_TUPLES, "Verify failed on Manf. Code: %x %x\n", SocketData->ManufacturerCode, ManufacturerCode)); return STATUS_UNSUCCESSFUL; } if (SocketData->ManufacturerInfo != ManufacturerInfo) { DebugPrint((PCMCIA_DEBUG_TUPLES, "Verify failed on Manf. Info: %x %x\n", SocketData->ManufacturerInfo, ManufacturerInfo)); return STATUS_UNSUCCESSFUL; } // // Get the CIS checksum // CisCrc = GetCRC(SocketData); if (SocketData->CisCrc != CisCrc) { DebugPrint((PCMCIA_DEBUG_TUPLES, "Verify failed on CRC: %x %x\n", SocketData->CisCrc, CisCrc)); return STATUS_UNSUCCESSFUL; } DebugPrint((PCMCIA_DEBUG_INFO, "skt %08x R2 CardId verified %x-%x-%x\n", Socket, ManufacturerCode, ManufacturerInfo, CisCrc )); return STATUS_SUCCESS; } VOID PcmciaCheckForRecognizedDevice( IN PSOCKET Socket, IN OUT PSOCKET_DATA SocketData ) /*++ Routine Description: Look at the configuration options on the PCCARD to determine if it is a serial port / modem / ATA device card. Arguments: Socket - Pointer to the socket which contains the device SocketData - the configuration information on the current PCCARD. Return Value: None - Modifications are made to the socket data structure. --*/ { ULONG modemPorts[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8}; ULONG ataPorts0[2] = { 0x1f0, 0x170}; BOOLEAN found = FALSE; UCHAR dataByte; UCHAR link; ULONG index; PUCHAR localBufferPointer; TUPLE_PACKET tuplePacket; PUCHAR tupleData; PCONFIG_ENTRY configEntry; NTSTATUS status; // // This piece of code searches the config data for I/O ranges that start at // some known industry standard, and updates the devicetype accordingly. // // This is such an ugly hack, I'm really skeptical that we should be doing // this anymore. I assume there must have been some broken hardware that needed // it, but that information is now lost. At some point, this whole for loop // should just be removed. // for (configEntry = SocketData->ConfigEntryChain; configEntry; configEntry = configEntry->NextEntry) { for (index = 0; index < 4; index++) { if (modemPorts[index] == configEntry->IoPortBase[0]) { SocketData->DeviceType = PCCARD_TYPE_SERIAL; found = TRUE; break; } if (index < 2) { if (ataPorts0[index] == configEntry->IoPortBase[0]) { if (configEntry->IoPortBase[1] == 0x376 || configEntry->IoPortBase[1] == 0x3f6 ) { SocketData->DeviceType = PCCARD_TYPE_ATA; found = TRUE; break; } } } } } switch(SocketData->DeviceType) { case PCCARD_TYPE_ATA: // // More smudges follow here for ATA cards to rub out buggy tuples // // Search for configurations which are not viable for ATA devices // and mark them invalid so they would not be reported to the I/O subsystem. // Also fix buggy tuples on ATA cards for (configEntry = SocketData->ConfigEntryChain; configEntry != NULL; configEntry = configEntry->NextEntry) { // // Adjust the IO resource requirement: ATA cards // typically have an incorrect length here // (+1) // if (configEntry->IoPortLength[1] > 0) { configEntry->IoPortLength[1]=0; } // // This next hack is to work around a problem with Viking SmartCard adapters. // This adapter doesn't like I/O ranges that are based at 0x70 or 0xf0, // so we bump up the alignment to 0x20 on unrestricted I/O ranges. // if ((SocketData->ManufacturerCode == 0x1df) && (configEntry->NumberOfIoPortRanges == 1) && (configEntry->IoPortBase[0] == 0) && (configEntry->IoPortLength[0] == 0xf) && (configEntry->IoPortAlignment[0] == 0x10)) { // alter alignment configEntry->IoPortAlignment[0] = 0x20; } if (configEntry->NumberOfMemoryRanges) { // // Don't use this configuration // configEntry->Flags |= PCMCIA_INVALID_CONFIGURATION; } } break; case PCCARD_TYPE_PARALLEL: // Search for configurations which are not viable for Parallel devices for (configEntry = SocketData->ConfigEntryChain; configEntry != NULL; configEntry = configEntry->NextEntry) { if (configEntry->NumberOfMemoryRanges) { // // Don't use this configuration // configEntry->Flags |= PCMCIA_INVALID_CONFIGURATION; } } break; case PCCARD_TYPE_SERIAL: { // // If card type is serial , check if it's actually a modem... // UCHAR tupleDataBuffer[MAX_TUPLE_DATA_LENGTH]; PUCHAR str, pChar; RtlZeroMemory(&tuplePacket, sizeof(TUPLE_PACKET)); tuplePacket.DesiredTuple = CISTPL_FUNCE; tuplePacket.TupleData = tupleDataBuffer; tuplePacket.Socket = Socket; tuplePacket.SocketData = SocketData; tuplePacket.TupleDataMaxLength = MAX_TUPLE_DATA_LENGTH; tuplePacket.TupleOffset = 0; tuplePacket.Function = SocketData->Function; status = GetFirstTuple(&tuplePacket); while (NT_SUCCESS(status)) { status = GetTupleData(&tuplePacket); if (!NT_SUCCESS(status) || (tuplePacket.TupleDataLength == 0)) { // something bad happened break; } if (tuplePacket.TupleData[0] >=1 && tuplePacket.TupleData[0] <=3) { SocketData->DeviceType = PCCARD_TYPE_MODEM; return; } status = GetNextTuple(&tuplePacket); } if (status == STATUS_DEVICE_NOT_READY) { return; } tuplePacket.DesiredTuple = CISTPL_VERS_1; status = GetFirstTuple(&tuplePacket); if (!NT_SUCCESS(status)) { return; } status = GetTupleData(&tuplePacket); if (!NT_SUCCESS(status) || (tuplePacket.TupleDataLength < 3)) { // something bad happened return; } str = tuplePacket.TupleData+2; for (;;) { if ( *str == 0xFF ) { // // End of strings // break; } // // Convert to upper case // for (pChar = str; *pChar ; pChar++) { *pChar = (UCHAR)toupper(*pChar); } if (strstr(str, "MODEM") || strstr(str, "FAX")) { SocketData->DeviceType = PCCARD_TYPE_MODEM; break; } // // Move onto next string.. // str = str + strlen(str)+1; // // Just in case... // if (str >= (tuplePacket.TupleData + tuplePacket.TupleDataLength)) { break; } } break; } } // Search for configurations which are not viable - because // the resources requested are not supported by the controller // or the OS - and mark them invalid so they would not be requested for (configEntry = SocketData->ConfigEntryChain; configEntry != NULL; configEntry = configEntry->NextEntry) { if ((configEntry->IrqMask == 0) && (configEntry->NumberOfIoPortRanges == 0) && (configEntry->NumberOfMemoryRanges == 0)) { // // This configuration doesn't need any resources!! // Obviously bogus. (IBM Etherjet-3FE2 has one of these) // configEntry->Flags |= PCMCIA_INVALID_CONFIGURATION; } } } BOOLEAN PcmciaQuickVerifyTupleChain( IN PUCHAR Buffer, IN ULONG Length ) /*++ Routine description: This routine is provided as a quick low-level check of the attribute data in the passed buffer. The tuples aren't interpreted in context, rather the tuple links are followed to see if the chain isn't total garbage. Note that none of the normal tuple handling functions are called, this is to avoid recursion, since this may be called from within normal tuple processing. Arguments: Buffer, Length - defines the buffer that contains attribute data Return Value: TRUE if the data in this buffer looks like a reasonable tuple chain, FALSE otherwise --*/ { ULONG TupleCount = 0; UCHAR code, link; ULONG Offset = 0; BOOLEAN retval = TRUE; if (Length < 2) { return FALSE; } code = *(PUCHAR)((ULONG_PTR)(Buffer)); link = *(PUCHAR)((ULONG_PTR)(Buffer)+1); while(code != CISTPL_END) { if (link == 0xff) { break; } if (code == CISTPL_NULL) { Offset += 1; } else if (code == CISTPL_NO_LINK) { Offset += 2; } else { Offset += (ULONG)link+2; } if (Offset >= (Length-1)) { retval = FALSE; break; } TupleCount++; code = *(PUCHAR)((ULONG_PTR)(Buffer)+Offset); link = *(PUCHAR)((ULONG_PTR)(Buffer)+Offset+1); } if (!TupleCount) { retval = FALSE; } return retval; } NTSTATUS PcmciaMemoryCardHack( IN PSOCKET Socket, IN PSOCKET_DATA SocketData ) /*++ Routine Description: This routine is called whenever we do not find a CIS of the card. We probe the card to determine if it is sram or not. Arguments SocketPtr - Point to the socket in which this card was found SocketData - Pointer to a pointer to the data structure which normally contains parsed tuple data. This will be filled in by this routine Return Value STATUS_SUCCESS --*/ { #define JEDEC_SRAM 0x0000 // JEDEC ID for SRAM cards #define JEDEC_ROM 0x0002 // JEDEC ID for ROM cards #define READ_ID_CMD 0x9090 PPDO_EXTENSION pdoExtension = SocketData->PdoExtension; USHORT OrigValue; USHORT ChkValue; USHORT ReadIdCmd = READ_ID_CMD; PAGED_CODE(); SetSocketFlag(Socket, SOCKET_CARD_MEMORY); SocketData->DeviceType = PCCARD_TYPE_MEMORY; SocketData->Flags = SDF_JEDEC_ID; SocketData->JedecId = JEDEC_ROM; // // Like win9x, we probe the card's common memory with a write to offset zero to see if // it looks like sram // if (((*(Socket->SocketFnPtr->PCBReadCardMemory)) (pdoExtension, PCCARD_COMMON_MEMORY, 0, (PUCHAR)&OrigValue, 2) == 2) && ((*(Socket->SocketFnPtr->PCBWriteCardMemory))(pdoExtension, PCCARD_COMMON_MEMORY, 0, (PUCHAR)&ReadIdCmd, 2) == 2) && ((*(Socket->SocketFnPtr->PCBReadCardMemory)) (pdoExtension, PCCARD_COMMON_MEMORY, 0, (PUCHAR)&ChkValue, 2) == 2) && ((*(Socket->SocketFnPtr->PCBWriteCardMemory))(pdoExtension, PCCARD_COMMON_MEMORY, 0, (PUCHAR)&OrigValue, 2) == 2)) { if (ChkValue == ReadIdCmd) { SocketData->JedecId = JEDEC_SRAM; } } if (pcmciaReportMTD0002AsError && (SocketData->JedecId == JEDEC_ROM)) { return STATUS_DEVICE_NOT_READY; } return STATUS_SUCCESS; }