Development of a User-Space Application for an HID Device, Using libhid
The Matrix is a USB bill validator, sometimes known as a note reader or bill acceptor, made by Validation Technologies International. The bundled software was developed for Microsoft Windows, but fortunately the device comes with low-level technical documentation that defines device-specific aspects, such as flow control, status bytes and local status LEDs.
The device is a Human Interface Device (HID), as identified by an enumeration process upon connection. The Windows device manager reports the device as such, as does usbfs on Linux. This article is specific to this particular HID device, so including all of its code probably is unnecessary, but it should provide help for developing for other HID-class devices.
After some initial research, I decided to develop user-space code using an in-development library called libhid, which provides a cross-platform way to access and interact with USB HID devices. libhid is implemented on top of libusb, so it does not depend directly on the kernel's USB support.
Another option for driving the Matrix is to use libusb directly, but doing so would be re-inventing the libhid wheel. A third option is to implement the Matrix as a kernel module, but it would incur the large overhead of learning kernel particulars. This option also would render the code platform-specific.
USB devices are categorized into device classes. A modem is in the communications class, and a speaker falls into the audio class. The HID class mainly consists of devices that people use to control computers. Examples of HID devices are mice, joysticks and force-feedback game controllers. Also included in the HID class are devices that may not require human interaction but do provide data in a similar format to HID-class devices, such as bar-code readers and, in my case, the Matrix note reader.
Information about a USB device is stored in segments of its ROM called descriptors. A diagram of the descriptor structure is provided in Figure 1, where an overall view of the hierarchy can be seen. When a USB device is attached to a USB bus, an enumeration process takes place that equates to the descriptors on the device being read into memory. Information about an HID-class device is contained in its HID report descriptors.
I plugged the device in to the Linux box in order to read the descriptors and monitor the device, the machine and the communications. I did this to try to get as much information as possible so I could have a better understanding of how to write code for the device.
A key component of these report descriptors is the usage information, which is defined in the USB HID Usage Tables (see the on-line Resources). Usage values describe three basic types of information about the device:
Controls—information about the state of the device such as on/off or enable/disable.
Data—all other information that passes between the device and the host.
Collections—groups of related controls and data.
Taken together, the usage page and usage number define a unique constant that describes a particular type of device or part of that device. For example, on the Generic Desktop usage page (page number 0x01), usage number 0x05 is a game pad, and usage number 0x39 is a hat switch.
Because my device is unique—it isn't a mouse, joystick or something commonly found in the examples of HID-class devices—the usage page is set to 65,440, which is a vendor-defined value. In comparing outputs of lsusb for other HID-class devices, they all had a defined usage page, such as Generic Desktop Controls or Game Controls. Because libhid still is in development, few previous examples of code are available to browse for reference. My work was much like an exploratory investigation.
On Linux, with a standard Debian 2.6.9 kernel and usbutils, I was able to see that Linux recognises the device as a USB HID device, bInterfaceClass = HID, and loads the hiddev kernel module. This module, or piece of kernel code, is a generic driver for HID devices. It is not specific to our needs—it mainly is used for mice, joysticks and the like—so it needs to be detached from the device or disabled (see the Communicating with the Device section).
The device, like all USB devices, is enumerated upon connection to the USB bus. So looking at the output of lsusb -vvv, run as root, for more information is helpful in determining what the device capabilities are. lsusb parses the usbfs filesystem into a more readable format:
[sample lsusb -vvv] Bus 001 Device 004: ID 0ce5:0003 Device Descriptor: ... idVendor 0x0ce5 idProduct 0x0003 ... Configuration Descriptor: ... Interface Descriptor: ... bNumEndpoints 1 bInterfaceClass 3 Human Interface Devices bInterfaceSubClass 0 No Subclass bInterfaceProtocol 0 None ... HID Device Descriptor: ... Report Descriptor: (length is 32) Item(Global):Usage Page,data=[0xa0 0xff]65440 (null) Item(Local ):Usage, data= [ 0x01 ] 1 (null) Item(Main ):Collection, data= [ 0x01 ] 1 Application Item(Local ):Usage, data= [ 0x03 ] 3 (null) Item(Global):Logical Minimum,data=[ 0x00 ] 0 Item(Global):Logical Maximum,data=[ 0xff ]255 Item(Global): Report Size, data= [ 0x08 ] 8 Item(Global): Report Count, data= [ 0x05 ] 5 Item(Main ): Input, data= [ 0x02 ] 2 Data Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Local ): Usage, data= [ 0x05 ] 5 (null) Item(Global):Logical Minimum,data=[ 0x00 ]0 Item(Global):Logical Maximum,data=[ 0xff ]255 Item(Global): Report Size, data= [ 0x08 ] 8 Item(Global): Report Count, data= [ 0x05 ] 5 Item(Main ): Output, data= [ 0x02 ] 2 Data Variable Absolute No_Wrap Linear Preferred_State No_Null_Position Non_Volatile Bitfield Item(Main ): End Collection, data=none
The above output—some of the information has been omitted—follows the hierarchy depicted in Figure 1. Some values of note are:
idVendor and idProduct—unique identifiers for all USB devices, used for identifying and accessing the device in code.
bNumEndpoints—lists the number of endpoints available in a device. This value actually means the number of endpoints in addition to the default endpoint, endpoint 0, available in every USB device.
bInterfaceClass—the value that determines that a device is an HID-class device.
bInterfaceSubClass—the subclass of a device, in this case, HID. For example, the boot interface subclass of the device must be bootable or available to the BIOS, such as a mouse or keyboard.
bInterfaceProtocol—the protocol used. Possible values are 0 for none, 1 for keyboard or 2 for mouse; additional information is available in the HID spec.
- March 2015 Issue of Linux Journal: System Administration
- High-Availability Storage with HA-LVM
- DNSMasq, the Pint-Sized Super Dæmon!
- Localhost DNS Cache
- Real-Time Rogue Wireless Access Point Detection with the Raspberry Pi
- Days Between Dates: the Counting
- The Usability of GNOME
- You're the Boss with UBOS
- Multitenant Sites
- Linux for Astronomers