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