|
|
/*++
Copyright (c) 1997-2000 Microsoft Corporation
Module Name:
id.c
Abstract:
This module contains functions used in the generation of responses to a IRP_MN_QUERY_ID IRP.
Author:
Peter Johnston (peterj) 08-Mar-1997
Revision History:
--*/
#include "pcip.h"
//++
//
// PciQueryId returns UNICODE strings when the ID type is DeviceID
// or InstanceID. For HardwareIDs and CompatibleIDs it returns a
// a zero terminated list of zero terminated UNICODE strings (MULTI_SZ).
//
// The normal process of converting a string to a unicode string involves
// taking it's length, allocating pool memory for the new string and
// calling RtlAnsiStringToUnicodeString to do the conversion. The following
// is an attempt to be a little more efficient in terms of both size and
// speed by keeping track of the relevant string data as it goes past in
// the process of creating the set of strings.
//
//--
#define MAX_ANSI_STRINGS 8
#define MAX_ANSI_BUFFER 256
typedef struct _PCI_ID_BUFFER { ULONG Count; // number of ansi strings
ANSI_STRING AnsiStrings[MAX_ANSI_STRINGS]; USHORT UnicodeSZSize[MAX_ANSI_STRINGS]; USHORT UnicodeBufferSize; PUCHAR NextFree; // first unused byte in buffer
UCHAR Bytes[MAX_ANSI_BUFFER];// buffer start address
} PCI_ID_BUFFER, *PPCI_ID_BUFFER;
//
// All functins in this module are pageable.
//
// Define prototypes for module local functions.
//
VOID PciIdPrintf( IN PPCI_ID_BUFFER IdBuffer, PCCHAR Format, ... );
VOID PciIdPrintfAppend( IN PPCI_ID_BUFFER IdBuffer, PCCHAR Format, ... );
VOID PciInitIdBuffer( IN PPCI_ID_BUFFER IdBuffer );
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PciGetDeviceDescriptionMessage)
#pragma alloc_text(PAGE, PciIdPrintf)
#pragma alloc_text(PAGE, PciIdPrintfAppend)
#pragma alloc_text(PAGE, PciInitIdBuffer)
#pragma alloc_text(PAGE, PciQueryId)
#pragma alloc_text(PAGE, PciQueryDeviceText)
#endif
VOID PciInitIdBuffer( IN PPCI_ID_BUFFER IdBuffer ) { IdBuffer->NextFree = IdBuffer->Bytes; IdBuffer->UnicodeBufferSize = 0; IdBuffer->Count = 0; }
VOID PciIdPrintf( IN PPCI_ID_BUFFER IdBuffer, PCCHAR Format, ... ) { ULONG index; PUCHAR buffer; LONG maxLength; va_list ap; PANSI_STRING ansiString; BOOLEAN ok;
PCI_ASSERT(IdBuffer->Count < MAX_ANSI_STRINGS);
//
// Make my life easier, keep repeated values in locals.
//
index = IdBuffer->Count; buffer = IdBuffer->NextFree; maxLength = MAX_ANSI_BUFFER - (LONG)(buffer - IdBuffer->Bytes); ansiString = &IdBuffer->AnsiStrings[index];
//
// Pass the format string and subsequent data into (effectively)
// sprintf.
//
va_start(ap, Format);
ok = SUCCEEDED(StringCbVPrintfA(buffer, maxLength, Format, ap));
ASSERT(ok);
va_end(ap);
//
// Turn this into a counted Ansi string
//
RtlInitAnsiString(ansiString, buffer); //
// Get the length of this string in a unicode world and record it
// for later when the whole set of strings gets converted (keep
// the total size also).
//
IdBuffer->UnicodeSZSize[index] = (USHORT)RtlAnsiStringToUnicodeSize(ansiString); IdBuffer->UnicodeBufferSize += IdBuffer->UnicodeSZSize[index];
//
// Bump buffer pointer for next iteration and the count.
//
IdBuffer->NextFree += ansiString->Length + 1; IdBuffer->Count++; }
VOID PciIdPrintfAppend( IN PPCI_ID_BUFFER IdBuffer, PCCHAR Format, ... ) { ULONG index; PUCHAR buffer; va_list ap; PANSI_STRING ansiString; SIZE_T maxLength, length; SIZE_T remainingCount = 0; BOOLEAN ok;
PCI_ASSERT(IdBuffer->Count);
//
// Make my life easier, keep repeated values in locals.
//
index = IdBuffer->Count - 1; buffer = IdBuffer->NextFree - 1; maxLength = MAX_ANSI_BUFFER - (ULONG)(buffer - IdBuffer->Bytes); ansiString = &IdBuffer->AnsiStrings[index];
//
// Pass the format string and subsequent data into (effectively)
// sprintf.
//
va_start(ap, Format);
ok = SUCCEEDED(StringCbVPrintfExA(buffer, maxLength, NULL, &remainingCount, 0, Format, ap));
ASSERT(ok);
length = maxLength - remainingCount;
va_end(ap);
PCI_ASSERT(length < maxLength);
//
// Increase the ansi string length by the length of the new
// portion of the string.
//
ansiString->Length += (USHORT)length; ansiString->MaximumLength += (USHORT)length;
//
// Get the length of this string in a unicode world and record it
// for later when the whole set of strings gets converted (keep
// the total size also).
//
IdBuffer->UnicodeSZSize[index] = (USHORT)RtlAnsiStringToUnicodeSize(ansiString); IdBuffer->UnicodeBufferSize += IdBuffer->UnicodeSZSize[index];
//
// Bump buffer pointer for next iteration.
//
IdBuffer->NextFree += length; }
NTSTATUS PciQueryId( IN PPCI_PDO_EXTENSION PdoExtension, IN BUS_QUERY_ID_TYPE IdType, IN OUT PWSTR *BusQueryId ) { PCI_ID_BUFFER idBuffer; UCHAR venDevString[sizeof("PCI\\VEN_vvvv&DEV_dddd")]; NTSTATUS status = STATUS_SUCCESS; UNICODE_STRING unicodeId; PVOID unicodeBuffer; ULONG i; ULONG subsystem; PPCI_PDO_EXTENSION current; BOOLEAN ok;
PAGED_CODE();
*BusQueryId = NULL;
//
// In all the following we want PCI\VEN_vvvv&DEV_dddd.
//
ok = SUCCEEDED(StringCbPrintfA(venDevString, sizeof(venDevString), "PCI\\VEN_%04X&DEV_%04X", PdoExtension->VendorId, PdoExtension->DeviceId));
ASSERT(ok);
PciInitIdBuffer(&idBuffer);
subsystem = (PdoExtension->SubsystemId << 16) | PdoExtension->SubsystemVendorId;
switch (IdType) { case BusQueryInstanceID:
//
// Caller wants an instance ID for this device. The PCI
// driver reports that it does NOT generate unique IDs for
// devices so PnP Manager will prepend bus information.
//
// The instance ID is of the form-
//
// AABBCCDDEEFF...XXYYZZ
//
// Where AA is the slot number (device/function) of the device
// on the bus, BB, CC,... XX, YY, ZZ are the slot number of the
// PCI-to-PCI bridges on their parent busses all the way up to
// the root. A device on the root bus will have only one entry,
// AA.
//
current = PdoExtension;
//
// Initialize empty buffer.
//
PciIdPrintf(&idBuffer,"");
for (;;) { PciIdPrintfAppend(&idBuffer, "%02X", PCI_DEVFUNC(current) );
if (PCI_PDO_ON_ROOT(current)) { break; } current = PCI_PARENT_PDO(current)->DeviceExtension; } break;
case BusQueryHardwareIDs: case BusQueryDeviceID:
//
// Hardware and Compatible IDs are generated as specified
// in the ACPI spec (section 6.1.2 in version 0.9).
//
// Hardware IDs are a list of identifiers of the form
//
// PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss&REV_rr
// PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss
// PCI\VEN_vvvv&DEV_dddd&REV_rr
// PCI\VEN_vvvv&DEV_dddd
//
// Where vvvv is the Vendor ID from config space,
// dddd is the Device ID,
// ssssssss is the Subsystem ID/Subsystem Vendor ID, and
// rr is the Revision ID.
//
// Device ID is the same as the first Hardware ID (ie most
// specific of all possible IDs).
//
PciIdPrintf(&idBuffer, "%s&SUBSYS_%08X&REV_%02X", venDevString, subsystem, PdoExtension->RevisionId);
if (IdType == BusQueryDeviceID) { break; }
PciIdPrintf(&idBuffer, "%s&SUBSYS_%08X", venDevString, subsystem);
//
// Fall thru.
//
case BusQueryCompatibleIDs:
//
// If the subsystem is non-zero, the second two are compatible
// IDs, otherwise they are hardware IDs.
//
if (((subsystem == 0) && (IdType == BusQueryHardwareIDs)) || ((subsystem != 0) && (IdType == BusQueryCompatibleIDs))) {
PciIdPrintf(&idBuffer, "%s&REV_%02X", venDevString, PdoExtension->RevisionId);
//
// Device ID is PCI\VEN_vvvv&DEV_dddd
//
PciIdPrintf(&idBuffer, "%s", venDevString); }
if (IdType == BusQueryHardwareIDs) {
//
// The comment in the Memphis code says "Add
// special Intel entry". Odd that these entries
// are absent from the spec. They are added for
// PIIX4 which has the same vendor and device IDs
// for two different sub class codes.
//
// These two entries are
//
// PCI\VEN_vvvv&DEV_dddd&CC_ccsspp
// PCI\VEN_vvvv&DEV_dddd&CC_ccss
//
// (See below for cc, ss and pp explanaitions).
//
PciIdPrintf(&idBuffer, "%s&CC_%02X%02X%02X", venDevString, PdoExtension->BaseClass, PdoExtension->SubClass, PdoExtension->ProgIf);
PciIdPrintf(&idBuffer, "%s&CC_%02X%02X", venDevString, PdoExtension->BaseClass, PdoExtension->SubClass); }
if (IdType == BusQueryCompatibleIDs) {
//
// The Compatible IDs list, consists of the above plus
//
// PCI\VEN_vvvv&CC_ccsspp
// PCI\VEN_vvvv&CC_ccss
// PCI\VEN_vvvv
// PCI\CC_ccsspp
// PCI\CC_ccss
//
// Where cc is the Class Code from config space,
// ss is the Sub-Class Code, and
// pp is the programming interface.
//
// WARNING: Revise the size of the buffer if you increase
// the above list.
//
PciIdPrintf(&idBuffer, "PCI\\VEN_%04X&CC_%02X%02X%02X", PdoExtension->VendorId, PdoExtension->BaseClass, PdoExtension->SubClass, PdoExtension->ProgIf);
PciIdPrintf(&idBuffer, "PCI\\VEN_%04X&CC_%02X%02X", PdoExtension->VendorId, PdoExtension->BaseClass, PdoExtension->SubClass);
PciIdPrintf(&idBuffer, "PCI\\VEN_%04X", PdoExtension->VendorId);
PciIdPrintf(&idBuffer, "PCI\\CC_%02X%02X%02X", PdoExtension->BaseClass, PdoExtension->SubClass, PdoExtension->ProgIf);
PciIdPrintf(&idBuffer, "PCI\\CC_%02X%02X", PdoExtension->BaseClass, PdoExtension->SubClass);
}
//
// HardwareIDs and CompatibleIDs are MULTI_SZ, add a
// NULL list to terminate it all.
//
PciIdPrintf(&idBuffer, "");
break;
default:
PciDebugPrint(PciDbgVerbose, "PciQueryId expected ID type = %d\n", IdType);
//PCI_ASSERT(0 && "Unexpected BUS_QUERY_ID_TYPE");
return STATUS_NOT_SUPPORTED; }
PCI_ASSERT(idBuffer.Count > 0);
//
// What we have is a (bunch of) ansi strings. What we need is a
// (bunch of) unicode strings.
//
unicodeBuffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, idBuffer.UnicodeBufferSize);
if (unicodeBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; }
//
// Build the (possibly MULTI_SZ) unicode string(s).
//
PciDebugPrint(PciDbgPrattling, "PciQueryId(%d)\n", IdType);
unicodeId.Buffer = unicodeBuffer; unicodeId.MaximumLength = idBuffer.UnicodeBufferSize;
for (i = 0; i < idBuffer.Count; i++) { PciDebugPrint(PciDbgPrattling, " <- \"%s\"\n", idBuffer.AnsiStrings[i].Buffer);
status = RtlAnsiStringToUnicodeString(&unicodeId, &idBuffer.AnsiStrings[i], FALSE); if (!NT_SUCCESS(status)) { PCI_ASSERT(NT_SUCCESS(status)); ExFreePool(unicodeBuffer); return status; }
//
// Bump the base pointer and decrement the max length for the
// next trip thru the loop.
//
unicodeId.Buffer += idBuffer.UnicodeSZSize[i] / sizeof(WCHAR); unicodeId.MaximumLength -= idBuffer.UnicodeSZSize[i]; }
*BusQueryId = unicodeBuffer; return status; }
PWSTR PciGetDescriptionMessage( IN ULONG MessageNumber, OUT PUSHORT MessageLength OPTIONAL ) { PWSTR description = NULL; NTSTATUS status; PMESSAGE_RESOURCE_ENTRY messageEntry;
status = RtlFindMessage(PciDriverObject->DriverStart, 11, // <-- I wonder what this is.
LANG_NEUTRAL, MessageNumber, &messageEntry);
if (NT_SUCCESS(status)) {
if (messageEntry->Flags & MESSAGE_RESOURCE_UNICODE) {
//
// Our caller wants a copy they can free, also we need to
// strip the trailing CR/LF. The Length field of the
// message structure includes both the header and the
// actual text.
//
// Note: The message resource entry length will always be a
// multiple of 4 bytes in length. The 2 byte null terminator
// could be in either the last or second last WCHAR position.
//
USHORT textLength;
textLength = messageEntry->Length - FIELD_OFFSET(MESSAGE_RESOURCE_ENTRY, Text) - 2 * sizeof(WCHAR);
description = (PWSTR)(messageEntry->Text); if (description[textLength / sizeof(WCHAR)] == 0) { textLength -= sizeof(WCHAR); }
PCI_ASSERT((LONG)textLength > 1); PCI_ASSERT(description[textLength / sizeof(WCHAR)] == 0x000a);
description = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, textLength);
if (description) {
//
// Copy the text except for the CR/LF/NULL
//
textLength -= sizeof(WCHAR); RtlCopyMemory(description, messageEntry->Text, textLength);
//
// New NULL terminator.
//
description[textLength / sizeof(WCHAR)] = 0; if (MessageLength) { *MessageLength = textLength; } }
} else {
//
// RtlFindMessage returns a string? Wierd.
//
ANSI_STRING ansiDescription; UNICODE_STRING unicodeDescription;
RtlInitAnsiString(&ansiDescription, messageEntry->Text);
//
// Strip CR/LF off the end of the string.
//
ansiDescription.Length -= 2;
//
// Turn it all into a unicode string so we can grab the buffer
// and return that to our caller.
//
status = RtlAnsiStringToUnicodeString( &unicodeDescription, &ansiDescription, TRUE );
description = unicodeDescription.Buffer;
if (MessageLength) { *MessageLength = unicodeDescription.Length; } } }
return description; }
PWSTR PciGetDeviceDescriptionMessage( IN UCHAR BaseClass, IN UCHAR SubClass ) { PWSTR deviceDescription = NULL; ULONG messageNumber;
messageNumber = (BaseClass << 8) | SubClass;
deviceDescription = PciGetDescriptionMessage(messageNumber, NULL);
if (!deviceDescription) {
#define TEMP_DESCRIPTION L"PCI Device"
deviceDescription = ExAllocatePool(PagedPool, sizeof(TEMP_DESCRIPTION)); if (deviceDescription) { RtlCopyMemory(deviceDescription, TEMP_DESCRIPTION, sizeof(TEMP_DESCRIPTION)); } }
return deviceDescription; }
NTSTATUS PciQueryDeviceText( IN PPCI_PDO_EXTENSION PdoExtension, IN DEVICE_TEXT_TYPE TextType, IN LCID LocaleId, IN OUT PWSTR *DeviceText ) { PWSTR locationFormat; SIZE_T textLength; USHORT messageLength; BOOLEAN ok;
PAGED_CODE(); switch (TextType) { case DeviceTextDescription:
*DeviceText = PciGetDeviceDescriptionMessage(PdoExtension->BaseClass, PdoExtension->SubClass); if (*DeviceText) { return STATUS_SUCCESS; } return STATUS_NOT_SUPPORTED;
case DeviceTextLocationInformation:
//
// Get the internationalized location description string from the
// resources of pci.sys. It contains 3 %u specifiers for each of
// Bus, Device & Function.
//
locationFormat = PciGetDescriptionMessage(PCI_LOCATION_TEXT, &messageLength);
if (locationFormat) { //
// Compute max size for location information string:
// The messageLength contains 3 %u format specifiers means that
// the messageLength contains 6 unprinted characters. We need to
// allow space for these numbers. Up to 3 digits for bus number
// 0-255, up to 2 digits for device 0-32 and up to 1 digit for
// function 0-7. Note we assume standard arabic numbers in this
// internationalzed string.
//
textLength = messageLength + ((3 + 2 + 2 - 6) * sizeof(WCHAR)) + sizeof(UNICODE_NULL); *DeviceText = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, textLength * sizeof(WCHAR)); if (*DeviceText) { ok = SUCCEEDED(StringCchPrintfW( *DeviceText, textLength, locationFormat, (ULONG) PdoExtension->ParentFdoExtension->BaseBus, (ULONG) PdoExtension->Slot.u.bits.DeviceNumber, (ULONG) PdoExtension->Slot.u.bits.FunctionNumber ));
ASSERT(ok);
} ExFreePool(locationFormat);
if (*DeviceText) { return STATUS_SUCCESS; } else { return STATUS_INSUFFICIENT_RESOURCES; } }
// fall thru if we couldn't get format string
default: return STATUS_NOT_SUPPORTED; } }
|