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.
947 lines
33 KiB
947 lines
33 KiB
/*++
|
|
|
|
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 <symbolicLinkName=COMx> 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, ("<GetFreeComPortNumber\n"));
|
|
|
|
return comNumber;
|
|
}
|
|
|
|
|
|
//
|
|
// the only time we want this is when we uninstall...
|
|
//
|
|
VOID
|
|
ReleaseCOMPort(
|
|
LONG comPortNumber
|
|
)
|
|
{
|
|
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, ("<ReleaseCOMPort\n"));
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Note: this is NT specific.
|
|
// Win98 is entirely different.
|
|
//
|
|
NTSTATUS
|
|
DoSerialPortNaming(
|
|
IN PDEVICE_EXTENSION PDevExt,
|
|
IN LONG ComPortNumber
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PWCHAR pwcComPortName=NULL;
|
|
static WCHAR comNamePrefix[] = L"\\DosDevices\\COM";
|
|
WCHAR buf[sizeof(comNamePrefix)/sizeof(WCHAR) + 4];
|
|
LONG numLen = MyLog(10, ComPortNumber)+1;
|
|
|
|
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, ("<DoSerialPortNaming (0x%x)\n", status));
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
UndoSerialPortNaming(
|
|
IN PDEVICE_EXTENSION PDevExt
|
|
)
|
|
{
|
|
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, ("<UndoSerialPortNaming\n"));
|
|
}
|
|
|
|
|
|
void NumToDecString(PWCHAR String, USHORT Number, USHORT stringLen)
|
|
{
|
|
const static WCHAR map[] = L"0123456789";
|
|
LONG i = 0;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(stringLen);
|
|
|
|
for (i = stringLen-1; i >= 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, ("<GetComPort %d\n", comNumber));
|
|
|
|
return comNumber;
|
|
}
|
|
#endif
|
|
|