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
lsusb
output)the product ID in hexadecimal (see the
lsusb
output)
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.