windows-nt/Source/XPSP1/NT/drivers/serial/serenum/string.c
2020-09-26 16:20:57 +08:00

947 lines
20 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
STRING.C
Abstract:
This module contains the functions used to parse the PNP COM ID
and save it in the appropriate UNICODE STRINGS. The main function
that is called is Serenum_ParseData. All other functions are called
by this main function.
@@BEGIN_DDKSPLIT
Author:
Jay Senior
@@END_DDKSPLIT
Environment:
kernel mode only
Notes:
@@BEGIN_DDKSPLIT
Revision History:
Louis J. Giliberto, Jr. 22-Mar-1998 Cleanup
@@END_DDKSPLIT
--*/
#include "pch.h"
#define MAX_DEVNODE_NAME 256 // Total size of Device ID
#ifdef ALLOC_PRAGMA
#pragma alloc_text (PAGE, Serenum_ParseData)
// Called by ParseData:
#pragma alloc_text (PAGE, Serenum_GetDevDesc)
#pragma alloc_text (PAGE, Serenum_GetDevCompId)
#pragma alloc_text (PAGE, Serenum_GetDevClass)
#pragma alloc_text (PAGE, Serenum_GetDevSerialNo)
#pragma alloc_text (PAGE, Serenum_GetDevName)
#pragma alloc_text (PAGE, Serenum_GetDevPnPRev)
#pragma alloc_text (PAGE, Serenum_GetDevOtherID)
#pragma alloc_text (PAGE, Serenum_InitMultiString)
#pragma alloc_text (PAGE, Serenum_SzCopy)
#pragma alloc_text (PAGE, Serenum_StrLen)
// Called by the above functions:
#pragma alloc_text (PAGE, Serenum_FixptToAscii)
#pragma alloc_text (PAGE, Serenum_HToI)
#endif
NTSTATUS
Serenum_ParseData(PFDO_DEVICE_DATA FdoData, PCHAR ReadBuffer, ULONG BufferLen,
PUNICODE_STRING hardwareIDs, PUNICODE_STRING compIDs,
PUNICODE_STRING deviceIDs, PUNICODE_STRING PDeviceDesc,
PUNICODE_STRING serialNo, PUNICODE_STRING pnpRev)
/*++
Routine Description:
Parses the PNP COM ID out of the buffer which is passed as the
first parameter and then saves the appropriate IDs as
UNICODE_STRINGS in the other passed parameters.
Return value:
NTSTATUS
--*/
{
PCHAR pOtherId;
PCHAR pPnpRev;
PCHAR pDevNodeName;
PCHAR pSerNo;
PCHAR pClass;
PCHAR pCompIdStar;
PCHAR pDesc;
PCHAR pStrBuffer = NULL;
NTSTATUS status;
PCHAR pDevName;
PCHAR pCompId;
int OtherIDLen;
int start;
BOOLEAN isMouse = FALSE;
PCHAR pMouseID = NULL;
UNREFERENCED_PARAMETER(BufferLen);
//
// Allocate the string buffers
//
pStrBuffer = ExAllocatePool(PagedPool, MAX_DEVNODE_NAME * 7 + 1);
if (pStrBuffer == NULL) {
status = STATUS_INSUFFICIENT_RESOURCES;
goto DoneParsingErr;
} else {
PCHAR pCurBuffer = pStrBuffer;
pOtherId = pCurBuffer;
*pOtherId = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
pPnpRev = pCurBuffer;
*pPnpRev = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
pDevNodeName = pCurBuffer;
*pDevNodeName = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
pSerNo = pCurBuffer;
*pSerNo = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
pClass = pCurBuffer;
*pClass = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
pCompIdStar = pCurBuffer;
pCompId = pCompIdStar + 1;
*pCompIdStar = '\0';
pCurBuffer += MAX_DEVNODE_NAME + 1;
pDesc = pCurBuffer;
*pDesc = '\0';
pCurBuffer += MAX_DEVNODE_NAME;
}
start = Serenum_SzCopy ("SERENUM\\", pDevNodeName);
pDevName = pDevNodeName + start;
start = 0;
RtlInitUnicodeString(hardwareIDs, NULL);
RtlInitUnicodeString(compIDs, NULL);
RtlInitUnicodeString(deviceIDs, NULL);
RtlInitUnicodeString(pnpRev, NULL);
RtlInitUnicodeString(serialNo, NULL);
//
// OtherID
//
start = Serenum_GetDevOtherID(ReadBuffer, pOtherId);
if (start > 16) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("Other ID string too long\n"));
status = STATUS_UNSUCCESSFUL;
goto DoneParsingErr;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Other ID: %s\n", pOtherId));
//
// See if this is a mouse
//
SerenumScanOtherIdForMouse(ReadBuffer, BufferLen, &pMouseID);
if (pMouseID != NULL && (*pMouseID == 'M' || *pMouseID == 'B')) {
isMouse = TRUE;
}
//
// PNP revision number
//
status = Serenum_GetDevPnPRev(FdoData, ReadBuffer, pPnpRev, &start);
if (!NT_SUCCESS(status)) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("PNP ID string bad\n"));
goto DoneParsingErr;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("PNP Revision: %s\n", pPnpRev));
//
// PNP device node name
// EISA ID followed by Product ID
//
Serenum_GetDevName(ReadBuffer, pDevName, &start);
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Node name: %s\n",
pDevNodeName));
//
// Device serial number
//
Serenum_GetDevSerialNo(ReadBuffer, pSerNo, &start);
if (Serenum_StrLen(pSerNo)) {
//
// This field exists - Make sure it is correct length.
//
if (Serenum_StrLen(pSerNo) != 8) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Serial number wrong"
" length\n"));
*pSerNo = '\0';
status = STATUS_UNSUCCESSFUL;
goto DoneParsingErr;
}
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Serial Number: %s\n", pSerNo));
//
// PNP class identifier
//
Serenum_GetDevClass(ReadBuffer, pClass, &start);
if (Serenum_StrLen(pClass) > 32) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Class ID string too long\n"
));
status = STATUS_UNSUCCESSFUL;
goto DoneParsingErr;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Class: %s\n", pClass));
if (_stricmp(pClass, "MOUSE") == 0) {
strcpy(pClass, "SERIAL_MOUSE");
}
//
// Compatible device ID
//
*pCompIdStar = '*';
Serenum_GetDevCompId(ReadBuffer, pCompId, &start);
if (Serenum_StrLen(pCompId) > 40) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Compatible driver ID"
" string too long\n"));
status = STATUS_UNSUCCESSFUL;
goto DoneParsingErr;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Compatible driver ID: %s\n",
pCompId));
//
// End-user legible Product Description
//
Serenum_GetDevDesc (ReadBuffer, pDesc, &start);
if (Serenum_StrLen(pDesc) > 40) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR, ("Device Description too"
" long\n"));
status = STATUS_UNSUCCESSFUL;
goto DoneParsingErr;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Device Description: %s\n",
pDesc));
DoneParsingErr:
if (pStrBuffer != NULL) {
//
// send back the good bits so that routine knows what driver to load
//
Serenum_InitMultiString (FdoData, hardwareIDs, pDevNodeName, pDevName,
NULL);
if (Serenum_StrLen(pCompId) > 0) {
if (!isMouse) {
Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
NULL);
} else {
Serenum_InitMultiString(FdoData, compIDs, pCompIdStar, pClass,
"SERIAL_MOUSE", NULL);
}
} else {
if (isMouse) {
Serenum_InitMultiString(FdoData, compIDs, "SERIAL_MOUSE", NULL);
}
}
Serenum_InitMultiString(FdoData, deviceIDs, pDevNodeName, NULL);
Serenum_InitMultiString(FdoData, PDeviceDesc, pDesc, NULL);
if (Serenum_StrLen(pSerNo)) {
Serenum_InitMultiString(FdoData, serialNo, pSerNo, NULL);
}
if (Serenum_StrLen(pPnpRev)) {
Serenum_InitMultiString(FdoData, pnpRev, pPnpRev, NULL);
}
ExFreePool(pStrBuffer);
}
return status;
}
NTSTATUS
Serenum_InitMultiString(PFDO_DEVICE_DATA FdoData, PUNICODE_STRING MultiString,
...)
/*++
This routine will take a null terminated list of ascii strings and combine
them together to generate a unicode multi-string block
Arguments:
MultiString - a unicode structure in which a multi-string will be built
... - a null terminated list of narrow strings which will be
combined together. This list must contain at least a
trailing NULL
Return Value:
NTSTATUS
--*/
{
ANSI_STRING ansiString;
NTSTATUS status;
PCSTR rawString;
PWSTR unicodeLocation;
ULONG multiLength = 0;
UNICODE_STRING unicodeString;
va_list ap;
ULONG i;
PAGED_CODE();
#if !DBG
UNREFERENCED_PARAMETER(FdoData);
#endif
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Entering Serenum_InitMultiString\n"));
va_start(ap,MultiString);
//
// Make sure that we won't leak memory
//
ASSERT(MultiString->Buffer == NULL);
rawString = va_arg(ap, PCSTR);
while (rawString != NULL) {
RtlInitAnsiString(&ansiString, rawString);
multiLength += RtlAnsiStringToUnicodeSize(&(ansiString));
rawString = va_arg(ap, PCSTR);
}
va_end( ap );
if (multiLength == 0) {
//
// Done
//
RtlInitUnicodeString(MultiString, NULL);
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Leaving Serenum_InitMultiString (1)\n"));
return STATUS_SUCCESS;
}
//
// We need an extra null
//
multiLength += sizeof(WCHAR);
MultiString->MaximumLength = (USHORT)multiLength;
MultiString->Buffer = ExAllocatePool(PagedPool, multiLength);
MultiString->Length = 0;
if (MultiString->Buffer == NULL) {
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Leaving Serenum_InitMultiString (2)\n"));
return STATUS_INSUFFICIENT_RESOURCES;
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Allocated %lu bytes for buffer\n", multiLength));
#if DBG
RtlFillMemory(MultiString->Buffer, multiLength, 0xff);
#endif
unicodeString.Buffer = MultiString->Buffer;
unicodeString.MaximumLength = (USHORT) multiLength;
va_start(ap, MultiString);
rawString = va_arg(ap, PCSTR);
while (rawString != NULL) {
RtlInitAnsiString(&ansiString,rawString);
status = RtlAnsiStringToUnicodeString(&unicodeString, &ansiString, FALSE);
//
// We don't allocate memory, so if something goes wrong here,
// its the function that's at fault
//
ASSERT(NT_SUCCESS(status));
//
// Check for any commas and replace them with NULLs
//
ASSERT(unicodeString.Length % sizeof(WCHAR) == 0);
for (i = 0; i < (unicodeString.Length / sizeof(WCHAR)); i++) {
if (unicodeString.Buffer[i] == L'\x2C' ||
unicodeString.Buffer[i] == L'\x0C' ) {
unicodeString.Buffer[i] = L'\0';
}
}
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer: %ws\n",
unicodeString.Buffer));
//
// Move the buffers along
//
unicodeString.Buffer += ((unicodeString.Length / sizeof(WCHAR)) + 1);
unicodeString.MaximumLength -= (unicodeString.Length + sizeof(WCHAR));
unicodeString.Length = 0;
//
// Next
//
rawString = va_arg(ap, PCSTR);
} // while
va_end(ap);
ASSERT(unicodeString.MaximumLength == sizeof(WCHAR));
//
// Stick the final null there
//
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("unicode buffer last addr: "
"%x\n", unicodeString.Buffer));
unicodeString.Buffer[0] = L'\0';
//
// Include the nulls in the length of the string
//
MultiString->Length = (USHORT)multiLength;
MultiString->MaximumLength = MultiString->Length;
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Leaving Serenum_InitMultiString (3)\n"));
return STATUS_SUCCESS;
}
int
Serenum_StrLen (
PCHAR string)
// Measures the length of a string
{
int i;
if (string == NULL) {
return 0;
}
for (i=0; string[i] != '\0'; i++) {
}
return i;
}
int
Serenum_SzCopy (
PCHAR source,
PCHAR dest)
// Copies a string
// Assumes the buffer is already allocated to be copied into
{
int i;
ASSERT (source);
ASSERT (dest);
for (i=0; source[i] != '\0'; i++) {
*dest++ = source[i];
}
return i;
}
//
// String extraction functions:
//
int
Serenum_GetDevOtherID(
PCHAR input,
PCHAR output)
{
int tail;
CHAR c;
tail = 0;
c = input[tail++];
while((tail < 17 ) && (c != '(') && (c != '(' - 0x20)) {
*output++ = c;
c = input[tail++];
}
*output = '\0';
return(tail-1);
}
/****************************************************************************
*
*
***************************************************************************/
int
Serenum_HToI(char c) {
if('0' <= c && c <= '9')
return(c - '0');
if('A' <= c && c <= 'F')
return(c - 'A' + 10);
if('a' <= c && c <= 'f')
return(c - 'a' + 10);
return(-1);
}
void
Serenum_FixptToAscii(
int n,
PCHAR output)
/****************************************************************************
*
*
***************************************************************************/
{
int tmp;
tmp = n / 100;
if(tmp >= 10)
*output++ = (CHAR)('0' + (tmp / 10));
*output++ = (CHAR)('0' + (tmp % 10));
*output++ = '.';
tmp = n % 100;
*output++ = (CHAR)('0' + (tmp / 10));
*output++ = (CHAR)('0' + (tmp % 10));
*output = '\0';
}
/****************************************************************************
*
*
***************************************************************************/
NTSTATUS
Serenum_GetDevPnPRev(PFDO_DEVICE_DATA FdoData, PCHAR input, PCHAR output,
int *start)
{
int tail;
int i;
char delta;
char c, begin_PnP, end_PnP_pos;
int sum, chk_sum, msd, lsd;
UNREFERENCED_PARAMETER(FdoData);
if (output == NULL || input == NULL) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("GetDevPnPRev Failed, NULL pointer!\n"));
return STATUS_UNSUCCESSFUL;
}
*output = '\0';
tail = *start;
if (input[tail] == 0) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("GetDevPnPRev Failed, input buffer empty!\n"));
return STATUS_UNSUCCESSFUL;
}
c = input[tail++];
while ((tail < 256) && (c != '(') && (c != '(' - 0x20)) {
c = input[tail++];
}
if (c != '(' && c != '(' - 0x20) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("GetDevPnPRev Failed, no Begin PnP char!\n"));
return STATUS_UNSUCCESSFUL;
}
begin_PnP = c;
delta = '(' - begin_PnP;
if (input[tail + 9] != ')' - delta) {
//
// compute checksum
//
sum = c;
i = tail;
while ( (i < 256) && (c != ( ')' - delta)) ) {
c = input[i++];
sum += c;
}
msd = input[i-3];
lsd = input[i-2];
sum -= msd;
sum -= lsd;
msd += delta;
lsd += delta;
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("checksum from device (chars) = %c%c\n", (char)msd,
(char)lsd));
msd = Serenum_HToI((char)msd);
if (msd < 0) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("Bad msd checksum digit\n"));
return STATUS_UNSUCCESSFUL;
}
lsd = Serenum_HToI((char)lsd);
if (lsd < 0) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("Bad lsd checksum digit\n"));
return STATUS_UNSUCCESSFUL;
}
chk_sum = (msd << 4) + lsd;
sum &= 0xff;
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("checksum read from device = %0x\n", chk_sum));
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE,
("Computed checksum = %0x\n", sum));
end_PnP_pos = (char)i;
if ( c - begin_PnP != ')' - '(' ) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("GetDevPnPRev Failed,BeginPnP didn't match "
"EndPnP\n"));
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("begin_PnP = %02x end_PnP = %02x\n", begin_PnP, c));
return STATUS_UNSUCCESSFUL;
}
//
// check the checksum
//
if (chk_sum != sum) {
Serenum_KdPrint(FdoData, SER_DBG_SS_ERROR,
("checksum Failed! Continuing...\n"));
// return STATUS_UNSUCCESSFUL; // Commented out in Memphis
}
i = end_PnP_pos;
input[i-3] = ')' - delta; // trash the checksum
input[i-2] = '\0'; // since we are done with it
}
if (input[tail] > 0x3f ||
input[tail+1] > 0x3f) {
Serenum_KdPrint(FdoData, SER_DBG_SS_TRACE, ("Bad PnP Rev digits\n"));
return STATUS_UNSUCCESSFUL;
}
i = (input[tail++] & 0x3f) << 6;
i |= (input[tail++]) & 0x3f;
Serenum_FixptToAscii(i, output);
i = tail;
//
// get ride of Mouse'output 0x20 bias in the string
//
while ( (i < 256) && delta ) {
input[i] += delta;
c = input[i++];
if ( c == ')' ) {
delta = 0; // indicate we are done
}
}
*start = tail;
return STATUS_SUCCESS;
}
/****************************************************************************
*
*
***************************************************************************/
void Serenum_GetDevName(
PCHAR input,
PCHAR output,
int *start)
{
int tail;
char c;
if(output == NULL || input == NULL)
return;
tail = *start;
// EISA ID
*output++ = input[tail++];
*output++ = input[tail++];
*output++ = input[tail++];
// Product ID
c = input[tail++];
if(Serenum_HToI(c) >= 0)
*output++ = c;
c = input[tail++];
if(Serenum_HToI(c) >= 0)
*output++ = c;
c = input[tail++];
if(Serenum_HToI(c) >= 0)
*output++ = c;
c = input[tail++];
if(Serenum_HToI(c) >= 0)
*output++ = c;
*output = '\0';
*start = tail;
return;
}
/****************************************************************************
*
*
***************************************************************************/
void Serenum_GetDevSerialNo(
PCHAR input,
PCHAR output,
int *start)
{
int tail, cnt;
char c;
if(output == NULL || input == NULL)
return;
*output = '\0';
tail = *start;
if( input[tail++] != '\\')
return;
c = input[tail++];
cnt = 0;
while(cnt < 8 && tail < 256 && ( c != '\\') && ( c != ')') ) {
cnt++;
if(Serenum_HToI(c) < 0)
break;
*output++ = c;
c = input[tail++];
}
*output = '\0';
*start = tail - 1;
return;
}
/****************************************************************************
*
*
***************************************************************************/
void Serenum_GetDevClass(
PCHAR input,
PCHAR output,
int *start)
{
int tail;
char c;
if(output == NULL || input == NULL)
return;
*output = '\0';
tail = *start;
if( input[tail++] != '\\')
return;
c = input[tail++];
while(tail < 256 && ( c != '\\') && ( c != ')') ) {
*output++ = c;
c = input[tail++];
}
*output = '\0';
*start = tail - 1;
return;
}
void Serenum_GetDevCompId(
PCHAR input,
PCHAR output,
int *start)
/****************************************************************************
*
*
***************************************************************************/
{
int tail;
char c;
if(output == NULL || input == NULL)
return;
*output = '\0';
tail = *start;
if( input[tail++] != '\\')
return;
c = input[tail++];
while(tail < 256 && ( c != '\\') && ( c != ')') ) {
*output++ = c;
//
// Put a * after every comma
//
if ('\x0C' == c || '\x2C' == c) {
*output++ = '*';
}
c = input[tail++];
}
*output = '\0';
*start = tail - 1;
}
void
Serenum_GetDevDesc(
PCHAR input,
PCHAR output,
int *start)
/****************************************************************************
*
*
***************************************************************************/
{
int tail;
char c;
if(output == NULL || input == NULL)
return;
*output = '\0';
tail = *start;
if( input[tail++] != '\\')
return;
c = input[tail++];
while(tail < 256 && ( c != '\\') && ( c != ')') ) {
*output++ = c;
c = input[tail++];
}
*output = '\0';
*start = tail - 1;
}