xref: /linux/drivers/hid/bpf/progs/Huion__KeydialK20-Bluetooth.bpf.c (revision d97e7d7c304f87419921f740743f7baa99f40539)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2024 Red Hat, Inc
3  */
4 
5 #include "vmlinux.h"
6 #include "hid_bpf.h"
7 #include "hid_bpf_helpers.h"
8 #include "hid_report_helpers.h"
9 #include <bpf/bpf_tracing.h>
10 
11 #define VID_HUION 0x256C
12 #define PID_KEYDIAL_K20_BLUETOOTH 0x8251
13 
14 HID_BPF_CONFIG(
15 	HID_DEVICE(BUS_BLUETOOTH, HID_GROUP_GENERIC, VID_HUION, PID_KEYDIAL_K20_BLUETOOTH),
16 );
17 
18 /* This is the same device as in 0010-Huion__KeydialK20 but connected via Bluetooth.
19  * It does not need (to support?) switching to a vendor mode so we just modify the
20  * existing mode.
21  *
22  * By default it exports two hidraw nodes, only the second one sends events.
23  *
24  * This is the first hidraw node which we disable:
25  *
26  * # Keydial mini-050
27  * # Report descriptor length: 114 bytes
28  * #   Bytes                          // Field Name                              Offset
29  * # ----------------------------------------------------------------------------------
30  * # �� 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
31  * # �� 0x09, 0x0e,                    // Usage (System Multi-Axis Controller)      2
32  * #   0xa1, 0x01,                    // Collection (Application)                  4
33  * # ┅ 0x85, 0x03,                    //   Report ID (3)                           6
34  * # �� 0x05, 0x0d,                    //   Usage Page (Digitizers)                 8
35  * #   0x75, 0x08,                    //   Report Size (8)                         10
36  * #   0x95, 0x01,                    //   Report Count (1)                        12
37  * # ┇ 0x81, 0x01,                    //   Input (Cnst,Arr,Abs)                    14
38  * # �� 0x09, 0x21,                    //   Usage (Puck)                            16
39  * #   0xa1, 0x02,                    //   Collection (Logical)                    18
40  * #   0x15, 0x00,                    //     Logical Minimum (0)                   20
41  * #   0x25, 0x01,                    //     Logical Maximum (1)                   22
42  * #   0x75, 0x01,                    //     Report Size (1)                       24
43  * #   0x95, 0x01,                    //     Report Count (1)                      26
44  * #   0xa1, 0x00,                    //     Collection (Physical)                 28
45  * # �� 0x05, 0x09,                    //       Usage Page (Button)                 30
46  * # �� 0x09, 0x01,                    //       Usage (Button 1)                    32
47  * # ┇ 0x81, 0x02,                    //       Input (Data,Var,Abs)                34
48  * # �� 0x05, 0x0d,                    //       Usage Page (Digitizers)             36
49  * # �� 0x09, 0x33,                    //       Usage (Touch)                       38
50  * # ┇ 0x81, 0x02,                    //       Input (Data,Var,Abs)                40
51  * #   0x95, 0x06,                    //       Report Count (6)                    42
52  * # ┇ 0x81, 0x03,                    //       Input (Cnst,Var,Abs)                44
53  * #   0xa1, 0x02,                    //       Collection (Logical)                46
54  * # �� 0x05, 0x01,                    //         Usage Page (Generic Desktop)      48
55  * # �� 0x09, 0x37,                    //         Usage (Dial)                      50
56  * #   0x16, 0x00, 0x80,              //         Logical Minimum (32768)           52
57  * #   0x26, 0xff, 0x7f,              //         Logical Maximum (32767)           55
58  * #   0x75, 0x10,                    //         Report Size (16)                  58
59  * #   0x95, 0x01,                    //         Report Count (1)                  60
60  * # ┇ 0x81, 0x06,                    //         Input (Data,Var,Rel)              62
61  * #   0x35, 0x00,                    //         Physical Minimum (0)              64
62  * #   0x46, 0x10, 0x0e,              //         Physical Maximum (3600)           66
63  * #   0x15, 0x00,                    //         Logical Minimum (0)               69
64  * #   0x26, 0x10, 0x0e,              //         Logical Maximum (3600)            71
65  * # �� 0x09, 0x48,                    //         Usage (Resolution Multiplier)     74
66  * # ║ 0xb1, 0x02,                    //         Feature (Data,Var,Abs)            76
67  * #   0x45, 0x00,                    //         Physical Maximum (0)              78
68  * #   0xc0,                          //       End Collection                      80
69  * #   0x75, 0x08,                    //       Report Size (8)                     81
70  * #   0x95, 0x01,                    //       Report Count (1)                    83
71  * # ┇ 0x81, 0x01,                    //       Input (Cnst,Arr,Abs)                85
72  * #   0x75, 0x08,                    //       Report Size (8)                     87
73  * #   0x95, 0x01,                    //       Report Count (1)                    89
74  * # ┇ 0x81, 0x01,                    //       Input (Cnst,Arr,Abs)                91
75  * #   0x75, 0x08,                    //       Report Size (8)                     93
76  * #   0x95, 0x01,                    //       Report Count (1)                    95
77  * # ┇ 0x81, 0x01,                    //       Input (Cnst,Arr,Abs)                97
78  * #   0x75, 0x08,                    //       Report Size (8)                     99
79  * #   0x95, 0x01,                    //       Report Count (1)                    101
80  * # ┇ 0x81, 0x01,                    //       Input (Cnst,Arr,Abs)                103
81  * #   0x75, 0x08,                    //       Report Size (8)                     105
82  * #   0x95, 0x01,                    //       Report Count (1)                    107
83  * # ┇ 0x81, 0x01,                    //       Input (Cnst,Arr,Abs)                109
84  * #   0xc0,                          //     End Collection                        111
85  * #   0xc0,                          //   End Collection                          112
86  * #   0xc0,                          // End Collection                            113
87  * R: 114 05 01 09 0e a1 01 85 03 05 0d 75 08 95 01 81 01 09 21 a1 02 15 00 25 01 75 01 95 01 a1 00 05 09 09 01 81 02 05 0d 09 33 81 02 95 06 81 03 a1 02 05 01 09 37 16 00 80 26 ff 7f 75 10 95 01 81 06 35 00 46 10 0e 15 00 26 10 0e 09 48 b1 02 45 00 c0 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 75 08 95 01 81 01 c0 c0 c0
88  * N: Keydial mini-050
89  * I: 5 256c 8251
90  *
91  * The second hidraw node is what sends events:
92  *
93  * # Keydial mini-050
94  * # Report descriptor length: 160 bytes
95  * #   Bytes                          // Field Name                              Offset
96  * # ----------------------------------------------------------------------------------
97  * # �� 0x05, 0x01,                    // Usage Page (Generic Desktop)              0
98  * # �� 0x09, 0x06,                    // Usage (Keyboard)                          2
99  * #   0xa1, 0x01,                    // Collection (Application)                  4
100  * # ┅ 0x85, 0x01,                    //   Report ID (1)                           6
101  * # �� 0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            8
102  * # �� 0x19, 0xe0,                    //   Usage Minimum (224)                     10
103  * # �� 0x29, 0xe7,                    //   Usage Maximum (231)                     12
104  * #   0x15, 0x00,                    //   Logical Minimum (0)                     14
105  * #   0x25, 0x01,                    //   Logical Maximum (1)                     16
106  * #   0x75, 0x01,                    //   Report Size (1)                         18
107  * #   0x95, 0x08,                    //   Report Count (8)                        20
108  * # ┇ 0x81, 0x02,                    //   Input (Data,Var,Abs)                    22
109  * #   0x95, 0x01,                    //   Report Count (1)                        24
110  * #   0x75, 0x08,                    //   Report Size (8)                         26
111  * # ┇ 0x81, 0x01,                    //   Input (Cnst,Arr,Abs)                    28
112  * #   0x95, 0x05,                    //   Report Count (5)                        30
113  * #   0x75, 0x01,                    //   Report Size (1)                         32
114  * # �� 0x05, 0x08,                    //   Usage Page (LED)                        34
115  * # �� 0x19, 0x01,                    //   Usage Minimum (1)                       36
116  * # �� 0x29, 0x05,                    //   Usage Maximum (5)                       38
117  * # ┊ 0x91, 0x02,                    //   Output (Data,Var,Abs)                   40
118  * #   0x95, 0x01,                    //   Report Count (1)                        42
119  * #   0x75, 0x03,                    //   Report Size (3)                         44
120  * # ┊ 0x91, 0x01,                    //   Output (Cnst,Arr,Abs)                   46
121  * #   0x95, 0x06,                    //   Report Count (6)                        48
122  * #   0x75, 0x08,                    //   Report Size (8)                         50
123  * #   0x15, 0x00,                    //   Logical Minimum (0)                     52
124  * #   0x25, 0xf1,                    //   Logical Maximum (241)                   54
125  * # �� 0x05, 0x07,                    //   Usage Page (Keyboard/Keypad)            56
126  * # �� 0x19, 0x00,                    //   Usage Minimum (0)                       58
127  * # �� 0x29, 0xf1,                    //   Usage Maximum (241)                     60
128  * # ┇ 0x81, 0x00,                    //   Input (Data,Arr,Abs)                    62
129  * #   0xc0,                          // End Collection                            64
130  * # �� 0x05, 0x0c,                    // Usage Page (Consumer)                     65
131  * # �� 0x09, 0x01,                    // Usage (Consumer Control)                  67
132  * #   0xa1, 0x01,                    // Collection (Application)                  69
133  * # ┅ 0x85, 0x02,                    //   Report ID (2)                           71
134  * # �� 0x05, 0x0c,                    //   Usage Page (Consumer)                   73
135  * # �� 0x19, 0x00,                    //   Usage Minimum (0)                       75
136  * # �� 0x2a, 0x80, 0x03,              //   Usage Maximum (896)                     77
137  * #   0x15, 0x00,                    //   Logical Minimum (0)                     80
138  * #   0x26, 0x80, 0x03,              //   Logical Maximum (896)                   82
139  * #   0x75, 0x10,                    //   Report Size (16)                        85
140  * #   0x95, 0x01,                    //   Report Count (1)                        87
141  * # ┇ 0x81, 0x00,                    //   Input (Data,Arr,Abs)                    89
142  * #   0xc0,                          // End Collection                            91
143  * # �� 0x05, 0x01,                    // Usage Page (Generic Desktop)              92
144  * # �� 0x09, 0x02,                    // Usage (Mouse)                             94
145  * #   0xa1, 0x01,                    // Collection (Application)                  96
146  * # �� 0x09, 0x01,                    //   Usage (Pointer)                         98
147  * # ┅ 0x85, 0x05,                    //   Report ID (5)                           100
148  * #   0xa1, 0x00,                    //   Collection (Physical)                   102
149  * # �� 0x05, 0x09,                    //     Usage Page (Button)                   104
150  * # �� 0x19, 0x01,                    //     Usage Minimum (1)                     106
151  * # �� 0x29, 0x05,                    //     Usage Maximum (5)                     108
152  * #   0x15, 0x00,                    //     Logical Minimum (0)                   110
153  * #   0x25, 0x01,                    //     Logical Maximum (1)                   112
154  * #   0x95, 0x05,                    //     Report Count (5)                      114
155  * #   0x75, 0x01,                    //     Report Size (1)                       116
156  * # ┇ 0x81, 0x02,                    //     Input (Data,Var,Abs)                  118
157  * #   0x95, 0x01,                    //     Report Count (1)                      120
158  * #   0x75, 0x03,                    //     Report Size (3)                       122
159  * # ┇ 0x81, 0x01,                    //     Input (Cnst,Arr,Abs)                  124
160  * # �� 0x05, 0x01,                    //     Usage Page (Generic Desktop)          126
161  * # �� 0x09, 0x30,                    //     Usage (X)                             128
162  * # �� 0x09, 0x31,                    //     Usage (Y)                             130
163  * #   0x16, 0x01, 0x80,              //     Logical Minimum (32769)               132
164  * #   0x26, 0xff, 0x7f,              //     Logical Maximum (32767)               135
165  * #   0x75, 0x10,                    //     Report Size (16)                      138
166  * #   0x95, 0x02,                    //     Report Count (2)                      140
167  * # ┇ 0x81, 0x06,                    //     Input (Data,Var,Rel)                  142
168  * # �� 0x05, 0x01,                    //     Usage Page (Generic Desktop)          144
169  * # �� 0x09, 0x38,                    //     Usage (Wheel)                         146
170  * #   0x15, 0x81,                    //     Logical Minimum (129)                 148
171  * #   0x25, 0x7f,                    //     Logical Maximum (127)                 150
172  * #   0x95, 0x01,                    //     Report Count (1)                      152
173  * #   0x75, 0x08,                    //     Report Size (8)                       154
174  * # ┇ 0x81, 0x06,                    //     Input (Data,Var,Rel)                  156
175  * #   0xc0,                          //   End Collection                          158
176  * #   0xc0,                          // End Collection                            159
177  * R: 160 05 01 09 06 a1 01 85 01 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 95 01 75 08 81 01 95 05 75 01 05 08 19 01 29 05 91 02 95 01 75 03 91 01 95 06 75 08 15 00 25 f1 05 07 19 00 29 f1 81 00 c0 05 0c 09 01 a1 01 85 02 05 0c 19 00 2a 80 03 15 00 26 80 03 75 10 95 01 81 00 c0 05 01 09 02 a1 01 09 01 85 05 a1 00 05 09 19 01 29 05 15 00 25 01 95 05 75 01 81 02 95 01 75 03 81 01 05 01 09 30 09 31 16 01 80 26 ff 7f 75 10 95 02 81 06 05 01 09 38 15 81 25 7f 95 01 75 08 81 06 c0 c0
178  * N: Keydial mini-050
179  * I: 5 256c 8251
180  * # Report descriptor:
181  * # ------- Input Report -------
182  * # ░ Report ID: 1
183  * # ░  | Report size: 72 bits
184  * # ░ Bit:    8       Usage: 0007/00e0: Keyboard/Keypad / Keyboard LeftControl      Logical Range:     0..=1
185  * # ░ Bit:    9       Usage: 0007/00e1: Keyboard/Keypad / Keyboard LeftShift        Logical Range:     0..=1
186  * # ░ Bit:   10       Usage: 0007/00e2: Keyboard/Keypad / Keyboard LeftAlt          Logical Range:     0..=1
187  * # ░ Bit:   11       Usage: 0007/00e3: Keyboard/Keypad / Keyboard Left GUI         Logical Range:     0..=1
188  * # ░ Bit:   12       Usage: 0007/00e4: Keyboard/Keypad / Keyboard RightControl     Logical Range:     0..=1
189  * # ░ Bit:   13       Usage: 0007/00e5: Keyboard/Keypad / Keyboard RightShift       Logical Range:     0..=1
190  * # ░ Bit:   14       Usage: 0007/00e6: Keyboard/Keypad / Keyboard RightAlt         Logical Range:     0..=1
191  * # ░ Bit:   15       Usage: 0007/00e7: Keyboard/Keypad / Keyboard Right GUI        Logical Range:     0..=1
192  * # ░ Bits:  16..=23  ######### Padding
193  * # ░ Bits:  24..=71  Usages:                                                       Logical Range:     0..=241
194  * # ░                 0007/0000: <unknown>
195  * # ░                 0007/0001: Keyboard/Keypad / ErrorRollOver
196  * # ░                 0007/0002: Keyboard/Keypad / POSTFail
197  * # ░                 0007/0003: Keyboard/Keypad / ErrorUndefined
198  * # ░                 0007/0004: Keyboard/Keypad / Keyboard A
199  * # ░                 ... use --full to see all usages
200  * # ------- Input Report -------
201  * # ▒ Report ID: 2
202  * # ▒  | Report size: 24 bits
203  * # ▒ Bits:   8..=23  Usages:                                                Logical Range:     0..=896
204  * # ▒                 000c/0000: <unknown>
205  * # ▒                 000c/0001: Consumer / Consumer Control
206  * # ▒                 000c/0002: Consumer / Numeric Key Pad
207  * # ▒                 000c/0003: Consumer / Programmable Buttons
208  * # ▒                 000c/0004: Consumer / Microphone
209  * # ▒                 ... use --full to see all usages
210  * # ------- Input Report -------
211  * # ▞ Report ID: 5
212  * # ▞  | Report size: 56 bits
213  * # ▞ Bit:    8       Usage: 0009/0001: Button / Button 1                           Logical Range:     0..=1
214  * # ▞ Bit:    9       Usage: 0009/0002: Button / Button 2                           Logical Range:     0..=1
215  * # ▞ Bit:   10       Usage: 0009/0003: Button / Button 3                           Logical Range:     0..=1
216  * # ▞ Bit:   11       Usage: 0009/0004: Button / Button 4                           Logical Range:     0..=1
217  * # ▞ Bit:   12       Usage: 0009/0005: Button / Button 5                           Logical Range:     0..=1
218  * # ▞ Bits:  13..=15  ######### Padding
219  * # ▞ Bits:  16..=31  Usage: 0001/0030: Generic Desktop / X                         Logical Range: 32769..=32767
220  * # ▞ Bits:  32..=47  Usage: 0001/0031: Generic Desktop / Y                         Logical Range: 32769..=32767
221  * # ▞ Bits:  48..=55  Usage: 0001/0038: Generic Desktop / Wheel                     Logical Range:   129..=127
222  * # ------- Output Report -------
223  * # ░ Report ID: 1
224  * # ░  | Report size: 16 bits
225  * # ░ Bit:    8       Usage: 0008/0001: LED / Num Lock                              Logical Range:     0..=1
226  * # ░ Bit:    9       Usage: 0008/0002: LED / Caps Lock                             Logical Range:     0..=1
227  * # ░ Bit:   10       Usage: 0008/0003: LED / Scroll Lock                           Logical Range:     0..=1
228  * # ░ Bit:   11       Usage: 0008/0004: LED / Compose                               Logical Range:     0..=1
229  * # ░ Bit:   12       Usage: 0008/0005: LED / Kana                                  Logical Range:     0..=1
230  * # ░ Bits:  13..=15  ######### Padding
231  * ##############################################################################
232  * # Event nodes:
233  * # - /dev/input/event12: "Keydial mini-050 Keyboard"
234  * # - /dev/input/event14: "Keydial mini-050 Mouse"
235  * ##############################################################################
236  * # Recorded events below in format:
237  * # E: <seconds>.<microseconds> <length-in-bytes> [bytes ...]
238  * #
239  *
240  * - Report ID 1 sends keyboard shortcuts when pressing the buttons, e.g.
241  *
242  * # ░  Report ID: 1 /
243  * # ░               Keyboard LeftControl:     0 |Keyboard LeftShift:     0 |Keyboard LeftAlt:     0 |Keyboard Left GUI:     0 |Keyboard RightControl:     0 |Keyboard RightShift:     0 |Keyboard RightAlt:     0 |Keyboard Right GUI:     0 |<8 bits padding> |0007/0000:     0| Keyboard K:    14| 0007/0000:     0| 0007/0000:     0| 0007/0000:     0| 0007/0000:     0
244  * E: 000000.000292 9 01 00 00 00 0e 00 00 00 00
245  *
246  * - Report ID 2 sends the button inside the wheel/dial thing
247  * # ▒  Report ID: 2 /
248  * # ▒               Play/Pause:   205
249  * E: 000134.347845 3 02 cd 00
250  * # ▒  Report ID: 2 /
251  * # ▒               000c/0000:     0
252  * E: 000134.444965 3 02 00 00
253  *
254  * - Report ID 5 sends the wheel relative events (always a double-event with the second as zero)
255  * # ▞  Report ID: 5 /
256  * # ▞               Button 1:     0 |Button 2:     0 |Button 3:     0 |Button 4:     0 |Button 5:     0 |<3 bits padding> |X:     0 |Y:     0 |Wheel:   255
257  * E: 000064.859915 7 05 00 00 00 00 00 ff
258  * # ▞  Report ID: 5 /
259  * # ▞               Button 1:     0 |Button 2:     0 |Button 3:     0 |Button 4:     0 |Button 5:     0 |<3 bits padding> |X:     0 |Y:     0 |Wheel:     0
260  * E: 000064.882009 7 05 00 00 00 00 00 00
261  */
262 
263 #define BT_PAD_REPORT_DESCRIPTOR_LENGTH 160
264 #define BT_PUCK_REPORT_DESCRIPTOR_LENGTH 114  // This one doesn't send events
265 #define BT_PAD_KBD_REPORT_ID 1
266 #define BT_PAD_CC_REPORT_ID 2
267 #define BT_PAD_MOUSE_REPORT_ID 5
268 #define BT_PAD_KBD_REPORT_LENGTH 9
269 #define BT_PAD_CC_REPORT_LENGTH 3
270 #define BT_PAD_MOUSE_REPORT_LENGTH 7
271 #define OUR_REPORT_ID 11 /* "randomly" picked report ID for our reports */
272 
273 __u32 last_button_state = 0;
274 
275 static const __u8 disabled_rdesc_puck[] = {
276 	FixedSizeVendorReport(BT_PUCK_REPORT_DESCRIPTOR_LENGTH)
277 };
278 
279 static const __u8 fixed_rdesc_pad[] = {
280 	UsagePage_GenericDesktop
281 	Usage_GD_Keypad
282 	CollectionApplication(
283 		// Byte 0
284 		ReportId(OUR_REPORT_ID)
285 		UsagePage_Digitizers
286 		Usage_Dig_TabletFunctionKeys
287 		CollectionPhysical(
288 			// Byte 1 is a button so we look like a tablet
289 			Usage_Dig_BarrelSwitch	 // BTN_STYLUS, needed so we get to be a tablet pad
290 			ReportCount(1)
291 			ReportSize(1)
292 			Input(Var|Abs)
293 			ReportCount(7) // Padding
294 			Input(Const)
295 			// Bytes 2/3 - x/y just exist so we get to be a tablet pad
296 			UsagePage_GenericDesktop
297 			Usage_GD_X
298 			Usage_GD_Y
299 			LogicalMinimum_i8(0x0)
300 			LogicalMaximum_i8(0x1)
301 			ReportCount(2)
302 			ReportSize(8)
303 			Input(Var|Abs)
304 			// Bytes 4-7 are the button state for 19 buttons + pad out to u32
305 			// We send the first 10 buttons as buttons 1-10 which is BTN_0 -> BTN_9
306 			UsagePage_Button
307 			UsageMinimum_i8(1)
308 			UsageMaximum_i8(10)
309 			LogicalMinimum_i8(0x0)
310 			LogicalMaximum_i8(0x1)
311 			ReportCount(10)
312 			ReportSize(1)
313 			Input(Var|Abs)
314 			// We send the other 9 buttons as buttons 0x31 and above -> BTN_A - BTN_TL2
315 			UsageMinimum_i8(0x31)
316 			UsageMaximum_i8(0x3a)
317 			ReportCount(9)
318 			ReportSize(1)
319 			Input(Var|Abs)
320 			ReportCount(13)
321 			ReportSize(1)
322 			Input(Const) // padding
323 			// Byte 8 is the wheel
324 			UsagePage_GenericDesktop
325 			Usage_GD_Wheel
326 			LogicalMinimum_i8(-1)
327 			LogicalMaximum_i8(1)
328 			ReportCount(1)
329 			ReportSize(8)
330 			Input(Var|Rel)
331 		)
332 		// Make sure we match our original report length
333 		FixedSizeVendorReport(BT_PAD_KBD_REPORT_LENGTH)
334 	)
335 };
336 
337 SEC(HID_BPF_RDESC_FIXUP)
338 int BPF_PROG(k20_bt_fix_rdesc, struct hid_bpf_ctx *hctx)
339 {
340 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
341 	__s32 rdesc_size = hctx->size;
342 
343 	if (!data)
344 		return 0; /* EPERM check */
345 
346 	if (rdesc_size == BT_PAD_REPORT_DESCRIPTOR_LENGTH) {
347 		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
348 		return sizeof(fixed_rdesc_pad);
349 	}
350 	if (rdesc_size == BT_PUCK_REPORT_DESCRIPTOR_LENGTH) {
351 		// This hidraw node doesn't send anything and can be ignored
352 		__builtin_memcpy(data, disabled_rdesc_puck, sizeof(disabled_rdesc_puck));
353 		return sizeof(disabled_rdesc_puck);
354 	}
355 
356 	return 0;
357 }
358 
359 SEC(HID_BPF_DEVICE_EVENT)
360 int BPF_PROG(k20_bt_fix_events, struct hid_bpf_ctx *hctx)
361 {
362 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 12 /* size */);
363 	struct pad_report {
364 		__u8 report_id;
365 		__u8 btn_stylus:1;
366 		__u8 pad:7;
367 		__u8 x;
368 		__u8 y;
369 		__u32 buttons;
370 		__u8 wheel;
371 	} __packed * pad_report = (struct pad_report *)data;
372 
373 	if (!data)
374 		return 0; /* EPERM check */
375 
376 	/* Report ID 1 - Keyboard events (button presses) */
377 	if (data[0] == BT_PAD_KBD_REPORT_ID) {
378 		const __u8 button_mapping[] = {
379 			0x0e, /* Button 1:  K */
380 			0x0a, /* Button 2:  G */
381 			0x0f, /* Button 3:  L */
382 			0x4c, /* Button 4:  Delete */
383 			0x0c, /* Button 5:  I */
384 			0x07, /* Button 6:  D */
385 			0x05, /* Button 7:  B */
386 			0x08, /* Button 8:  E */
387 			0x16, /* Button 9:  S */
388 			0x1d, /* Button 10: Z */
389 			0x06, /* Button 11: C */
390 			0x19, /* Button 12: V */
391 			0xff, /* Button 13: LeftControl */
392 			0xff, /* Button 14: LeftAlt */
393 			0xff, /* Button 15: LeftShift */
394 			0x28, /* Button 16: Return Enter */
395 			0x2c, /* Button 17: Spacebar */
396 			0x11, /* Button 18: N */
397 		};
398 
399 		__u8 modifiers = data[1];
400 		__u32 buttons = 0;
401 
402 		if (modifiers & 0x01) { /* Control */
403 			buttons |= BIT(12);
404 		}
405 		if (modifiers & 0x02) { /* Shift */
406 			buttons |= BIT(14);
407 		}
408 		if (modifiers & 0x04) { /* Alt */
409 			buttons |= BIT(13);
410 		}
411 
412 		for (int i = 4; i < BT_PAD_KBD_REPORT_LENGTH; i++) {
413 			if (!data[i])
414 				break;
415 
416 			for (size_t b = 0; b < ARRAY_SIZE(button_mapping); b++) {
417 				if (data[i] != 0xff && data[i] == button_mapping[b]) {
418 					buttons |= BIT(b);
419 					break;
420 				}
421 			}
422 		}
423 
424 		last_button_state = buttons;
425 
426 		pad_report->report_id = OUR_REPORT_ID;
427 		pad_report->btn_stylus = 0;
428 		pad_report->x = 0;
429 		pad_report->y = 0;
430 		pad_report->buttons = buttons;
431 		pad_report->wheel = 0;
432 
433 		return sizeof(struct pad_report);
434 	}
435 
436 	/* Report ID 2 - Consumer control events (the button inside the wheel) */
437 	if (data[0] == BT_PAD_CC_REPORT_ID) {
438 		const __u8 PlayPause = 0xcd;
439 
440 		if (data[1] == PlayPause)
441 			last_button_state |= BIT(18);
442 		else
443 			last_button_state &= ~BIT(18);
444 
445 		pad_report->report_id = OUR_REPORT_ID;
446 		pad_report->btn_stylus = 0;
447 		pad_report->x = 0;
448 		pad_report->y = 0;
449 		pad_report->buttons = last_button_state;
450 		pad_report->wheel = 0;
451 
452 		return sizeof(struct pad_report);
453 	}
454 
455 	/* Report ID 5 - Mouse events (wheel rotation) */
456 	if (data[0] == BT_PAD_MOUSE_REPORT_ID) {
457 		__u8 wheel_delta = data[6];
458 
459 		pad_report->report_id = OUR_REPORT_ID;
460 		pad_report->btn_stylus = 0;
461 		pad_report->x = 0;
462 		pad_report->y = 0;
463 		pad_report->buttons = last_button_state;
464 		pad_report->wheel = wheel_delta;
465 
466 		return sizeof(struct pad_report);
467 	}
468 
469 	return 0;
470 }
471 
472 HID_BPF_OPS(keydial_k20_bluetooth) = {
473 	.hid_device_event = (void *)k20_bt_fix_events,
474 	.hid_rdesc_fixup = (void *)k20_bt_fix_rdesc,
475 };
476 
477 SEC("syscall")
478 int probe(struct hid_bpf_probe_args *ctx)
479 {
480 	switch (ctx->rdesc_size) {
481 	case BT_PAD_REPORT_DESCRIPTOR_LENGTH:
482 	case BT_PUCK_REPORT_DESCRIPTOR_LENGTH:
483 		ctx->retval = 0;
484 		break;
485 	default:
486 		ctx->retval = -EINVAL;
487 	}
488 
489 	return 0;
490 }
491 
492 char _license[] SEC("license") = "GPL";
493