-
Notifications
You must be signed in to change notification settings - Fork 11
Linux Implementation
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.
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 & 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.
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 poll()
to wait for the next monitor event.
Asychronous transfers are implemented by createing a USB Request Block (URB) and submitting it. The background thread (LinuxAsyncTask
) then uses poll()
to wait for completed transfers. The result of each transfer is retrieved by reaping the URB and calling the client's completion handler.
The tricky part is that poll()
needs to be waken up whenever a new asynchronous transfer is added or removed as the list of file descriptors to wait for changes. The list of file descriptors is updated in a different thread than the one calling poll()
. So care must be taken to make it multi-threading safe.
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 needed anyway on Windows to learn about the interface associations and functions.
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. Waitings is implemented with Object.wait()
and Object.notify()
, with a timeout if needed. If a timeout occurs, the transfer is aborted.