Linux libudev versions

PaulStoffregen

Well-known member
TL;DR = Please run "locate libudev.so" in a Linux terminal and let me know what it shows? Also mention which distro & version you have have.

For example, Ubuntu 12.04.5 (64 bit) gives this:

Code:
/lib/x86_64-linux-gnu/libudev.so.0
/lib/x86_64-linux-gnu/libudev.so.0.13.0

Ubuntu 14.04.5 (64 bit) gives this:

Code:
/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.3.5
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.3.5
/usr/lib/x86_64-linux-gnu/libudev.so

On Raspberry Pi, I first had to run "sudo apt-get install mlocate" and then "sudo updatedb". Then I see this:

Code:
/lib/arm-linux-gnueabihf/libudev.so.0
/lib/arm-linux-gnueabihf/libudev.so.0.13.0
/lib/arm-linux-gnueabihf/libudev.so.1
/lib/arm-linux-gnueabihf/libudev.so.1.5.0
/usr/lib/arm-linux-gnueabihf/libudev.so

I'm debating whether it's *finally* time to start using udev to find USB devices, rather than the kludgey combination of scanning the /dev and /dev/bus/usb folders which is currently baked into Teensy Loader and the utilities Teensyduino uses when you do stuff in Arduino.

The only problem is which version of the library to link. libudev.so.0 is the oldest, but have most distros dropped backwards compatibility? Linking against libudev.so.1 means no compatibility with older distros having only libudev.so.0. Static linking either version seems like a pretty bad idea, since the library really wants to match whatever conventions that distro's kernel & udev system is using...
 
Up board: Ubuntu 16.04.3
Code:
kurt@kurt-UP-CHT01:~$ locate libudev.so
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.6.4
/usr/lib/x86_64-linux-gnu/libudev.so

UP2 running Linux ubilinux4 4.9.18-ubilinux+ #6 SMP Mon Jun 19 14:58:51 IST 2017 x86_64
Not sure about the command to find it, but I did a cd /lib and did:
Code:
kurt@ubilinux4:/lib$ find . | grep libudev.so
./x86_64-linux-gnu/libudev.so.1
./x86_64-linux-gnu/libudev.so.1.6.5

Tried something similar on Odroid XU4: Welcome to Ubuntu 16.04.3 LTS (GNU/Linux 4.9.44-56 armv7l)
Code:
odroid@odroid:~$ find /lib| grep  libudev.so
/lib/arm-linux-gnueabihf/libudev.so.1
/lib/arm-linux-gnueabihf/libudev.so.1.6.4
 
I have 3 Fedora systems. My backup system is running Fedora 17, and it shows:

Code:
/usr/lib/libudev.so.0
/usr/lib/libudev.so.0.13.1
/usr/lib64/libudev.so
/usr/lib64/libudev.so.0
/usr/lib64/libudev.so.0.13.1

The main system I use is running Fedora 24:
Code:
/usr/lib/libudev.so.1
/usr/lib/libudev.so.1.6.2
/usr/lib64/libudev.so
/usr/lib64/libudev.so.1
/usr/lib64/libudev.so.1.6.2
/usr/lib64/libudev.so.1.6.4

And my laptop is running Fedora 25:
Code:
/usr/lib64/libudev.so.1
/usr/lib64/libudev.so.1.6.5

Frankly, I need to spend the time to upgrade the backup system to Fedora 25 (or 26), so if I couldn't run Teensy arduino on it, it would give me one more kick to upgrade it.
 
Until now, I've built the x64 releases on a Ubuntu 10.04 (in virtual machines), in hopes of being as backwards compatible as possible.

Using udev will require building on 14.04. Not sure what that will do for compatibility with the huge number of GUI libs?

Arduino is about to release 1.8.5. I'm installing 2 new virtual machines right now. Will try to get a beta later today using udev. If problems turn up, I'll revert to the old way.
 
16.04.3 LTS (Xenial Xerus) 64-bit
locate libudev.so
/home/linux/myarduino/.arduino15/packages/arduino/tools/openocd/0.9.0-arduino/lib/libudev.so.0
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.6.4


Ubuntu 14.04.5 LTS 32-bit
/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.3.5

Ubuntu 16.04.3 LTS 32-bit
/home/linux/libudev.so.0
/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.6.4
 
Please give 1.40-beta1 a try. Does it work on your Linux systems?

(almost certainly will not work on MichaelMeissner old Fedora 17 system...)

Also, if you look in the Help > Verbose Info window, you'll see messages like this:

Code:
09:45:30.404: new USB device: dev=/dev/bus/usb/002/078, loc=2-1.5.4, vid=16C0, pid=0478, ver=0103

The "2-1.5.4" is supposed to represent the hardware location. It's supposed to stay the same no matter what type of device you program Teensy to become.
 
Worked on UP2 mentioned above, on 1.8.4 Fresh install programmed blink on T3.6

UPdate: Worked on Odroid XU4 - (ARM ubuntu above)

Update post: Worked on my Windows 10 machine as well...
 
Last edited:
Code:
uname -a
Linux frank-lubuntu 4.4.0-96-generic #119-Ubuntu SMP Tue Sep 12 14:58:51 UTC 2017 i686 i686 i686 GNU/Linux

locate libudev.so
/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.6.4
 
lsb_release -a
Distributor ID: Ubuntu
Description: Ubuntu 16.04.3 LTS
Release: 16.04
Codename: xenial

uname -a
Linux xx 4.10.0-35-generic #39~16.04.1-Ubuntu SMP Wed Sep 13 09:02:42 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.6.4
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.6.4


Ubuntu 14.04.5
/lib/i386-linux-gnu/libudev.so.1
/lib/i386-linux-gnu/libudev.so.1.3.5
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.3.5

I imagine it will be difficult for something that has to be compatible with hardware (USB), I think it does make sense to cutoff support at some point.
12.04 would be a good cutoff but yes 14.04 might be better. LTS versions only are supported for 5 years, so I think it's reasonable to follow that schedule. As long as older versions of teensyduino work I think that's OK. Maybe posting up to which versions are compatible, or just list the dates of the versions, so that people can figure it out.

I stuck with older versions for awhile, but at some point too many things break that one typically has to upgrade. Unless you use that machine for 1 specific purpose that you don't care about anything else.
 
Last edited:
Debian 8.9

/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.5.0
/storage/Downloads/espressobin/ubuntu/fs/lib/aarch64-linux-gnu/libudev.so.1
/storage/Downloads/espressobin/ubuntu/fs/lib/aarch64-linux-gnu/libudev.so.1.3.5
/usr/lib/x86_64-linux-gnu/libudev.so

Debian 9.1
/lib/x86_64-linux-gnu/libudev.so.1
/lib/x86_64-linux-gnu/libudev.so.1.6.5
 
rather than the kludgey combination of scanning the /dev and /dev/bus/usb folders
What's so kludgey about that?

If you were to glob() and parse /sys/bus/usb/devices/*/uevent (or /sys/bus/usb/devices/*/{idVendor,idProduct,dev,busnum,devnum} first, and then do an nftw() over /dev and /dev/bus/usb to find the node with the correct st_rdev, it'd be rock solid, in my opinion. The /sys/ hierarchy is managed by the kernel, and is a stable userspace interface. If the device is not there, the kernel does not see it. The nftw() search over /dev/ and /dev/bus/usb/ locates the already-created device nodes on all distros; and if not found, you can tell the user that the device exists, but their userspace (udev) put the device node somewhere unreachable.

I wouldn't trust libudev at all, long-term, myself. Greg KH I do trust, he's a solid developer, but Kay Sievers.. no, not at all; there is a reason his contributions are not accepted to the Linux kernel anymore. And from past experience (initial kbus submission for example), I do not expect GregKH to even try to keep Sievers' brainfarts in check.

This is just my opinion, though.
 
The trouble with /dev/bus/usb/ is difficulty to discover the hub hierarchy. I did manage to get it working for root access by using the USBDEVFS_HUB_PORTINFO ioctl on all the devices with bDeviceClass = 9 in their device descriptor. Maybe there's some way to get non-root access with a broad udev rule? But that seems pretty invasive. Even now, some people object to the 0666 mode for just Teensy devices and suggest I publish a more secure user or group based scheme.

The sysfs names (given by libudev, and findable by looking around in /sys/bus/usb/) do seem to uniquely identify the bus-hubs chain. I'm not sure if that's a long-term reliable feature, but it seems to be the best available for non-root access.

The reason I want names that precisely identify the bus and hubs/ports, but not any encoding of the device, is to (someday) redo how Teensyduino populates Arduino's Tools > Ports menu. Today it just lists whatever serial devices happen to be found at the moment you clicked the menu. Eventually I want it to show every Teensy, even if it's in bootloader mode or one of the non-Serial modes. I might even do some sort of a daemon that keeps a brief history, so the menu can have proper data during the brief moment Teensy reboots and hasn't reconnected or USB enumeration hasn't finished.

Today the process is pretty haphazard (use the first one found), and really only designed for a single Teensy.

Ideally, that unique hub/port identifier will get passed to teensy_reboot, so the exact device you choose will always be the one to reboot, rather than the first it can find. Of course, it would also get passed to Teensy Loader, so the correct one would get programmed.

In a *really* utopian distant future, I might even hack the Arduino IDE to make the Tools > Ports setting independent in each window, and maybe even allow multiple instances of the serial monitor to open. From a UI perspective, there would need to be some cue which serial monitors go with which code windows.

But for now, the goal is just to take the first step of this long journey. Just getting a dependable identifier of the hardware location is the first essential step.

FWIW, the other systems do have proper APIs. On Windows it's SetupDiGetDeviceRegistryProperty with SPDRP_LOCATION_INFORMATION. On Macintosh is IORegistryEntryGetLocationInPlane(). If Linux has an actual API for a hardware location identifier, I'd *really* like to know what it is. So far the names the kernel creates within /sys/bus/usb seem to be the best option.
 
The sysfs names (given by libudev, and findable by looking around in /sys/bus/usb/) do seem to uniquely identify the bus-hubs chain. I'm not sure if that's a long-term reliable feature, but it seems to be the best available for non-root access.
See Linux kernel sysfs rules for the /sys/ stability. Linus has a very heavy emphasis, a strict rule, on userspace interfaces being backwards-compatible, unless there is a really compelling reason -- and it is exactly that kind of end-user-centric stance that makes me trust it long-term.

To summarise:
  • The /sys/bus/usb/ subtree may move to /sys/subsystem/usb/ at some point.
    If it does, the old path will still certainly work (through a symlink /sys/bus/usb -> ../subsystem/usb ).
  • The pseudofiles /sys/bus/usb/devices/*/{idProduct,idVendor,bcdDevice,bNumConfigurations,bNumInterfaces,busnum,devnum,devpath,dev,uevent} etc. are attributes, world-readable by default for exactly this purpose, and should not change in backwards-incompatible ways.
    Although not explicitly stated so in the kernel sysfs rules document, it is expected that userspace examines these to find USB devices.
On monitoring USB events:
  • You can use a netlink socket to listen for uevents. Here is a trivial example, that does not parse the actual netlink payload (which is in an ancillary message), and only prints the message string.
    However, I am not sure how stable the string part is. (The ancillary payload format is stable, as it is described in RFC 3549.) Might need to query the kernel devs at LKML to find out (although do expect the usual suspects to tell you to use libudev instead).
  • In current kernels, you can set an inotify watch on /sys/bus/usb/devices/ (for OPEN events) to detect changes in USB devices. Whenever it fires, just rescan the directory and attribute pseudofiles (or just /sys/bus/usb/devices/*/uevent ).
    Unfortunately, I don't recall which kernel added the feature, but if you install inotify-tools package, you can test if it works by running inotifywait -m /sys/bus/usb/devices/ and plugging in any USB device or hub.
  • Older kernels do not support an inotify watch on /sys/bus/usb/devices/ (for detecting USB hotplug), so to detect changes and recently-plugged in devices, you should just periodically scan the directory (using e.g. glob() or similar).
  • Because sysfs files are generated by the kernel on demand, that scan is quite lightweight, and does not involve physical storage other than RAM. In other words, a short scanning interval, say a tenth of a second or so, is perfectly acceptable wrt. resource use; even on embedded Linux systems.

libudev I do not trust the same way, as the systemd developers consider it theirs, and if they change something, they just tell application/library developers to recompile their sources against the latest version; and the resulting binary might, or might not, work with older versions of the library. Good luck maintaining an application across the entire Linux distro jungle that way..

I might even do some sort of a daemon that keeps a brief history, so the menu can have proper data during the brief moment Teensy reboots and hasn't reconnected or USB enumeration hasn't finished.
In Linux, that would be pretty much trivial to implement for a single client, say using an unix domain socket that provides a list of devices, one device per line, using format " ID=1\tEVENT=ATTACH\tDEVICE=/dev/bus/usb/001/001\tBUSNUM=1\tDEVNUM=1\tTYPE=Teensy 3.6\n" for each. For multiple clients (say, each instance being a separate client), you'd need a bit of buffering and an event counter (in addition to arbitrary device identifiers). In the other direction, have simple commands like LIST\n that causes the daemon to emit the information (with EVENT=LIST) for all attached (supported) devices. Should be easy to extend to serial ports and such, too.

How to make that cross-platform, especially to Windows, I do not know.

If Linux has an actual API for a hardware location identifier
For USB devices, that would be the device attributes busnum, devnum, devpath, and maxchild in /sys/bus/usb/devices/*/ . The busnum indicates the usb bus on the machine, devnum the device on the usb bus, maxchild the maximum number of sub-devices (0 if not a hub), and devpath the hub hierarchy.

None of these describe the kernel device hierarchy, only the USB bus hierarchy; this is the stable interface for USB devices in Linux.

The format for the devpath is dotted decimal format, <bus> [ '.' <port> ]* where <bus> is the bus number, and <port> is the hardware port number on the hub. The devpath only depends on cabling.

If you want the actual device node for an USB device, you do need to read the dev attribute, and either create it (a character device using mknod()) or find the device under the /dev hierarchy. USB devices are in /dev/, /dev/usb/, or /dev/bus/usb/*/, the last having format /dev/bus/usb/%03d/%03d with busnum and devnum .

If a Linux distribution does not follow the above, it is perfectly okay to yell at the maintainers for breaking compatibility. They usually end up fixing it (which is not much of a hassle, using symlinks).
 
Older kernels do not support an inotify watch on /sys/bus/usb/devices/ (for detecting USB hotplug), so to detect changes and recently-plugged in devices, you should just periodically scan the directory

That's what Teensy Loader has always done, using a 1/4 second polling interval. Someday I'd like to change to inotify on Linux and WM_DEVICECHANGE on Windows and truly responding to Mac run loops (today the Mac run loop is executed by the polling).

The huge challenge is how to integrate those with wxWidgets. The reality is quite a lot of platform specific stuff doesn't integrate well. Digging into the wxWidgets internals kinda defeats the time-saving purpose of using a cross platform GUI toolkit. The reality of limited dev time on my part usually means just living with the wxWidgets' lead-common-denominator cross platform design decisions and crafting as little platform specific USB code as I can, which still turns out to be quite a chore. I really need to put my dev time into things like USBHost, Audio, and the next Teensy....

libudev I do not trust the same way, as the systemd developers consider it theirs, and if they change something, they just tell application/library developers to recompile their sources against the latest version; and the resulting binary might, or might not, work with older versions of the library. Good luck maintaining an application across the entire Linux distro jungle that way..

I really appreciate the honest feedback. For now, libudev has been the path of least resistance. Had I read this a week ago, might have avoided it. But for now it seems to be working well, though I guess we'll really find out in a few weeks after 1.40 releases.

Like libusb 0.1 before it, when libudev breaks (the libusb 1.0 backwards compatibility layer was filled with bugs and memory leaks - and the libusb devs created a page basically saying no bugs would be fixed and they didn't even want to hear about 0.1 compatibility issues), I'll probably end up redoing the libudev work as parsing through the /sys files.
 
That's what Teensy Loader has always done, using a 1/4 second polling interval.
That's absolutely fine even on an embedded machine with a slow CPU. If you instrument that with e.g. clock_gettime() (for both CLOCK_REALTIME and CLOCK_THREAD_CPUTIME_ID), you'll see it does not consume much CPU time at all.

The huge challenge is how to integrate those with wxWidgets.
I've done some integration (for fun, not as a career) with both Python (Gtk+ and Qt) and C (Gtk+), and I've gotten very, very fond of the separate-process model with pipe or socket-based IPC. It just cuts through all the mess, really. The only downside is that you cannot package it as a monolithic executable anymore, really.

You see, I have a friend with roughly a similar situation: an open-source(-able) GUI, written in C (Gtk+), C++ (Qt) or Python (Qt or Gtk+); with very platform-specific needs (USB and serial port management, with filtering/translation), and some optional proprietary+commercial add-ons. (The typical value-add proposition, in other words.)

The best solution I have found is to have subsidiary worker processes, launched by the GUI process, but executing platform-specific scripts or binaries; with a a simple, extensible, preferably text-based protocol over pipes/socket pairs between the GUI and each subsidiary process. For hardware, the shell-like uevent description, with tab separators (NAME=VALUE pairs separated by \t with each record terminated by \n or \r\n) is not only easy to produce and parse, but also extensible -- and whitespace in values is easily allowed and handled by converting all linear whitespace to a single space.

Obviously, the single-threaded GUI itself needs to spin in an event loop, typically with short (I use 10ms to 50ms) sleeps if nothing to process. Alternatively, one could handle the subsidiary process IPC communications with the GUI idle handler, but I'm too paranoid to trust handling important stuff (say, when the machine is CPU-starved) to anything named "idle".

the libusb devs created a page basically saying no bugs would be fixed and they didn't even want to hear about 0.1 compatibility issues
This is what I fully expect to happen to libudev if systemd developers get kdbus included in the mainline kernels.
 
Back
Top