libudev to the rescue!

Well, on Linux at least, for the moment…

Some time ago when I was writing part of oaCapture I wanted to be able to tell whether the currently connected camera was a Philips SPC900. Ideally, given that I knew, say, that I was communicating via /dev/video0 I wanted to be able to find out what USB vendor id and product id were associated with the USB device (if any) connected to to /dev/video0 and see if they were the values for the SPC900.

I couldn’t work out how to do that, so in the end I opted for an ugly “solution” where I found the name of the camera and compared it with a suitable string. Clearly that’s not great because the string may well not be exactly identical in all versions of the camera and/or firmware.

I’ve now got to the point where I’m trying to add filter wheel support. I have a Xagyl FW5125 USB filter wheel and I found myself in a similar, but even more awkward, position. I know the filter wheel will show up on Linux as /dev/ttyUSBn where n will depend on how many other USB serial devices there are on the system and in what order they were initialised. I also know the VID/PID and serial number for the filter wheel. The question is, given one, how do I find the other to see if I have the right device and not some other USB serial hardware with an FTDI (as that’s what it uses) chipset?

The force was strong in me this time however and I discovered some information on libudev which can traverse the Linux sysfs filesystem and drag all sorts of useful data back out of it. In this case for me it meant that I could look at all the serial/tty devices, find which of them were on a USB bus and then look at the vendor id, product id and serial number to see if I had the right one, and find the device name under /dev used to communicate with it. Everything I could have wanted, and more!

The code looks something like this:

{
  struct udev* udev;
  struct udev_enumerate* enumerate;
  struct udev_list_entry* devices;
  struct udev_list_entry* dev_list_entry;
  struct udev_device* dev;
  const char* serialNo;
  const char* path;
  const char* deviceNode;
  const char* vid;
  const char* pid;

  if (!( udev = udev_new())) {
    fprintf ( stderr, "can't get connection to udev\n" );
    return -1;
  }

  enumerate = udev_enumerate_new ( udev );
  udev_enumerate_add_match_subsystem ( enumerate, "tty" );
  udev_enumerate_scan_devices ( enumerate );
  devices = udev_enumerate_get_list_entry ( enumerate );
  udev_list_entry_foreach ( dev_list_entry, devices ) {

    path = udev_list_entry_get_name ( dev_list_entry );
    dev = udev_device_new_from_syspath ( udev, path );
    deviceNode = udev_device_get_devnode ( dev );

    if (( dev = udev_device_get_parent_with_subsystem_devtype ( dev, "usb",
        "usb_device" ))) {
      vid = udev_device_get_sysattr_value ( dev, "idVendor" );
      pid = udev_device_get_sysattr_value ( dev, "idProduct" );
      if ( !strcmp ( "0403", vid ) && !strcmp ( "6001", pid )) {
        serialNo = udev_device_get_sysattr_value ( dev, "serial" );
        if ( !strcmp ( serialNo, "A4008T44" )) {
          // I have my filter wheel...
        }
      }
    }
    udev_device_unref ( dev );
  }
  udev_enumerate_unref ( enumerate );
  udev_unref ( udev );

  return 0;
}

At the time the filter wheel is identifed, deviceNode contains the path of the character device required to communicate with it (eg. /dev/ttyUSB1).

Now I just have to work out how I’m going to do that on OSX 🙂

This entry was posted in Astronomy, Computing, Linux and tagged , . Bookmark the permalink.

1 Response to libudev to the rescue!

  1. Peter Ellinger says:

    May the force be with you always James. Got to get hold of this software a I’ve just bought a ZWO ASI 120MM. What’s it call and where do I get it? Must investigate further.

Leave a Reply

Your email address will not be published. Required fields are marked *