/*++ Copyright (c) 1991 Microsoft Corporation Module Name: tuples.c Abstract: This program converses with the PCMCIA support driver to display tuple and other information. Author: Bob Rinne Environment: User process. Notes: Revision History: Ravisankar Pudipeddi (ravisp) June 27 1997 - command line options & support for multiple controllers Neil Sandlin (neilsa) Sept 20, 1998 - more commands --*/ #include // // Tuple output strings // StringTable CommandCodes[] = { "CISTPL_NULL", CISTPL_NULL, "CISTPL_DEVICE", CISTPL_DEVICE, "CISTPL_LONGLINK_MFC", CISTPL_LONGLINK_MFC, "CISTPL_CHECKSUM", CISTPL_CHECKSUM, "CISTPL_LONGLINK_A", CISTPL_LONGLINK_A, "CISTPL_LONGLINK_C", CISTPL_LONGLINK_C, "CISTPL_LINKTARGET", CISTPL_LINKTARGET, "CISTPL_NO_LINK", CISTPL_NO_LINK, "CISTPL_VERS_1", CISTPL_VERS_1, "CISTPL_ALTSTR", CISTPL_ALTSTR, "CISTPL_DEVICE_A", CISTPL_DEVICE_A, "CISTPL_JEDEC_C", CISTPL_JEDEC_C, "CISTPL_JEDEC_A", CISTPL_JEDEC_A, "CISTPL_CONFIG", CISTPL_CONFIG, "CISTPL_CFTABLE_ENTRY", CISTPL_CFTABLE_ENTRY, "CISTPL_DEVICE_OC", CISTPL_DEVICE_OC, "CISTPL_DEVICE_OA", CISTPL_DEVICE_OA, "CISTPL_GEODEVICE", CISTPL_GEODEVICE, "CISTPL_GEODEVICE_A", CISTPL_GEODEVICE_A, "CISTPL_MANFID", CISTPL_MANFID, "CISTPL_FUNCID", CISTPL_FUNCID, "CISTPL_FUNCE", CISTPL_FUNCE, "CISTPL_VERS_2", CISTPL_VERS_2, "CISTPL_FORMAT", CISTPL_FORMAT, "CISTPL_GEOMETRY", CISTPL_GEOMETRY, "CISTPL_BYTEORDER", CISTPL_BYTEORDER, "CISTPL_DATE", CISTPL_DATE, "CISTPL_BATTERY", CISTPL_BATTERY, "CISTPL_ORG", CISTPL_ORG, // // CISTPL_END must be the last one in the table. // "CISTPL_END", CISTPL_END }; // // Procedures // NTSTATUS ReadTuple( IN HANDLE Handle, IN LONG SlotNumber, IN PUCHAR Buffer, IN LONG BufferSize ) /*++ Routine Description: Perform the NT function to get the tuple data from the pcmcia support driver. Arguments: Handle - an open handle to the driver. SlotNumber - The socket offset Buffer - return buffer for the data. BufferSize - the size of the return buffer area. Return Value: The results of the NT call. --*/ { NTSTATUS status; IO_STATUS_BLOCK statusBlock; TUPLE_REQUEST commandBlock; commandBlock.Socket = (USHORT) SlotNumber; status = NtDeviceIoControlFile(Handle, NULL, NULL, NULL, &statusBlock, IOCTL_GET_TUPLE_DATA, &commandBlock, sizeof(commandBlock), Buffer, BufferSize); return status; } PUCHAR FindTupleCodeName( UCHAR TupleCode ) /*++ Routine Description: Return an ascii string that describes the tuple code provided. Arguments: TupleCode - what code to look up. Return Value: A string pointer - always. --*/ { ULONG index; for (index = 0; CommandCodes[index].CommandCode != CISTPL_END; index++) { if (CommandCodes[index].CommandCode == TupleCode) { return CommandCodes[index].CommandName; } } return "Command Unknown"; } PUCHAR DeviceTypeString[] = { "DTYPE_NULL", "DTYPE_ROM", "DTYPE_OTPROM", "DTYPE_EPROM", "DTYPE_EEPROM", "DTYPE_FLASH", "DTYPE_SRAM", "DTYPE_DRAM", "Reserved8", "Reserved9", "Reserveda", "Reservedb", "Reservedc", "DTYPE_FUNCSPEC", "DTYPE_EXTEND" "Reservedf", }; PUCHAR DeviceSpeedString[] = { "DSPEED_NULL", "DSPEED_250NS", "DSPEED_200NS", "DSPEED_150NS", "DSPEED_100NS", "DSPEED_RES1", "DSPEED_RES2", "DSPEED_EXT" }; VOID DisplayDeviceTuple( PUCHAR TupleBuffer, UCHAR TupleSize ) /*++ Routine Description: Display the data at the given pointer as a CISTPL_DEVICE structure. Arguments: TupleBuffer - the CISTPL_DEVICE to display. TupleSize - the link value for the tuple. Return Value: None --*/ { UCHAR mantissa = MANTISSA_RES1; UCHAR exponent; UCHAR deviceTypeCode; UCHAR wps; UCHAR deviceSpeed; UCHAR temp; temp = *TupleBuffer; deviceTypeCode = DeviceTypeCode(temp); wps = DeviceWPS(temp); deviceSpeed = DeviceSpeedField(temp); temp = *(TupleBuffer + 1); if (deviceSpeed == DSPEED_EXT) { exponent = SpeedExponent(temp); mantissa = SpeedMantissa(temp); } printf("DeviceType: %s DeviceSpeed: ", DeviceTypeString[deviceTypeCode]); if (mantissa != MANTISSA_RES1) { printf("Mantissa %.2x, Exponent %.2x\n", mantissa, exponent); } else { printf("%s\n", DeviceSpeedString[deviceSpeed]); } } VOID DisplayVers1( PUCHAR TupleBuffer, UCHAR TupleSize, USHORT Crc ) /*++ Routine Description: Display the data as a Version tuple Arguments: TupleBuffer - the CISTPL_DEVICE to display. TupleSize - the link value for the tuple. Return Value: None --*/ { PUCHAR string; PUCHAR cp; // // Step around the MAJOR and MINOR codes of 4/1 at // the beginning of the tuple to get to the strings. // string = TupleBuffer; string++; string++; printf("Manufacturer:\t%s\n", string); while (*string++) { } printf("Product Name:\t%s\n", string); printf("CRC: \t%.4x\n", Crc); while (*string++) { } printf("Product Info:\t"); if (isprint(*string)) { printf("%s", string); } else { while (*string) { printf("%.2x ", *string); string++; } } printf("\n"); } VOID DisplayConfigTuple( PUCHAR TupleBuffer, UCHAR TupleSize ) /*++ Routine Description: Display the data at the given pointer as a CISTPL_CONFIG tuple. Arguments: TupleBuffer - the CISTPL_DEVICE to display. TupleSize - the link value for the tuple. Return Value: None --*/ { UCHAR sizeField; UCHAR tpccRfsz; UCHAR tpccRmsz; UCHAR tpccRasz; UCHAR last; ULONG baseAddress; PUCHAR ptr; sizeField = *TupleBuffer; last = *(TupleBuffer + 1); tpccRfsz = TpccRfsz(sizeField); tpccRmsz = TpccRmsz(sizeField); tpccRasz = TpccRasz(sizeField); printf("TPCC_SZ %.2x (%.2x/%.2x/%.2x) - Last %.2x\n", sizeField, tpccRasz, tpccRmsz, tpccRfsz, last); baseAddress = 0; ptr = TupleBuffer + 2; switch (tpccRasz) { case 3: baseAddress = *(ptr + 3) << 24; case 2: baseAddress |= *(ptr + 2) << 16; case 1: baseAddress |= *(ptr + 1) << 8; default: baseAddress |= *ptr; } printf("Base Address: %8x - ", baseAddress); ptr += tpccRasz + 1; baseAddress = 0; switch (tpccRmsz) { case 3: baseAddress = *(ptr + 3) << 24; case 2: baseAddress |= *(ptr + 2) << 16; case 1: baseAddress |= *(ptr + 1) << 8; default: baseAddress |= *ptr; } printf("Register Presence Mask: %8x\n", baseAddress); } PUCHAR ProcessMemSpace( PUCHAR Buffer, UCHAR MemSpace ) /*++ Routine Description: Display and process memspace information Arguments: Buffer - start of memspace information MemSpace - the memspace value from the feature byte. Return Value: location of byte after all memory space information --*/ { PUCHAR ptr = Buffer; UCHAR item = *ptr++; UCHAR lengthSize; UCHAR addrSize; UCHAR number; UCHAR hasHostAddress; ULONG cardAddress; ULONG length; ULONG hostAddress; if (MemSpace == 3) { lengthSize = (item & 0x18) >> 3; addrSize = (item & 0x60) >> 5; number = (item & 0x07) + 1; hasHostAddress = item & 0x80; printf("(0x%.2x) %s - %d entries - LengthSize %d - AddrSize %d\n", item, hasHostAddress ? "Host address" : "no host", number, lengthSize, addrSize); while (number) { cardAddress = length = hostAddress = 0; switch (lengthSize) { case 3: length |= (*(ptr + 2)) << 16; case 2: length |= (*(ptr + 1)) << 8; case 1: length |= *ptr; } ptr += lengthSize; switch (addrSize) { case 3: cardAddress |= (*(ptr + 2)) << 16; case 2: cardAddress |= (*(ptr + 1)) << 8; case 1: cardAddress |= *ptr; } ptr += addrSize; if (hasHostAddress) { switch (addrSize) { case 3: hostAddress |= (*(ptr + 2)) << 16; case 2: hostAddress |= (*(ptr + 1)) << 8; case 1: hostAddress |= *ptr; } printf("\tHost 0x%.8x ", hostAddress * 256); ptr += addrSize; } else { printf("\t"); } printf("Card 0x%.8x Size 0x%.8x\n", cardAddress * 256, length * 256); number--; } } return ptr; } USHORT VoltageConversionTable[16] = { 10, 12, 13, 14, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, 90 }; UCHAR ConvertVoltage( UCHAR MantissaExponentByte, UCHAR ExtensionByte ) /*++ Routine Description: Arguments: Return Value: --*/ { USHORT power; USHORT value; value = VoltageConversionTable[(MantissaExponentByte >> 3) & 0x0f]; power = 1; if ((MantissaExponentByte & EXTENSION_BYTE_FOLLOWS) && (ExtensionByte < 100)) { value = (100 * value + (ExtensionByte & 0x7f)); power += 2; } power = (MantissaExponentByte & 0x07) - 4 - power; while (power > 0) { value *= 10; power--; } while (power < 0) { value /= 10; power++; } return (UCHAR) value; } PUCHAR PowerTypeTable[] = { "Nominal", "Minimum", "Maximum", "Static", "Average", "Peak", "PwrDown" }; PUCHAR VoltagePinTable[] = { "Vcc", "Vpp1", "Vpp2" }; PUCHAR ProcessPower( PUCHAR Buffer, UCHAR FeatureByte ) /*++ Routine Description: Display and process power information Arguments: Power - start of power information Return Value: location of byte after all power information --*/ { UCHAR powerSelect; UCHAR bit; UCHAR item; UCHAR entries; PUCHAR ptr = Buffer; UCHAR count = FeatureByte; powerSelect = *ptr; printf("Parameter Selection Byte = 0x%.2x\n", powerSelect); entries = 0; while (entries < count) { powerSelect = *ptr++; printf("\t%s \"%d%d%d%d%d%d%d%d\"\n", VoltagePinTable[entries], powerSelect & 0x80 ? 1 : 0, powerSelect & 0x40 ? 1 : 0, powerSelect & 0x20 ? 1 : 0, powerSelect & 0x10 ? 1 : 0, powerSelect & 0x08 ? 1 : 0, powerSelect & 0x04 ? 1 : 0, powerSelect & 0x02 ? 1 : 0, powerSelect & 0x01 ? 1 : 0); for (bit = 0; bit < 7; bit++) { if (powerSelect & (1 << bit)) { if (!bit) { // // Convert nominal power for output. // item = ConvertVoltage(*ptr, (UCHAR) (*ptr & EXTENSION_BYTE_FOLLOWS ? *(ptr + 1) : (UCHAR) 0)); } printf("\t\t%s power =\t%d/10 volts\n", PowerTypeTable[bit], item); while (*ptr++ & EXTENSION_BYTE_FOLLOWS) { } } } entries++; } return ptr; } PUCHAR ProcessTiming( PUCHAR Buffer ) /*++ Routine Description: Arguments: Return Value: --*/ { PUCHAR ptr = Buffer; UCHAR item = *ptr++; UCHAR reservedScale = (item & 0xe0) >> 5; UCHAR readyBusyScale = (item & 0x1c) >> 2; UCHAR waitScale = (item & 0x03); printf("Timing (0x%.2x): reservedScale 0x%.2x, readyBusyScale 0x%.2x, waitScale 0x%.2x\n", item, reservedScale, readyBusyScale, waitScale); if (waitScale != 3) { printf("\tWaitSpeed 0x%.2x\n", *ptr); ptr++; while (*ptr & EXTENSION_BYTE_FOLLOWS) { ptr++; } } if (readyBusyScale != 7) { printf("\tReadyBusySpeed 0x%.2x\n", *ptr); ptr++; while (*ptr & EXTENSION_BYTE_FOLLOWS) { ptr++; } } if (reservedScale != 7) { printf("\tReservedSpeed 0x%.2x\n", *ptr); ptr++; while (*ptr & EXTENSION_BYTE_FOLLOWS) { ptr++; } } return ptr; } PUCHAR ProcessIoSpace( PUCHAR Buffer ) /*++ Routine Description: Display and process iospace information Arguments: Buffer - start of IoSpace information Return Value: location of byte after all power information --*/ { UCHAR item; UCHAR ioAddrLines; UCHAR bus8; UCHAR bus16; UCHAR ranges; UCHAR lengthSize; UCHAR addressSize; ULONG address; PUCHAR ptr = Buffer; item = *ptr++; ioAddrLines = item & IO_ADDRESS_LINES_MASK; bus8 = Is8BitAccess(item); bus16 = Is16BitAccess(item); ranges = HasRanges(item); printf("IoSpace (%.2x): IoAddressLines %.2d - %s/%s\n", item, ioAddrLines, bus8 ? "8bit" : "", bus16 ? "16bit" : ""); // // This is what it looks like the IBM token ring card // does. It is unclear in the specification if this // is correct or not. // if ((!ranges) && (!ioAddrLines)) { ranges = 0xFF; } if (ranges) { if (ranges == 0xff) { // // This is based on the tuple data as given by // the IBM token ring card. This is not the // way I would interpret the specification. // addressSize = 2; lengthSize = 1; ranges = 1; } else { item = *ptr++; ranges = item & 0x0f; ranges++; addressSize = GetAddressSize(item); lengthSize = GetLengthSize(item); } while (ranges) { address = 0; switch (addressSize) { case 4: address |= (*(ptr + 3)) << 24; case 3: address |= (*(ptr + 2)) << 16; case 2: address |= (*(ptr + 1)) << 8; case 1: address |= *ptr; } ptr += addressSize; printf("\tStart %.8x - Length ", address); address = 0; switch (lengthSize) { case 4: address |= (*(ptr + 3)) << 24; case 3: address |= (*(ptr + 2)) << 16; case 2: address |= (*(ptr + 1)) << 8; case 1: address |= *ptr; } ptr += lengthSize; printf("%.8x\n", address); ranges--; } } else { printf("\tResponds to all ranges.\n"); } return ptr; } PUCHAR ProcessIrq( PUCHAR Buffer ) /*++ Routine Description: Display and process irq information Arguments: Buffer - start of irq information Return Value: location of byte after all irq information --*/ { PUCHAR ptr = Buffer; UCHAR level; USHORT mask; ULONG irqNumber; level = *ptr++; 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 = *ptr++; } if (level & 0x80) { printf("Share "); } if (level & 0x40) { printf("Pulse "); } if (level & 0x20) { printf("Level "); } if (level & 0x10) { mask = *ptr | (*(ptr + 1) << 8); ptr += 2; printf("mask = %.4x - ", mask); for (irqNumber = 0; mask; irqNumber++, mask = mask >> 1) { if (mask & 0x0001) { printf("IRQ%d ", irqNumber); } } printf("- "); if (level & 0x08) { printf("Vend "); } if (level & 0x04) { printf("Berr "); } if (level & 0x02) { printf("IOCK "); } if (level & 0x01) { printf("NMI"); } printf("\n"); } else { printf("irq = %d\n", level & 0x0f); } return ptr; } PUCHAR InterfaceTypeStrings[] = { "Memory", "I/O", "Reserved 2", "Reserved 3", "Custom 0", "Custom 1", "Custom 2", "Custom 3", "Reserved 8", "Reserved 9", "Reserved a", "Reserved b", "Reserved c", "Reserved d", "Reserved e", "Reserved f", }; VOID DisplayCftableEntryTuple( PUCHAR TupleBuffer, UCHAR TupleSize ) /*++ Routine Description: Display the data at the given pointer as a CISTPL_CFTABLE_ENTRY tuple. Arguments: TupleBuffer - the CISTPL_DEVICE to display. TupleSize - the link value for the tuple. Return Value: None --*/ { UCHAR temp; UCHAR item; UCHAR defaultbit; UCHAR memSpace; UCHAR power; PUCHAR ptr; temp = *TupleBuffer; item = IntFace(temp); defaultbit = Default(temp); temp = ConfigEntryNumber(temp); printf("ConfigurationEntryNumber %.2x (%s/%s)\n", temp, item ? "intface" : "", defaultbit ? "default" : ""); ptr = TupleBuffer + 1; if (item) { temp = *ptr++; item = temp & 0x0F; printf("InterfaceDescription (%.2x) %s (%s/%s/%s/%s)\n", temp, InterfaceTypeStrings[item], temp & 0x80 ? "WaitReq" : "", temp & 0x40 ? "RdyBsy" : "", temp & 0x20 ? "WP" : "", temp & 0x10 ? "BVD" : ""); } item = *ptr++; memSpace = MemSpaceInformation(item); power = PowerInformation(item); printf("The following structures are present:\n"); switch (power) { case 3: printf("Vcc, Vpp1, Vpp2; "); break; case 2: printf("Vcc and Vpp; "); break; case 1: printf("Vcc; "); break; case 0: break; } if (power) { ptr = ProcessPower(ptr, power); } if (TimingInformation(item)) { ptr = ProcessTiming(ptr); } if (IoSpaceInformation(item)) { ptr = ProcessIoSpace(ptr); } if (IRQInformation(item)) { printf("IRQ: "); ptr = ProcessIrq(ptr); } switch (memSpace) { case 3: printf("Memory selection: "); break; case 2: printf("Length and Card Address: "); break; case 1: printf("2-byte length: "); break; case 0: break; } if (memSpace) { ptr = ProcessMemSpace(ptr, memSpace); } if (MiscInformation(item)) { printf("Misc fields present"); } printf("\n"); } 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, }; USHORT GetCRC( PUCHAR TupleBuffer ) /*++ 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: TupleBuffer - the tuple data Return Value: A USHORT CRC value. --*/ { USHORT crc = 0; USHORT index; USHORT length; PUCHAR tupleData; PUCHAR cp; PUCHAR tplBuffer; UCHAR tupleCode; UCHAR linkValue; UCHAR tmp; // // Calculate CRC // tplBuffer = TupleBuffer; printf("Calculating CRC "); while (1) { tupleData = tplBuffer + 2; tupleCode = *tplBuffer++; if (tupleCode == CISTPL_END) { break; } linkValue = (tupleCode) ? *tplBuffer++ : 0; length = linkValue; printf("%x", tupleCode); for (index = 0; TplList[index] != CISTPL_END; index++) { if (tupleCode == TplList[index]) { // // This one is included in the CRC calculation // printf("*", tupleCode); if (tupleCode == CISTPL_VERS_1) { cp = tupleData + 2; // // Include all of the manufacturer name. // while (*cp) { cp++; } // // Include the product string // cp++; while (*cp) { cp++; } cp++; length = (USHORT)(cp - tupleData); } for (cp = tupleData; length; length--, cp++) { tmp = *cp ^ (UCHAR)crc; crc = (crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4]; } break; } } printf(" "); tplBuffer = tplBuffer + linkValue; } printf("++\n"); return crc; } VOID DumpTuple( PUCHAR Buffer ) /*++ Routine Description: Control routine to process the tuple data. Arguments: Buffer - the tuple data. Return Value: None --*/ { PUCHAR tupleBuffer = Buffer; PUCHAR tupleCodeName; USHORT crc; UCHAR index; UCHAR tupleCode; UCHAR linkValue; crc = GetCRC(tupleBuffer); while (1) { tupleCode = *tupleBuffer++; linkValue = (tupleCode) ? *tupleBuffer : 0; if (tupleCode == CISTPL_END) { break; } tupleCodeName = FindTupleCodeName(tupleCode); printf("Tuple Code\t%s\t%.2x - Link %.2x:", tupleCodeName, tupleCode, linkValue); if (linkValue) { for (index = 0; index < linkValue; index++) { if ((index & 0x0F) == 0) { printf("\n"); } printf(" %.2x", *(tupleBuffer + index + 1)); } } printf("\n"); tupleBuffer++; switch (tupleCode) { case CISTPL_DEVICE: DisplayDeviceTuple(tupleBuffer, linkValue); break; case CISTPL_VERS_1: DisplayVers1(tupleBuffer, linkValue, crc); break; case CISTPL_CONFIG: DisplayConfigTuple(tupleBuffer, linkValue); break; case CISTPL_CFTABLE_ENTRY: DisplayCftableEntryTuple(tupleBuffer, linkValue); break; case CISTPL_LONGLINK_MFC: case CISTPL_LONGLINK_A: case CISTPL_LONGLINK_C: case CISTPL_LINKTARGET: case CISTPL_NO_LINK: default: break; } tupleBuffer = tupleBuffer + linkValue; printf("\n"); } } VOID DumpCIS( IN PHOST_INFO hostInfo ) /*++ Routine Description: Arguments: Return Value: --*/ { NTSTATUS status; PUCHAR currentBufferPointer; UCHAR hexBuffer[260]; UCHAR ascii[100]; ULONG i; UCHAR c; PUCHAR buffer; HANDLE handle; handle = GetHandleForIoctl(hostInfo); if (handle == INVALID_HANDLE_VALUE) { return; } buffer = malloc(BUFFER_SIZE); if (!buffer) { printf("Unable to malloc\n"); NtClose(handle); return; } memset(buffer, 0, BUFFER_SIZE); status = ReadTuple(handle, hostInfo->SocketNumber, buffer, BUFFER_SIZE); // // Don't bother dumping tuples for cards that aren't there. // if (!NT_SUCCESS(status)) { NtClose(handle); return; } printf("\nCIS Tuples for Socket Number %d:\n\n", hostInfo->SocketNumber); hexBuffer[0] = '\0'; ascii[0] = '\0'; currentBufferPointer = buffer; for (i = 0; i < 512; i++) { c = *currentBufferPointer; sprintf(hexBuffer, "%s %.2x", hexBuffer, c); c = isprint(c) ? c : '.'; sprintf(ascii, "%s%c", ascii, c); currentBufferPointer++; // // Display the line every 16 bytes. // if ((i & 0x0f) == 0x0f) { printf("%s", hexBuffer); printf(" *%s*\n", ascii); hexBuffer[0] = '\0'; ascii[0] = '\0'; } } printf("%s", hexBuffer); printf("\t\t*%s*\n\n", ascii); DumpTuple(buffer); NtClose(handle); }