For a few years now, I’ve been fighting a weird problem: X-CTU (which is a software utility provided by Digi for programming XBee modules) is only available for Windows. I do most of my development in Linux so X-CTU is always a pain to work with. It does run pretty well under Wine if you need to use it with Linux or Mac OS X, but when you run it under Wine, it doesn’t detect any serial ports:

X-CTU1

Now of course, we all know that you have to add a symlink in your ~/.wine/dosdevices directory to link Wine to your computer’s serial ports:

# ls -l ~/.wine/dosdevices/
total 0
lrwxrwxrwx 1 doug doug  8 Oct 30 21:30 a:: -> /dev/fd0
lrwxrwxrwx 1 doug doug 10 Oct 30 21:30 c: -> ../drive_c
lrwxrwxrwx 1 doug doug 10 Mar 29 18:08 com1 -> /dev/ttyS0
lrwxrwxrwx 1 doug doug 10 Mar 29 18:08 com2 -> /dev/ttyS1
lrwxrwxrwx 1 doug doug  8 Oct 30 21:30 d:: -> /dev/sr0
lrwxrwxrwx 1 doug doug  1 Oct 30 21:30 z: -> /

But even after doing that, X-CTU still doesn’t detect anything. All of the workarounds that I have found require you to define a user COM port in X-CTU:

X-CTU_UserCOM

After going through that process, you can pick the user COM port and X-CTU works perfectly fine (aside from not being able to download newer firmware versions from Digi’s site). As soon as you quit X-CTU, though, the user COM ports you have defined are gone. So whenever you re-open X-CTU, you have to redefine your user COM port. It gets old. So that’s the problem I’ve been fighting: having to manually add the user COM port every time I open X-CTU.

Today I got fed up and ran X-CTU with all of Wine’s debugging information enabled so I could get a clear idea of what X-CTU does when it first loads, in an attempt to figure out how to get the serial ports to show up. Good news: I got it working and now my serial ports show up automatically when I open X-CTU!

X-CTU_fixed

I’d like to explain how X-CTU detects attached serial ports, what Wine does in response, and finally, how you can get it working for yourself. Let’s dive in!

How X-CTU detects attached serial ports

X-CTU uses Windows’ Setup API to get a list of attached serial ports. I ran it with Wine set for full debugging and traced out the calls to Setup API functions to figure out exactly what it does. It starts out with a call to SetupDiClassGuidsFromName which, given the name of a device class (“Ports” in this case), returns a list of GUIDs that go with that class. Next, it calls SetupDiGetClassDevs with the list of GUIDs to get a list of devices that belong to the Ports class. It goes through the list of devices and requests the “friendly name” of each port by calling SetupDiGetDeviceRegistryProperty. The “friendly name” will look like one of these examples:

  • USB Serial Port (COM5)
  • Communications Port (COM1)
  • Printer Port (LPT1)
  • Blah blah blah port (COM7)

Notice how the friendly name always seems to end with (COM#) and it also includes other ports like printer ports. Well, X-CTU uses this info to detect the port — if the name of the port contains the string “(COM”, then it grabs the number directly after that string and uses it as the COM port number. It also ignores parallel ports.

So to get Wine to correctly populate the list, we need to figure out what Wine is doing in response to the three Windows functions I listed above. This information was readily available by both checking out the debug trace from earlier and also reading the Wine source code. Let’s go there now…

What Wine does when the setup API functions are called

SetupDiClassGuidsFromName searches in the registry for classes named “Port”. I don’t think it behaves exactly like an actual Windows machine, but here’s what it does on Wine. It searches for subkeys of:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Class

and looks for subkeys that have a class matching the name provided to it. In Wine, it finds the class with GUID {4d36e978-e325-11ce-bfc1-08002be10318}, which according to Microsoft is for COM and LPT ports. Anyway, that’s the single GUID it finds in Wine.

SetupDiGetClassDevs searches in:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum

for items that match the GUID we found earlier. The above key contains various keys that represent different categories. Then the categories contain keys that represent devices, and the devices contain keys that represent instances of the devices (I believe). The gist of it is that it goes three levels deep through the Enum directory to try to find anything that has a string value “ClassGUID”. If the GUID matches the GUID we found earlier, Wine decides it’s a serial port and returns it in the list of discovered devices. This is the root cause of the whole problem — nothing is put into the registry automatically by Wine for these serial ports. So we’ll definitely need to add this manually, as we’ll see later.

SetupDiGetDeviceRegistryProperty is finally used to get the friendly name for the port. It looks in the same location it looked for the ClassGUID value, but this time it looks for a string called FriendlyName — which, as you guessed it, contains a string in the format of my examples above.

Once I figured this out, I was pretty much home free. So without further adieu, here are the instructions for getting it working.

What to add to the registry

The key (no pun intended) is to add your serial ports as subkeys of:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum

to satisfy the functions I described in the section above:

  • Create a subkey of Enum and call it SERIAL (although the name you use doesn’t really matter–I believe it searches everything, not just the SERIAL subkey).
  • Create a subkey of SERIAL and call it COM1 (if your port is COM1 — although this name doesn’t really matter)
  • Create a subkey of the COM1 key you just created and call it COM1 also (this name doesn’t really matter either though)
  • In the final COM1 subkey you created, add two string values:
    • ClassGUID — containing the value {4D36E978-E325-11CE-BFC1-08002BE10318} (the GUID for the Ports class)
    • FriendlyName — containing a name in the format “Serial Port (COM1)” without the quotes of course.
      • Make sure the name ends with the COM port name in parentheses as in this example — (COM1). It has to be that way or it won’t work–it might appear in the list, but it will fail to open unless you do it exactly in that format.
      • This is what X-CTU actually uses to decide which port to open. The other “COM1” subkeys we added in the earlier steps aren’t checked for anything — I just named them that way for clarity while you’re browsing the registry.

You can make these modifications using regedit in Wine (type “wine regedit” in a terminal window). Here’s an example screenshot to make it clear what you have to add:

X-CTU_regedit

That’s it! You’re done. The ports should now appear automatically in X-CTU. Assuming you have also created the symlinks in ~/.wine/dosdevices for the COM ports you added, they should also be operational.

Conclusion

This is tested in Ubuntu 12.10 with Wine 1.4.1. I would imagine if you can figure out where the dosdevices folder is to stick the symlinks, it will probably work in Mac OS X as well. Your mileage may vary. Good luck!

This strategy definitely works for X-CTU, but it’s not a generic strategy that will work for any Windows program under Wine. Different programs use different methods to get a list of serial ports. Some programs may check for a different key called PortName next to FriendlyName. X-CTU in particular only checks for FriendlyName. If you’re trying to get this to work with a different Windows program, play around. Check programming tutorials to see the various methods people use to enumerate COM ports on Windows. Figure out which method your particular program is using — disassemble it, check what functions it links against, run it in Wine with debugging enabled, etc. Once you’ve figured it out, use Wine’s debugging facilities (and the Wine source code) to see what Wine is doing in response to the various functions that are called. Chances are good that it is looking into the registry and you just need to tweak your registry to give the Windows functions the results they are expecting.

I hope this helps someone out there someday!

Trackback

25 comments

  1. this tutorial was helpful i managed to get the ‘my serial port’ to show up when i run xctu but still unable to open port i think i performed the symlink correctly here’s what i get when i list my symlonk sessions
    total 8
    lrwxrwxrwx 1 demeterius demeterius 10 Jul 27 09:59 c: -> ../drive_c
    lrwxrwxrwx 1 demeterius demeterius 12 Aug 16 01:29 COM1 -> /dev/ttyUSB0
    lrwxrwxrwx 1 demeterius demeterius 12 Aug 15 01:24 com10 -> /dev/ttyUSB0
    lrwxrwxrwx 1 demeterius demeterius 12 Aug 15 01:34 COM11 -> /dev/ttyUSB0
    lrwxrwxrwx 1 demeterius demeterius 12 Aug 15 01:50 COM12 -> /dev/ttyUSB0
    lrwxrwxrwx 1 demeterius demeterius 13 Aug 15 02:03 COM14 -> /dev/ttyUSB0/
    lrwxrwxrwx 1 demeterius demeterius 12 Aug 15 02:00 com2 -> /dev/ttyUSB0
    lrwxrwxrwx 1 demeterius demeterius 8 Aug 8 23:33 d:: -> /dev/sdb
    drwxrwxr-x 2 demeterius demeterius 4096 Aug 15 01:19 link
    lrwxrwxrwx 1 demeterius demeterius 49 Aug 15 00:44 Link to Untitled Folder -> /home/demeterius/.wine/dosdevices/Untitled Folder
    drwxrwxr-x 2 demeterius demeterius 4096 Aug 15 00:43 Untitled Folder
    lrwxrwxrwx 1 demeterius demeterius 1 Jul 27 09:59 z: -> /
    demeterius@gub1:~$

    any help would be appreciated Thanks

  2. It looks like you have a ton of them all pointing to ttyUSB0, which shouldn’t hurt, but I would still remove all of the COM links and start over. Also, I’ve always seen that they should be named lowercase (like how you did with com2 and com10). So here’s what I would do:

    Remove all the existing com port symlinks, and then only create a single one for COM1 that has a lowercase name, pointing to your USB serial port:

    rm ~/.wine/dosdevices/COM*
    rm ~/.wine/dosdevices/com*
    ln -s /dev/ttyUSB0 ~/.wine/dosdevices/com1

    Then make sure your FriendlyName key in the registry ends with (COM1) to match the COM1 port you set up:

    My Serial Port (COM1)

    That should make it work, I’m thinking. Hope this helps!

  3. Debugging with trying to proAIS2 (Marine AIS Software) running under Wine. It needs one more key in addition: Enum\SERIAL\COM1\COM1\PortName = “COM1”.

    Without this the software shows the port as busy.

  4. Wine bug report filed at:

    “ProAIS2 Serial Port enumeration requires Registry Local_Machine/…/Enum/SERIAL/COM1/COM1/*”
    http://bugs.winehq.org/show_bug.cgi?id=34497

  5. Hey Paul,

    Thanks for sharing your experience and the link to the bug report! Glad to hear this information was able to help out. So it definitely sounds like it’s a good idea to add all three keys: ClassGUID, PortName, and FriendlyName.

  6. Hi Sir Doug Brown
    thanks for your helpful informations.
    I have problem again in showing up my ports
    in the section “HKEY_LOCAL_MACHINE\System\CurrentControlSet”
    I can’t find Enum
    Would you help me a little bit?

  7. Hmmmmmm….I’m not totally sure. You could definitely try creating the Enum key manually if it doesn’t already exist. Maybe your version of Wine is looking elsewhere too, but I doubt it. I’d recommend trying to create the Enum key and see if that works.

  8. Hello,
    I’m trying to run XCTU version: 6.1.0, and I can’t find neither the “user COM port” nor PC settings (or any other advaced settings.

    So I tried your regedit “hack”, but it doesn’t work either. The com port appears in the list of XCTU, but when I try to connect an Xbee, it fails with the following error message :
    “Could not find any device in port COM1 > Error initializing XBee device parameters > COM1: SerOpenPort failed::5003”

  9. Have you made the appropriate symlink for com1 in ~/.wine/dosdevices? The regedit hack by itself would make the port appear but won’t enable the port to actually work unless the symlink in dosdevices is present.

  10. Bram Verkerk @ 2014-08-28 13:07

    Thanks for the instructions. It seems to work on my Mint Linux Eeepc wich I use on board the boat.

    To setup the AIS transponder I need Proais2 software.

    The comport appears after changing the settings conform your instructions in regedit.

    However, no matter how I tried the com port gives the message: “com1 in use”. Changing the symlink to other TTYUSB ports. The message “in use” stays.

    Also tried to chmod. But no results.

    How to free the usb ports from the busy state ?

  11. Bram, did you see Paul Sladen’s comment above? He described another key that has to be added in order for his port to not show up as busy using that software.

  12. Bram Verkerk @ 2014-08-28 14:46

    Dear Doug,

    I have allready added the PortName Item but no result.

    It seems to work, but always Serial Port Com1 is busy.

    Is there any way to see what is the USBs ports are doing. I have tried it on different Mint Linux systems, but “in Use” remains. I can do it of course with a Windows system. It is only the settings of the AIS unit. The Linux plotter software ProCPN is running fine via USB. But that is not via Wine.

    The usb Port in Linux OpenCPN in the Eeepc, that worked, showed up as: ttyACM0 ???

  13. I’m just not sure what else to recommend at this point. Can you open the port with a normal Linux terminal program, or does it appear busy that way too? Sometimes USB serial ports get funky permissions. In Ubuntu, I usually have to add myself to the dialout group to ensure that I get permission to use a USB serial port (and log out and back in after adding myself to the dialout group, of course).

  14. Bram Verkerk @ 2014-08-29 01:39

    Dear Doug,

    in the OpenCPN plotter software GPSd and GPSd-client is used to connect with the USB port of the AIS transceiver. The USB port is seen as ttyACMO.

    ttyACM0 seems to be a kind of modem connection.

    I’am allready connected with the dialout en TTY user permissions set.

    There must be a (simple) solution.

    Bram

  15. Bram Verkerk @ 2014-08-29 11:12

    Dear Doug,

    connecting via ACM0 once seems to be clear luck.

    I cannot repeat the connection. In Windows it appeared that the com port is a virtual comport. I think that is the problem.

    Also dmesg mentioned also a virtual com port.

    Bram

  16. Hi Bram,

    As long as you have your symlink to the COM port correct in ~/.wine/dosdevices, the only other thing I can recommend is to run your program in wine with a ton of debugging output enabled and see if you can determine what it’s trying to do when it opens the port. In particular, it might be helpful to check debugging output on registry accesses. The trouble with these serial port issues is that every program is a little bit different in how it finds and opens serial ports, so it’s hard to give generic advice that will work in any program.

  17. That worked, many thanks!

  18. That worked!!
    It saved me from virtualbox running xp.
    Thanks a lot!

  19. thanks a lot

  20. Hi Brown,

    I am using ubuntu 14.04 LTS.

    I am same problem as yours not able to detect port in xctu using wine….

    I did all the instruction as said by you…Till now no sloution..plss help

    Thank you

  21. Just a quick update for this old, but still very relevant article: I tested these steps with the “Next generation” XCTU version under wine, and they work there too.

    Unlike the older XCTU versions, this NG version does not allow manually specifying the ports, so without modifying the registry, XCTU NG is not usable at all, making this article even more relevant 😀

  22. hi, i would like to ask for the solution to my problem. i received an error while connecting an Xbee to my computer. Error: Could not find any device in port COM5 > Error initializing Xbee device parameters > COM5: SerOpenPortfailed: Port not valid.
    Thank you

  23. Seb.Sch. @ 2015-08-28 15:05

    I just wanted to get an old winNT program working and bumped into the same issue. Unfortunately it didn’t help to simply add the registry key above. I did further research and debugging of my application and endcountered my app was trying to read from HKLM\HARDWARE\DEVICEMAP\SERIALCOMM.
    Google took me here:
    http://wine-wiki.org/index.php/Wine_Registry#Serial_Com_Port
    adding [HARDWARE\\DEVICEMAP\\SERIALCOMM] 1131331688
    “COM1″=”COM1” to ~/.wine/systemreg is doing the trick. Thanks a lot for your useful information. It pointed me in the right direction.

  24. Sujan Swearingen @ 2016-09-12 12:42

    This is great information but the port assignments change and sometimes points to COM2, COM3, or COM4. Is it required to create subkeys for each of these. Can the subkey values be the same ClassGUID or does each require specific numbering?

  25. Sergey Lepeshkin @ 2018-02-28 03:22

    Hi!

    I just want to tell community about my solution to related problem.

    Problem: Program tries to automatically find serial port, which is emulated by Prolific pl2303 chip. Search is done by inspection of hardware id for every port, listed in registry.

    Solution: Hardware identification must be added to registry. I’ve just copied related records from windows xp registry to wine, and all works fine.

    My .reg file is listed below. It adds support for COM1 as PL2303 with hardware id Vid_067b&Pid_2303

    REGEDIT4

    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\Vid_067b&Pid_2303]

    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\Vid_067b&Pid_2303\5&1ee599c2&0&1]
    “Capabilities”=dword:00000004
    “Class”=”Ports”
    “ClassGUID”=”{4D36E978-E325-11CE-BFC1-08002BE10318}”
    “CompatibleIDs”=hex(7):55,53,42,5c,43,6c,61,73,73,5f,66,66,26,53,75,62,43,6c,\
    61,73,73,5f,30,30,26,50,72,6f,74,5f,30,30,00,55,53,42,5c,43,6c,61,73,73,5f,\
    66,66,26,53,75,62,43,6c,61,73,73,5f,30,30,00,55,53,42,5c,43,6c,61,73,73,5f,\
    66,66,00,00
    “ConfigFlags”=dword:00000000
    “DeviceDesc”=”Prolific USB-to-Serial Comm Port”
    “Driver”=”{4D36E978-E325-11CE-BFC1-08002BE10318}\\0002”
    “FriendlyName”=”Prolific USB-to-Serial Comm Port (COM1)”
    “HardwareID”=hex(7):55,53,42,5c,56,69,64,5f,30,36,37,62,26,50,69,64,5f,32,33,\
    30,33,26,52,65,76,5f,30,33,30,30,00,55,53,42,5c,56,69,64,5f,30,36,37,62,26,\
    50,69,64,5f,32,33,30,33,00,00
    “LocationInformation”=”USB-Serial Controller”
    “Mfg”=”Prolific”
    “Service”=”Ser2pl”
    “UINumber”=dword:00000000
    “UpperFilters”=hex(7):73,65,72,65,6e,75,6d,00,00

    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\Vid_067b&Pid_2303\5&1ee599c2&0&1\Device Parameters]
    “ExtPropDescSemaphore”=dword:00000001
    “PollingPeriod”=dword:00000000
    “PortName”=”COM1”
    “SymbolicName”=”\\??\\USB#Vid_067b&Pid_2303#5&1ee599c2&0&1#{a5dcbf10-6530-11d2-901f-00c04fb951ed}”

    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum\USB\Vid_067b&Pid_2303\5&1ee599c2&0&1\LogConf]

Add your comment now