Matching BPF programs to a device
This tool supports multiple ways of matching a BPF program to a HID device:
Manual loading
Users can manually attach a HID-BPF program to a device:
$ sudo udev-hid-bpf add /sys/bus/hid/devices/0003:05F3:0405 trace_hid_events.bpf.o
$ sudo udev-hid-bpf add /sys/bus/hid/devices/0003:05F3:0405 /path/to/my-hack.bpf.o
Multiple devices and object files can be provided by separating devices and BPF
object files with a literal -. This gives us a convenient shortcut, useful
especially during development:
$ sudo udev-hid-bpf add - builddir/src/bpf/0010-Foo__Bar.bpf.o
By leaving the device list empty (- is the first argument to add),
udev-hid-bpf will look up local devices based on the metadata compiled into the given BPF programs.
Note that the filename does not need to be a full path. The list of available BPF programs can be shown with:
$ udev-hid-bpf list-bpf-programs
Note
If invoked from the git repository, this will show the BPF programs
in the currently configured lookup directories. Use
udev-hid-bpf list-bpf-programs --bpfdir builddir to list the
programs in the builddir.
Metadata in the HID-BPF sources (modalias matches)
Each HID-BPF program can tell which devices it is supposed to be bound to. If those metadata are given, udev will automatically bind the HID-BPF program to the device on plug.
To do so, add metadata to your HID-BPF sources specifying the bus, the
HID group, and the vendor and product IDs. Here’s an example of a
BPF program that matches multiple different devices and uses the #defines
in hid_bpf_helpers.h:
HID_BPF_CONFIG(
/* A specific Logitech (0x046D) USB device on the generic HID group */
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x046D, 0x1234),
/* A specific Yubikey (0x1040) USB device on the generic HID group */
HID_DEVICE(0x3, HID_GROUP_GENERIC, 0x1040, 0x0407),
/* Any logitech (0x046D) bluetooth device on the generic HID group */
HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, 0x046D, HID_PID_ANY),
/* Any i2c device */
HID_DEVICE(BUS_I2C, HID_GROUP_ANY, HID_VID_ANY, HID_PID_ANY)
);
As you can see, the arguments to the HID_DEVICE macro are
the bus as either numerical value or one of
BUS_USB,BUS_BLUETOOTH, …the HID group as either numerical value or one of
HID_GROUP_GENERIC, …the vendor ID in hexadecimal (see the
lsusboutput)the product ID in hexadecimal (see the
lsusboutput)
As used in the example above, BUS_ANY, HID_GROUP_ANY, HID_VID_ANY
and HID_PID_ANY are wildcards.
For the curious, there is a page on HID-BPF metadata that explains how these metadata are embedded in the resulting BPF object.
The easiest way to obtain the metadata is to use the
udev-hid-bpf list-devices command:
$ udev-hid-bpf list-devices
/sys/bus/hid/devices/0003:045E:07A5.0001
- name: Microsoft Microsoft® 2.4GHz Transceiver v9.0
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x045E, 0x07A5)
/sys/bus/hid/devices/0003:045E:07A5.0002
- name: Microsoft Microsoft® 2.4GHz Transceiver v9.0
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x045E, 0x07A5)
/sys/bus/hid/devices/0003:045E:07A5.0003
- name: Microsoft Microsoft® 2.4GHz Transceiver v9.0
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x045E, 0x07A5)
/sys/bus/hid/devices/0003:046D:4088.0009
- name: Logitech ERGO K860
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_LOGITECH_DJ_DEVICE, 0x046D, 0x046D)
/sys/bus/hid/devices/0003:046D:C52B.0004
- name: Logitech USB Receiver
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x046D, 0xC52B)
/sys/bus/hid/devices/0003:046D:C52B.0005
- name: Logitech USB Receiver
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x046D, 0xC52B)
/sys/bus/hid/devices/0003:046D:C52B.0006
- name: Logitech USB Receiver
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x046D, 0xC52B)
/sys/bus/hid/devices/0003:1050:0407.0007
- name: Yubico YubiKey OTP+FIDO+CCID
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x1050, 0x0407)
/sys/bus/hid/devices/0003:1050:0407.0008
- name: Yubico YubiKey OTP+FIDO+CCID
- device entry: HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x1050, 0x0407)
As shown above, many devices export multiple HID interfaces. See Run-time probe for details on how to handle this situation.
Alternatively, the bus, group, vendor ID and product ID (b, g, v, p)
can be extracted from the modalias of the device as provided by the kernel:
$ cat /sys/bus/hid/devices/0003:04D9:A09F.0009/modalias
hid:b0003g0001v000004D9p0000A09F
$ cat /sys/class/hidraw/hidraw0/device/modalias
hid:b0003g0001v000004D9p0000A09F
Just strip out the hid: prefix, extract the bus, group, vid, pid and done.
Run-time probe
Sometimes having just the vendor/product ID is not enough to know if a program needs to be loaded.
For example, one mouse I am doing tests with (mouse_invert_y.bpf.c with
HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, 0x04d9, 0xa09f)) exports 3 HID interfaces,
but the BPF program only applies to one of those HID interfaces.
udev-hid-bpf provides a similar functionality as the kernel with a probe function.
Before loading and attaching any BPF program to a given HID device, udev-hid-bpf executes the syscall probe in the .bpf.c file if there is any.
SEC("syscall")
int probe(struct hid_bpf_probe_args *ctx)
{
/* zero if we want to bind, nonzero otherwise*/
ctx->retval = 0;
return 0;
}
The arguments of this syscall are basically the unique id of the HID device, its report descriptor and its report descriptor size:
struct hid_bpf_probe_args {
unsigned int hid;
unsigned int rdesc_size; /* number of valid bytes */
unsigned char rdesc[4096]; /* the actual report descriptor */
int retval;
};
If the BPF program sets the ctx->retval to zero, the BPF program is loaded for this device. A nonzero value (typically -EINVAL)
prevents the BPF program from loading. See the
mouse_invert_y.bpf.c program for an example of this
functionality or the Probing section of the Tutorial.
Also note that probe is executed as a SEC("syscall"), which means that the bpf function
hid_bpf_hw_request() is available if you need to configure the device before customizing
it with HID-BPF.