/*++ 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; }