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.
732 lines
21 KiB
732 lines
21 KiB
/*++
|
|
|
|
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;
|
|
}
|
|
}
|