1
votes

here is my problem: I developed a custom serial driver that relies on Microsoft serenum.sys to get serial ports enumerated. Now, I applied this serial driver on top of a multifunction device that splits a single device in six different serial ports - when this happens, the port numbering is completely scrambled, so I need that Serial0 gets COM1, for example, and so on.

I searched a bit, and found this: Change COM port via registry, command line or software?

I tried to use ComDB to get the port names that I need, but while I can make port names available, I don't seem to have an efficient way to associate a SPECIFIC serial port (ie. Serial0) with a SPECIFIC COM port number (ie. COM1).

Has anyone already managed to solve this issue?

1
In times where fixed serial ports are rare and usb-serial-adapters are used, this is quite hard to achieve... A still ugly, but probably the best solution is to implement some kind of "scanning" for your device over all (currently unused) serial ports (or to let the user choose the right one of course)Ctx
That was I am trying to avoid. My driver is fully compatible with the SerialPropPages, so you can change port name by UI as usual. My problem is that I need, for example, to rename COM5 to COM1, because names are given horribly.Alessio Massuoli
It is the serial port driver that creates the legacy Dos name (like "COM1") with IoCreateSymbolicLink(). Renaming is not an option. If you can't fix the driver then consider looking it up. Backgrounder: osronline.com/article.cfm?id=381Hans Passant
That is what I do in the driver and what I should modify at a later time, if possible. Otherwise, the alternative would just be to create a coinstaller to set PortName before DIF_INSTALLDEVICE. The problem is that I have to be sure that this ALWAYS happens, and this device binds to a lot of hardware IDs. So yes, your comment is in the right direction, but I need to get around to this.Alessio Massuoli

1 Answers

0
votes

Solved in two parts, with still an open issue. Starting from the WDK Driver samples on GitHub, https://github.com/Microsoft/Windows-driver-samples

  1. On the SerialReadSymName() function, I changed it to return the symbolic name I wanted, rather than have it use the index provided from ComDB. Note that I had to remove serenum from UpperFilters key in the inf file to have it work with this.

  2. Now I had the working device name (ie. COM1, COM2 etc.) that could be used, but at this stage I got the serial port "Friendly Name" renamed anyway. To solve this contextually, I updated the SerialCreateOpen() with the following code:

    if (!extension->FriendlyNameSet) //Added this guard to the device extension { DWORD f_set = 1; HANDLE keyHandle; MySerialSetFriendlyName(extension); extension->FriendlyNameSet = TRUE; }

With the function MySerialSetFriendlyName() defined as follows:

NTSTATUS MySerialSetFriendlyName(PSERIAL_DEVICE_EXTENSION pDevExt)
{
    NTSTATUS status = STATUS_SUCCESS;

    WCHAR *FriendlyName, *fnprefix, *fnsuffix, *instanceId;
    ULONG FriendlyNameLength, instanceLength, temp, i;

    fnprefix = BASE_FRIENDLY_NAME_PREFIX_STR;
    fnsuffix = BASE_FRIENDLY_NAME_SUFFIX_STR;



    temp = pDevExt->InstanceIdentifier;
    instanceLength = 0;
    while (temp)
    {
        instanceLength++;

        if (temp < 10)
            temp = 0;
        else
        {
            temp /= 10;
            if (!temp)
                instanceLength++;
        }
    }

    if (instanceLength)
    {
        instanceId = ExAllocatePool(NonPagedPool, instanceLength);
        temp = pDevExt->InstanceIdentifier;
        WCHAR digit = L'X';

        for (i = 0; i < instanceLength; i++)
        {
            switch (temp % 10)
            {
            case 0: digit = L'0'; break;
            case 1: digit = L'1'; break;
            case 2: digit = L'2'; break;
            case 3: digit = L'3'; break;
            case 4: digit = L'4'; break;
            case 5: digit = L'5'; break;
            case 6: digit = L'6'; break;
            case 7: digit = L'7'; break;
            case 8: digit = L'8'; break;
            case 9: digit = L'9'; break;
            default: digit = L'X'; break;
            }

            instanceId[instanceLength - i - 1] = digit;
            temp /= 10;
        }

    FriendlyNameLength = BASE_FRIENDLY_NAME_PREFIX_LEN + instanceLength + BASE_FRIENDLY_NAME_SUFFIX_LEN + 1;

    FriendlyName = ExAllocatePool(NonPagedPool, FriendlyNameLength * sizeof(WCHAR));
    RtlZeroMemory(FriendlyName, FriendlyNameLength * sizeof(WCHAR));

    for (i = 0; i < BASE_FRIENDLY_NAME_PREFIX_LEN; i++)
    {
        FriendlyName[i] = fnprefix[i];
    }

    for (i = 0; i < instanceLength; i++)
    {
        FriendlyName[i + BASE_FRIENDLY_NAME_PREFIX_LEN] = instanceId[i];
    }

    for (i = 0; i < BASE_FRIENDLY_NAME_SUFFIX_LEN; i++)
    {
        FriendlyName[i + BASE_FRIENDLY_NAME_PREFIX_LEN + instanceLength] = fnsuffix[i];
    }

    if (pDevExt->PnpRegistryKey)
        status = RtlWriteRegistryValue(RTL_REGISTRY_ABSOLUTE, (PCWSTR)pDevExt->PnpRegistryKey, (PCWSTR)L"FriendlyName", REG_SZ, FriendlyName, FriendlyNameLength * sizeof(WCHAR));
    else
        status = STATUS_INSUFFICIENT_RESOURCES;

    KdPrintEx((DPFLTR_DEFAULT_ID, DPFLTR_ERROR_LEVEL, "%s :: Changed friendly name to %S (NTSTATUS %08X)\n", __FUNCTION__, FriendlyName, status));

    if (!NT_SUCCESS(status))
    {
        status = STATUS_SUCCESS; //it is enough that we came through here
    }
}

return status;
}

(BASE_FRIENDLY_NAME_... are macros that store my chosen friendly name prefix (let's say L"Communications Port (COM") and suffix (let's say L")". You can make them whatever you need, but you really need to store their length in separate macros, then)

That way, friendly name would be set at first device open. To be sure that this means that device gets the right name right away, I just wrote a Device Coinstaller that on the DIF_INSTALLDEVICE command, on post-processing, quickly opened and closed the port. This deserves an article in itself and it is simple enough, so I won't publish this now.

The only open issue is this: - This way, ComDB still memorizes the wrong numbering sequence. You can force it to behave as you wish by changing the Com Name Arbiter bitmasks in the Windows Registry, as specified in the link I provided in the question, but you cannot do that for COM1 and COM2, that are still bound to certain I/O ranges and IRQs.

So, it works, provided you will never need to access ComDB in your applications!