1 .. SPDX-License-Identifier: GPL-2.0 1 .. SPDX-License-Identifier: GPL-2.0 2 2 3 ======= 3 ======= 4 HID-BPF 4 HID-BPF 5 ======= 5 ======= 6 6 7 HID is a standard protocol for input devices b 7 HID is a standard protocol for input devices but some devices may require 8 custom tweaks, traditionally done with a kerne 8 custom tweaks, traditionally done with a kernel driver fix. Using the eBPF 9 capabilities instead speeds up development and 9 capabilities instead speeds up development and adds new capabilities to the 10 existing HID interfaces. 10 existing HID interfaces. 11 11 12 .. contents:: 12 .. contents:: 13 :local: 13 :local: 14 :depth: 2 14 :depth: 2 15 15 16 16 17 When (and why) to use HID-BPF 17 When (and why) to use HID-BPF 18 ============================= 18 ============================= 19 19 20 There are several use cases when using HID-BPF 20 There are several use cases when using HID-BPF is better 21 than standard kernel driver fix: 21 than standard kernel driver fix: 22 22 23 Dead zone of a joystick 23 Dead zone of a joystick 24 ----------------------- 24 ----------------------- 25 25 26 Assuming you have a joystick that is getting o 26 Assuming you have a joystick that is getting older, it is common to see it 27 wobbling around its neutral point. This is usu 27 wobbling around its neutral point. This is usually filtered at the application 28 level by adding a *dead zone* for this specifi 28 level by adding a *dead zone* for this specific axis. 29 29 30 With HID-BPF, we can apply this filtering in t 30 With HID-BPF, we can apply this filtering in the kernel directly so userspace 31 does not get woken up when nothing else is hap 31 does not get woken up when nothing else is happening on the input controller. 32 32 33 Of course, given that this dead zone is specif 33 Of course, given that this dead zone is specific to an individual device, we 34 can not create a generic fix for all of the sa 34 can not create a generic fix for all of the same joysticks. Adding a custom 35 kernel API for this (e.g. by adding a sysfs en 35 kernel API for this (e.g. by adding a sysfs entry) does not guarantee this new 36 kernel API will be broadly adopted and maintai 36 kernel API will be broadly adopted and maintained. 37 37 38 HID-BPF allows the userspace program to load t 38 HID-BPF allows the userspace program to load the program itself, ensuring we 39 only load the custom API when we have a user. 39 only load the custom API when we have a user. 40 40 41 Simple fixup of report descriptor 41 Simple fixup of report descriptor 42 --------------------------------- 42 --------------------------------- 43 43 44 In the HID tree, half of the drivers only fix 44 In the HID tree, half of the drivers only fix one key or one byte 45 in the report descriptor. These fixes all requ 45 in the report descriptor. These fixes all require a kernel patch and the 46 subsequent shepherding into a release, a long 46 subsequent shepherding into a release, a long and painful process for users. 47 47 48 We can reduce this burden by providing an eBPF 48 We can reduce this burden by providing an eBPF program instead. Once such a 49 program has been verified by the user, we can 49 program has been verified by the user, we can embed the source code into the 50 kernel tree and ship the eBPF program and load 50 kernel tree and ship the eBPF program and load it directly instead of loading 51 a specific kernel module for it. 51 a specific kernel module for it. 52 52 53 Note: distribution of eBPF programs and their 53 Note: distribution of eBPF programs and their inclusion in the kernel is not 54 yet fully implemented 54 yet fully implemented 55 55 56 Add a new feature that requires a new kernel A 56 Add a new feature that requires a new kernel API 57 ---------------------------------------------- 57 ------------------------------------------------ 58 58 59 An example for such a feature are the Universa 59 An example for such a feature are the Universal Stylus Interface (USI) pens. 60 Basically, USI pens require a new kernel API b 60 Basically, USI pens require a new kernel API because there are new 61 channels of communication that our HID and inp 61 channels of communication that our HID and input stack do not support. 62 Instead of using hidraw or creating new sysfs 62 Instead of using hidraw or creating new sysfs entries or ioctls, we can rely 63 on eBPF to have the kernel API controlled by t 63 on eBPF to have the kernel API controlled by the consumer and to not 64 impact the performances by waking up userspace 64 impact the performances by waking up userspace every time there is an 65 event. 65 event. 66 66 67 Morph a device into something else and control 67 Morph a device into something else and control that from userspace 68 ---------------------------------------------- 68 ------------------------------------------------------------------ 69 69 70 The kernel has a relatively static mapping of 70 The kernel has a relatively static mapping of HID items to evdev bits. 71 It cannot decide to dynamically transform a gi 71 It cannot decide to dynamically transform a given device into something else 72 as it does not have the required context and a 72 as it does not have the required context and any such transformation cannot be 73 undone (or even discovered) by userspace. 73 undone (or even discovered) by userspace. 74 74 75 However, some devices are useless with that st 75 However, some devices are useless with that static way of defining devices. For 76 example, the Microsoft Surface Dial is a pushb 76 example, the Microsoft Surface Dial is a pushbutton with haptic feedback that 77 is barely usable as of today. 77 is barely usable as of today. 78 78 79 With eBPF, userspace can morph that device int 79 With eBPF, userspace can morph that device into a mouse, and convert the dial 80 events into wheel events. Also, the userspace 80 events into wheel events. Also, the userspace program can set/unset the haptic 81 feedback depending on the context. For example 81 feedback depending on the context. For example, if a menu is visible on the 82 screen we likely need to have a haptic click e 82 screen we likely need to have a haptic click every 15 degrees. But when 83 scrolling in a web page the user experience is 83 scrolling in a web page the user experience is better when the device emits 84 events at the highest resolution. 84 events at the highest resolution. 85 85 86 Firewall 86 Firewall 87 -------- 87 -------- 88 88 89 What if we want to prevent other users to acce 89 What if we want to prevent other users to access a specific feature of a 90 device? (think a possibly broken firmware upda 90 device? (think a possibly broken firmware update entry point) 91 91 92 With eBPF, we can intercept any HID command em 92 With eBPF, we can intercept any HID command emitted to the device and 93 validate it or not. 93 validate it or not. 94 94 95 This also allows to sync the state between the 95 This also allows to sync the state between the userspace and the 96 kernel/bpf program because we can intercept an 96 kernel/bpf program because we can intercept any incoming command. 97 97 98 Tracing 98 Tracing 99 ------- 99 ------- 100 100 101 The last usage is tracing events and all the f 101 The last usage is tracing events and all the fun we can do we BPF to summarize 102 and analyze events. 102 and analyze events. 103 103 104 Right now, tracing relies on hidraw. It works 104 Right now, tracing relies on hidraw. It works well except for a couple 105 of issues: 105 of issues: 106 106 107 1. if the driver doesn't export a hidraw node, 107 1. if the driver doesn't export a hidraw node, we can't trace anything 108 (eBPF will be a "god-mode" there, so this m 108 (eBPF will be a "god-mode" there, so this may raise some eyebrows) 109 2. hidraw doesn't catch other processes' reque 109 2. hidraw doesn't catch other processes' requests to the device, which 110 means that we have cases where we need to a 110 means that we have cases where we need to add printks to the kernel 111 to understand what is happening. 111 to understand what is happening. 112 112 113 High-level view of HID-BPF 113 High-level view of HID-BPF 114 ========================== 114 ========================== 115 115 116 The main idea behind HID-BPF is that it works 116 The main idea behind HID-BPF is that it works at an array of bytes level. 117 Thus, all of the parsing of the HID report and 117 Thus, all of the parsing of the HID report and the HID report descriptor 118 must be implemented in the userspace component 118 must be implemented in the userspace component that loads the eBPF 119 program. 119 program. 120 120 121 For example, in the dead zone joystick from ab 121 For example, in the dead zone joystick from above, knowing which fields 122 in the data stream needs to be set to ``0`` ne 122 in the data stream needs to be set to ``0`` needs to be computed by userspace. 123 123 124 A corollary of this is that HID-BPF doesn't kn 124 A corollary of this is that HID-BPF doesn't know about the other subsystems 125 available in the kernel. *You can not directly 125 available in the kernel. *You can not directly emit input event through the 126 input API from eBPF*. 126 input API from eBPF*. 127 127 128 When a BPF program needs to emit input events, 128 When a BPF program needs to emit input events, it needs to talk with the HID 129 protocol, and rely on the HID kernel processin 129 protocol, and rely on the HID kernel processing to translate the HID data into 130 input events. 130 input events. 131 131 132 In-tree HID-BPF programs and ``udev-hid-bpf`` << 133 ============================================= << 134 << 135 Official device fixes are shipped in the kerne << 136 ``drivers/hid/bpf/progs`` directory. This allo << 137 ``tools/testing/selftests/hid``. << 138 << 139 However, the compilation of these objects is n << 140 given that they need an external tool to be lo << 141 `udev-hid-bpf <https://libevdev.pages.freedesk << 142 << 143 For convenience, that external repository dupl << 144 ``drivers/hid/bpf/progs`` into its own ``src/b << 145 distributions to not have to pull the entire k << 146 those HID-BPF fixes. ``udev-hid-bpf`` also has << 147 objects files depending on the kernel the user << 148 << 149 Available types of programs 132 Available types of programs 150 =========================== 133 =========================== 151 134 152 HID-BPF is built "on top" of BPF, meaning that !! 135 HID-BPF is built "on top" of BPF, meaning that we use tracing method to 153 declare our programs. 136 declare our programs. 154 137 155 HID-BPF has the following attachment types ava 138 HID-BPF has the following attachment types available: 156 139 157 1. event processing/filtering with ``SEC("stru !! 140 1. event processing/filtering with ``SEC("fmod_ret/hid_bpf_device_event")`` in libbpf 158 2. actions coming from userspace with ``SEC("s 141 2. actions coming from userspace with ``SEC("syscall")`` in libbpf 159 3. change of the report descriptor with ``SEC( !! 142 3. change of the report descriptor with ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` in libbpf 160 ``SEC("struct_ops.s/hid_rdesc_fixup")`` in << 161 143 162 A ``hid_device_event`` is calling a BPF progra !! 144 A ``hid_bpf_device_event`` is calling a BPF program when an event is received from 163 the device. Thus we are in IRQ context and can 145 the device. Thus we are in IRQ context and can act on the data or notify userspace. 164 And given that we are in IRQ context, we can n 146 And given that we are in IRQ context, we can not talk back to the device. 165 147 166 A ``syscall`` means that userspace called the 148 A ``syscall`` means that userspace called the syscall ``BPF_PROG_RUN`` facility. 167 This time, we can do any operations allowed by 149 This time, we can do any operations allowed by HID-BPF, and talking to the device is 168 allowed. 150 allowed. 169 151 170 Last, ``hid_rdesc_fixup`` is different from th !! 152 Last, ``hid_bpf_rdesc_fixup`` is different from the others as there can be only one 171 BPF program of this type. This is called on `` 153 BPF program of this type. This is called on ``probe`` from the driver and allows to 172 change the report descriptor from the BPF prog !! 154 change the report descriptor from the BPF program. Once a ``hid_bpf_rdesc_fixup`` 173 program has been loaded, it is not possible to 155 program has been loaded, it is not possible to overwrite it unless the program which 174 inserted it allows us by pinning the program a 156 inserted it allows us by pinning the program and closing all of its fds pointing to it. 175 157 176 Note that ``hid_rdesc_fixup`` can be declared << 177 << 178 << 179 Developer API: 158 Developer API: 180 ============== 159 ============== 181 160 182 Available ``struct_ops`` for HID-BPF: << 183 ------------------------------------- << 184 << 185 .. kernel-doc:: include/linux/hid_bpf.h << 186 :identifiers: hid_bpf_ops << 187 << 188 << 189 User API data structures available in programs 161 User API data structures available in programs: 190 ---------------------------------------------- 162 ----------------------------------------------- 191 163 192 .. kernel-doc:: include/linux/hid_bpf.h 164 .. kernel-doc:: include/linux/hid_bpf.h 193 :identifiers: hid_bpf_ctx << 194 165 195 Available API that can be used in all HID-BPF !! 166 Available tracing functions to attach a HID-BPF program: 196 ---------------------------------------------- !! 167 -------------------------------------------------------- >> 168 >> 169 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c >> 170 :functions: hid_bpf_device_event hid_bpf_rdesc_fixup >> 171 >> 172 Available API that can be used in all HID-BPF programs: >> 173 ------------------------------------------------------- 197 174 198 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispat 175 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 199 :identifiers: hid_bpf_get_data !! 176 :functions: hid_bpf_get_data 200 177 201 Available API that can be used in syscall HID- !! 178 Available API that can be used in syscall HID-BPF programs: 202 ---------------------------------------------- !! 179 ----------------------------------------------------------- 203 180 204 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispat 181 .. kernel-doc:: drivers/hid/bpf/hid_bpf_dispatch.c 205 :identifiers: hid_bpf_hw_request hid_bpf_hw !! 182 :functions: hid_bpf_attach_prog hid_bpf_hw_request hid_bpf_allocate_context hid_bpf_release_context 206 183 207 General overview of a HID-BPF program 184 General overview of a HID-BPF program 208 ===================================== 185 ===================================== 209 186 210 Accessing the data attached to the context 187 Accessing the data attached to the context 211 ------------------------------------------ 188 ------------------------------------------ 212 189 213 The ``struct hid_bpf_ctx`` doesn't export the 190 The ``struct hid_bpf_ctx`` doesn't export the ``data`` fields directly and to access 214 it, a bpf program needs to first call :c:func: 191 it, a bpf program needs to first call :c:func:`hid_bpf_get_data`. 215 192 216 ``offset`` can be any integer, but ``size`` ne 193 ``offset`` can be any integer, but ``size`` needs to be constant, known at compile 217 time. 194 time. 218 195 219 This allows the following: 196 This allows the following: 220 197 221 1. for a given device, if we know that the rep 198 1. for a given device, if we know that the report length will always be of a certain value, 222 we can request the ``data`` pointer to poin 199 we can request the ``data`` pointer to point at the full report length. 223 200 224 The kernel will ensure we are using a corre 201 The kernel will ensure we are using a correct size and offset and eBPF will ensure 225 the code will not attempt to read or write 202 the code will not attempt to read or write outside of the boundaries:: 226 203 227 __u8 *data = hid_bpf_get_data(ctx, 0 /* o 204 __u8 *data = hid_bpf_get_data(ctx, 0 /* offset */, 256 /* size */); 228 205 229 if (!data) 206 if (!data) 230 return 0; /* ensure data is correct, 207 return 0; /* ensure data is correct, now the verifier knows we 231 * have 256 bytes available 208 * have 256 bytes available */ 232 209 233 bpf_printk("hello world: %02x %02x %02x", 210 bpf_printk("hello world: %02x %02x %02x", data[0], data[128], data[255]); 234 211 235 2. if the report length is variable, but we kn 212 2. if the report length is variable, but we know the value of ``X`` is always a 16-bit 236 integer, we can then have a pointer to that 213 integer, we can then have a pointer to that value only:: 237 214 238 __u16 *x = hid_bpf_get_data(ctx, offset, 215 __u16 *x = hid_bpf_get_data(ctx, offset, sizeof(*x)); 239 216 240 if (!x) 217 if (!x) 241 return 0; /* something went wrong */ 218 return 0; /* something went wrong */ 242 219 243 *x += 1; /* increment X by one */ 220 *x += 1; /* increment X by one */ 244 221 245 Effect of a HID-BPF program 222 Effect of a HID-BPF program 246 --------------------------- 223 --------------------------- 247 224 248 For all HID-BPF attachment types except for :c !! 225 For all HID-BPF attachment types except for :c:func:`hid_bpf_rdesc_fixup`, several eBPF 249 programs can be attached to the same device. I !! 226 programs can be attached to the same device. 250 :c:func:`hid_rdesc_fixup` while another is alr !! 227 251 kernel will return `-EINVAL` when attaching th !! 228 Unless ``HID_BPF_FLAG_INSERT_HEAD`` is added to the flags while attaching the 252 !! 229 program, the new program is appended at the end of the list. 253 Unless ``BPF_F_BEFORE`` is added to the flags !! 230 ``HID_BPF_FLAG_INSERT_HEAD`` will insert the new program at the beginning of the 254 program is appended at the end of the list. !! 231 list which is useful for e.g. tracing where we need to get the unprocessed events 255 ``BPF_F_BEFORE`` will insert the new program a !! 232 from the device. 256 useful for e.g. tracing where we need to get t << 257 233 258 Note that if there are multiple programs using !! 234 Note that if there are multiple programs using the ``HID_BPF_FLAG_INSERT_HEAD`` flag, 259 only the most recently loaded one is actually 235 only the most recently loaded one is actually the first in the list. 260 236 261 ``SEC("struct_ops/hid_device_event")`` !! 237 ``SEC("fmod_ret/hid_bpf_device_event")`` 262 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !! 238 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 263 239 264 Whenever a matching event is raised, the eBPF 240 Whenever a matching event is raised, the eBPF programs are called one after the other 265 and are working on the same data buffer. 241 and are working on the same data buffer. 266 242 267 If a program changes the data associated with 243 If a program changes the data associated with the context, the next one will see 268 the modified data but it will have *no* idea o 244 the modified data but it will have *no* idea of what the original data was. 269 245 270 Once all the programs are run and return ``0`` 246 Once all the programs are run and return ``0`` or a positive value, the rest of the 271 HID stack will work on the modified data, with 247 HID stack will work on the modified data, with the ``size`` field of the last hid_bpf_ctx 272 being the new size of the input stream of data 248 being the new size of the input stream of data. 273 249 274 A BPF program returning a negative error disca 250 A BPF program returning a negative error discards the event, i.e. this event will not be 275 processed by the HID stack. Clients (hidraw, i 251 processed by the HID stack. Clients (hidraw, input, LEDs) will **not** see this event. 276 252 277 ``SEC("syscall")`` 253 ``SEC("syscall")`` 278 ~~~~~~~~~~~~~~~~~~ 254 ~~~~~~~~~~~~~~~~~~ 279 255 280 ``syscall`` are not attached to a given device 256 ``syscall`` are not attached to a given device. To tell which device we are working 281 with, userspace needs to refer to the device b 257 with, userspace needs to refer to the device by its unique system id (the last 4 numbers 282 in the sysfs path: ``/sys/bus/hid/devices/xxxx 258 in the sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``). 283 259 284 To retrieve a context associated with the devi 260 To retrieve a context associated with the device, the program must call 285 hid_bpf_allocate_context() and must release it !! 261 :c:func:`hid_bpf_allocate_context` and must release it with :c:func:`hid_bpf_release_context` 286 before returning. 262 before returning. 287 Once the context is retrieved, one can also re 263 Once the context is retrieved, one can also request a pointer to kernel memory with 288 hid_bpf_get_data(). This memory is big enough !! 264 :c:func:`hid_bpf_get_data`. This memory is big enough to support all input/output/feature 289 reports of the given device. 265 reports of the given device. 290 266 291 ``SEC("struct_ops/hid_rdesc_fixup")`` !! 267 ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` 292 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !! 268 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 293 269 294 The ``hid_rdesc_fixup`` program works in a sim !! 270 The ``hid_bpf_rdesc_fixup`` program works in a similar manner to 295 of ``struct hid_driver``. !! 271 ``.report_fixup`` of ``struct hid_driver``. 296 272 297 When the device is probed, the kernel sets the 273 When the device is probed, the kernel sets the data buffer of the context with the 298 content of the report descriptor. The memory a 274 content of the report descriptor. The memory associated with that buffer is 299 ``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). 275 ``HID_MAX_DESCRIPTOR_SIZE`` (currently 4kB). 300 276 301 The eBPF program can modify the data buffer at 277 The eBPF program can modify the data buffer at-will and the kernel uses the 302 modified content and size as the report descri 278 modified content and size as the report descriptor. 303 279 304 Whenever a struct_ops containing a ``SEC("stru !! 280 Whenever a ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is attached (if no 305 is attached (if no program was attached before !! 281 program was attached before), the kernel immediately disconnects the HID device 306 the HID device and does a reprobe. !! 282 and does a reprobe. 307 283 308 In the same way, when this struct_ops is detac !! 284 In the same way, when the ``SEC("fmod_ret/hid_bpf_rdesc_fixup")`` program is 309 on the device. !! 285 detached, the kernel issues a disconnect on the device. 310 286 311 There is no ``detach`` facility in HID-BPF. De 287 There is no ``detach`` facility in HID-BPF. Detaching a program happens when 312 all the user space file descriptors pointing a !! 288 all the user space file descriptors pointing at a program are closed. 313 Thus, if we need to replace a report descripto 289 Thus, if we need to replace a report descriptor fixup, some cooperation is 314 required from the owner of the original report 290 required from the owner of the original report descriptor fixup. 315 The previous owner will likely pin the struct_ !! 291 The previous owner will likely pin the program in the bpffs, and we can then 316 replace it through normal bpf operations. 292 replace it through normal bpf operations. 317 293 318 Attaching a bpf program to a device 294 Attaching a bpf program to a device 319 =================================== 295 =================================== 320 296 321 We now use standard struct_ops attachment thro !! 297 ``libbpf`` does not export any helper to attach a HID-BPF program. 322 But given that we need to attach a struct_ops !! 298 Users need to use a dedicated ``syscall`` program which will call 323 must set ``hid_id`` in the struct_ops map befo !! 299 ``hid_bpf_attach_prog(hid_id, program_fd, flags)``. 324 300 325 ``hid_id`` is the unique system ID of the HID 301 ``hid_id`` is the unique system ID of the HID device (the last 4 numbers in the 326 sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:z 302 sysfs path: ``/sys/bus/hid/devices/xxxx:yyyy:zzzz:0000``) 327 303 328 One can also set ``flags``, which is of type ` !! 304 ``progam_fd`` is the opened file descriptor of the program to attach. >> 305 >> 306 ``flags`` is of type ``enum hid_bpf_attach_flags``. 329 307 330 We can not rely on hidraw to bind a BPF progra 308 We can not rely on hidraw to bind a BPF program to a HID device. hidraw is an 331 artefact of the processing of the HID device, 309 artefact of the processing of the HID device, and is not stable. Some drivers 332 even disable it, so that removes the tracing c 310 even disable it, so that removes the tracing capabilities on those devices 333 (where it is interesting to get the non-hidraw 311 (where it is interesting to get the non-hidraw traces). 334 312 335 On the other hand, the ``hid_id`` is stable fo 313 On the other hand, the ``hid_id`` is stable for the entire life of the HID device, 336 even if we change its report descriptor. 314 even if we change its report descriptor. 337 315 338 Given that hidraw is not stable when the devic 316 Given that hidraw is not stable when the device disconnects/reconnects, we recommend 339 accessing the current report descriptor of the 317 accessing the current report descriptor of the device through the sysfs. 340 This is available at ``/sys/bus/hid/devices/BU 318 This is available at ``/sys/bus/hid/devices/BUS:VID:PID.000N/report_descriptor`` as a 341 binary stream. 319 binary stream. 342 320 343 Parsing the report descriptor is the responsib 321 Parsing the report descriptor is the responsibility of the BPF programmer or the userspace 344 component that loads the eBPF program. 322 component that loads the eBPF program. 345 323 346 An (almost) complete example of a BPF enhanced 324 An (almost) complete example of a BPF enhanced HID device 347 ============================================== 325 ========================================================= 348 326 349 *Foreword: for most parts, this could be imple 327 *Foreword: for most parts, this could be implemented as a kernel driver* 350 328 351 Let's imagine we have a new tablet device that 329 Let's imagine we have a new tablet device that has some haptic capabilities 352 to simulate the surface the user is scratching 330 to simulate the surface the user is scratching on. This device would also have 353 a specific 3 positions switch to toggle betwee 331 a specific 3 positions switch to toggle between *pencil on paper*, *cray on a wall* 354 and *brush on a painting canvas*. To make thin 332 and *brush on a painting canvas*. To make things even better, we can control the 355 physical position of the switch through a feat 333 physical position of the switch through a feature report. 356 334 357 And of course, the switch is relying on some u 335 And of course, the switch is relying on some userspace component to control the 358 haptic feature of the device itself. 336 haptic feature of the device itself. 359 337 360 Filtering events 338 Filtering events 361 ---------------- 339 ---------------- 362 340 363 The first step consists in filtering events fr 341 The first step consists in filtering events from the device. Given that the switch 364 position is actually reported in the flow of t 342 position is actually reported in the flow of the pen events, using hidraw to implement 365 that filtering would mean that we wake up user 343 that filtering would mean that we wake up userspace for every single event. 366 344 367 This is OK for libinput, but having an externa 345 This is OK for libinput, but having an external library that is just interested in 368 one byte in the report is less than ideal. 346 one byte in the report is less than ideal. 369 347 370 For that, we can create a basic skeleton for o 348 For that, we can create a basic skeleton for our BPF program:: 371 349 372 #include "vmlinux.h" 350 #include "vmlinux.h" 373 #include <bpf/bpf_helpers.h> 351 #include <bpf/bpf_helpers.h> 374 #include <bpf/bpf_tracing.h> 352 #include <bpf/bpf_tracing.h> 375 353 376 /* HID programs need to be GPL */ 354 /* HID programs need to be GPL */ 377 char _license[] SEC("license") = "GPL"; 355 char _license[] SEC("license") = "GPL"; 378 356 379 /* HID-BPF kfunc API definitions */ 357 /* HID-BPF kfunc API definitions */ 380 extern __u8 *hid_bpf_get_data(struct hid_bpf 358 extern __u8 *hid_bpf_get_data(struct hid_bpf_ctx *ctx, 381 unsigned int off 359 unsigned int offset, 382 const size_t __s 360 const size_t __sz) __ksym; >> 361 extern int hid_bpf_attach_prog(unsigned int hid_id, int prog_fd, u32 flags) __ksym; 383 362 384 struct { 363 struct { 385 __uint(type, BPF_MAP_TYPE_RINGBUF); 364 __uint(type, BPF_MAP_TYPE_RINGBUF); 386 __uint(max_entries, 4096 * 64); 365 __uint(max_entries, 4096 * 64); 387 } ringbuf SEC(".maps"); 366 } ringbuf SEC(".maps"); 388 367 >> 368 struct attach_prog_args { >> 369 int prog_fd; >> 370 unsigned int hid; >> 371 unsigned int flags; >> 372 int retval; >> 373 }; >> 374 >> 375 SEC("syscall") >> 376 int attach_prog(struct attach_prog_args *ctx) >> 377 { >> 378 ctx->retval = hid_bpf_attach_prog(ctx->hid, >> 379 ctx->prog_fd, >> 380 ctx->flags); >> 381 return 0; >> 382 } >> 383 389 __u8 current_value = 0; 384 __u8 current_value = 0; 390 385 391 SEC("struct_ops/hid_device_event") !! 386 SEC("?fmod_ret/hid_bpf_device_event") 392 int BPF_PROG(filter_switch, struct hid_bpf_c 387 int BPF_PROG(filter_switch, struct hid_bpf_ctx *hid_ctx) 393 { 388 { 394 __u8 *data = hid_bpf_get_data(hid_ctx, 389 __u8 *data = hid_bpf_get_data(hid_ctx, 0 /* offset */, 192 /* size */); 395 __u8 *buf; 390 __u8 *buf; 396 391 397 if (!data) 392 if (!data) 398 return 0; /* EPERM check */ 393 return 0; /* EPERM check */ 399 394 400 if (current_value != data[152]) { 395 if (current_value != data[152]) { 401 buf = bpf_ringbuf_reserve(&rin 396 buf = bpf_ringbuf_reserve(&ringbuf, 1, 0); 402 if (!buf) 397 if (!buf) 403 return 0; 398 return 0; 404 399 405 *buf = data[152]; 400 *buf = data[152]; 406 401 407 bpf_ringbuf_commit(buf, 0); 402 bpf_ringbuf_commit(buf, 0); 408 403 409 current_value = data[152]; 404 current_value = data[152]; 410 } 405 } 411 406 412 return 0; 407 return 0; 413 } 408 } 414 409 415 SEC(".struct_ops.link") !! 410 To attach ``filter_switch``, userspace needs to call the ``attach_prog`` syscall 416 struct hid_bpf_ops haptic_tablet = { !! 411 program first:: 417 .hid_device_event = (void *)filter_swi << 418 }; << 419 << 420 << 421 To attach ``haptic_tablet``, userspace needs t << 422 412 423 static int attach_filter(struct hid *hid_ske 413 static int attach_filter(struct hid *hid_skel, int hid_id) 424 { 414 { 425 int err, link_fd; !! 415 int err, prog_fd; 426 !! 416 int ret = -1; 427 hid_skel->struct_ops.haptic_tablet->hi !! 417 struct attach_prog_args args = { 428 err = hid__load(skel); !! 418 .hid = hid_id, 429 if (err) !! 419 }; 430 return err; !! 420 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 431 !! 421 .ctx_in = &args, 432 link_fd = bpf_map__attach_struct_ops(h !! 422 .ctx_size_in = sizeof(args), 433 if (!link_fd) { !! 423 ); 434 fprintf(stderr, "can not attac !! 424 435 return -1; !! 425 args.prog_fd = bpf_program__fd(hid_skel->progs.filter_switch); 436 } !! 426 >> 427 prog_fd = bpf_program__fd(hid_skel->progs.attach_prog); >> 428 >> 429 err = bpf_prog_test_run_opts(prog_fd, &tattrs); >> 430 if (err) >> 431 return err; 437 432 438 return link_fd; /* the fd of the creat !! 433 return args.retval; /* the fd of the created bpf_link */ 439 } 434 } 440 435 441 Our userspace program can now listen to notifi 436 Our userspace program can now listen to notifications on the ring buffer, and 442 is awaken only when the value changes. 437 is awaken only when the value changes. 443 438 444 When the userspace program doesn't need to lis 439 When the userspace program doesn't need to listen to events anymore, it can just 445 close the returned bpf link from :c:func:`atta !! 440 close the returned fd from :c:func:`attach_filter`, which will tell the kernel to 446 detach the program from the HID device. 441 detach the program from the HID device. 447 442 448 Of course, in other use cases, the userspace p 443 Of course, in other use cases, the userspace program can also pin the fd to the 449 BPF filesystem through a call to :c:func:`bpf_ 444 BPF filesystem through a call to :c:func:`bpf_obj_pin`, as with any bpf_link. 450 445 451 Controlling the device 446 Controlling the device 452 ---------------------- 447 ---------------------- 453 448 454 To be able to change the haptic feedback from 449 To be able to change the haptic feedback from the tablet, the userspace program 455 needs to emit a feature report on the device i 450 needs to emit a feature report on the device itself. 456 451 457 Instead of using hidraw for that, we can creat 452 Instead of using hidraw for that, we can create a ``SEC("syscall")`` program 458 that talks to the device:: 453 that talks to the device:: 459 454 460 /* some more HID-BPF kfunc API definitions * 455 /* some more HID-BPF kfunc API definitions */ 461 extern struct hid_bpf_ctx *hid_bpf_allocate_ 456 extern struct hid_bpf_ctx *hid_bpf_allocate_context(unsigned int hid_id) __ksym; 462 extern void hid_bpf_release_context(struct h 457 extern void hid_bpf_release_context(struct hid_bpf_ctx *ctx) __ksym; 463 extern int hid_bpf_hw_request(struct hid_bpf 458 extern int hid_bpf_hw_request(struct hid_bpf_ctx *ctx, 464 __u8* data, 459 __u8* data, 465 size_t len, 460 size_t len, 466 enum hid_report_ 461 enum hid_report_type type, 467 enum hid_class_r 462 enum hid_class_request reqtype) __ksym; 468 463 469 464 470 struct hid_send_haptics_args { 465 struct hid_send_haptics_args { 471 /* data needs to come at offset 0 so w 466 /* data needs to come at offset 0 so we can do a memcpy into it */ 472 __u8 data[10]; 467 __u8 data[10]; 473 unsigned int hid; 468 unsigned int hid; 474 }; 469 }; 475 470 476 SEC("syscall") 471 SEC("syscall") 477 int send_haptic(struct hid_send_haptics_args 472 int send_haptic(struct hid_send_haptics_args *args) 478 { 473 { 479 struct hid_bpf_ctx *ctx; 474 struct hid_bpf_ctx *ctx; 480 int ret = 0; 475 int ret = 0; 481 476 482 ctx = hid_bpf_allocate_context(args->h 477 ctx = hid_bpf_allocate_context(args->hid); 483 if (!ctx) 478 if (!ctx) 484 return 0; /* EPERM check */ 479 return 0; /* EPERM check */ 485 480 486 ret = hid_bpf_hw_request(ctx, 481 ret = hid_bpf_hw_request(ctx, 487 args->data, 482 args->data, 488 10, 483 10, 489 HID_FEATURE_R 484 HID_FEATURE_REPORT, 490 HID_REQ_SET_R 485 HID_REQ_SET_REPORT); 491 486 492 hid_bpf_release_context(ctx); 487 hid_bpf_release_context(ctx); 493 488 494 return ret; 489 return ret; 495 } 490 } 496 491 497 And then userspace needs to call that program 492 And then userspace needs to call that program directly:: 498 493 499 static int set_haptic(struct hid *hid_skel, 494 static int set_haptic(struct hid *hid_skel, int hid_id, __u8 haptic_value) 500 { 495 { 501 int err, prog_fd; 496 int err, prog_fd; 502 int ret = -1; 497 int ret = -1; 503 struct hid_send_haptics_args args = { 498 struct hid_send_haptics_args args = { 504 .hid = hid_id, 499 .hid = hid_id, 505 }; 500 }; 506 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, 501 DECLARE_LIBBPF_OPTS(bpf_test_run_opts, tattrs, 507 .ctx_in = &args, 502 .ctx_in = &args, 508 .ctx_size_in = sizeof(args), 503 .ctx_size_in = sizeof(args), 509 ); 504 ); 510 505 511 args.data[0] = 0x02; /* report ID of t 506 args.data[0] = 0x02; /* report ID of the feature on our device */ 512 args.data[1] = haptic_value; 507 args.data[1] = haptic_value; 513 508 514 prog_fd = bpf_program__fd(hid_skel->pr 509 prog_fd = bpf_program__fd(hid_skel->progs.set_haptic); 515 510 516 err = bpf_prog_test_run_opts(prog_fd, 511 err = bpf_prog_test_run_opts(prog_fd, &tattrs); 517 return err; 512 return err; 518 } 513 } 519 514 520 Now our userspace program is aware of the hapt 515 Now our userspace program is aware of the haptic state and can control it. The 521 program could make this state further availabl 516 program could make this state further available to other userspace programs 522 (e.g. via a DBus API). 517 (e.g. via a DBus API). 523 518 524 The interesting bit here is that we did not cr 519 The interesting bit here is that we did not created a new kernel API for this. 525 Which means that if there is a bug in our impl 520 Which means that if there is a bug in our implementation, we can change the 526 interface with the kernel at-will, because the 521 interface with the kernel at-will, because the userspace application is 527 responsible for its own usage. 522 responsible for its own usage.
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.