/*++ 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\-- 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\--DEV- 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\--- // // but Win2000 had a bug where this was generated instead: // // PCMCIA\-- // // 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++; } } }