1dfae6becSBenjamin Tissoires.. SPDX-License-Identifier: GPL-2.0 2dfae6becSBenjamin Tissoires 3dfae6becSBenjamin Tissoires======= 4dfae6becSBenjamin TissoiresHID-BPF 5dfae6becSBenjamin Tissoires======= 6dfae6becSBenjamin Tissoires 7dfae6becSBenjamin TissoiresHID is a standard protocol for input devices but some devices may require 8dfae6becSBenjamin Tissoirescustom tweaks, traditionally done with a kernel driver fix. Using the eBPF 9dfae6becSBenjamin Tissoirescapabilities instead speeds up development and adds new capabilities to the 10dfae6becSBenjamin Tissoiresexisting HID interfaces. 11dfae6becSBenjamin Tissoires 12dfae6becSBenjamin Tissoires.. contents:: 13dfae6becSBenjamin Tissoires :local: 14dfae6becSBenjamin Tissoires :depth: 2 15dfae6becSBenjamin Tissoires 16dfae6becSBenjamin Tissoires 17dfae6becSBenjamin TissoiresWhen (and why) to use HID-BPF 18dfae6becSBenjamin Tissoires============================= 19dfae6becSBenjamin Tissoires 20dfae6becSBenjamin TissoiresThere are several use cases when using HID-BPF is better 21dfae6becSBenjamin Tissoiresthan standard kernel driver fix: 22dfae6becSBenjamin Tissoires 23dfae6becSBenjamin TissoiresDead zone of a joystick 24dfae6becSBenjamin Tissoires----------------------- 25dfae6becSBenjamin Tissoires 26dfae6becSBenjamin TissoiresAssuming you have a joystick that is getting older, it is common to see it 27dfae6becSBenjamin Tissoireswobbling around its neutral point. This is usually filtered at the application 28dfae6becSBenjamin Tissoireslevel by adding a *dead zone* for this specific axis. 29dfae6becSBenjamin Tissoires 30dfae6becSBenjamin TissoiresWith HID-BPF, we can apply this filtering in the kernel directly so userspace 31dfae6becSBenjamin Tissoiresdoes not get woken up when nothing else is happening on the input controller. 32dfae6becSBenjamin Tissoires 33dfae6becSBenjamin TissoiresOf course, given that this dead zone is specific to an individual device, we 34dfae6becSBenjamin Tissoirescan not create a generic fix for all of the same joysticks. Adding a custom 35dfae6becSBenjamin Tissoireskernel API for this (e.g. by adding a sysfs entry) does not guarantee this new 36dfae6becSBenjamin Tissoireskernel API will be broadly adopted and maintained. 37dfae6becSBenjamin Tissoires 38dfae6becSBenjamin TissoiresHID-BPF allows the userspace program to load the program itself, ensuring we 39dfae6becSBenjamin Tissoiresonly load the custom API when we have a user. 40dfae6becSBenjamin Tissoires 41dfae6becSBenjamin TissoiresSimple fixup of report descriptor 42dfae6becSBenjamin Tissoires--------------------------------- 43dfae6becSBenjamin Tissoires 44dfae6becSBenjamin TissoiresIn the HID tree, half of the drivers only fix one key or one byte 45dfae6becSBenjamin Tissoiresin the report descriptor. These fixes all require a kernel patch and the 46dfae6becSBenjamin Tissoiressubsequent shepherding into a release, a long and painful process for users. 47dfae6becSBenjamin Tissoires 48dfae6becSBenjamin TissoiresWe can reduce this burden by providing an eBPF program instead. Once such a 49dfae6becSBenjamin Tissoiresprogram has been verified by the user, we can embed the source code into the 50dfae6becSBenjamin Tissoireskernel tree and ship the eBPF program and load it directly instead of loading 51dfae6becSBenjamin Tissoiresa specific kernel module for it. 52dfae6becSBenjamin Tissoires 53dfae6becSBenjamin TissoiresNote: distribution of eBPF programs and their inclusion in the kernel is not 54dfae6becSBenjamin Tissoiresyet fully implemented 55dfae6becSBenjamin Tissoires 56dfae6becSBenjamin TissoiresAdd a new feature that requires a new kernel API 57dfae6becSBenjamin Tissoires------------------------------------------------ 58dfae6becSBenjamin Tissoires 59dfae6becSBenjamin TissoiresAn example for such a feature are the Universal Stylus Interface (USI) pens. 60dfae6becSBenjamin TissoiresBasically, USI pens require a new kernel API because there are new 61dfae6becSBenjamin Tissoireschannels of communication that our HID and input stack do not support. 62dfae6becSBenjamin TissoiresInstead of using hidraw or creating new sysfs entries or ioctls, we can rely 63dfae6becSBenjamin Tissoireson eBPF to have the kernel API controlled by the consumer and to not 64dfae6becSBenjamin Tissoiresimpact the performances by waking up userspace every time there is an 65dfae6becSBenjamin Tissoiresevent. 66dfae6becSBenjamin Tissoires 67dfae6becSBenjamin TissoiresMorph a device into something else and control that from userspace 68dfae6becSBenjamin Tissoires------------------------------------------------------------------ 69dfae6becSBenjamin Tissoires 70dfae6becSBenjamin TissoiresThe kernel has a relatively static mapping of HID items to evdev bits. 71dfae6becSBenjamin TissoiresIt cannot decide to dynamically transform a given device into something else 72dfae6becSBenjamin Tissoiresas it does not have the required context and any such transformation cannot be 73dfae6becSBenjamin Tissoiresundone (or even discovered) by userspace. 74dfae6becSBenjamin Tissoires 75dfae6becSBenjamin TissoiresHowever, some devices are useless with that static way of defining devices. For 76dfae6becSBenjamin Tissoiresexample, the Microsoft Surface Dial is a pushbutton with haptic feedback that 77dfae6becSBenjamin Tissoiresis barely usable as of today. 78dfae6becSBenjamin Tissoires 79dfae6becSBenjamin TissoiresWith eBPF, userspace can morph that device into a mouse, and convert the dial 80dfae6becSBenjamin Tissoiresevents into wheel events. Also, the userspace program can set/unset the haptic 81dfae6becSBenjamin Tissoiresfeedback depending on the context. For example, if a menu is visible on the 82dfae6becSBenjamin Tissoiresscreen we likely need to have a haptic click every 15 degrees. But when 83dfae6becSBenjamin Tissoiresscrolling in a web page the user experience is better when the device emits 84dfae6becSBenjamin Tissoiresevents at the highest resolution. 85dfae6becSBenjamin Tissoires 86dfae6becSBenjamin TissoiresFirewall 87dfae6becSBenjamin Tissoires-------- 88dfae6becSBenjamin Tissoires 89dfae6becSBenjamin TissoiresWhat if we want to prevent other users to access a specific feature of a 90dfae6becSBenjamin Tissoiresdevice? (think a possibly broken firmware update entry point) 91dfae6becSBenjamin Tissoires 92dfae6becSBenjamin TissoiresWith eBPF, we can intercept any HID command emitted to the device and 93dfae6becSBenjamin Tissoiresvalidate it or not. 94dfae6becSBenjamin Tissoires 95dfae6becSBenjamin TissoiresThis also allows to sync the state between the userspace and the 96dfae6becSBenjamin Tissoireskernel/bpf program because we can intercept any incoming command. 97dfae6becSBenjamin Tissoires 98dfae6becSBenjamin TissoiresTracing 99dfae6becSBenjamin Tissoires------- 100dfae6becSBenjamin Tissoires 101dfae6becSBenjamin TissoiresThe last usage is tracing events and all the fun we can do we BPF to summarize 102dfae6becSBenjamin Tissoiresand analyze events. 103dfae6becSBenjamin Tissoires 104dfae6becSBenjamin TissoiresRight now, tracing relies on hidraw. It works well except for a couple 105dfae6becSBenjamin Tissoiresof issues: 106dfae6becSBenjamin Tissoires 107dfae6becSBenjamin Tissoires1. if the driver doesn't export a hidraw node, we can't trace anything 108dfae6becSBenjamin Tissoires (eBPF will be a "god-mode" there, so this may raise some eyebrows) 109dfae6becSBenjamin Tissoires2. hidraw doesn't catch other processes' requests to the device, which 110dfae6becSBenjamin Tissoires means that we have cases where we need to add printks to the kernel 111dfae6becSBenjamin Tissoires to understand what is happening. 112dfae6becSBenjamin Tissoires 113dfae6becSBenjamin TissoiresHigh-level view of HID-BPF 114dfae6becSBenjamin Tissoires========================== 115dfae6becSBenjamin Tissoires 116dfae6becSBenjamin TissoiresThe main idea behind HID-BPF is that it works at an array of bytes level. 117dfae6becSBenjamin TissoiresThus, all of the parsing of the HID report and the HID report descriptor 118dfae6becSBenjamin Tissoiresmust be implemented in the userspace component that loads the eBPF 119dfae6becSBenjamin Tissoiresprogram. 120dfae6becSBenjamin Tissoires 121dfae6becSBenjamin TissoiresFor example, in the dead zone joystick from above, knowing which fields 122dfae6becSBenjamin Tissoiresin the data stream needs to be set to ``0`` needs to be computed by userspace. 123dfae6becSBenjamin Tissoires 124dfae6becSBenjamin TissoiresA corollary of this is that HID-BPF doesn't know about the other subsystems 125dfae6becSBenjamin Tissoiresavailable in the kernel. *You can not directly emit input event through the 126dfae6becSBenjamin Tissoiresinput API from eBPF*. 127dfae6becSBenjamin Tissoires 128dfae6becSBenjamin TissoiresWhen a BPF program needs to emit input events, it needs to talk with the HID 129dfae6becSBenjamin Tissoiresprotocol, and rely on the HID kernel processing to translate the HID data into 130dfae6becSBenjamin Tissoiresinput events. 131dfae6becSBenjamin Tissoires 1325f42e19dSBenjamin TissoiresIn-tree HID-BPF programs and ``udev-hid-bpf`` 1335f42e19dSBenjamin Tissoires============================================= 1345f42e19dSBenjamin Tissoires 1355f42e19dSBenjamin TissoiresOfficial device fixes are shipped in the kernel tree as source in the 1365f42e19dSBenjamin Tissoires``drivers/hid/bpf/progs`` directory. This allows to add selftests to them in 1375f42e19dSBenjamin Tissoires``tools/testing/selftests/hid``. 1385f42e19dSBenjamin Tissoires 1395f42e19dSBenjamin TissoiresHowever, the compilation of these objects is not part of a regular kernel compilation 1405f42e19dSBenjamin Tissoiresgiven that they need an external tool to be loaded. This tool is currently 1415f42e19dSBenjamin Tissoires`udev-hid-bpf <https://libevdev.pages.freedesktop.org/udev-hid-bpf/index.html>`_. 1425f42e19dSBenjamin Tissoires 1435f42e19dSBenjamin TissoiresFor convenience, that external repository duplicates the files from here in 1445f42e19dSBenjamin Tissoires``drivers/hid/bpf/progs`` into its own ``src/bpf/stable`` directory. This allows 1455f42e19dSBenjamin Tissoiresdistributions to not have to pull the entire kernel source tree to ship and package 1465f42e19dSBenjamin Tissoiresthose HID-BPF fixes. ``udev-hid-bpf`` also has capabilities of handling multiple 1475f42e19dSBenjamin Tissoiresobjects files depending on the kernel the user is running. 1485f42e19dSBenjamin Tissoires 149dfae6becSBenjamin TissoiresAvailable types of programs 150dfae6becSBenjamin Tissoires=========================== 151dfae6becSBenjamin Tissoires 152c5958697SBenjamin TissoiresHID-BPF is built "on top" of BPF, meaning that we use bpf struct_ops method to 153dfae6becSBenjamin Tissoiresdeclare our programs. 154dfae6becSBenjamin Tissoires 155dfae6becSBenjamin TissoiresHID-BPF has the following attachment types available: 156dfae6becSBenjamin Tissoires 157c5958697SBenjamin Tissoires1. event processing/filtering with ``SEC("struct_ops/hid_device_event")`` in libbpf 158dfae6becSBenjamin Tissoires2. actions coming from userspace with ``SEC("syscall")`` in libbpf 159c5958697SBenjamin Tissoires3. change of the report descriptor with ``SEC("struct_ops/hid_rdesc_fixup")`` or 160c5958697SBenjamin Tissoires ``SEC("struct_ops.s/hid_rdesc_fixup")`` in libbpf 161dfae6becSBenjamin Tissoires 162c5958697SBenjamin TissoiresA ``hid_device_event`` is calling a BPF program when an event is received from 163dfae6becSBenjamin Tissoiresthe device. Thus we are in IRQ context and can act on the data or notify userspace. 164dfae6becSBenjamin TissoiresAnd given that we are in IRQ context, we can not talk back to the device. 165dfae6becSBenjamin Tissoires 166dfae6becSBenjamin TissoiresA ``syscall`` means that userspace called the syscall ``BPF_PROG_RUN`` facility. 167dfae6becSBenjamin TissoiresThis time, we can do any operations allowed by HID-BPF, and talking to the device is 168dfae6becSBenjamin Tissoiresallowed. 169dfae6becSBenjamin Tissoires 170c5958697SBenjamin TissoiresLast, ``hid_rdesc_fixup`` is different from the others as there can be only one 171dfae6becSBenjamin TissoiresBPF program of this type. This is called on ``probe`` from the driver and allows to 172c5958697SBenjamin Tissoireschange the report descriptor from the BPF program. Once a ``hid_rdesc_fixup`` 173dfae6becSBenjamin Tissoiresprogram has been loaded, it is not possible to overwrite it unless the program which 174dfae6becSBenjamin Tissoiresinserted it allows us by pinning the program and closing all of its fds pointing to it. 175dfae6becSBenjamin Tissoires 176c5958697SBenjamin TissoiresNote that ``hid_rdesc_fixup`` can be declared as sleepable (``SEC("struct_ops.s/hid_rdesc_fixup")``). 177c5958697SBenjamin Tissoires 178c5958697SBenjamin Tissoires 179dfae6becSBenjamin TissoiresDeveloper API: 180dfae6becSBenjamin Tissoires============== 181dfae6becSBenjamin Tissoires 182c5958697SBenjamin TissoiresAvailable ``struct_ops`` for HID-BPF: 183c5958697SBenjamin Tissoires------------------------------------- 184c5958697SBenjamin Tissoires 185c5958697SBenjamin Tissoires.. kernel-doc:: include/linux/hid_bpf.h 186c5958697SBenjamin Tissoires :identifiers: hid_bpf_ops 187c5958697SBenjamin Tissoires 188c5958697SBenjamin Tissoires 189dfae6becSBenjamin TissoiresUser API data structures available in programs: 190dfae6becSBenjamin Tissoires----------------------------------------------- 191dfae6becSBenjamin Tissoires 192dfae6becSBenjamin Tissoires.. kernel-doc:: include/linux/hid_bpf.h 193c5958697SBenjamin Tissoires :identifiers: hid_bpf_ctx 194dfae6becSBenjamin Tissoires 195c5958697SBenjamin TissoiresAvailable API that can be used in all HID-BPF struct_ops programs: 196c5958697SBenjamin Tissoires------------------------------------------------------------------ 197dfae6becSBenjamin Tissoires 198dfae6becSBenjamin Tissoires.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 199c5958697SBenjamin Tissoires :identifiers: hid_bpf_get_data 200dfae6becSBenjamin Tissoires 201c5958697SBenjamin TissoiresAvailable API that can be used in syscall HID-BPF programs or in sleepable HID-BPF struct_ops programs: 202c5958697SBenjamin Tissoires------------------------------------------------------------------------------------------------------- 203dfae6becSBenjamin Tissoires 204dfae6becSBenjamin Tissoires.. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 2059acbb7baSBenjamin Tissoires :identifiers: hid_bpf_hw_request hid_bpf_hw_output_report hid_bpf_input_report hid_bpf_try_input_report hid_bpf_allocate_context hid_bpf_release_context 206dfae6becSBenjamin Tissoires 207dfae6becSBenjamin TissoiresGeneral overview of a HID-BPF program 208dfae6becSBenjamin Tissoires===================================== 209dfae6becSBenjamin Tissoires 210dfae6becSBenjamin TissoiresAccessing the data attached to the context 211dfae6becSBenjamin Tissoires------------------------------------------ 212dfae6becSBenjamin Tissoires 213dfae6becSBenjamin TissoiresThe ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access 214dfae6becSBenjamin Tissoiresit, a bpf program needs to first call :c:func:`hid_bpf_get_data`. 215dfae6becSBenjamin Tissoires 216dfae6becSBenjamin Tissoires``offset`` can be any integer, but ``size`` needs to be constant, known at compile 217dfae6becSBenjamin Tissoirestime. 218dfae6becSBenjamin Tissoires 219dfae6becSBenjamin TissoiresThis allows the following: 220dfae6becSBenjamin Tissoires 221dfae6becSBenjamin Tissoires1. for a given device, if we know that the report length will always be of a certain value, 222dfae6becSBenjamin Tissoires we can request the ``data`` pointer to point at the full report length. 223dfae6becSBenjamin Tissoires 224dfae6becSBenjamin Tissoires The kernel will ensure we are using a correct size and offset and eBPF will ensure 225dfae6becSBenjamin Tissoires the code will not attempt to read or write outside of the boundaries:: 226dfae6becSBenjamin Tissoires 227dfae6becSBenjamin Tissoires __u8 *data = hid_bpf_get_data(ctx, 0 /* offset */, 256 /* size */); 228dfae6becSBenjamin Tissoires 229dfae6becSBenjamin Tissoires if (!data) 230dfae6becSBenjamin Tissoires return 0; /* ensure data is correct, now the verifier knows we 231dfae6becSBenjamin Tissoires * have 256 bytes available */ 232dfae6becSBenjamin Tissoires 233dfae6becSBenjamin Tissoires bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]); 234dfae6becSBenjamin Tissoires 235dfae6becSBenjamin Tissoires2. if the report length is variable, but we know the value of ``X`` is always a 16-bit 236dfae6becSBenjamin Tissoires integer, we can then have a pointer to that value only:: 237dfae6becSBenjamin Tissoires 238dfae6becSBenjamin Tissoires __u16 *x = hid_bpf_get_data(ctx, offset, sizeof(*x)); 239dfae6becSBenjamin Tissoires 240dfae6becSBenjamin Tissoires if (!x) 241dfae6becSBenjamin Tissoires return 0; /* something went wrong */ 242dfae6becSBenjamin Tissoires 243dfae6becSBenjamin Tissoires *x += 1; /* increment X by one */ 244dfae6becSBenjamin Tissoires 245dfae6becSBenjamin TissoiresEffect of a HID-BPF program 246dfae6becSBenjamin Tissoires--------------------------- 247dfae6becSBenjamin Tissoires 248c5958697SBenjamin TissoiresFor all HID-BPF attachment types except for :c:func:`hid_rdesc_fixup`, several eBPF 249c5958697SBenjamin Tissoiresprograms can be attached to the same device. If a HID-BPF struct_ops has a 250c5958697SBenjamin Tissoires:c:func:`hid_rdesc_fixup` while another is already attached to the device, the 251c5958697SBenjamin Tissoireskernel will return `-EINVAL` when attaching the struct_ops. 252dfae6becSBenjamin Tissoires 253c5958697SBenjamin TissoiresUnless ``BPF_F_BEFORE`` is added to the flags while attaching the program, the new 254c5958697SBenjamin Tissoiresprogram is appended at the end of the list. 255c5958697SBenjamin Tissoires``BPF_F_BEFORE`` will insert the new program at the beginning of the list which is 256c5958697SBenjamin Tissoiresuseful for e.g. tracing where we need to get the unprocessed events from the device. 257dfae6becSBenjamin Tissoires 258c5958697SBenjamin TissoiresNote that if there are multiple programs using the ``BPF_F_BEFORE`` flag, 259dfae6becSBenjamin Tissoiresonly the most recently loaded one is actually the first in the list. 260dfae6becSBenjamin Tissoires 261c5958697SBenjamin Tissoires``SEC("struct_ops/hid_device_event")`` 262c5958697SBenjamin Tissoires~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 263dfae6becSBenjamin Tissoires 264dfae6becSBenjamin TissoiresWhenever a matching event is raised, the eBPF programs are called one after the other 265dfae6becSBenjamin Tissoiresand are working on the same data buffer. 266dfae6becSBenjamin Tissoires 267dfae6becSBenjamin TissoiresIf a program changes the data associated with the context, the next one will see 268dfae6becSBenjamin Tissoiresthe modified data but it will have *no* idea of what the original data was. 269dfae6becSBenjamin Tissoires 270dfae6becSBenjamin TissoiresOnce all the programs are run and return ``0`` or a positive value, the rest of the 271dfae6becSBenjamin TissoiresHID stack will work on the modified data, with the ``size`` field of the last hid_bpf_ctx 272dfae6becSBenjamin Tissoiresbeing the new size of the input stream of data. 273dfae6becSBenjamin Tissoires 274dfae6becSBenjamin TissoiresA BPF program returning a negative error discards the event, i.e. this event will not be 275dfae6becSBenjamin Tissoiresprocessed by the HID stack. Clients (hidraw, input, LEDs) will **not** see this event. 276dfae6becSBenjamin Tissoires 277dfae6becSBenjamin Tissoires``SEC("syscall")`` 278dfae6becSBenjamin Tissoires~~~~~~~~~~~~~~~~~~ 279dfae6becSBenjamin Tissoires 280dfae6becSBenjamin Tissoires``syscall`` are not attached to a given device. To tell which device we are working 281dfae6becSBenjamin Tissoireswith, userspace needs to refer to the device by its unique system id (the last 4 numbers 282dfae6becSBenjamin Tissoiresin the sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``). 283dfae6becSBenjamin Tissoires 284dfae6becSBenjamin TissoiresTo retrieve a context associated with the device, the program must call 285c5958697SBenjamin Tissoireshid_bpf_allocate_context() and must release it with hid_bpf_release_context() 286dfae6becSBenjamin Tissoiresbefore returning. 287dfae6becSBenjamin TissoiresOnce the context is retrieved, one can also request a pointer to kernel memory with 288c5958697SBenjamin Tissoireshid_bpf_get_data(). This memory is big enough to support all input/output/feature 289dfae6becSBenjamin Tissoiresreports of the given device. 290dfae6becSBenjamin Tissoires 291c5958697SBenjamin Tissoires``SEC("struct_ops/hid_rdesc_fixup")`` 292c5958697SBenjamin Tissoires~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 293dfae6becSBenjamin Tissoires 294c5958697SBenjamin TissoiresThe ``hid_rdesc_fixup`` program works in a similar manner to ``.report_fixup`` 295c5958697SBenjamin Tissoiresof ``struct hid_driver``. 296dfae6becSBenjamin Tissoires 297dfae6becSBenjamin TissoiresWhen the device is probed, the kernel sets the data buffer of the context with the 298dfae6becSBenjamin Tissoirescontent of the report descriptor. The memory associated with that buffer is 299dfae6becSBenjamin Tissoires``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). 300dfae6becSBenjamin Tissoires 301dfae6becSBenjamin TissoiresThe eBPF program can modify the data buffer at-will and the kernel uses the 302dfae6becSBenjamin Tissoiresmodified content and size as the report descriptor. 303dfae6becSBenjamin Tissoires 304c5958697SBenjamin TissoiresWhenever a struct_ops containing a ``SEC("struct_ops/hid_rdesc_fixup")`` program 305c5958697SBenjamin Tissoiresis attached (if no program was attached before), the kernel immediately disconnects 306c5958697SBenjamin Tissoiresthe HID device and does a reprobe. 307dfae6becSBenjamin Tissoires 308c5958697SBenjamin TissoiresIn the same way, when this struct_ops is detached, the kernel issues a disconnect 309c5958697SBenjamin Tissoireson the device. 310dfae6becSBenjamin Tissoires 311dfae6becSBenjamin TissoiresThere is no ``detach`` facility in HID-BPF. Detaching a program happens when 312c5958697SBenjamin Tissoiresall the user space file descriptors pointing at a HID-BPF struct_ops link are closed. 313dfae6becSBenjamin TissoiresThus, if we need to replace a report descriptor fixup, some cooperation is 314dfae6becSBenjamin Tissoiresrequired from the owner of the original report descriptor fixup. 315c5958697SBenjamin TissoiresThe previous owner will likely pin the struct_ops link in the bpffs, and we can then 316dfae6becSBenjamin Tissoiresreplace it through normal bpf operations. 317dfae6becSBenjamin Tissoires 318dfae6becSBenjamin TissoiresAttaching a bpf program to a device 319dfae6becSBenjamin Tissoires=================================== 320dfae6becSBenjamin Tissoires 321c5958697SBenjamin TissoiresWe now use standard struct_ops attachment through ``bpf_map__attach_struct_ops()``. 322c5958697SBenjamin TissoiresBut given that we need to attach a struct_ops to a dedicated HID device, the caller 323c5958697SBenjamin Tissoiresmust set ``hid_id`` in the struct_ops map before loading the program in the kernel. 324dfae6becSBenjamin Tissoires 325dfae6becSBenjamin Tissoires``hid_id`` is the unique system ID of the HID device (the last 4 numbers in the 326dfae6becSBenjamin Tissoiressysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``) 327dfae6becSBenjamin Tissoires 328c5958697SBenjamin TissoiresOne can also set ``flags``, which is of type ``enum hid_bpf_attach_flags``. 329dfae6becSBenjamin Tissoires 330dfae6becSBenjamin TissoiresWe can not rely on hidraw to bind a BPF program to a HID device. hidraw is an 331dfae6becSBenjamin Tissoiresartefact of the processing of the HID device, and is not stable. Some drivers 3322f7f4efbSRandy Dunlapeven disable it, so that removes the tracing capabilities on those devices 333dfae6becSBenjamin Tissoires(where it is interesting to get the non-hidraw traces). 334dfae6becSBenjamin Tissoires 335dfae6becSBenjamin TissoiresOn the other hand, the ``hid_id`` is stable for the entire life of the HID device, 336dfae6becSBenjamin Tissoireseven if we change its report descriptor. 337dfae6becSBenjamin Tissoires 338dfae6becSBenjamin TissoiresGiven that hidraw is not stable when the device disconnects/reconnects, we recommend 339dfae6becSBenjamin Tissoiresaccessing the current report descriptor of the device through the sysfs. 340dfae6becSBenjamin TissoiresThis is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a 341dfae6becSBenjamin Tissoiresbinary stream. 342dfae6becSBenjamin Tissoires 343dfae6becSBenjamin TissoiresParsing the report descriptor is the responsibility of the BPF programmer or the userspace 344dfae6becSBenjamin Tissoirescomponent that loads the eBPF program. 345dfae6becSBenjamin Tissoires 346dfae6becSBenjamin TissoiresAn (almost) complete example of a BPF enhanced HID device 347dfae6becSBenjamin Tissoires========================================================= 348dfae6becSBenjamin Tissoires 349dfae6becSBenjamin Tissoires*Foreword: for most parts, this could be implemented as a kernel driver* 350dfae6becSBenjamin Tissoires 351dfae6becSBenjamin TissoiresLet's imagine we have a new tablet device that has some haptic capabilities 352dfae6becSBenjamin Tissoiresto simulate the surface the user is scratching on. This device would also have 353dfae6becSBenjamin Tissoiresa specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall* 354dfae6becSBenjamin Tissoiresand *brush on a painting canvas*. To make things even better, we can control the 355dfae6becSBenjamin Tissoiresphysical position of the switch through a feature report. 356dfae6becSBenjamin Tissoires 357dfae6becSBenjamin TissoiresAnd of course, the switch is relying on some userspace component to control the 358dfae6becSBenjamin Tissoireshaptic feature of the device itself. 359dfae6becSBenjamin Tissoires 360dfae6becSBenjamin TissoiresFiltering events 361dfae6becSBenjamin Tissoires---------------- 362dfae6becSBenjamin Tissoires 363dfae6becSBenjamin TissoiresThe first step consists in filtering events from the device. Given that the switch 364dfae6becSBenjamin Tissoiresposition is actually reported in the flow of the pen events, using hidraw to implement 365dfae6becSBenjamin Tissoiresthat filtering would mean that we wake up userspace for every single event. 366dfae6becSBenjamin Tissoires 367dfae6becSBenjamin TissoiresThis is OK for libinput, but having an external library that is just interested in 368dfae6becSBenjamin Tissoiresone byte in the report is less than ideal. 369dfae6becSBenjamin Tissoires 370dfae6becSBenjamin TissoiresFor that, we can create a basic skeleton for our BPF program:: 371dfae6becSBenjamin Tissoires 372dfae6becSBenjamin Tissoires #include "vmlinux.h" 373dfae6becSBenjamin Tissoires #include <bpf/bpf_helpers.h> 374dfae6becSBenjamin Tissoires #include <bpf/bpf_tracing.h> 375dfae6becSBenjamin Tissoires 376dfae6becSBenjamin Tissoires /* HID programs need to be GPL */ 377dfae6becSBenjamin Tissoires char _license[] SEC("license") = "GPL"; 378dfae6becSBenjamin Tissoires 379dfae6becSBenjamin Tissoires /* HID-BPF kfunc API definitions */ 380dfae6becSBenjamin Tissoires extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, 381dfae6becSBenjamin Tissoires unsigned int offset, 382dfae6becSBenjamin Tissoires const size_t __sz) __ksym; 383dfae6becSBenjamin Tissoires 384dfae6becSBenjamin Tissoires struct { 385dfae6becSBenjamin Tissoires __uint(type, BPF_MAP_TYPE_RINGBUF); 386dfae6becSBenjamin Tissoires __uint(max_entries, 4096 * 64); 387dfae6becSBenjamin Tissoires } ringbuf SEC(".maps"); 388dfae6becSBenjamin Tissoires 389dfae6becSBenjamin Tissoires __u8 current_value = 0; 390dfae6becSBenjamin Tissoires 391c5958697SBenjamin Tissoires SEC("struct_ops/hid_device_event") 392dfae6becSBenjamin Tissoires int BPF_PROG(filter_switch, struct hid_bpf_ctx *hid_ctx) 393dfae6becSBenjamin Tissoires { 394dfae6becSBenjamin Tissoires __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 192 /* size */); 395dfae6becSBenjamin Tissoires __u8 *buf; 396dfae6becSBenjamin Tissoires 397dfae6becSBenjamin Tissoires if (!data) 398dfae6becSBenjamin Tissoires return 0; /* EPERM check */ 399dfae6becSBenjamin Tissoires 400dfae6becSBenjamin Tissoires if (current_value != data[152]) { 401dfae6becSBenjamin Tissoires buf = bpf_ringbuf_reserve(&ringbuf, 1, 0); 402dfae6becSBenjamin Tissoires if (!buf) 403dfae6becSBenjamin Tissoires return 0; 404dfae6becSBenjamin Tissoires 405dfae6becSBenjamin Tissoires *buf = data[152]; 406dfae6becSBenjamin Tissoires 407dfae6becSBenjamin Tissoires bpf_ringbuf_commit(buf, 0); 408dfae6becSBenjamin Tissoires 409dfae6becSBenjamin Tissoires current_value = data[152]; 410dfae6becSBenjamin Tissoires } 411dfae6becSBenjamin Tissoires 412dfae6becSBenjamin Tissoires return 0; 413dfae6becSBenjamin Tissoires } 414dfae6becSBenjamin Tissoires 415c5958697SBenjamin Tissoires SEC(".struct_ops.link") 416c5958697SBenjamin Tissoires struct hid_bpf_ops haptic_tablet = { 417c5958697SBenjamin Tissoires .hid_device_event = (void *)filter_switch, 418c5958697SBenjamin Tissoires }; 419c5958697SBenjamin Tissoires 420c5958697SBenjamin Tissoires 421c5958697SBenjamin TissoiresTo attach ``haptic_tablet``, userspace needs to set ``hid_id`` first:: 422dfae6becSBenjamin Tissoires 423dfae6becSBenjamin Tissoires static int attach_filter(struct hid *hid_skel, int hid_id) 424dfae6becSBenjamin Tissoires { 425c5958697SBenjamin Tissoires int err, link_fd; 426dfae6becSBenjamin Tissoires 427c5958697SBenjamin Tissoires hid_skel->struct_ops.haptic_tablet->hid_id = hid_id; 428c5958697SBenjamin Tissoires err = hid__load(skel); 4294b9a3f49SBenjamin Tissoires if (err) 430dfae6becSBenjamin Tissoires return err; 4314b9a3f49SBenjamin Tissoires 432c5958697SBenjamin Tissoires link_fd = bpf_map__attach_struct_ops(hid_skel->maps.haptic_tablet); 433c5958697SBenjamin Tissoires if (!link_fd) { 434c5958697SBenjamin Tissoires fprintf(stderr, "can not attach HID-BPF program: %m\n"); 435c5958697SBenjamin Tissoires return -1; 436c5958697SBenjamin Tissoires } 437c5958697SBenjamin Tissoires 438c5958697SBenjamin Tissoires return link_fd; /* the fd of the created bpf_link */ 439dfae6becSBenjamin Tissoires } 440dfae6becSBenjamin Tissoires 441dfae6becSBenjamin TissoiresOur userspace program can now listen to notifications on the ring buffer, and 442dfae6becSBenjamin Tissoiresis awaken only when the value changes. 443dfae6becSBenjamin Tissoires 4444b9a3f49SBenjamin TissoiresWhen the userspace program doesn't need to listen to events anymore, it can just 445c5958697SBenjamin Tissoiresclose the returned bpf link from :c:func:`attach_filter`, which will tell the kernel to 4464b9a3f49SBenjamin Tissoiresdetach the program from the HID device. 4474b9a3f49SBenjamin Tissoires 4484b9a3f49SBenjamin TissoiresOf course, in other use cases, the userspace program can also pin the fd to the 4494b9a3f49SBenjamin TissoiresBPF filesystem through a call to :c:func:`bpf_obj_pin`, as with any bpf_link. 4504b9a3f49SBenjamin Tissoires 451dfae6becSBenjamin TissoiresControlling the device 452dfae6becSBenjamin Tissoires---------------------- 453dfae6becSBenjamin Tissoires 454dfae6becSBenjamin TissoiresTo be able to change the haptic feedback from the tablet, the userspace program 455dfae6becSBenjamin Tissoiresneeds to emit a feature report on the device itself. 456dfae6becSBenjamin Tissoires 457dfae6becSBenjamin TissoiresInstead of using hidraw for that, we can create a ``SEC("syscall")`` program 458dfae6becSBenjamin Tissoiresthat talks to the device:: 459dfae6becSBenjamin Tissoires 460dfae6becSBenjamin Tissoires /* some more HID-BPF kfunc API definitions */ 461dfae6becSBenjamin Tissoires extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym; 462dfae6becSBenjamin Tissoires extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym; 463dfae6becSBenjamin Tissoires extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, 464dfae6becSBenjamin Tissoires __u8* data, 465dfae6becSBenjamin Tissoires size_t len, 466dfae6becSBenjamin Tissoires enum hid_report_type type, 467dfae6becSBenjamin Tissoires enum hid_class_request reqtype) __ksym; 468dfae6becSBenjamin Tissoires 469dfae6becSBenjamin Tissoires 470dfae6becSBenjamin Tissoires struct hid_send_haptics_args { 471dfae6becSBenjamin Tissoires /* data needs to come at offset 0 so we can do a memcpy into it */ 472dfae6becSBenjamin Tissoires __u8 data[10]; 473dfae6becSBenjamin Tissoires unsigned int hid; 474dfae6becSBenjamin Tissoires }; 475dfae6becSBenjamin Tissoires 476dfae6becSBenjamin Tissoires SEC("syscall") 477dfae6becSBenjamin Tissoires int send_haptic(struct hid_send_haptics_args *args) 478dfae6becSBenjamin Tissoires { 479dfae6becSBenjamin Tissoires struct hid_bpf_ctx *ctx; 480dfae6becSBenjamin Tissoires int ret = 0; 481dfae6becSBenjamin Tissoires 482dfae6becSBenjamin Tissoires ctx = hid_bpf_allocate_context(args->hid); 483dfae6becSBenjamin Tissoires if (!ctx) 484dfae6becSBenjamin Tissoires return 0; /* EPERM check */ 485dfae6becSBenjamin Tissoires 486dfae6becSBenjamin Tissoires ret = hid_bpf_hw_request(ctx, 487dfae6becSBenjamin Tissoires args->data, 488dfae6becSBenjamin Tissoires 10, 489dfae6becSBenjamin Tissoires HID_FEATURE_REPORT, 490dfae6becSBenjamin Tissoires HID_REQ_SET_REPORT); 491dfae6becSBenjamin Tissoires 492dfae6becSBenjamin Tissoires hid_bpf_release_context(ctx); 493dfae6becSBenjamin Tissoires 494dfae6becSBenjamin Tissoires return ret; 495dfae6becSBenjamin Tissoires } 496dfae6becSBenjamin Tissoires 497dfae6becSBenjamin TissoiresAnd then userspace needs to call that program directly:: 498dfae6becSBenjamin Tissoires 499dfae6becSBenjamin Tissoires static int set_haptic(struct hid *hid_skel, int hid_id, __u8 haptic_value) 500dfae6becSBenjamin Tissoires { 501dfae6becSBenjamin Tissoires int err, prog_fd; 502dfae6becSBenjamin Tissoires int ret = -1; 503dfae6becSBenjamin Tissoires struct hid_send_haptics_args args = { 504dfae6becSBenjamin Tissoires .hid = hid_id, 505dfae6becSBenjamin Tissoires }; 506dfae6becSBenjamin Tissoires DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 507dfae6becSBenjamin Tissoires .ctx_in = &args, 508dfae6becSBenjamin Tissoires .ctx_size_in = sizeof(args), 509dfae6becSBenjamin Tissoires ); 510dfae6becSBenjamin Tissoires 511dfae6becSBenjamin Tissoires args.data[0] = 0x02; /* report ID of the feature on our device */ 512dfae6becSBenjamin Tissoires args.data[1] = haptic_value; 513dfae6becSBenjamin Tissoires 514dfae6becSBenjamin Tissoires prog_fd = bpf_program__fd(hid_skel->progs.set_haptic); 515dfae6becSBenjamin Tissoires 516dfae6becSBenjamin Tissoires err = bpf_prog_test_run_opts(prog_fd, &tattrs); 517dfae6becSBenjamin Tissoires return err; 518dfae6becSBenjamin Tissoires } 519dfae6becSBenjamin Tissoires 520dfae6becSBenjamin TissoiresNow our userspace program is aware of the haptic state and can control it. The 521dfae6becSBenjamin Tissoiresprogram could make this state further available to other userspace programs 522dfae6becSBenjamin Tissoires(e.g. via a DBus API). 523dfae6becSBenjamin Tissoires 524dfae6becSBenjamin TissoiresThe interesting bit here is that we did not created a new kernel API for this. 525dfae6becSBenjamin TissoiresWhich means that if there is a bug in our implementation, we can change the 526dfae6becSBenjamin Tissoiresinterface with the kernel at-will, because the userspace application is 527dfae6becSBenjamin Tissoiresresponsible for its own usage. 528