windows-nt/Source/XPSP1/NT/base/busdrv/pccard/pcmcibus/id.c

908 lines
24 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1996 Microsoft Corporation
Module Name:
id.c
Abstract:
This module contains the code to handle IRP_MN_QUERY_ID
Authors:
Ravisankar Pudipeddi (ravisp)
Environment:
Kernel mode only
Notes:
Revision History:
Neil Sandlin (neilsa) - split off from pdopnp.c
--*/
#include "pch.h"
//
// Internal References
//
NTSTATUS
PcmciaGenerateDeviceId(
IN PSOCKET_DATA SocketData,
IN ULONG FunctionNumber,
OUT PUCHAR *DeviceId
);
BOOLEAN
PcmciaCheckInstance(
IN PUCHAR DeviceId,
IN ULONG Instance
);
NTSTATUS
PcmciaGetDeviceType(
IN PDEVICE_OBJECT Pdo,
IN ULONG FunctionNumber,
OUT PUCHAR DeviceType
);
VOID
PcmciaFilterIdString(
IN PUCHAR pIn,
OUT PUCHAR pOut,
ULONG MaxLen
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PcmciaGenerateDeviceId)
#pragma alloc_text(PAGE, PcmciaGetDeviceId)
#pragma alloc_text(PAGE, PcmciaGetHardwareIds)
#pragma alloc_text(PAGE, PcmciaGetCompatibleIds)
#pragma alloc_text(PAGE, PcmciaGetDeviceType)
#pragma alloc_text(PAGE, PcmciaFilterIdString)
#endif
#define PCMCIA_MAX_DEVICE_TYPE_SUPPORTED 12
const
UCHAR *PcmciaCompatibleIds[PCMCIA_MAX_DEVICE_TYPE_SUPPORTED+1] = {
"", // Unknown..
"", // Memory card (RAM, ROM, EPROM, Flash)
"", // Serial I/O port, includes modems
"", // Parallel printer port
"*PNP0600", // Disk driver (ATA)
"", // Video interface
"", // Local Area Network adapter
"", // Auto Increment Mass Storage card
"", // SCSI bridge card
"", // Security card
"*PNP0D00", // Multi-Function 3.0 PC Card
"", // Flash memory card
"*PNPC200", // Modem card (sync with PCCARD_TYPE_MODEM)
};
NTSTATUS
PcmciaGenerateDeviceId(
IN PSOCKET_DATA SocketData,
IN ULONG FunctionNumber,
OUT PUCHAR *DeviceId
)
/*++
The device ID is created from tuple information on the PC Card
The goal is to create a unique ID for each
card. The device ID is created from the manufacturer name string,
the product name string, and a 16-bit CRC of a set of tuples.
The ID is created by concatenating the "PCMCIA" prefix, the manufacturer
name string, the product name string and the 16-bit CRC for the card.
PCMCIA\<manuf_name>-<prod_name>-<crc>
If the CISTPL_VERS_1 tuple is not available, or the manufacturer name is
NULL, the string "UNKNOWN_MANUFACTURER" will be included in its place.
If this is for a child function within a multifunctionn card, the generated
device id would be:
PCMCIA\<manuf_name>-<prod_name>-DEV<function number>-<crc>
This device id is compatible with win 9x device id's (excluding the instance
number which is returned separtely by handling another IRP.
Arguments:
Pdo - Pointer to the physical device object for the pc-card
FunctionNumber - Function number of the function in a multi-function card.
If this is PCMCIA_MULTIFUNCTION_PARENT, then the requested device id
is for the parent device - not for any individual function
DeviceId - Pointer to the string in which device id is returned
Return Value
Status
--*/
{
PUCHAR deviceId;
PAGED_CODE();
deviceId = ExAllocatePool(PagedPool, PCMCIA_MAXIMUM_DEVICE_ID_LENGTH);
if (deviceId == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
//
// Generate the device id
//
if (*(SocketData->Mfg) == '\0' ) {
//
// No manufacturer name available
//
if (FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) {
//
// Device id for the pc-card
//
if (SocketData->Flags & SDF_JEDEC_ID) {
//
// Flash memory cards have the special device id
//
sprintf(deviceId, "%s\\%s-%04x",
PCMCIA_ID_STRING,
PCMCIA_MEMORY_ID_STRING,
SocketData->JedecId);
} else {
sprintf(deviceId, "%s\\%s-%04X",
PCMCIA_ID_STRING,
PCMCIA_UNKNOWN_MANUFACTURER_STRING,
SocketData->CisCrc);
}
} else {
//
// This is for the individual multifunction child
//
sprintf(deviceId, "%s\\%s-DEV%d-%04X",
PCMCIA_ID_STRING,
PCMCIA_UNKNOWN_MANUFACTURER_STRING,
FunctionNumber,
SocketData->CisCrc);
}
} else {
UCHAR Mfg[MAX_MANFID_LENGTH];
UCHAR Ident[MAX_IDENT_LENGTH];
PcmciaFilterIdString(SocketData->Mfg, Mfg, MAX_MANFID_LENGTH);
PcmciaFilterIdString(SocketData->Ident, Ident, MAX_IDENT_LENGTH);
if (FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) {
//
// Device id for the pc-card
//
sprintf(deviceId, "%s\\%s-%s-%04X",
PCMCIA_ID_STRING,
Mfg,
Ident,
SocketData->CisCrc);
} else {
//
// This is for the individual multifunction child
//
sprintf(deviceId, "%s\\%s-%s-DEV%d-%04X",
PCMCIA_ID_STRING,
Mfg,
Ident,
FunctionNumber,
SocketData->CisCrc);
}
}
*DeviceId = deviceId;
if ((FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) &&
(SocketData->PdoExtension != NULL) &&
(SocketData->PdoExtension->DeviceId == NULL)) {
//
// Keep a copy of the device id
//
PPDO_EXTENSION pdoExtension = SocketData->PdoExtension;
pdoExtension->DeviceId = ExAllocatePool(NonPagedPool, strlen(deviceId)+1);
if (pdoExtension->DeviceId) {
RtlCopyMemory(pdoExtension->DeviceId, deviceId, strlen(deviceId)+1);
}
}
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaGetDeviceId(
IN PDEVICE_OBJECT Pdo,
IN ULONG FunctionNumber,
OUT PUNICODE_STRING DeviceId
)
/*++
Generated device id is returned for the supplied pc-card
Arguments:
Pdo - Pointer to the physical device object for the pc-card
FunctionNumber - Function number of the function in a multi-function card.
If this is PCMCIA_MULTIFUNCTION_PARENT, then the requested device id
is for the parent device - not for any individual function
DeviceId - Pointer to the unicode string in which device id is returned
Return Value
Status
--*/
{
PPDO_EXTENSION pdoExtension=Pdo->DeviceExtension;
PSOCKET_DATA socketData = pdoExtension->SocketData;
ANSI_STRING ansiId;
PUCHAR deviceId;
NTSTATUS status;
PAGED_CODE();
ASSERT(DeviceId);
if (!socketData) {
ASSERT (socketData);
return STATUS_DEVICE_NOT_READY;
}
status = PcmciaGenerateDeviceId(socketData,
FunctionNumber,
&deviceId);
if (!NT_SUCCESS(status)) {
return status;
}
DebugPrint((PCMCIA_DEBUG_INFO, "pdo %08x Device Id=%s\n", Pdo, deviceId));
RtlInitAnsiString(&ansiId, deviceId);
status = RtlAnsiStringToUnicodeString(DeviceId,
&ansiId,
TRUE);
ExFreePool(deviceId);
return status;
}
NTSTATUS
PcmciaGetHardwareIds(
IN PDEVICE_OBJECT Pdo,
IN ULONG FunctionNumber,
OUT PUNICODE_STRING HardwareIds
)
/*++
Routine Description:
This routine generates the hardware id's for the given PC-Card and returns them
as a Unicode multi-string.
Hardware ids for PC-Cards are:
1. The device id of the PC-Card
2. The device id of the PC-Card with the CRC replaced with the Manufacturer code and
Manufacturer info fields obtained from the tuple information on the PC-Card
These hardware id's are compatible with win 9x hardware ids
Arguments:
Pdo - Pointer to device object representing the PC-Card
FunctionNumber - Function number of the function in a multi-function card.
If this is PCMCIA_MULTIFUNCTION_PARENT, then the requested hardware id
is for the parent device - not for any individual function
HardwareIds - Pointer to the unicode string which contains the hardware id's as a multi-string
Return value:
--*/
{
PPDO_EXTENSION pdoExtension=Pdo->DeviceExtension;
PSOCKET socket = pdoExtension->Socket;
PSOCKET_DATA socketData = pdoExtension->SocketData;
NTSTATUS status;
PSTR strings[4] = {NULL};
PUCHAR hwId, hwId2;
UCHAR deviceType;
UCHAR stringCount = 0;
PAGED_CODE();
if (!socketData) {
ASSERT (socketData);
return STATUS_DEVICE_NOT_READY;
}
//
// get the device type for later use
//
status = PcmciaGetDeviceType(Pdo, FunctionNumber, &deviceType);
if (!NT_SUCCESS(status)) {
return status;
}
//
// The first hardware id is identical to the device id
// Generate the device id
//
status = PcmciaGenerateDeviceId(socketData,
FunctionNumber,
&strings[stringCount++]);
if (!NT_SUCCESS(status)) {
return status;
}
try {
status = STATUS_INSUFFICIENT_RESOURCES;
hwId = ExAllocatePool(PagedPool, PCMCIA_MAXIMUM_DEVICE_ID_LENGTH);
if (!hwId) {
leave;
}
strings[stringCount++] = hwId;
//
// The second hardware is the device id with the CRC replaced
// with the manufacturer code and info
//
if (*(socketData->Mfg) == '\0' ) {
//
// No manufacturer name available
//
if (FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) {
if (socketData->Flags & SDF_JEDEC_ID) {
sprintf(hwId, "%s\\%s-%04x",
PCMCIA_ID_STRING,
PCMCIA_MEMORY_ID_STRING,
socketData->JedecId);
} else {
sprintf(hwId, "%s\\%s-%04X-%04X",
PCMCIA_ID_STRING,
PCMCIA_UNKNOWN_MANUFACTURER_STRING,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
}
} else {
sprintf(hwId, "%s\\%s-DEV%d-%04X-%04X", PCMCIA_ID_STRING,
PCMCIA_UNKNOWN_MANUFACTURER_STRING,
FunctionNumber,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
}
} else {
UCHAR Mfg[MAX_MANFID_LENGTH];
UCHAR Ident[MAX_IDENT_LENGTH];
PcmciaFilterIdString(socketData->Mfg, Mfg, MAX_MANFID_LENGTH);
PcmciaFilterIdString(socketData->Ident, Ident, MAX_IDENT_LENGTH);
//
// Here a mistake on Win2000 is forcing us to now generate two different
// IDs. The intended and documented form at this point is to generate:
//
// PCMCIA\<mfg>-<ident>-<code>-<info>
//
// but Win2000 had a bug where this was generated instead:
//
// PCMCIA\<mfg>-<code>-<info>
//
// So now we generate both in case someone started using the bogus format.
//
hwId2 = ExAllocatePool(PagedPool, PCMCIA_MAXIMUM_DEVICE_ID_LENGTH);
if (!hwId2) {
leave;
}
strings[stringCount++] = hwId2;
if (FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) {
sprintf(hwId, "%s\\%s-%s-%04X-%04X", PCMCIA_ID_STRING,
Mfg,
Ident,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
sprintf(hwId2, "%s\\%s-%04X-%04X", PCMCIA_ID_STRING,
Mfg,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
} else {
sprintf(hwId, "%s\\%s-%s-DEV%d-%04X-%04X",
PCMCIA_ID_STRING,
Mfg,
Ident,
FunctionNumber,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
sprintf(hwId2, "%s\\%s-DEV%d-%04X-%04X",
PCMCIA_ID_STRING,
Mfg,
FunctionNumber,
socketData->ManufacturerCode,
socketData->ManufacturerInfo);
}
}
if (deviceType == PCCARD_TYPE_ATA) {
hwId = ExAllocatePool(PagedPool, PCMCIA_MAXIMUM_DEVICE_ID_LENGTH);
if (!hwId) {
leave;
}
strings[stringCount++] = hwId;
sprintf(hwId, "%s\\%s",
PCMCIA_ID_STRING,
PcmciaCompatibleIds[PCCARD_TYPE_ATA]);
}
status = PcmciaStringsToMultiString(strings , stringCount, HardwareIds);
} finally {
while(stringCount != 0) {
ExFreePool(strings[--stringCount]);
}
}
return status;
}
NTSTATUS
PcmciaGetCompatibleIds(
IN PDEVICE_OBJECT Pdo,
IN ULONG FunctionNumber,
OUT PUNICODE_STRING CompatibleIds
)
/*++
Routine Description:
This routine returns the compatible ids for the given PC-Card.
Compatible id's are generated based on the Function Id of the PC-Card
obtained from the CISTPL_FUNCID in the CIS tuple info. on the PC-Card.
A table lookup is done based on the CISTPL_FUNCID to obtain the compatible id
This compatible id is identical to the win 9x generated compatible ids
Arguments:
Pdo - Pointer to the device object representing the PC-Card
FunctionNumber - Function number of the function in a multi-function card.
If this is PCMCIA_MULTIFUNCTION_PARENT, then the requested compatibleid
is for the parent device - not for any individual function
CompatibleIds - Pointer to the unicode string which would contain the compatible ids
as a multi-string on return
Return value:
STATUS_SUCCESS
Any other status - could not generate compatible ids
--*/
{
UCHAR deviceType ;
NTSTATUS status;
PCSTR strings[1] = {""};
PAGED_CODE();
status = PcmciaGetDeviceType(Pdo, FunctionNumber, &deviceType);
if (!NT_SUCCESS(status)) {
return status;
}
if ((deviceType == PCCARD_TYPE_RESERVED) ||
(deviceType > PCMCIA_MAX_DEVICE_TYPE_SUPPORTED)) {
status = PcmciaStringsToMultiString(strings, 1, CompatibleIds);
} else {
status = PcmciaStringsToMultiString(&PcmciaCompatibleIds[deviceType], 1, CompatibleIds);
}
return status;
}
NTSTATUS
PcmciaGetDeviceType(
IN PDEVICE_OBJECT Pdo,
IN ULONG FunctionNumber,
OUT PUCHAR DeviceType
)
/*++
Routine Description:
This routine returns the device type for the given PC-Card.
device type is obtained from the CISTPL_FUNCID in the CIS tuple info. on the PC-Card.
Arguments:
Pdo - Pointer to the device object representing the PC-Card
FunctionNumber - Function number of the function in a multi-function card.
If this is PCMCIA_MULTIFUNCTION_PARENT, then the requested compatibleid
is for the parent device - not for any individual function
Return value:
device type
--*/
{
UCHAR deviceType ;
PPDO_EXTENSION pdoExtension;
PAGED_CODE();
pdoExtension = Pdo->DeviceExtension;
if (IsDeviceMultifunction(pdoExtension)) {
if (FunctionNumber == PCMCIA_MULTIFUNCTION_PARENT) {
//
// This is for the root multifunction pc-card
//
deviceType = PCCARD_TYPE_MULTIFUNCTION3;
} else {
//
// This is for the individual multifunction child
//
PSOCKET_DATA socketData;
ULONG index;
for (socketData = pdoExtension->SocketData, index = 0; (socketData != NULL);
socketData = socketData->Next,index++) {
if (socketData->Function == FunctionNumber) {
//
// Found the child;
//
break;
}
}
if (!socketData) {
ASSERT (socketData);
return STATUS_DEVICE_NOT_READY;
}
deviceType = socketData->DeviceType;
}
} else {
//
// This is a run-of-the mill single function card
//
deviceType = pdoExtension->SocketData->DeviceType;
}
*DeviceType = deviceType;
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaStringsToMultiString(
IN PCSTR * Strings,
IN ULONG Count,
IN PUNICODE_STRING MultiString
)
/*++
Routine Description:
This routine formats a set of supplied strings into a multi string format, terminating
it with a double '\0' character
Arguments:
Strings - Pointer to an array of strings
Count - Number of strings in the supplied array which are packed into the multi-string
MultiString - Pointer to the Unicode string which packs the supplied string as a multi-string
terminated by double NULL
Return value:
STATUS_SUCCESS
STATUS_INSUFFICIENT_RESOURCES - Could not allocate memory for the multi-string
--*/
{
ULONG i, multiStringLength=0;
UNICODE_STRING tempMultiString;
PCSTR * currentString;
ANSI_STRING ansiString;
NTSTATUS status;
ASSERT (MultiString->Buffer == NULL);
for (i = Count, currentString = Strings; i > 0;i--, currentString++) {
RtlInitAnsiString(&ansiString, *currentString);
multiStringLength += RtlAnsiStringToUnicodeSize(&ansiString);
}
ASSERT(multiStringLength != 0);
multiStringLength += sizeof(WCHAR);
MultiString->Buffer = ExAllocatePool(PagedPool, multiStringLength);
if (MultiString->Buffer == NULL) {
return STATUS_INSUFFICIENT_RESOURCES;
}
MultiString->MaximumLength = (USHORT) multiStringLength;
MultiString->Length = (USHORT) multiStringLength;
tempMultiString = *MultiString;
for (i = Count, currentString = Strings; i > 0;i--, currentString++) {
RtlInitAnsiString(&ansiString, *currentString);
status = RtlAnsiStringToUnicodeString(&tempMultiString,
&ansiString,
FALSE);
ASSERT(NT_SUCCESS(status));
((PSTR) tempMultiString.Buffer) += tempMultiString.Length + sizeof(WCHAR);
};
//
// Add one more NULL to terminate the multi string
//
RtlZeroMemory(tempMultiString.Buffer, sizeof(WCHAR));
return STATUS_SUCCESS;
}
NTSTATUS
PcmciaGetInstanceId(
IN PDEVICE_OBJECT Pdo,
OUT PUNICODE_STRING InstanceId
)
/*++
Routine Description:
This routine generates a unique instance id (1 upwards) for the supplied
PC-Card which is guaranteed not to clash with any other instance ids under
the same pcmcia controller, for the same type of card.
A new instance id is computed only if it was not already present for the PC-Card.
Arguments:
Pdo - Pointer to the device object representing the PC-Card
InstanceId - Pointer to a unicode string which will contain the generated
instance id.
Memory for the unicode string allocated by this routine.
Caller's responsibility to free it .
Return value:
STATUS_SUCCESS
STATUS_UNSUCCESSFUL - Currently there's a cap on the maximum value of instance id - 999999
This status returned only if more than 999999 PC-Cards exist under
this PCMCIA controller!
Any other status - Something failed in the string allocation/conversion
--*/
{
PPDO_EXTENSION pdoExtension=Pdo->DeviceExtension;
PSOCKET socket = pdoExtension->Socket;
PSOCKET_DATA socketData = pdoExtension->SocketData;
ULONG instance;
NTSTATUS status;
ANSI_STRING sizeString;
ASSERT(InstanceId);
if (!socketData) {
return STATUS_DEVICE_NOT_READY;
}
//
// Allocate memory for the unicode string
// Maximum of 6 digits in the instance..
//
RtlInitAnsiString(&sizeString, "123456");
status = RtlAnsiStringToUnicodeString(InstanceId, &sizeString, TRUE);
if (!NT_SUCCESS(status)) {
return status;
}
//
// Don't recompute instance if it's already present
//
if (socketData->Instance) {
status = RtlIntegerToUnicodeString(socketData->Instance, 10, InstanceId);
} else {
KIRQL OldIrql;
//
// Synchronize access to prevent two identical ids/instances
//
KeAcquireSpinLock(&PcmciaGlobalLock, &OldIrql);
//
// assume failure
//
status = STATUS_UNSUCCESSFUL;
for (instance = 1; instance <= PCMCIA_MAX_INSTANCE; instance++) {
if (PcmciaCheckInstance(pdoExtension->DeviceId,
instance)) {
socketData->Instance = instance;
break;
}
}
KeReleaseSpinLock(&PcmciaGlobalLock, OldIrql);
if (socketData->Instance) {
status = RtlIntegerToUnicodeString(instance, 10, InstanceId);
}
}
if (!NT_SUCCESS(status)) {
RtlFreeUnicodeString(InstanceId);
}
return status;
}
BOOLEAN
PcmciaCheckInstance(
IN PUCHAR DeviceId,
IN ULONG Instance
)
/*++
Routine Description:
This routine checks to see if the supplied instance id clashes with any other PC-card
with the same device id
Arguments:
SocketList - Pointer to the list of sockets on the PCMCIA controller
DeviceId - Pointer to the device id of the PC-Card for which the Instance Id is being checked
Instance - Instance Id which needs to be verified
Return value:
TRUE - Instance is unique for the given DeviceId and may be used
FALSE - Instance clashes with another instance id for the same device id
--*/
{
PPDO_EXTENSION pdoExtension;
PFDO_EXTENSION fdoExtension;
PSOCKET_DATA socketData;
PDEVICE_OBJECT fdo, pdo;
for (fdo = FdoList; fdo != NULL; fdo = fdoExtension->NextFdo) {
fdoExtension = fdo->DeviceExtension;
ASSERT (fdoExtension);
if (!IsDeviceStarted(fdoExtension)) {
continue;
}
for (pdo = fdoExtension->PdoList; pdo != NULL; pdo = pdoExtension->NextPdoInFdoChain) {
pdoExtension = pdo->DeviceExtension;
socketData = pdoExtension->SocketData;
if (IsDevicePhysicallyRemoved(pdoExtension)) {
//
// going to be removed soon
//
continue;
}
if (!socketData) {
//
// socketData already cleaned up
//
continue;
}
//
// If an instance has not
// been assigned yet to this card, skip
//
if (socketData->Instance == 0) {
continue;
}
//
// If this socket's device id matches the given socket's device id
// compare the instances: if equal, then this instance is not ok.
//
//
if ((pdoExtension->DeviceId == NULL) || (DeviceId == NULL)) {
continue;
}
if ((strcmp(pdoExtension->DeviceId, DeviceId)==0) &&
(socketData->Instance == Instance)) {
return FALSE;
}
}
}
//
// Instance is ok and unique
//
return TRUE;
}
VOID
PcmciaFilterIdString(
IN PUCHAR pIn,
OUT PUCHAR pOut,
ULONG MaxLen
)
/*++
Filters out characters that shouldn't appear in device id's
Arguments:
pIn - pointer to input string
pOut - pointer to output string
MaxLen - size of buffers
Return Value
none
--*/
{
ULONG i;
for (i=0; i < MaxLen; i++) {
if (*pIn == 0) {
*pOut = 0;
break;
}
if (*pIn >= ' ' && *pIn < 0x7F) {
*pOut++ = *pIn++;
} else {
pIn++;
}
}
}