/*++ Copyright (c) 1999-2000 Microsoft Corporation Module Name : COMPORT.C Abstract: Most of this code was liberated from posusb.sys Author: Jeff Midkiff (jeffmi) 08-24-99 -- */ #include "wceusbsh.h" void NumToDecString(PWCHAR String, USHORT Number, USHORT stringLen); LONG MyLog(ULONG base, ULONG num); PVOID MemDup(PVOID dataPtr, ULONG length); LONG WStrNCmpI(PWCHAR s1, PWCHAR s2, ULONG n); ULONG LAtoD(PWCHAR string); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGEWCE0, GetFreeComPortNumber) #pragma alloc_text(PAGEWCE0, ReleaseCOMPort) #pragma alloc_text(PAGEWCE0, DoSerialPortNaming) #pragma alloc_text(PAGEWCE0, UndoSerialPortNaming) #pragma alloc_text(PAGEWCE0, NumToDecString) #pragma alloc_text(PAGEWCE0, MyLog) #pragma alloc_text(PAGEWCE0, MemDup) #pragma alloc_text(PAGEWCE0, WStrNCmpI) #pragma alloc_text(PAGEWCE0, LAtoD) #endif LONG GetFreeComPortNumber( VOID ) /*++ Routine Description: Find the index of the next unused serial COM port name in the system (e.g. COM3, COM4, etc). Arguments: Return Value: Return COM port number or -1 if unsuccessful. --*/ { LONG comNumber = -1; DbgDump(DBG_INIT, (">GetFreeComPortNumber\n")); PAGED_CODE(); if (g_isWin9x){ /* * Windows 98 * Find the first unused name under Hardware\DeviceMap\SerialComm. * * BUGBUG: * This algorithm does not find all the COM ports reserved * by modems. May want to port tomgreen's AllocateCommPort * function from \faulty\Wdm10\usb\driver\ccport\utils.c */ HANDLE hKey; UNICODE_STRING keyName; NTSTATUS status; OBJECT_ATTRIBUTES objectAttributes; RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\Hardware\\DeviceMap\\SerialComm"); InitializeObjectAttributes( &objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR)NULL); status = ZwOpenKey(&hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &objectAttributes); if (NT_SUCCESS(status)){ #define MAX_COMPORT_NAME_LEN (sizeof("COMxxxx")-1) UCHAR keyValueBytes[sizeof(KEY_VALUE_FULL_INFORMATION)+(MAX_COMPORT_NAME_LEN+1)*sizeof(WCHAR)+sizeof(ULONG)]; PKEY_VALUE_FULL_INFORMATION keyValueInfo = (PKEY_VALUE_FULL_INFORMATION)keyValueBytes; ULONG i, actualLen; ULONG keyIndex = 0; /* * This bitmask represents the used COM ports. * Bit i set indicates com port i+1 is reserved. * Initialize with COM1 and COM2 reserved. * * BUGBUG - only works for up to 32 ports. */ ULONG comNameMask = 3; do { status = ZwEnumerateValueKey( hKey, keyIndex++, KeyValueFullInformation, keyValueInfo, sizeof(keyValueBytes), &actualLen); if (NT_SUCCESS(status)){ if (keyValueInfo->Type == REG_SZ){ PWCHAR valuePtr = (PWCHAR)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset); if (!WStrNCmpI(valuePtr, L"COM", 3)){ /* * valuePtr+3 points the index portion of the COMx string, * but we can't call LAtoD on it because it is * NOT NULL-TERMINATED. * So copy the index into our own buffer, * null-terminate that, * and call LAtoD to get the numerical index. */ WCHAR comPortIndexString[4+1]; ULONG thisComNumber; for (i = 0; (i < 4) && (i < keyValueInfo->DataLength/sizeof(WCHAR)); i++){ comPortIndexString[i] = valuePtr[3+i]; } comPortIndexString[i] = UNICODE_NULL; thisComNumber = LAtoD(comPortIndexString); if (thisComNumber == 0){ ASSERT(thisComNumber != 0); } else if (thisComNumber <= sizeof(ULONG)*8){ comNameMask |= 1 << (thisComNumber-1); } else { ASSERT(thisComNumber <= sizeof(ULONG)*8); } } } } } while (NT_SUCCESS(status)); /* * First clear bit in comNameMask represents the first available COM name. */ for (i = 0; i < sizeof(ULONG)*8; i++){ if (!(comNameMask & (1 << i))){ WCHAR comName[] = L"COMxxxx"; ULONG comNumLen; /* * Save the COM port number that we're returning. */ comNumber = i+1; DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #%d\n", comNumber)); /* * Write a temporary COMx=COMx holder value to the SERIALCOMM key * so that no other PDOs get this COM port number. * This value will get overwritten by when the pdo is started. */ comNumLen = MyLog(10, comNumber)+1; ASSERT(comNumLen <= 4); NumToDecString(comName+3, (USHORT)comNumber, (USHORT)comNumLen); comName[3+comNumLen] = UNICODE_NULL; status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM", comName, REG_SZ, comName, (3 + comNumLen + 1) * sizeof(WCHAR)); ASSERT(NT_SUCCESS(status)); break; } } } else { DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwOpenKey failed with status 0x%x\n", status)); } } else { /* * Windows NT. * Use the COM Name Arbiter bitmap. */ HANDLE hKey; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING keyName; NTSTATUS status; RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter"); InitializeObjectAttributes( &objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR)NULL); status = ZwOpenKey( &hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &objectAttributes); if (NT_SUCCESS(status)){ UNICODE_STRING valueName; PVOID rawData; ULONG dataSize; RtlInitUnicodeString(&valueName, L"ComDB"); ASSERT(hKey); dataSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION); /* * Allocate one extra byte in case we have to add a byte to ComDB */ rawData = ExAllocatePool(NonPagedPool, dataSize+1); if (rawData){ status = ZwQueryValueKey( hKey, &valueName, KeyValuePartialInformation, rawData, dataSize, &dataSize); if (status == STATUS_BUFFER_OVERFLOW){ ExFreePool(rawData); ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); /* * Allocate one extra byte in case we have to add a byte to ComDB */ rawData = ExAllocatePool(NonPagedPool, dataSize+1); if (rawData){ status = ZwQueryValueKey( hKey, &valueName, KeyValuePartialInformation, rawData, dataSize, &dataSize); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)){ PKEY_VALUE_PARTIAL_INFORMATION keyPartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)rawData; ULONG b, i; BOOLEAN done = FALSE; ASSERT(dataSize >= FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); ASSERT(keyPartialInfo->Type == REG_BINARY); /* * The ComDB value is just a bit mask where bit n set indicates * that COM port # n+1 is taken. * Get the index of the first unset bit; starting with bit 2 (COM3). */ for (b = 0; (b < dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) && !done; b++){ for (i = (b == 0) ? 2 : 0; (i < 8) && !done; i++){ if (keyPartialInfo->Data[b] & (1 << i)){ /* * This COM port (#8*b+i+1) is taken, go to the next one. */ } else { /* * Found a free COM port. * Write the value back with the new bit set. * Only write back the number of bytes we read earlier. * Only use this COM port if the write succeeds. * * Note: careful with the size of the KEY_VALUE_PARTIAL_INFORMATION * struct. Its real size is 0x0D bytes, * but the compiler aligns it to 0x10 bytes. * So use FIELD_OFFSET, not sizeof, to determine * how many bytes to write. */ keyPartialInfo->Data[b] |= (1 << i); status = ZwSetValueKey( hKey, &valueName, 0, REG_BINARY, (PVOID)keyPartialInfo->Data, dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); if (NT_SUCCESS(status)){ comNumber = 8*b + i + 1; DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #0x%x\n", comNumber)); } else { DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwSetValueKey failed with 0x%x\n", status)); } done = TRUE; } } } if ((b == dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)) && !done){ /* * No more available bits in ComDB, so add a byte. */ ASSERT(comNumber == -1); ASSERT(b > 0); DbgDump(DBG_WRN, ("ComDB overflow -- adding new byte")); keyPartialInfo->Data[b] = 1; dataSize++; status = ZwSetValueKey( hKey, &valueName, 0, REG_BINARY, (PVOID)keyPartialInfo->Data, dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); if (NT_SUCCESS(status)){ comNumber = 8*b + 1; DbgDump(DBG_INIT, ("GetFreeComPortNumber: got free COM port #0x%x.", comNumber)); } else { DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwSetValueKey #2 failed with 0x%x.", status)); } } ASSERT(comNumber != -1); } else { DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwQueryValueKey failed with 0x%x.", status)); } /* * Check that we didn't fail the second allocation before freeing this buffer. */ if (rawData){ ExFreePool(rawData); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } status = ZwClose(hKey); ASSERT(NT_SUCCESS(status)); } else { DbgDump(DBG_ERR, ("GetFreeComPortNumber: ZwOpenKey failed with 0x%x.", status)); } } ASSERT(comNumber != -1); DbgDump(DBG_INIT, ("ReleaseCOMPort: %d\n", comPortNumber)); PAGED_CODE(); if (g_isWin9x){ /* * We punt on this for Win9x. * That's ok since the SERIALCOMM keys are dynamically-generated at each boot, * so if start fails a COM port number will just be unavailable until the next boot. */ DbgDump(DBG_WRN, ("ReleaseCOMPort: not implemented for Win9x\n")); // BUGBUG } else { HANDLE hKey = NULL; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING keyName; NTSTATUS status; if ( !(comPortNumber > 0)) { DbgDump(DBG_ERR, ("ReleaseCOMPort - INVALID_PARAMETER: %d\n", comPortNumber )); // BUGBUG return; } RtlInitUnicodeString(&keyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter"); InitializeObjectAttributes( &objectAttributes, &keyName, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR)NULL); status = ZwOpenKey(&hKey, KEY_QUERY_VALUE | KEY_SET_VALUE, &objectAttributes); if (NT_SUCCESS(status)){ UNICODE_STRING valueName; PVOID rawData; ULONG dataSize; RtlInitUnicodeString(&valueName, L"ComDB"); ASSERT(hKey); dataSize = sizeof(KEY_VALUE_PARTIAL_INFORMATION); rawData = ExAllocatePool(NonPagedPool, dataSize); if (rawData){ status = ZwQueryValueKey( hKey, &valueName, KeyValuePartialInformation, rawData, dataSize, &dataSize); if (status == STATUS_BUFFER_OVERFLOW){ ExFreePool(rawData); ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); rawData = ExAllocatePool(NonPagedPool, dataSize); if (rawData){ status = ZwQueryValueKey( hKey, &valueName, KeyValuePartialInformation, rawData, dataSize, &dataSize); } else { status = STATUS_INSUFFICIENT_RESOURCES; } } if (NT_SUCCESS(status)){ PKEY_VALUE_PARTIAL_INFORMATION keyPartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)rawData; ASSERT(dataSize > FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); ASSERT(keyPartialInfo->Type == REG_BINARY); /* * The ComDB value is just a bit mask where bit n set indicates * that COM port # n+1 is taken. * Get the index of the first unset bit; starting with bit 2 (COM3). * * Note: careful with the size of the KEY_VALUE_PARTIAL_INFORMATION * struct. Its real size is 0x0D bytes, * but the compiler aligns it to 0x10 bytes. * So use FIELD_OFFSET, not sizeof, to determine * how many bytes to write. */ ASSERT(comPortNumber >= 3); if ((comPortNumber > 0) && (comPortNumber <= (LONG)(dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))*8)){ //ASSERT(keyPartialInfo->Data[(comPortNumber-1)/8] & (1 << ((comPortNumber-1) & 7))); keyPartialInfo->Data[(comPortNumber-1)/8] &= ~(1 << ((comPortNumber-1) & 7)); status = ZwSetValueKey( hKey, &valueName, 0, REG_BINARY, (PVOID)keyPartialInfo->Data, dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data)); if (NT_SUCCESS(status)){ DbgDump(DBG_INIT, ("ReleaseCOMPort: released COM port # 0x%x\n", comPortNumber)); } else { DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwSetValueKey failed with 0x%x\n", status)); } } else { ASSERT((comPortNumber > 0) && (comPortNumber <= (LONG)(dataSize-FIELD_OFFSET(KEY_VALUE_PARTIAL_INFORMATION, Data))*8)); } } else { DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwQueryValueKey failed with 0x%x\n", status)); } /* * Check that we didn't fail the second allocation before freeing this buffer. */ if (rawData){ ExFreePool(rawData); } } else { status = STATUS_INSUFFICIENT_RESOURCES; } status = ZwClose(hKey); ASSERT(NT_SUCCESS(status)); } else { DbgDump(DBG_ERR, ("ReleaseCOMPort: ZwOpenKey failed with 0x%x\n", status)); } } DbgDump(DBG_INIT, ("DoSerialPortNaming %d\n", ComPortNumber)); PAGED_CODE(); ASSERT(PDevExt); ASSERT((numLen > 0) && (numLen <= 4)); ASSERT_SERIAL_PORT(PDevExt->SerialPort); RtlCopyMemory(buf, comNamePrefix, sizeof(comNamePrefix)); NumToDecString( buf+sizeof(comNamePrefix)/sizeof(WCHAR)-1, (USHORT)ComPortNumber, (USHORT)numLen ); buf[sizeof(comNamePrefix)/sizeof(WCHAR) - 1 + numLen] = UNICODE_NULL; pwcComPortName = MemDup(buf, sizeof(buf)); if (pwcComPortName) { // // create symbolic link for the SerialPort interface // RtlInitUnicodeString( &PDevExt->SerialPort.Com.SerialPortName, pwcComPortName); ASSERT( PDevExt->DeviceName.Buffer ); ASSERT( PDevExt->SerialPort.Com.SerialPortName.Buffer ); status = IoCreateSymbolicLink( &PDevExt->SerialPort.Com.SerialPortName, &PDevExt->DeviceName ); if (NT_SUCCESS(status)) { // // let the system know there is another SERIALCOMM entry under HKLM\DEVICEMAP\SERIALCOMM // UNICODE_STRING comPortSuffix; PDevExt->SerialPort.Com.SerialSymbolicLink = TRUE; /* * Create the '\Device\WCEUSBSI000x = COMx' entry */ RtlInitUnicodeString(&comPortSuffix, PDevExt->SerialPort.Com.SerialPortName.Buffer+(sizeof(L"\\DosDevices\\")-sizeof(WCHAR))/sizeof(WCHAR)); //ASSERT( PDevExt->SerialPort.Com.SerialCOMMname.Buffer ); status = RtlWriteRegistryValue( RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM", PDevExt->DeviceName.Buffer, REG_SZ, comPortSuffix.Buffer, comPortSuffix.Length + sizeof(WCHAR) ); if (NT_SUCCESS(status)){ PDevExt->SerialPort.Com.PortNumber = ComPortNumber; if (g_isWin9x){ NTSTATUS tmpStatus; /* * Delete the temporary 'COMx=COMx' holder value we created earlier. */ tmpStatus = RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, L"SERIALCOMM", comPortSuffix.Buffer); //ASSERT(NT_SUCCESS(tmpStatus)); #if DBG if ( !NT_SUCCESS(tmpStatus) ) { DbgDump(DBG_WRN, ("RtlDeleteRegistryValue error: 0x%x\n", tmpStatus)); } #endif } } else { DbgDump(DBG_ERR, ("RtlWriteRegistryValue error: 0x%x\n", status)); LogError( NULL, PDevExt->DeviceObject, 0, 0, 0, ERR_SERIALCOMM, status, SERIAL_REGISTRY_WRITE_FAILED, PDevExt->DeviceName.Length + sizeof(WCHAR), PDevExt->DeviceName.Buffer, 0, NULL ); } } else { DbgDump(DBG_ERR, ("IoCreateSymbolicLink error: 0x%x\n", status)); LogError( NULL, PDevExt->DeviceObject, 0, 0, 0, ERR_COMM_SYMLINK, status, SERIAL_NO_SYMLINK_CREATED, PDevExt->SerialPort.Com.SerialPortName.Length + sizeof(WCHAR), PDevExt->SerialPort.Com.SerialPortName.Buffer, PDevExt->DeviceName.Length + sizeof(WCHAR), PDevExt->DeviceName.Buffer ); TEST_TRAP(); } } else { status = STATUS_INSUFFICIENT_RESOURCES; DbgDump(DBG_ERR, ("DoSerialPortNaming error: 0x%x\n", status)); LogError( NULL, PDevExt->DeviceObject, 0, 0, 0, ERR_COMM_SYMLINK, status, SERIAL_INSUFFICIENT_RESOURCES, 0, NULL, 0, NULL ); } DbgDump(DBG_INIT, ("UndoSerialPortNaming\n")); PAGED_CODE(); ASSERT(PDevExt); ASSERT_SERIAL_PORT(PDevExt->SerialPort); if (!g_ExposeComPort) { DbgDump(DBG_INIT, ("!g_ExposeComPort\n")); return; } // remove our entry from ComDB ReleaseCOMPort( PDevExt->SerialPort.Com.PortNumber ); if (PDevExt->SerialPort.Com.SerialPortName.Buffer && PDevExt->SerialPort.Com.SerialSymbolicLink) { IoDeleteSymbolicLink(&PDevExt->SerialPort.Com.SerialPortName); } if (PDevExt->SerialPort.Com.SerialPortName.Buffer != NULL) { ExFreePool(PDevExt->SerialPort.Com.SerialPortName.Buffer); RtlInitUnicodeString(&PDevExt->SerialPort.Com.SerialPortName, NULL); } if (PDevExt->SerialPort.Com.SerialCOMMname.Buffer != NULL) { ExFreePool(PDevExt->SerialPort.Com.SerialCOMMname.Buffer); RtlInitUnicodeString(&PDevExt->SerialPort.Com.SerialCOMMname, NULL); } if (PDevExt->DeviceName.Buffer != NULL) { RtlDeleteRegistryValue( RTL_REGISTRY_DEVICEMAP, SERIAL_DEVICE_MAP, PDevExt->DeviceName.Buffer); ExFreePool(PDevExt->DeviceName.Buffer); RtlInitUnicodeString(&PDevExt->DeviceName, NULL); } DbgDump(DBG_INIT, ("= 0; i--) { String[i] = map[Number % 10]; Number /= 10; } } LONG MyLog(ULONG base, ULONG num) { LONG result; ASSERT(num); PAGED_CODE(); for (result = -1; num; result++){ num /= base; } return result; } PVOID MemDup(PVOID dataPtr, ULONG length) { PVOID newPtr; PAGED_CODE(); newPtr = (PVOID)ExAllocatePool(NonPagedPool, length); // BUGBUG allow paged if (newPtr){ RtlCopyMemory(newPtr, dataPtr, length); } return newPtr; } LONG WStrNCmpI(PWCHAR s1, PWCHAR s2, ULONG n) { ULONG result; PAGED_CODE(); while (n && *s1 && *s2 && ((*s1|0x20) == (*s2|0x20))){ s1++, s2++; n--; } if (n){ result = ((*s1|0x20) > (*s2|0x20)) ? 1 : ((*s1|0x20) < (*s2|0x20)) ? -1 : 0; } else { result = 0; } return result; } ULONG LAtoD(PWCHAR string) /*++ Routine Description: Convert a decimal string (without the '0x' prefix) to a ULONG. Arguments: string - null-terminated wide-char decimal-digit string Return Value: ULONG value --*/ { ULONG i, result = 0; PAGED_CODE(); for (i = 0; string[i]; i++){ if ((string[i] >= L'0') && (string[i] <= L'9')){ result *= 10; result += (string[i] - L'0'); } else { ASSERT(0); break; } } return result; } #if 0 VOID NumToHexString( PWCHAR String, USHORT Number, USHORT stringLen ) { const static WCHAR map[] = L"0123456789ABCDEF"; LONG i = 0; PAGED_CODE(); ASSERT(stringLen); for (i = stringLen-1; i >= 0; i--) { String[i] = map[Number & 0x0F]; Number >>= 4; } } LONG GetComPort( PDEVICE_OBJECT PDevObj, ULONG ComInterfaceIndex ) /*++ Routine Description: Get the serial COM port index for a serial interface we're about to create. If this is the first plug-in, call GetFreeComPortNumber to reserve a new static COM port for this device and store it in our software key. If this is not the first plug-in, it should be sitting in the registry. ComInterfaceIndex - is our zero-based device interface index, 0000, 0001, etc. Arguments: Return Value: Return COM port number or -1 if unsuccessful. --*/ { PDEVICE_EXTENSION pDevExt = PDevObj->DeviceExtension; LONG comNumber = -1; NTSTATUS status; HANDLE hRegDevice; DbgDump(DBG_INIT, (">GetComPort\n")); PAGED_CODE(); status = IoOpenDeviceRegistryKey( pDevExt->PDO, /*PLUGPLAY_REGKEY_DEVICE,*/ PLUGPLAY_REGKEY_DRIVER, KEY_READ, &hRegDevice); if (NT_SUCCESS(status)){ UNICODE_STRING keyName; PKEY_VALUE_FULL_INFORMATION keyValueInfo; ULONG keyValueTotalSize, actualLength; // // PLUGPLAY_REGKEY_DEVICE is under HKLM\System\CCS\Enum\USB\ROOT_HUB\4&574193&0 // PLUGPLAY_REGKEY_DRIVER is under HKLM\System\CCS\Class\{Your_GUID}\000x // WCHAR interfaceKeyName[] = L"COMPortForInterfaceXXXX"; NumToHexString( interfaceKeyName+sizeof(interfaceKeyName)/sizeof(WCHAR)-1-4, (USHORT)ComInterfaceIndex, 4); RtlInitUnicodeString(&keyName, interfaceKeyName); keyValueTotalSize = sizeof(KEY_VALUE_FULL_INFORMATION) + keyName.Length*sizeof(WCHAR) + sizeof(ULONG); keyValueInfo = ExAllocatePool(PagedPool, keyValueTotalSize); if (keyValueInfo){ status = ZwQueryValueKey( hRegDevice, &keyName, KeyValueFullInformation, keyValueInfo, keyValueTotalSize, &actualLength); if (NT_SUCCESS(status)){ ASSERT(keyValueInfo->Type == REG_DWORD); ASSERT(keyValueInfo->DataLength == sizeof(ULONG)); comNumber = (LONG)*((PULONG)(((PCHAR)keyValueInfo)+keyValueInfo->DataOffset)); DbgDump(DBG_INIT, ("GetComPort: read COM port# 0x%x for interface 0x%x from registry\n", (ULONG)comNumber, ComInterfaceIndex)); } else { /* * No COM port number recorded in registry. * Allocate a new static COM port from the COM name arbiter * and record it in our software key for the next PnP. */ comNumber = GetFreeComPortNumber(); if (comNumber == -1){ DbgDump(DBG_ERR, ("GetComPort: GetFreeComPortNumber failed\n")); } else { status = ZwSetValueKey( hRegDevice, &keyName, 0, REG_DWORD, &comNumber, sizeof(ULONG)); if (!NT_SUCCESS(status)){ DbgDump(DBG_ERR, ("GetComPort: ZwSetValueKey failed with status 0x%x\n", status)); } } } ExFreePool(keyValueInfo); } else { ASSERT(keyValueInfo); } ZwClose(hRegDevice); } else { DbgDump(DBG_ERR, ("GetComPort: IoOpenDeviceRegistryKey failed with 0x%x\n", status)); } DbgDump(DBG_INIT, ("