.. _how_it_works: How it works ============ Below is a high-level overview of how HID-BPF works and how ``udev-hid-bpf`` enables us to take advantage of it immediately on plug-in. Note that the udev-hid-bpf repository contains there distinct components: - ``udev-hid-bpf`` the binary that loads HID-BPF programs - A set of HID-BPF programs (see the ``src/bpf`` directory) - Scaffolding to automate loading of HID-BPF programs via udev. How HID-BPF works ----------------- HID-BPF is a feature available in kernels 6.3 and newer. Most input devices today are HID devices (HID can be used over USB, Bluetooth, etc.). HID devices have two packets of information that get exchanged between the host and the device: the HID Report Descriptor and HID Reports. Both are simply arrays of bytes, the HID Report Descriptor describes how the bytes in HID Reports can be interpreted - it will effectively say e.g "bit 8 is button left" or "bits 9-16 is a relative x axis". The HID Report Descriptor is static for a device and only exchanged once. HID Reports are the actual events. Reports can be input reports (device to host, i.e. an event), output reports (host to device, e.g. setting an LED) and feature report (bidirectional, for on-device-configuration). The following only focuses on input reports but the same applies to output and feature reports. See :ref:`hid_primer` for more details. HID-BPF is a kernel feature that allows inserting an eBPF program that can change the data that is exchanged between the host and the device. Typically, a report by the device is sent as-is to the kernel (``hid-generic`` is the recipient driver in virtually all cases):: device: [ab|12|cd|34] -------------------------------> [kernel] But where a HID BPF program is loaded for that device, that program can change the data before it arrives at the kernel driver:: device: [ab|12|cd|34] ----->[HID BPF program] | v [ab|99|cd|34]----------> [kernel] This applies to both HID Report Descriptors and to HID Reports. In other words, the BPF program can either change the data itself (e.g. "changing y to -y") or it can change how the data is interpreted (e.g. change "bits 1/2/3 are buttons left/middle/right" to "bits 1/2/3 are buttons right/middle/left"). How udev-hid-bpf works ---------------------- ``udev-hid-bpf`` - the binary - works similar to ``modprobe`` or ``insmod``. It opens a given ``.bpf.o`` compiled BPF object file and loads it for the given device. The binary can be invoked manually but in most cases we will trigger it via udev. During ``meson compile`` we generate udev rules and hwdb entries that tells us which of our BPF object files matches which device. For example, such a hwdb entry may look like this:: hid-bpf:hid:b0003g0001v000024AEp00002015 HID_BPF_T_002=0009-Rapoo__M50-Plus-Silent.bpf.o Together with our udev rule this hwdb entry means that if a device with the ``0x24AE`` vendor ID and ``0x2015`` product ID is plugged in, the udev property ``HID_BPF_T_002`` (ignore the weird name) is set to the value ``0009-Rapoo__M50-Plus-Silent.bpf.o``. Our udev rule also calls ``udev-hid-bpf`` for every device with such a property set and ``udev-hid-bpf`` will thus load the ``0009-Rapoo__M50-Plus-Silent.bpf.o`` BPF object file for the newly plugged-in HID device. In summary, our flow is: 1. HID device is plugged in, udev runs its rules 2. our udev rule runs the hwdb builtin for the device's modalias * the hwdb builtin sets the udev properties ``HID_BPF_...`` if there is a match 3. our udev rule runs the ``udev-hid-bpf`` binary if there is a ``HID_BPF...`` property * ``udev-hid-bpf`` loads the BPF program for the device 4. device sends HID reports, altered by BPF if applicable