Issue enumerating Pololu virtual COM ports on Windows

Good day,

While attempting to dynamically locate the COM ports that are exposed by the Maestro board, I discovered that there is an issue with the .INF provided by Pololu.

I use the Windows SetupDI API (from Python) to enumerate all the ‘PORTS’ devices. Problem is that the Maestro (virtual) Command Port and TTL Port COM ports do not get enumerated.

I ran into the same issue on a previous project and wanted to let you know the solution: you need to include the serenum.sys driver as one that is attached to your device(s). See here for an example of what it ought to look like.

I may need to add this locally to the INF so our project can work well. If so, I will post what I come up here.

Thank you,
Tyler

Do you have more information on what serenum.sys is and why it is needed?

A while ago, I made some C# code to enumerate the COM ports of the jrk (another product we sell with the same INF structure as the Maestro). I was able to do it without changing the INF file, so you should be able to do the same thing for the Maestro.

I used SetupDiGetClassDevs to get the list of Ports devices, and SetupDiEnumDeviceInfo to look at individual devices. How does your code work? If you post it, I could probably give some advice.

–David

Sure, here it is. Just keep it just between you and me. :wink:

Thank you,
Tyler
setupdi.7z (1.66 KB)

Okay. Try this:

GetClassDevs(ctypes.byref(USB_PORTS), 0, 0, DIGCF_PRESENT)

I think your problem was that you were using the DIGCF_DEVICEINTERFACE flag when you shouldn’t. The Maestro’s virtual COM ports don’t have a device interface GUID, so you must find them using the Ports class GUID. If you specify the DIGCF_DEVICEINTERFACE flag, then SetupAPI treats your GUID as a device interface GUID, but it’s actually a class GUID.

–David

Excellent. Thank you for the tip. Now I have to add new functions to get the right device properties…

Will send an updated version when I get it done.

Thanks again,
Tyler

New info: I am extremely frustrated.

I changed the code to look for devices by the GUID of the device type. Then, I looked for a way to get the equivalent of the DevicePath that can be retrieved as in the device interface case (using the GetDeviceProperty call). BUT, I can find nothing that works for an open call to the ‘serial’ port (CreateFile). The ‘Physical Device Object name’ seems like it ought to work, but it doesn’t.

I am guessing you will suggest I simply take the ‘Friendly name’ property and parse out the COM port value. But I really dislike that idea!

I want to enumerate the devices and get a DevicePath I can use directly into a CreateFile call to talk to the serial port. I can do this with all the previous USB-Serial devices I have used in the past.

Unless you have some better idea, I think I am going to try to add the serenum.sys driver to the pololu_usb_to_serial.inf file.

Thank you,
Tyler

This type of low-level Windows programming can be pretty frustrating because generally these functions aren’t very well documented and they have tons of arguments, all of which have to be exactly right. I’ve been there.

Does this mean my suggestion worked for you?

I don’t think GetDeviceProperty does what you want. The only strings that I know of that will work with CreateFile are like “\.\USBSER000”, “\.\COM4”, and “COM4”. In my code, I call SetupDiGetCustomDeviceProperty to get the value of the PortName property, which will be like “COM4”. That should work for you.

Me too! Yuck!

I know what a Device Instance Path is but I’m not sure what a DevicePath is. What would an example DevicePath look like? How do you get a DevicePath, and how does serenum.sys help you do it?

Anyway, you should be able to avoid that pain if you use SetupDiGetCustomDeviceProperty.

–David

Friday morning update:

  • Your suggestion of removing the DIGCF_DEVICEINTERFACE and using the SetupDiGetDeviceProperty functions worked - to a degree. It was a good excuse to fill in the implementation of that particular function, which also required me to implement a DEVPROPKEY struct wrapper to define the property keys that that function requires. But as mentioned, none of the property values was immediately useful.

  • I just added the SetupDiGetCustomDeviceProperty function and tested with the ‘PortName’, and that definitely returns something much more useful. But, see the next item.

  • I spent a few hours last night re-working the pololu_usb_to_serial.inf file to include the serenum.sys driver. BTW, this allows it to be enumerated as a device interface, and also - according to what I have read - supports the WM_DEVICECHANGE message. I have thus far tested the .inf on Vista x64 and Windows 7 x64, and it appears to do exactly what I want in both cases.

As to why I want this: it returns a DevicePath which looks something like one of the following (Pololu, Keyspan and built-in COM1 as examples):

\?\usb#vid_1ffb&pid_0089&mi_00#7&2127787a&0&0000#{4d36e978-e325-11ce-bfc1-08002be10318}
\?\keyspan#*usa19hmap#00_00#{4d36e978-e325-11ce-bfc1-08002be10318}
\?\acpi#pnp0501#1#{4d36e978-e325-11ce-bfc1-08002be10318}

Any of these can be passed directly into a CreateFile call to open that particular COM port, which is what I want(ed). This of course also works with the pyserial module, which does the CreateFile somewhere inside.

I have attached the .INF and my updates setupdi.py wrappers if you are interested.

Thank you,
Tyler
my_pololu_updates.7z (3.29 KB)

Thanks for sharing your modified INF file here; maybe someone else will find it useful. However, you still haven’t explained to me how you get a DevicePath and why serenum.sys is required, so I’m not convinced that we should modify our official INF files.

Thanks, I didn’t know that a string like this could be used in CreateFile. That string looks a lot like the Device Instance ID of the Maestro’s Command Port, except the last part, which is just the GUID of the Ports class (which can maybe be left off, because I don’t see how it would help identify the device).

I think you can get the Device Intance ID using SetupDiGetDeviceInstanceId, but then you would have to do some minor string editing to make it look like a DevicePath.

–David

The DevicePath is retrieved via the SetupDiGetDeviceInterfaceDetail call in the GetClassDevs enumeration of the PORTS type with the DIGCF_DEVICEINTERFACE flag set.

I suspect that the serenum.sys module is the one that is constructing the DevicePath retrieved from this enumeration, and is likely the driver handling the CreateFile call as well. This is the purpose of having the serenum as a ‘Device upper filter’ for the usbser driver.

After adding the code to get device properties, I did try to use the ‘Device Instance Path’ to open the port, but that did not work at all. I will take a look at the GetDeviceInstanceId function and see what it returns.

Thanks,
Tyler