Development of a User-Space Application for an HID Device, Using libhid

When it's time to get a new device working on Linux, every piece of information helps, whether it's reading the hardware documentation, snooping data, reading sample code or even running utilities on a non-Linux OS.

Communicating with the Device

A block diagram depicting the flow of control of data is shown in Figure
2. It may help in picturing where your code fits in with respect to the
libraries and the device. From my investigation, I know that control messages
periodically are written by way of the control pipe, and interrupt reads are
made through endpoint 0.

The control pipe is used for three tasks: receiving and responding to requests
for USB control and class data; transmitting data when polled by the HID class
driver, using the Get_Report request; and receiving data from the host.
The Interrupt pipe is used for two tasks: receiving asynchronous, or
unrequested, data from the device and transmitting low-latency data to the
device.

Figure 2. The new driver uses libhid, which depends on libusb.

The kernel has a DEBUG feature that
can be activated in order to log extra information
about what is happening when communicating
with the device. To do this, a file in the
kernel source needs to be modified. In the
/usr/src/linux/drivers/usr/input/hid-core.c file,
these two lines need to be changed from:

#undef DEBUG
#undef DEBUG_DATA

to:

#define DEBUG
#define DEBUG_DATA

The module needs to be recompiled and installed. Once this is done, the
modules should prove helpful in determining whether your code is working and
doing what you expect.

Sample code containing some helpful comments comes with libhid. The
file test_libhid.c in the libhid/test directory is a good place to start
writing code for the device. Below is a snippet of that code, along with
some more explanation of the functions; details are omitted for brevity:

The first thing to do is identify the particular
device we want to talk to. This is done with the
HIDInterfaceMatcher call simply by entering the
vendor ID and the product ID. These two identifiers
are all that is required to identify any USB
device. If you have more than one identical device, it is possible to
separate them by serial number, that is, two matrix note readers would have
the same vendor ID and product ID but different serial numbers. The
HIDInterfaceMatcher call can do this; see the comments in the test_libhid.c
file.

After some variable setup, the next is to detach the kernel driver
from the HID device. Upon insertion of the HID device, the kernel
usually loads the usbhid module, which we don't want. We do have a few
options, however, for unloading it or for not loading it in the first
place. One such way is to enter this command:

root@localhost #> modprobe -r usbhid

When the hid_force_open function runs, it attempts, n times, to detach
the device before it fails. The device is now free from any control,
so our code now “opens” the device. As with any USB device,
it is necessary to send control information to the device to activate
it. This information must be sent periodically in order for the device
to remain active. If the control poll stops, the device deactivates
after a certain timeout.

Writing to a device requires the HID usage path and its length, plus a packet
and its length. To find this out, we need to parse the usage
tree—the output of lsusb -vvv—and obtain the path
to the interface we want. As with everything else, there are various
methods for determining the path. At this stage, a lot of time was spent determining what
path to write to, and a number of tools are helpful here, such as:

The test_libhid.c code: when the correct vendor and product ID
are entered in the code, the function hid_dump_tree, which uses the
MGE hidparser (see Resources), which parses the HID usage tree and places the
available usages at its leaves, outputs the available paths.

A Windows application available from Arnaud, one of the libhid authors,
also parses the usage tree and produces a nice GUI output, as shown in
Figure 3.

By parsing the output of lsusb -vvv, run
as root, it is possible to parse the tree manually to determine the
path. This process is explained in the comments of test_libhid.c code.

Figure 3. Understanding a device: one way to browse
the available nodes of the HID tree is to use
the SystemSoft HID Browser.

From the above methods, we now have a path value we can use for the
hid_set_output_report. Once we know where to write to, it's a
matter of what to send. This information should be in the technical
documentation that comes with the device, and it can be verified with the
USB-sniffing tools. As with the particular device I was using,
verifying the format of a packet with the sniffing tools turned out to be
important, as the information in the documentation didn't match what the sniff
log reported (see the Snooping section).

Once the control message or output report is sent, we can start to
read from the read pipe, endpoint0. The function needed is an interrupt
read function. It already exists in libusb, but a corresponding libhid
function doesn't. The developers of libhid simply hadn't come across
a device that required it yet, so I studied the format of the other
functions and implemented it appropriately. I also added a new error
code to the existing list. These additions are being considered for
inclusion in the latest version of libhid.

At this stage, once the interrupt read value is stored, I then parse
this value, as per the Matrix documentation, to display the results
to the user. For this device, that equates to information such as,
“A ten-euro note has been inserted” or “The cash box is
disconnected” and other such device-specific information. The
details are unnecessary for the purposes of this article, but if anyone
requires this detail, feel free to contact me.

This process is repeated for as long as the driver is running. We
must keep polling the device to keep it active. There is a status LED
on the device that turns green when the device is active and remains
orange when inactive. The goal for quite some time was to make the
little light go green.

As Linux continues to play an ever increasing role in corporate data centers and institutions, ensuring the integrity and protection of these systems must be a priority. With 60% of the world's websites and an increasing share of organization's mission-critical workloads running on Linux, failing to stop malware and other advanced threats on Linux can increasingly impact an organization's reputation and bottom line.

Most companies incorporate backup procedures for critical data, which can be restored quickly if a loss occurs. However, fewer companies are prepared for catastrophic system failures, in which they lose all data, the entire operating system, applications, settings, patches and more, reducing their system(s) to “bare metal.” After all, before data can be restored to a system, there must be a system to restore it to.

In this one hour webinar, learn how to enhance your existing backup strategies for better disaster recovery preparedness using Storix System Backup Administrator (SBAdmin), a highly flexible bare-metal recovery solution for UNIX and Linux systems.