mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2772 lines
68 KiB
2772 lines
68 KiB
/*++
|
|
|
|
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;
|
|
}
|