Menu

Raspberry Pi, C and custom HID Devices

So I’m writing a program in C that needs to interact with a custom HID device I built. This program will be running on a Raspberry Pi. This isn’t a massively complicated task but it can be daunting when there’s not a single “barebone” example or tutorial out there on how to do this. So I decided to write this sort of guide in case it may come in handy for anyone (including myself, in a future).

Compiling libhid

Libhid is an open source library designed on top of libusb to deal with HID devices, so the first step is compiling libhid. I’d say this is relatively straight-forward except for the fact that “as-is”, the library fails to build in the Pi. Luckily the problem is a single line of code in one of the examples (yes, and that prevents the whole library from being compiled and installed).

Why is this working? Well, in the original code, len is assigned a value but never used in the function (and apparently this warning is promoted to error by the make script).

To avoid this problem we have to “use” the variable somehow in the code, so we fix it with a dummy “len = len” statement. Since this line is no longer using the “custom” variable, we need to do the same with it, so it won’t raise the same “variable not used” error that len was triggering.

With this you can run ./configure, make and make install as you normally would and compile libhid without problems.

Using libhid

To be honest libhid is really easy to use despite its flaws, but the almost non-existent documentation (a doxygen API listing and a couple of example files) makes it kinda hard to get started with it.

But don’t worry, as I said before it’s really easy to use.

The first thing we want to do is opening our HID device (provided you have its vendor ID and Product ID). Here’s a handy function for that:

#include <hid.h>
HIDInterface *openUsbDevice(int vid, int pid){
HIDInterface *hidDev;
HIDInterfaceMatcher hidDevSpec;
// This will be used to find the device. You can optionally specify
// a custom matcher function to check other properties of the USB
// device before asserting a match, but since VID and PID suffice to
// find our device we don't need that.
hidDevSpec.vendor_id = vid;
hidDevSpec.product_id = pid;
hidDevSpec.matcher_fn = NULL;
hidDevSpec.custom_data = NULL;
hidDevSpec.custom_data_length = 0;
// init the library and create the interface
if (!hid_is_initialised() && hid_init() != HID_RET_SUCCESS) {
return NULL;
}
hidDev = hid_new_HIDInterface();
// Open the device. Since it's most-likely already opened by the
// kernel modules we need to close it first. hid_force_open does
// exactly that. The "4" here is the number of times it will try to
// close and gain control over the device before giving up.
if (hid_force_open (hidDev, 0, &hidDevSpec, 4) != HID_RET_SUCCESS) {
return NULL;
}
return hidDev;
}

If you try this code in your Pi it will probably compile just fine (you may need to add -l hid when compiling to link the libhid library to your code), but it will fail to open the device.

Why? because you probably don’t have enough privileges to access usb devices in your Pi.

Fixing usb permissions

This can be fixed by adding a udev rule that will “mount” USB devices with enough permissions for you to actually do something with it. I saw this “fix” detailed on a forum, but I couldn’t find the link now, so I’ll explain the process here the best I can.

You’ll need to create a /etc/udev/rules.d/50-usbdevices.rules file with the following:

SUBSYSTEM=="usb", ACTION=="add", MODE="0664", GROUP="plugdev"

This will give read-write permissions over usb devices to anyone in the plugdev group. The user “pi” is already member of this group.

You can also specify a particular vendor and product id so it will only run this rule for that device:

For the changes to have effect you can reboot your Pior you can run the following commands instead:

udevadm control --reload-rules
udevadm trigger

(unplug your device and plug it again)

Now you should be able to open the device with no problem.

Back to libhid…

Finally, we want to read and write data to it. This step will be a little different depending on the USB device you are trying to talk to. In my case, it’s a simple HID device that takes a 64-byte input buffer containing a command with parameters, performs and action depending on the command, and returns the result, in another 64-byte buffer.

We need to find the endpoint addresses. To do that, we use lsusb. Assuming our device’s VID is 0x1000 and its PID is 0x2000 the command to dump info about the device will be lsusb -d 1000:2000 -vvv.

We are only interested in the report description. For my device I get this:

As you can see, there’s a single report declared, with its usage page at “0x00 0xff” (65280). From the decimal equivalent (in parentheses) we can guess that the address is being printed LSB first, so in our code this would actually be 0xff00. This makes sense since the USB specs state that vendor specific usage pages should be in the FF00-FFFF range.

Then there’s a bunch of Item usage specs, setting max data sizes and range of values, but among the “Items” you’ll see one labeled “Input”, and one labeled “Output”, both being “arrays” of 64 bytes with range 0 to 255 (unsigned).

The report data size is 64, for both reading and writing, and in the Endpoint table at the bottom you can see that the “Input” buffer is at address 0x81 (offset from the page address) and the Output buffer is at 0x01.