Writing to serial port from KMDF driver

I’m trying to write to a serial port from a KMDF driver as a logging mechanism. I’m using the WdfIoxxx APIs as I’m planning to use this same functionality in a minifilter later. My problem is that WdfIoTargetOpen is failing with STATUS_OBJECT_NAME_NOT_FOUND status. I tried all kinds of symbolic links and object names to reference the COM port but the result is the same. Here’s a snippet of how I’m trying to open the port :

        DECLARE_CONST_UNICODE_STRING(portName, L"\\Device\\COM4");
        WDF_IO_TARGET_OPEN_PARAMS_INIT_OPEN_BY_NAME(
            &params,
            &portName,
            MAXIMUM_ALLOWED);  
        status = WdfIoTargetOpen(m_ioTarget, &params);

PS: To conform to the rules, I’m a student and this is for an academic project.

!object in the debugger can be very helpful. Does !object \Device\COM4 report that the device exists? Specifically for serial ports, the convention is that the symbolic link name is COMX and it points to \Device\SerialX or something similar. So you would need to open L"\??\COM4" to open via the symbolic link name.

Yeah I also tried that and it failed. Enumerating \Device objects in windbg shows Serial0 and using winobj I could find \Global??\COM4 which is a symlink to \Device\Serial0.

This usually comes down to a typo and getting the double \ just a little bit wrong. What string literal are you using for the symbolic link name?

Theoretically, L"\\DosDevices\\COM4" should also work. Any special name that works at a command prompt should be available in \DosDevices.

https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-ms-dos-device-names

This finally worked. I’m not sure what I was doing wrong but I turned back to using ZwCreateFile, had some errors with it, debugged it, figured out my driver is being loaded before serial.sys so that’s a problem set aside. Some more tweaks later ZwCreateFile worked and I decided to go back to WdfIoTargetOpen and it miraculously worked. I’m sorry if I can’t say for sure what the problem was besides my driver being loaded before serial.sys. For reference, I ended up using L"\\device\\Serial0"

For a production quality driver, \Device\Serial0 is a pretty fragile name. For a student experiment, it is OK (but not great).

For the load order issue where you are loading before the serial driver, you can register for device interface arrival notifications with IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange, PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES, &GUID_DEVINTERFACE_COMPORT, …). You can then use the provided symbolic link name(s) (they look like gibberish) to open the com port.

Okay great, since I’ve successfully written to the serial port I’ll try your method to solve the load order issue. One other thing, I read somewhere that a serial port could not be concurrently open, is that true ? If so, that would be a problem for me because various drivers would be logging to it at the same time.

Correct. Serial ports are opened with exclusive access, enforced by the serial driver. If you need multiple drivers writing to the serial port then you should have one driver open the serial port, create a device object in which your other drivers can open, and pass through the logging requests from your driver through to the serial port.

Yeah that’s what I decided to do. I found a close enough open-source driver project that I’m going to port for my needs. It has buffering capabilities and flushes data to the COM port with spinlock synchronisation. But I’m still not sure If building it as an export driver and export a logging function or as a device driver and send manually built IRPs ?

if it matters, you can’t implement an export driver with KMDF. since this is a time bound project, go with the simplest path forward that makes it work. that is probably an export driver with a bunch of global state (since it can’t have a device object of its own), but it is your judgement call at the end of the day

Okay then, I’ll find out what works well for me and report it back here for future reference. Thank you for all the help !

But wait, if a KMDF driver can’t be an export driver, that means I can’t use the WdfIoXXX functions to write to the COM port. What should I do then ? the ZwXXX functions have an IRQL constraint and you advice people not to use the WDM in your faq ?

if a KMDF driver can’t be an export driver, that means I can’t use the WdfIoXXX functions to write to the COM port. What should I do then ?

As a wise man once wrote: “it is your judgement call at the end of the day” – If you prefer to do this in WDF, then write a WDF driver that implements the functionality you want. You can communicate to that WDF driver any way you want: Irps, Bus Interface, whatever…

Peter

you advice people not to use the WDM in your faq

You do what you have to do. KMDF is a HUGE leap forward if you’re doing dispatching, PnP, and power management, and it’s foolish to attempt a full driver without it. However, an export driver doesn’t do ANY of those things. It’s just a repository of code snippets. Most of the WdfIoXxx functions are rather thin wrappers around their WDM analogs.