Skip to content

Linux Implementation

Manuel Bl edited this page Feb 25, 2024 · 4 revisions

The Linux implementation is probably the simplest implementation as the USB API on Linux is straight-forward to use. The main challenge on Linux is the poor documentation. There is minimal documentation in The Linux-USB Host Side API. It partially covers USB Request Blocks (URBs). But operations like USBDEVFS_DISCARDURB aren't documented at all.

Also see Linux reference code.

Device enumeration and monitoring

For the device enumeration and monitoring, the libudev functions are used. The library is straight-forward to use, both because it provides the necessary features and because the API is built for use in C and is a good match for Java's Foreign Function And Memory API.

libudev is officially deprecated. The replacement is libsystemd. But it seems that libsystemd isn't as widely available as libudev. A migration to libsystemd is planned and will be straight-forward as the two libraries have a very similar API.

The biggest issue with libudev and libsystemd is the lack of documentation. There are a few man pages. But they are very limited.

Registry background thread

The background thread on Linux is very simple as well. libudev provides a file descriptor for the monitor. So the background thread can simply use epoll_wait() to wait for the next monitor event.

Asynchronous task completion thread

Asynchronous transfers are implemented by creating a USB Request Block (URB) and submitting it. The background thread (LinuxAsyncTask) then uses epoll_wait() to wait for completed transfers. The result of each transfer is retrieved by reaping the URB and calling the client's completion handler.

Descriptive information

Linux does not provide any APIs to query any descriptive information about interfaces and endpoints. There are two options. The first option would be to read the pseudo-files in /sys/bus/usb/devices/n-n.n. It is quite cumbersome and would require many file operations.

The second and chosen option is to read all data from the USB device's file path (returned by libudev). It's a concatenation of the device descriptor and configuration descriptor. The descriptors are then parsed by this library (see net.codecrete.usb.common.ConfigurationParser). The configuration parser is also needed on Windows to deal with interface associations and functions.

Device communication

Device communication is straight-forward as well as the API is very similar to the one used by this library. A device needs to be opened. Then ioctl() can be used to claim interfaces and communicate with endpoints.

Transfer operations create an asynchronous Transfer request, submit it and wait for it to complete. Waiting is implemented with Object.wait() and Object.notify(), with a timeout if needed. If a timeout occurs, the transfer is aborted.