How to send HID commands and read HID data from Linux Kernel Module?

212 views Asked by At

I'm building a Linux kernel module that needs to read an HID device, to send data to a hypervisor using RPC communication. These assumptions are necessary, as I'm working in embedded Linux and the SoC I'm developing for, requires a kernel module to send this type of data and send back data through registered callbacks.

I know that there are libraries such as libusb / hidapi that can simply send a HID command and read HID data from userspace, like this:

hidapitester --vidpid 2752:0012 -l 8 --open --send-output 0x03,0x53,0x02,0x58  --read-input

Where an ID will point to the device, and we can send and read bytes according to the manufacturer spec. We can also do this writing a program using their library functions, which ultimately are based on HIDRAW.

The issue is, these are libraries that work on userspace, but compiling a kernel module with userspace functions is not possible, thus these libraries won't work. HIDRAW relies mostly on IOCTL so using the ioctl() calls was tried but not successful either.

How to write and read to an HID device that I know the ID of from a Linux kernel module?

I've tried using hidapi and hidraw functions or just the direct ioctl() call from the kernel module but I get:

error: implicit declaration of function 'ioctl'

or the equivalent with the library functions and after some research I realized these are userspace functions.

1

There are 1 answers

0
Aaron Moreno On

I managed to send an HID report/command with a kernel module.

To do so one has to create a module (read about the Linux kernel module structure if you are not familiar) and use the struct hid_driver. Here is necessary to register some functions that will be called once our driver is recognized as the appropriate one for a given device:

static struct hid_driver my_hid_driver = {
    .name = "my-hid-receiver",
    .id_table = my_id_table,
    .probe = my_probe_function,
    .remove = my_remove_function,
    .raw_event = my_raw_event
};

To make our driver be called when an HID device is connected, we use the id_table like this:

static const struct hid_device_id my_id_table[] = {
    { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, <ID OF THE VENDOR>, <ID OF THE PRODUCT>) },
    {}
};

Where ID OF THE VENDOR and ID OF THE PRODUCT can be replaced for the hex values of vendor and product. These can be read manually once using lsusb or usb-devices in userspace. Documentation of other drivers doing more specificity to select a device, or use a single module to target different types of devices is available at drivers/hid/ in the Linux kernel source tree.

After that, the ->probe() function will be a good starting point as this is called when the driver is first assigned to a device. We get the hid_device as an argument when the device is connected and the function is called. Inside we should call some necessary functions like hid_parse(), hid_hw_start() and hid_hw_open(), then we are ready to send a report.

Here we can use hid_hw_request(), a function from hid-core, for this purpose.

If we want to send 5 bytes of the following data [0x01,0xff,0x01,0x33,0x0f] we can do it like this:

struct hid_report *report;
struct hid_report_enum *output_report_enum;

output_report_enum = &hdev->report_enum[HID_OUTPUT_REPORT]; // Getting the report from the hid_device hdev
report = output_report_enum->report_id_hash[<ID>]; // hid_device hdev is passed to probe as argument

report->field[0]->value[0] = 0x01; // Actual data to be sent to the device
report->field[0]->value[1] = 0xFF;
report->field[0]->value[2] = 0x01;
report->field[0]->value[3] = 0x33;
report->field[0]->value[4] = 0x0F;

hid_hw_request(hdev, report, HID_REQ_SET_REPORT); // request to set the data

Keep in mind that ID and field are specific to how your HID device works, I just used the simplest configuration here.

If we want to do a reading, we can use HID_INPUT_REPORT for the enum and HID_REQ_GET_REPORT for the argument hid_hw_request().

We can take a look at the documentation in include/linux/hid.h, hid-core and drivers/hid/ inside the Linux kernel source tree, to find practical examples as well as the different exposed functions to use the HID layers.

Also https://www.kernel.org/doc/html/latest/hid/hid-transport.html can be useful if one is doing more low level operations.