The other day, I was working on a TI AM3517-based Linux device and I needed to open a serial port at a weird baud rate (31250 bps). The AM3517 is one of TI’s Sitara processors, which are very similar to the popular OMAP chips. The AM3517 is definitely physically capable of doing this baud rate — it starts out with a 48 MHz clock, then it oversamples by either 16X or 13X (thus giving you a 3 MHz or 3.692308 MHz base clock), and then you can supply an integer divisor at that point. With the 16X oversampling, a divisor of 96 would give you 48,000,000 / 16 / 96 = 31250 baud. There was no question that the AM3517 could do the job.

Although the hardware supports it, Linux made it difficult to get the job done. Well, I guess it’s more accurate to say that the combination of Linux and the C library made it difficult.

First of all, the standard way of setting the serial speed is to do the following:

struct termios tio;
tcgetattr(fd, &tio);
cfsetispeed(&tio, B115200);
cfsetospeed(&tio, B115200);
/* do other miscellaneous setup options with the flags here */
tcsetattr(fd, TCSANOW, &tio);

That’s great, except only some standard baud rates have constants: B9600, B19200, B38400, B57600, B115200, etc. There’s no B31250 constant, and you can’t just pass a number to cfsetispeed() and cfsetospeed(). It doesn’t work that way because the speeds are really combinations of bits that get set in the c_cflag member of the termios struct. So essentially, the standard POSIX tcsetattr/tcgetattr API is kind of lame.

I already knew there were ways to get around that problem. I started out by trying the old method that setserial uses:

struct serial_struct serialsettings;
ioctl(fd, TIOCGSERIAL, &serialsettings);
serialsettings.flags &= ~ASYNC_SPD_MASK;
serialsettings.flags |= ASYNC_SPD_CUST;
serialsettings.custom_divisor = 96;
ioctl(fd, TIOCSSERIAL, &serialsettings);

After running that, opening the port with a baud rate of 38400 is supposed to actually use the custom divisor. It’s an ugly way to do it, but it works on a Linux 2.4-based device that I’ve used in the past.

It compiled just fine, but first of all, as soon as my program tried to do the above code, the kernel (the device I’m using has Linux 2.6.37) warned me that setting a custom divisor is deprecated. Worse yet, the TIOCSSERIAL ioctl failed with an error of EINVAL. This is because the kernel’s OMAP serial driver (drivers/serial/omap-serial.c, or drivers/tty/serial/omap-serial.c on newer kernels) doesn’t support that call: serial_omap_verify_port() function returns -EINVAL. So deprecated or not, it just plain doesn’t work with the OMAP serial driver.

I did some looking and dug up a discussion about a replacement for the old, deprecated TIOCSSERIAL method. I also found some other promising leads, including a post on StackOverflow asking the same question.

The discussion about a replacement comes to a conclusion: if the CBAUDEX bit in the c_cflag member of the termios structure is set without any of the other bits from the other B#### constants, then you can put an integer value for the desired baud rate into the c_ispeed and c_ospeed members in the struct. Then, I found a draft patch for this change. The patch includes a new constant called BOTHER (that’s B-OTHER, not “bother!”) defined as the same thing as CBAUDEX by itself.

The patch ended up changing–nowadays, you’ll find that in the kernel, there’s a struct termios and then a struct termios2. termios2 is the new struct that implements the new speed fields. I looked in my glibc header file bits/termios.h, and glibc’s header file actually includes the c_ispeed and c_ospeed members in the standard termios struct. However, the actual code that talks to the Linux kernel, at least in my case, was copying the termios data into an old-style termios struct and using the old-style ioctl that doesn’t look at c_ispeed and c_ospeed.

So after a ton of messing around, I figured out what it takes to do a new-style termios call to set a custom baud rate on newer (2.6+) versions of Linux:

#include <asm/termios.h>

struct termios2 tio;
ioctl(fd, TCGETS2, &tio);
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ispeed = 31250;
tio.c_ospeed = 31250;
/* do other miscellaneous setup options with the flags here */
ioctl(fd, TCSETS2, &tio);

After doing that, everything worked fine and the OMAP UART worked perfectly at 31250 baud, confirmed with an oscilloscope.

I ran into some #include hell with asm/termios.h, especially if I already had the normal termios.h included elsewhere, so you might have to copy relevant struct definitions into your own code if you can’t get it to work (I know, nasty!). The other thing to keep in mind is that the definition of NCCS might be different between your libc and kernel, so you need to make sure you’re using the kernel’s definition of NCCS. I believe asm/termbits.h has the correct NCCS and structures defined.

Make sure you don’t do another normal tcsetattr() call after the TCSETS2 ioctl, because I’m guessing it’ll probably revert the custom baud rate back to a standard baud rate if you do. I’m not 100% sure on that though.

Anyway, I hope this helps someone. In the end, it wasn’t really that complicated to get working, but it took a lot of research and playing around to understand how to make it work. Maybe I’m missing something, but it seems like glibc and the kernel don’t play well together in this case. It’s also possible that glibc supports this all perfectly and my glibc is compiled incorrectly or something, but either way, manually doing the new-style ioctl like I did above seems to work.

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!

I recently dug out an old KVM switch I was playing with back in 2005–the Linkskey LKU-UA02. It’s a compact two-output switch with two USB ports (keyboard and mouse), a VGA port, a speaker jack, and a microphone jack. It works great, but I was a little saddened after I got it and realized it had no Linux or Mac OS X drivers. It was still OK because it has a physical button for changing between which computer to control, but it also had a cool little Windows-only utility for controlling it through software or locking the audio output to a specific computer–and I couldn’t use any of those features from my Mac or a Linux machine.

In 2005 (a.k.a. my college days), I had attempted to write a control utility for it in OS X. I installed USB sniffing software on my Windows PC and recorded the USB traffic as I sent various commands through the Windows control program. I gave it my best shot, even going so far as to ask for a bit of help on Apple’s USB mailing list, but I never figured out how to get past a roadblock I was running into when opening the device in Apple’s I/O Kit. In hindsight, I was probably jumping into something just a little too complicated for my knowledge at the time. With that said, the best way to learn something in the programming world is to do it! It wasn’t wasted time; I definitely learned a lot about USB in the process. I actually believe that the device has something invalid in the descriptor for the endpoint I was using (a zero value for bInterval), and older versions of Mac OS X choked on that problem, so it may not have even been my fault–I think that was the reason I eventually gave up. In fact, if I look at the latest (as of this writing) version of AppleUSBOHCI_UIM.cpp, I see that it still complains if you try to create an interrupt endpoint with a polling rate of zero (it says, “that’s illegal!”), while the analogous UHCI controller code does not have this check.

When I stumbled upon the switch again last week, I decided to take another stab at it. This time, I decided to use libusb, which is better suited for the task given its simplicity compared to I/O Kit and its cross-platform compatibility. I ended up succeeding! Not only does it work in OS X, but it also works great in Linux and Windows 7.

The vendor ID of this KVM switch is 10d5 and the product ID is 000d. The chipset appears to be made by Uni Class, so it’s possible that other KVM switches use the exact same chipset (and thus may be compatible with my control software). In particular, some Googling seems to imply that the TRENDnet TK-407 has the exact same vendor and device ID, although it’s a 4-port KVM switch–I can see how the protocol would scale in that case to switch between four outputs, so I have left room in my code to allow switching between four outputs. Anyway, if you have a device with the same device and vendor ID, you should try it out!

Here is the code (just compile it with your favorite C compiler, remembering to link against libusb-1.0):

#include <libusb-1.0/libusb.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// USB vendor and product ID of the KVM device
#define KVM_VENDOR_ID           0x10D5
#define KVM_DEVICE_ID           0x000D

bool is_kvm(libusb_device *dev);
void print_usage(char *argv[]);

int main(int argc, char *argv[]) {
    int exit_code = 0;

    // Make sure there is a single argument passed to the app
    if (argc != 2) {
        print_usage(argv);
        exit_code = 1;
        goto exit_err_early;
    }

    // Convert the argument to a number
    char *endptr;
    unsigned long index = strtoul(argv[1], &endptr, 10);
    if ((endptr == argv[1]) || (index > 3)) {
        print_usage(argv);
        exit_code = 1;
        goto exit_err_early;
    }

    // Initialize libusb
    int result;
    result = libusb_init(NULL);
    if (result) {
        fprintf(stderr, "Unable to initialize libusb.\n");
        exit_code = 1;
        goto exit_err_early;
    }

    // Find devices
    libusb_device **list;
    libusb_device *found = NULL;
    ssize_t cnt = libusb_get_device_list(NULL, &list);
    ssize_t i = 0;
    if (cnt < 0) {
        fprintf(stderr, "Unable to get list of USB devices.\n");
        exit_code = 1;
        goto exit_err_dev_list;
    }

    // Look for a match
    for (i = 0; i < cnt; i++) {
        libusb_device *device = list[i];
        if (is_kvm(device)) {
            found = device;
            break;
        }
    }

    // Did we find the KVM?
    if (found) {
        libusb_device_handle *handle;

        // Open the device...
        result = libusb_open(found, &handle);
        if (result) {
            fprintf(stderr, "Unable to open KVM device.\n");
            exit_code = 1;
            goto exit_error_open;
        }

        // Claim interface 1...
        result = libusb_claim_interface(handle, 1);
        if (result) {
            fprintf(stderr, "Unable to claim KVM interface.\n");
            exit_code = 1;
            goto exit_error_claim_interface;
        }

        // Send the interrupt transfer
        unsigned char transfer[8] = "\x01\x00\x00\x78\x01\x3B\x00\x00";
        transfer[1] = (unsigned char)index;
        int bytes_sent = 0;
        result = libusb_interrupt_transfer(handle,
            0x02 /* EP 2 OUT */,
            transfer,
            sizeof(transfer),
            &bytes_sent,
            0 /* no timeout */
        );

        // Check result of transfer
        if (result) {
            fprintf(stderr, "Unable to send KVM switch message.\n");
            exit_code = 1;
            goto exit_error_transfer;
        }

        // Print result
        printf("Switched to KVM output %lu.\n", index);

        // Clean up
exit_error_transfer:
        result = libusb_release_interface(handle, 1);
        if (result) {
            fprintf(stderr, "Error releasing interface 1.\n");
        }

exit_error_claim_interface:
        libusb_close(handle);
    } else {
        fprintf(stderr, "Unable to find KVM USB device.\n");
    }

exit_error_open:
    libusb_free_device_list(list, 1);
exit_err_dev_list:
    libusb_exit(NULL);
exit_err_early:
    return exit_code;
}

// Checks a libusb_device to see if it matches
bool is_kvm(libusb_device *dev) {
    // Grab the descriptor...
    struct libusb_device_descriptor desc;
    if (libusb_get_device_descriptor(dev, &desc) != 0) {
        fprintf(stderr, "Warning: Unable to get device descriptor.\n");
        return false;
    }

    // And see if the vendor/product ID matches
    if ((desc.idVendor == KVM_VENDOR_ID) &&
        (desc.idProduct == KVM_DEVICE_ID)) {
        return true;
    }

    // No match, so false
    return false;
}

// Prints usage info about the program
void print_usage(char *argv[]) {
    fprintf(stderr, "Usage: %s <index of KVM output (0-3)>\n", argv[0]);
}

I have noticed occasional error messages saying “Unable to send KVM switch message”, but despite the error, it still switches correctly. My guess is that the USB device disappears before I get a chance to completely close it. There may be a few small improvements needed in that regard, even if the improvement is just to always assume success at that point and not print an error message.

I plan on making a graphical interface (probably with Qt) for easy control in the future, but for now this command line utility works great! There are some extra features available in the KVM switch’s protocol such as automatically cycling between the outputs, fixing the audio port to a specific output, and determining which output is the currently active output. I haven’t implemented any of that extra protocol yet, but it shouldn’t be too difficult to do.

I do want to figure out how to do the equivalent task in I/O Kit at some point just for my own sanity and for some closure on the problem I had back in 2005, but it’s not high on my priority list. After my research into Apple’s open source code described above, I’m fairly sure that I still won’t be able to make it work on an older PowerPC computer at all. It will probably work OK with newer Intel computers, though. In fact, it would be interesting to test the libusb solution on a PowerPC computer to see if it works at all (my guess is it won’t work). So much to try and so little time!

After recently installing Ubuntu 11.10 onto my Mac mini, I’ve been mildly annoyed when I connect to it through PuTTY from my Windows machine. It’s working fine, except gcc displays a weird “â” character instead of quotes in its error messages. I figured it was some weird locale or terminal setting I hadn’t configured properly in Ubuntu because I did a minimal install with only a few server packages, but I was dead wrong. I tried SSHing to a standard Ubuntu 11.10 install with a regular desktop environment and everything, and it still had the weird character in PuTTY!

It turns out that it’s really simple — PuTTY defaults to an ISO-8859-1 character set and Ubuntu defaults to a UTF-8 character set. All I had to do was change my PuTTY settings to use UTF-8 instead:

(The highlight is kind of hard to see, but I clicked “Translation” underneath the “Window” category on the left side to get to that screen.)

After making that change in PuTTY, it works perfectly. Passing the gcc output to hexdump -C shows that the quote characters are represented as:

0xE2 0x80 0x98

and

0xE2 0x80 0x99

(which are UTF-8 sequences for ‘ and ’, respectively). Sure enough, 0xE2 in ISO-8859-1 is â, and 0x80, 0x98, and 0x99 are C1 control codes, which don’t actually display a character. So that’s the “why” behind this whole situation.

I know this probably seems like a simple thing to write a blog post about, but sometimes it’s kind of freaky when you do a minimal install of a Linux distribution and little glitches like this pop up because you forgot to install or configure a standard package that everyone tends to use. Even though that wasn’t the case here, I know others will run into the same problem and suspect the same thing I did originally, so I hope this helps someone else out (and maybe teaches a little bit of trivia in the process)!

This should also apply to other PowerPC Macs such as the G3, G4, iBook, and various iMac models (I think)…

I wanted to install Ubuntu 11.10 onto my Mac mini after replacing its hard drive. I found some excellent netboot install directions by Evan Martin, which I was able to follow (although I used the files from this directory for the netboot). However, I ran into a small problem when beginning the install–the netboot image for 11.10 does not include the parallel ATA driver for Macs (pata_macio.ko). It causes the installer to not detect any hard drives.

The Ubuntu FAQ I linked to above suggests installing 11.04 and then upgrading to 11.10. I didn’t feel like doing an upgrade install, so I decided to go another route. Here’s how I did it (starting at the point where I was told that no hard drives could be detected)…

  1. I manually downloaded the PowerPC kernel package.
  2. Next, I extracted lib/modules/3.0.0-12-powerpc/kernel/drivers/ata/pata_macio.ko by opening the .deb file with Archive Manager, and stuck it in my TFTP server directory.
  3. Almost there…I used tftp to grab it from my TFTP server and put it in /tmp on the Mac mini
    • Get to a console on the Mac mini by pressing Alt-F2. Remember, on an Apple keyboard, Alt is the option key and you may have to hold down the “fn” key to get F2 to be recognized as F2 instead of a brightness key.
  4. Finally, I inserted the module with insmod, switched back to the installer by pressing Alt-F1, and continued on with my install. I think I had to go back one step and try again, and then the hard drive was recognized.

By the way, it sounds like this will be fixed in 12.04. Yippee! Until then, this is another way of getting it done. I hope this helps someone else out there…