xref: /linux/Documentation/hid/hid-bpf.rst (revision c771600c6af14749609b49565ffb4cac2959710d)
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