xref: /linux/drivers/hid/bpf/progs/Huion__Inspiroy-2-M.bpf.c (revision 24f171c7e145f43b9f187578e89b0982ce87e54c)
1*501ea61eSBenjamin Tissoires // SPDX-License-Identifier: GPL-2.0-only
2*501ea61eSBenjamin Tissoires /* Copyright (c) 2024 Red Hat, Inc
3*501ea61eSBenjamin Tissoires  */
4*501ea61eSBenjamin Tissoires 
5*501ea61eSBenjamin Tissoires #include "vmlinux.h"
6*501ea61eSBenjamin Tissoires #include "hid_bpf.h"
7*501ea61eSBenjamin Tissoires #include "hid_bpf_helpers.h"
8*501ea61eSBenjamin Tissoires #include "hid_report_helpers.h"
9*501ea61eSBenjamin Tissoires #include <bpf/bpf_tracing.h>
10*501ea61eSBenjamin Tissoires 
11*501ea61eSBenjamin Tissoires #define VID_HUION 0x256C
12*501ea61eSBenjamin Tissoires #define PID_INSPIROY_2_M 0x0067
13*501ea61eSBenjamin Tissoires 
14*501ea61eSBenjamin Tissoires HID_BPF_CONFIG(
15*501ea61eSBenjamin Tissoires 	HID_DEVICE(BUS_USB, HID_GROUP_GENERIC, VID_HUION, PID_INSPIROY_2_M),
16*501ea61eSBenjamin Tissoires );
17*501ea61eSBenjamin Tissoires 
18*501ea61eSBenjamin Tissoires /* Filled in by udev-hid-bpf */
19*501ea61eSBenjamin Tissoires char UDEV_PROP_HUION_FIRMWARE_ID[64];
20*501ea61eSBenjamin Tissoires 
21*501ea61eSBenjamin Tissoires /* The prefix of the firmware ID we expect for this device. The full firmware
22*501ea61eSBenjamin Tissoires  * string has a date suffix, e.g. HUION_T21j_221221
23*501ea61eSBenjamin Tissoires  */
24*501ea61eSBenjamin Tissoires char EXPECTED_FIRMWARE_ID[] = "HUION_T21k_";
25*501ea61eSBenjamin Tissoires 
26*501ea61eSBenjamin Tissoires /* How this BPF program works: the tablet has two modes, firmware mode and
27*501ea61eSBenjamin Tissoires  * tablet mode. In firmware mode (out of the box) the tablet sends button events
28*501ea61eSBenjamin Tissoires  * and the dial as keyboard combinations. In tablet mode it uses a vendor specific
29*501ea61eSBenjamin Tissoires  * hid report to report everything instead.
30*501ea61eSBenjamin Tissoires  * Depending on the mode some hid reports are never sent and the corresponding
31*501ea61eSBenjamin Tissoires  * devices are mute.
32*501ea61eSBenjamin Tissoires  *
33*501ea61eSBenjamin Tissoires  * To switch the tablet use e.g.  https://github.com/whot/huion-switcher
34*501ea61eSBenjamin Tissoires  * or one of the tools from the digimend project
35*501ea61eSBenjamin Tissoires  *
36*501ea61eSBenjamin Tissoires  * This BPF works for both modes. The huion-switcher tool sets the
37*501ea61eSBenjamin Tissoires  * HUION_FIRMWARE_ID udev property - if that is set then we disable the firmware
38*501ea61eSBenjamin Tissoires  * pad and pen reports (by making them vendor collections that are ignored).
39*501ea61eSBenjamin Tissoires  * If that property is not set we fix all hidraw nodes so the tablet works in
40*501ea61eSBenjamin Tissoires  * either mode though the drawback is that the device will show up twice if
41*501ea61eSBenjamin Tissoires  * you bind it to all event nodes
42*501ea61eSBenjamin Tissoires  *
43*501ea61eSBenjamin Tissoires  * Default report descriptor for the first exposed hidraw node:
44*501ea61eSBenjamin Tissoires  *
45*501ea61eSBenjamin Tissoires  * # HUION Huion Tablet_H641P
46*501ea61eSBenjamin Tissoires  * # Report descriptor length: 18 bytes
47*501ea61eSBenjamin Tissoires  * # 0x06, 0x00, 0xff,              // Usage Page (Vendor Defined Page 0xFF00)   0
48*501ea61eSBenjamin Tissoires  * # 0x09, 0x01,                    // Usage (Vendor Usage 0x01)                 3
49*501ea61eSBenjamin Tissoires  * # 0xa1, 0x01,                    // Collection (Application)                  5
50*501ea61eSBenjamin Tissoires  * # 0x85, 0x08,                    //   Report ID (8)                           7
51*501ea61eSBenjamin Tissoires  * # 0x75, 0x58,                    //   Report Size (88)                        9
52*501ea61eSBenjamin Tissoires  * # 0x95, 0x01,                    //   Report Count (1)                        11
53*501ea61eSBenjamin Tissoires  * # 0x09, 0x01,                    //   Usage (Vendor Usage 0x01)               13
54*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,                    //   Input (Data,Var,Abs)                    15
55*501ea61eSBenjamin Tissoires  * # 0xc0,                          // End Collection                            17
56*501ea61eSBenjamin Tissoires  * R: 18 06 00 ff 09 01 a1 01 85 08 75 58 95 01 09 01 81 02 c0
57*501ea61eSBenjamin Tissoires  *
58*501ea61eSBenjamin Tissoires  * This rdesc does nothing until the tablet is switched to raw mode, see
59*501ea61eSBenjamin Tissoires  * https://github.com/whot/huion-switcher
60*501ea61eSBenjamin Tissoires  *
61*501ea61eSBenjamin Tissoires  *
62*501ea61eSBenjamin Tissoires  * Second hidraw node is the Pen. This one sends events until the tablet is
63*501ea61eSBenjamin Tissoires  * switched to raw mode, then it's mute.
64*501ea61eSBenjamin Tissoires  *
65*501ea61eSBenjamin Tissoires  * # Report descriptor length: 93 bytes
66*501ea61eSBenjamin Tissoires  * # 0x05, 0x0d,          // Usage Page (Digitizers)                   0
67*501ea61eSBenjamin Tissoires  * # 0x09, 0x02,          // Usage (Pen)                               2
68*501ea61eSBenjamin Tissoires  * # 0xa1, 0x01,          // Collection (Application)                  4
69*501ea61eSBenjamin Tissoires  * # 0x85, 0x0a,          //   Report ID (10)                          6
70*501ea61eSBenjamin Tissoires  * # 0x09, 0x20,          //   Usage (Stylus)                          8
71*501ea61eSBenjamin Tissoires  * # 0xa1, 0x01,          //   Collection (Application)                10
72*501ea61eSBenjamin Tissoires  * # 0x09, 0x42,          //     Usage (Tip Switch)                    12
73*501ea61eSBenjamin Tissoires  * # 0x09, 0x44,          //     Usage (Barrel Switch)                 14
74*501ea61eSBenjamin Tissoires  * # 0x09, 0x45,          //     Usage (Eraser)                        16
75*501ea61eSBenjamin Tissoires  * # 0x09, 0x3c,          //     Usage (Invert)                        18 <-- has no Invert eraser
76*501ea61eSBenjamin Tissoires  * # 0x15, 0x00,          //     Logical Minimum (0)                   20
77*501ea61eSBenjamin Tissoires  * # 0x25, 0x01,          //     Logical Maximum (1)                   22
78*501ea61eSBenjamin Tissoires  * # 0x75, 0x01,          //     Report Size (1)                       24
79*501ea61eSBenjamin Tissoires  * # 0x95, 0x06,          //     Report Count (6)                      26
80*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  28
81*501ea61eSBenjamin Tissoires  * # 0x09, 0x32,          //     Usage (In Range)                      30
82*501ea61eSBenjamin Tissoires  * # 0x75, 0x01,          //     Report Size (1)                       32
83*501ea61eSBenjamin Tissoires  * # 0x95, 0x01,          //     Report Count (1)                      34
84*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  36
85*501ea61eSBenjamin Tissoires  * # 0x81, 0x03,          //     Input (Cnst,Var,Abs)                  38
86*501ea61eSBenjamin Tissoires  * # 0x05, 0x01,          //     Usage Page (Generic Desktop)          40
87*501ea61eSBenjamin Tissoires  * # 0x09, 0x30,          //     Usage (X)                             42
88*501ea61eSBenjamin Tissoires  * # 0x09, 0x31,          //     Usage (Y)                             44
89*501ea61eSBenjamin Tissoires  * # 0x55, 0x0d,          //     Unit Exponent (-3)                    46 <-- change to -2
90*501ea61eSBenjamin Tissoires  * # 0x65, 0x33,          //     Unit (EnglishLinear: in³)             48 <-- change in³ to in
91*501ea61eSBenjamin Tissoires  * # 0x26, 0xff, 0x7f,    //     Logical Maximum (32767)               50
92*501ea61eSBenjamin Tissoires  * # 0x35, 0x00,          //     Physical Minimum (0)                  53
93*501ea61eSBenjamin Tissoires  * # 0x46, 0x00, 0x08,    //     Physical Maximum (2048)               55 <-- invalid size
94*501ea61eSBenjamin Tissoires  * # 0x75, 0x10,          //     Report Size (16)                      58
95*501ea61eSBenjamin Tissoires  * # 0x95, 0x02,          //     Report Count (2)                      60
96*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  62
97*501ea61eSBenjamin Tissoires  * # 0x05, 0x0d,          //     Usage Page (Digitizers)               64
98*501ea61eSBenjamin Tissoires  * # 0x09, 0x30,          //     Usage (Tip Pressure)                  66
99*501ea61eSBenjamin Tissoires  * # 0x26, 0xff, 0x1f,    //     Logical Maximum (8191)                68
100*501ea61eSBenjamin Tissoires  * # 0x75, 0x10,          //     Report Size (16)                      71
101*501ea61eSBenjamin Tissoires  * # 0x95, 0x01,          //     Report Count (1)                      73
102*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  75
103*501ea61eSBenjamin Tissoires  * # 0x09, 0x3d,          //     Usage (X Tilt)                        77 <-- No tilt reported
104*501ea61eSBenjamin Tissoires  * # 0x09, 0x3e,          //     Usage (Y Tilt)                        79
105*501ea61eSBenjamin Tissoires  * # 0x15, 0x81,          //     Logical Minimum (-127)                81
106*501ea61eSBenjamin Tissoires  * # 0x25, 0x7f,          //     Logical Maximum (127)                 83
107*501ea61eSBenjamin Tissoires  * # 0x75, 0x08,          //     Report Size (8)                       85
108*501ea61eSBenjamin Tissoires  * # 0x95, 0x02,          //     Report Count (2)                      87
109*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //     Input (Data,Var,Abs)                  89
110*501ea61eSBenjamin Tissoires  * # 0xc0,                //   End Collection                          91
111*501ea61eSBenjamin Tissoires  * # 0xc0,                // End Collection                            92
112*501ea61eSBenjamin Tissoires  * R: 93 05 0d 09 02 a1 01 85 0a 09 20 a1 01 09 42 09 44 09 45 09 3c 15 00 25 01 7501 95 06 81 02 09 32 75 01 95 01 81 02 81 03 05 01 09 30 09 31 55 0d 65 33 26 ff7f 35 00 46 00 08 75 10 95 02 81 02 05 0d 09 30 26 ff 1f 75 10 95 01 81 02 09 3d09 3e 15 81 25 7f 75 08 95 02 81 02 c0 c0
113*501ea61eSBenjamin Tissoires  *
114*501ea61eSBenjamin Tissoires  * Third hidraw node is the pad which sends a combination of keyboard shortcuts until
115*501ea61eSBenjamin Tissoires  * the tablet is switched to raw mode, then it's mute:
116*501ea61eSBenjamin Tissoires  *
117*501ea61eSBenjamin Tissoires  * # Report descriptor length: 65 bytes
118*501ea61eSBenjamin Tissoires  * # 0x05, 0x01,          // Usage Page (Generic Desktop)              0
119*501ea61eSBenjamin Tissoires  * # 0x09, 0x06,          // Usage (Keyboard)                          2
120*501ea61eSBenjamin Tissoires  * # 0xa1, 0x01,          // Collection (Application)                  4
121*501ea61eSBenjamin Tissoires  * # 0x85, 0x03,          //   Report ID (3)                           6
122*501ea61eSBenjamin Tissoires  * # 0x05, 0x07,          //   Usage Page (Keyboard/Keypad)            8
123*501ea61eSBenjamin Tissoires  * # 0x19, 0xe0,          //   UsageMinimum (224)                      10
124*501ea61eSBenjamin Tissoires  * # 0x29, 0xe7,          //   UsageMaximum (231)                      12
125*501ea61eSBenjamin Tissoires  * # 0x15, 0x00,          //   Logical Minimum (0)                     14
126*501ea61eSBenjamin Tissoires  * # 0x25, 0x01,          //   Logical Maximum (1)                     16
127*501ea61eSBenjamin Tissoires  * # 0x75, 0x01,          //   Report Size (1)                         18
128*501ea61eSBenjamin Tissoires  * # 0x95, 0x08,          //   Report Count (8)                        20
129*501ea61eSBenjamin Tissoires  * # 0x81, 0x02,          //   Input (Data,Var,Abs)                    22
130*501ea61eSBenjamin Tissoires  * # 0x05, 0x07,          //   Usage Page (Keyboard/Keypad)            24
131*501ea61eSBenjamin Tissoires  * # 0x19, 0x00,          //   UsageMinimum (0)                        26
132*501ea61eSBenjamin Tissoires  * # 0x29, 0xff,          //   UsageMaximum (255)                      28
133*501ea61eSBenjamin Tissoires  * # 0x26, 0xff, 0x00,    //   Logical Maximum (255)                   30
134*501ea61eSBenjamin Tissoires  * # 0x75, 0x08,          //   Report Size (8)                         33
135*501ea61eSBenjamin Tissoires  * # 0x95, 0x06,          //   Report Count (6)                        35
136*501ea61eSBenjamin Tissoires  * # 0x81, 0x00,          //   Input (Data,Arr,Abs)                    37
137*501ea61eSBenjamin Tissoires  * # 0xc0,                // End Collection                            39
138*501ea61eSBenjamin Tissoires  * # 0x05, 0x0c,          // Usage Page (Consumer)                     40
139*501ea61eSBenjamin Tissoires  * # 0x09, 0x01,          // Usage (Consumer Control)                  42
140*501ea61eSBenjamin Tissoires  * # 0xa1, 0x01,          // Collection (Application)                  44
141*501ea61eSBenjamin Tissoires  * # 0x85, 0x04,          //   Report ID (4)                           46
142*501ea61eSBenjamin Tissoires  * # 0x19, 0x00,          //   UsageMinimum (0)                        48
143*501ea61eSBenjamin Tissoires  * # 0x2a, 0x3c, 0x02,    //   UsageMaximum (572)                      50
144*501ea61eSBenjamin Tissoires  * # 0x15, 0x00,          //   Logical Minimum (0)                     53
145*501ea61eSBenjamin Tissoires  * # 0x26, 0x3c, 0x02,    //   Logical Maximum (572)                   55
146*501ea61eSBenjamin Tissoires  * # 0x95, 0x01,          //   Report Count (1)                        58
147*501ea61eSBenjamin Tissoires  * # 0x75, 0x10,          //   Report Size (16)                        60
148*501ea61eSBenjamin Tissoires  * # 0x81, 0x00,          //   Input (Data,Arr,Abs)                    62
149*501ea61eSBenjamin Tissoires  * # 0xc0,                // End Collection                            64
150*501ea61eSBenjamin Tissoires  * R: 65 05 01 09 06 a1 01 85 03 05 07 19 e0 29 e7 15 00 25 01 75 01 95 08 81 02 0507 19 00 29 ff 26 ff 00 75 08 95 06 81 00 c0 05 0c 09 01 a1 01 85 04 19 00 2a 3c02 15 00 26 3c 02 95 01 75 10 81 00 c0
151*501ea61eSBenjamin Tissoires  * N: HUION Huion Tablet_H641P
152*501ea61eSBenjamin Tissoires  */
153*501ea61eSBenjamin Tissoires 
154*501ea61eSBenjamin Tissoires #define PAD_REPORT_DESCRIPTOR_LENGTH 133
155*501ea61eSBenjamin Tissoires #define PEN_REPORT_DESCRIPTOR_LENGTH 93
156*501ea61eSBenjamin Tissoires #define VENDOR_REPORT_DESCRIPTOR_LENGTH 36
157*501ea61eSBenjamin Tissoires #define PAD_REPORT_ID 3
158*501ea61eSBenjamin Tissoires #define PEN_REPORT_ID 10
159*501ea61eSBenjamin Tissoires #define VENDOR_REPORT_ID 8
160*501ea61eSBenjamin Tissoires #define PAD_REPORT_LENGTH 8
161*501ea61eSBenjamin Tissoires #define PEN_REPORT_LENGTH 10
162*501ea61eSBenjamin Tissoires #define VENDOR_REPORT_LENGTH 12
163*501ea61eSBenjamin Tissoires 
164*501ea61eSBenjamin Tissoires 
165*501ea61eSBenjamin Tissoires __u16 last_button_state;
166*501ea61eSBenjamin Tissoires 
167*501ea61eSBenjamin Tissoires static const __u8 fixed_rdesc_pad[] = {
168*501ea61eSBenjamin Tissoires 	UsagePage_GenericDesktop
169*501ea61eSBenjamin Tissoires 	Usage_GD_Keypad
170*501ea61eSBenjamin Tissoires 	CollectionApplication(
171*501ea61eSBenjamin Tissoires 		// -- Byte 0 in report
172*501ea61eSBenjamin Tissoires 		ReportId(PAD_REPORT_ID)
173*501ea61eSBenjamin Tissoires 		LogicalMinimum_i8(0)
174*501ea61eSBenjamin Tissoires 		LogicalMaximum_i8(1)
175*501ea61eSBenjamin Tissoires 		UsagePage_Digitizers
176*501ea61eSBenjamin Tissoires 		Usage_Dig_TabletFunctionKeys
177*501ea61eSBenjamin Tissoires 		CollectionPhysical(
178*501ea61eSBenjamin Tissoires 			// Byte 1 in report - just exists so we get to be a tablet pad
179*501ea61eSBenjamin Tissoires 			Usage_Dig_BarrelSwitch // BTN_STYLUS
180*501ea61eSBenjamin Tissoires 			ReportCount(1)
181*501ea61eSBenjamin Tissoires 			ReportSize(1)
182*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
183*501ea61eSBenjamin Tissoires 			ReportCount(7) // padding
184*501ea61eSBenjamin Tissoires 			Input(Const)
185*501ea61eSBenjamin Tissoires 			// Bytes 2/3 in report - just exists so we get to be a tablet pad
186*501ea61eSBenjamin Tissoires 			UsagePage_GenericDesktop
187*501ea61eSBenjamin Tissoires 			Usage_GD_X
188*501ea61eSBenjamin Tissoires 			Usage_GD_Y
189*501ea61eSBenjamin Tissoires 			ReportCount(2)
190*501ea61eSBenjamin Tissoires 			ReportSize(8)
191*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
192*501ea61eSBenjamin Tissoires 			// Byte 4 in report is the wheel
193*501ea61eSBenjamin Tissoires 			Usage_GD_Wheel
194*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(-1)
195*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(1)
196*501ea61eSBenjamin Tissoires 			ReportCount(1)
197*501ea61eSBenjamin Tissoires 			ReportSize(8)
198*501ea61eSBenjamin Tissoires 			Input(Var|Rel)
199*501ea61eSBenjamin Tissoires 			// Byte 5 is the button state
200*501ea61eSBenjamin Tissoires 			UsagePage_Button
201*501ea61eSBenjamin Tissoires 			UsageMinimum_i8(0x1)
202*501ea61eSBenjamin Tissoires 			UsageMaximum_i8(0x8)
203*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(0x1)
204*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(0x8)
205*501ea61eSBenjamin Tissoires 			ReportCount(1)
206*501ea61eSBenjamin Tissoires 			ReportSize(8)
207*501ea61eSBenjamin Tissoires 			Input(Arr|Abs)
208*501ea61eSBenjamin Tissoires 		)
209*501ea61eSBenjamin Tissoires 		// Make sure we match our original report length
210*501ea61eSBenjamin Tissoires 		FixedSizeVendorReport(PAD_REPORT_LENGTH)
211*501ea61eSBenjamin Tissoires 	)
212*501ea61eSBenjamin Tissoires };
213*501ea61eSBenjamin Tissoires 
214*501ea61eSBenjamin Tissoires static const __u8 fixed_rdesc_pen[] = {
215*501ea61eSBenjamin Tissoires 	UsagePage_Digitizers
216*501ea61eSBenjamin Tissoires 	Usage_Dig_Pen
217*501ea61eSBenjamin Tissoires 	CollectionApplication(
218*501ea61eSBenjamin Tissoires 		// -- Byte 0 in report
219*501ea61eSBenjamin Tissoires 		ReportId(PEN_REPORT_ID)
220*501ea61eSBenjamin Tissoires 		Usage_Dig_Pen
221*501ea61eSBenjamin Tissoires 		CollectionPhysical(
222*501ea61eSBenjamin Tissoires 			// -- Byte 1 in report
223*501ea61eSBenjamin Tissoires 			Usage_Dig_TipSwitch
224*501ea61eSBenjamin Tissoires 			Usage_Dig_BarrelSwitch
225*501ea61eSBenjamin Tissoires 			Usage_Dig_SecondaryBarrelSwitch // maps eraser to BTN_STYLUS2
226*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(0)
227*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(1)
228*501ea61eSBenjamin Tissoires 			ReportSize(1)
229*501ea61eSBenjamin Tissoires 			ReportCount(3)
230*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
231*501ea61eSBenjamin Tissoires 			ReportCount(4)  // Padding
232*501ea61eSBenjamin Tissoires 			Input(Const)
233*501ea61eSBenjamin Tissoires 			Usage_Dig_InRange
234*501ea61eSBenjamin Tissoires 			ReportCount(1)
235*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
236*501ea61eSBenjamin Tissoires 			ReportSize(16)
237*501ea61eSBenjamin Tissoires 			ReportCount(1)
238*501ea61eSBenjamin Tissoires 			PushPop(
239*501ea61eSBenjamin Tissoires 				UsagePage_GenericDesktop
240*501ea61eSBenjamin Tissoires 				Unit(cm)
241*501ea61eSBenjamin Tissoires 				UnitExponent(-1)
242*501ea61eSBenjamin Tissoires 				PhysicalMinimum_i16(0)
243*501ea61eSBenjamin Tissoires 				PhysicalMaximum_i16(160)
244*501ea61eSBenjamin Tissoires 				LogicalMinimum_i16(0)
245*501ea61eSBenjamin Tissoires 				LogicalMaximum_i16(32767)
246*501ea61eSBenjamin Tissoires 				Usage_GD_X
247*501ea61eSBenjamin Tissoires 				Input(Var|Abs) // Bytes 2+3
248*501ea61eSBenjamin Tissoires 				PhysicalMinimum_i16(0)
249*501ea61eSBenjamin Tissoires 				PhysicalMaximum_i16(100)
250*501ea61eSBenjamin Tissoires 				LogicalMinimum_i16(0)
251*501ea61eSBenjamin Tissoires 				LogicalMaximum_i16(32767)
252*501ea61eSBenjamin Tissoires 				Usage_GD_Y
253*501ea61eSBenjamin Tissoires 				Input(Var|Abs) // Bytes 4+5
254*501ea61eSBenjamin Tissoires 			)
255*501ea61eSBenjamin Tissoires 			UsagePage_Digitizers
256*501ea61eSBenjamin Tissoires 			Usage_Dig_TipPressure
257*501ea61eSBenjamin Tissoires 			LogicalMinimum_i16(0)
258*501ea61eSBenjamin Tissoires 			LogicalMaximum_i16(8191)
259*501ea61eSBenjamin Tissoires 			Input(Var|Abs) // Byte 6+7
260*501ea61eSBenjamin Tissoires 			// Two bytes padding so we don't need to change the report at all
261*501ea61eSBenjamin Tissoires 			ReportSize(8)
262*501ea61eSBenjamin Tissoires 			ReportCount(2)
263*501ea61eSBenjamin Tissoires 			Input(Const) // Byte 6+7
264*501ea61eSBenjamin Tissoires 		)
265*501ea61eSBenjamin Tissoires 	)
266*501ea61eSBenjamin Tissoires };
267*501ea61eSBenjamin Tissoires 
268*501ea61eSBenjamin Tissoires static const __u8 fixed_rdesc_vendor[] = {
269*501ea61eSBenjamin Tissoires 	UsagePage_Digitizers
270*501ea61eSBenjamin Tissoires 	Usage_Dig_Pen
271*501ea61eSBenjamin Tissoires 	CollectionApplication(
272*501ea61eSBenjamin Tissoires 		// Byte 0
273*501ea61eSBenjamin Tissoires 		// We leave the pen on the vendor report ID
274*501ea61eSBenjamin Tissoires 		ReportId(VENDOR_REPORT_ID)
275*501ea61eSBenjamin Tissoires 		Usage_Dig_Pen
276*501ea61eSBenjamin Tissoires 		CollectionPhysical(
277*501ea61eSBenjamin Tissoires 			// Byte 1 are the buttons
278*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(0)
279*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(1)
280*501ea61eSBenjamin Tissoires 			ReportSize(1)
281*501ea61eSBenjamin Tissoires 			Usage_Dig_TipSwitch
282*501ea61eSBenjamin Tissoires 			Usage_Dig_BarrelSwitch
283*501ea61eSBenjamin Tissoires 			Usage_Dig_SecondaryBarrelSwitch
284*501ea61eSBenjamin Tissoires 			ReportCount(3)
285*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
286*501ea61eSBenjamin Tissoires 			ReportCount(4) // Padding
287*501ea61eSBenjamin Tissoires 			Input(Const)
288*501ea61eSBenjamin Tissoires 			Usage_Dig_InRange
289*501ea61eSBenjamin Tissoires 			ReportCount(1)
290*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
291*501ea61eSBenjamin Tissoires 			ReportSize(16)
292*501ea61eSBenjamin Tissoires 			ReportCount(1)
293*501ea61eSBenjamin Tissoires 			PushPop(
294*501ea61eSBenjamin Tissoires 				UsagePage_GenericDesktop
295*501ea61eSBenjamin Tissoires 				Unit(cm)
296*501ea61eSBenjamin Tissoires 				UnitExponent(-1)
297*501ea61eSBenjamin Tissoires 				// Note: reported logical range differs
298*501ea61eSBenjamin Tissoires 				// from the pen report ID for x and y
299*501ea61eSBenjamin Tissoires 				LogicalMinimum_i16(0)
300*501ea61eSBenjamin Tissoires 				LogicalMaximum_i16(32000)
301*501ea61eSBenjamin Tissoires 				PhysicalMinimum_i16(0)
302*501ea61eSBenjamin Tissoires 				PhysicalMaximum_i16(160)
303*501ea61eSBenjamin Tissoires 				// Bytes 2/3 in report
304*501ea61eSBenjamin Tissoires 				Usage_GD_X
305*501ea61eSBenjamin Tissoires 				Input(Var|Abs)
306*501ea61eSBenjamin Tissoires 				LogicalMinimum_i16(0)
307*501ea61eSBenjamin Tissoires 				LogicalMaximum_i16(20000)
308*501ea61eSBenjamin Tissoires 				PhysicalMinimum_i16(0)
309*501ea61eSBenjamin Tissoires 				PhysicalMaximum_i16(100)
310*501ea61eSBenjamin Tissoires 				// Bytes 4/5 in report
311*501ea61eSBenjamin Tissoires 				Usage_GD_Y
312*501ea61eSBenjamin Tissoires 				Input(Var|Abs)
313*501ea61eSBenjamin Tissoires 			)
314*501ea61eSBenjamin Tissoires 			// Bytes 6/7 in report
315*501ea61eSBenjamin Tissoires 			LogicalMinimum_i16(0)
316*501ea61eSBenjamin Tissoires 			LogicalMaximum_i16(8192)
317*501ea61eSBenjamin Tissoires 			Usage_Dig_TipPressure
318*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
319*501ea61eSBenjamin Tissoires 		)
320*501ea61eSBenjamin Tissoires 	)
321*501ea61eSBenjamin Tissoires 	UsagePage_GenericDesktop
322*501ea61eSBenjamin Tissoires 	Usage_GD_Keypad
323*501ea61eSBenjamin Tissoires 	CollectionApplication(
324*501ea61eSBenjamin Tissoires 		// Byte 0
325*501ea61eSBenjamin Tissoires 		ReportId(PAD_REPORT_ID)
326*501ea61eSBenjamin Tissoires 		LogicalMinimum_i8(0)
327*501ea61eSBenjamin Tissoires 		LogicalMaximum_i8(1)
328*501ea61eSBenjamin Tissoires 		UsagePage_Digitizers
329*501ea61eSBenjamin Tissoires 		Usage_Dig_TabletFunctionKeys
330*501ea61eSBenjamin Tissoires 		CollectionPhysical(
331*501ea61eSBenjamin Tissoires 			// Byte 1 are the buttons
332*501ea61eSBenjamin Tissoires 			Usage_Dig_BarrelSwitch	 // BTN_STYLUS, needed so we get to be a tablet pad
333*501ea61eSBenjamin Tissoires 			ReportCount(1)
334*501ea61eSBenjamin Tissoires 			ReportSize(1)
335*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
336*501ea61eSBenjamin Tissoires 			ReportCount(7) // Padding
337*501ea61eSBenjamin Tissoires 			Input(Const)
338*501ea61eSBenjamin Tissoires 			// Bytes 2/3 - x/y just exist so we get to be a tablet pad
339*501ea61eSBenjamin Tissoires 			UsagePage_GenericDesktop
340*501ea61eSBenjamin Tissoires 			Usage_GD_X
341*501ea61eSBenjamin Tissoires 			Usage_GD_Y
342*501ea61eSBenjamin Tissoires 			ReportCount(2)
343*501ea61eSBenjamin Tissoires 			ReportSize(8)
344*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
345*501ea61eSBenjamin Tissoires 			// Bytes 4 and 5 are the button state
346*501ea61eSBenjamin Tissoires 			UsagePage_Button
347*501ea61eSBenjamin Tissoires 			UsageMinimum_i8(0x1)
348*501ea61eSBenjamin Tissoires 			UsageMaximum_i8(0xa)
349*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(0x0)
350*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(0x1)
351*501ea61eSBenjamin Tissoires 			ReportCount(10)
352*501ea61eSBenjamin Tissoires 			ReportSize(1)
353*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
354*501ea61eSBenjamin Tissoires 			Usage_i8(0x31) // maps to BTN_SOUTH
355*501ea61eSBenjamin Tissoires 			ReportCount(1)
356*501ea61eSBenjamin Tissoires 			Input(Var|Abs)
357*501ea61eSBenjamin Tissoires 			ReportCount(5)
358*501ea61eSBenjamin Tissoires 			Input(Const)
359*501ea61eSBenjamin Tissoires 			// Byte 6 is the wheel
360*501ea61eSBenjamin Tissoires 			UsagePage_GenericDesktop
361*501ea61eSBenjamin Tissoires 			Usage_GD_Wheel
362*501ea61eSBenjamin Tissoires 			LogicalMinimum_i8(-1)
363*501ea61eSBenjamin Tissoires 			LogicalMaximum_i8(1)
364*501ea61eSBenjamin Tissoires 			ReportCount(1)
365*501ea61eSBenjamin Tissoires 			ReportSize(8)
366*501ea61eSBenjamin Tissoires 			Input(Var|Rel)
367*501ea61eSBenjamin Tissoires 		)
368*501ea61eSBenjamin Tissoires 		// Make sure we match our original report length
369*501ea61eSBenjamin Tissoires 		FixedSizeVendorReport(VENDOR_REPORT_LENGTH)
370*501ea61eSBenjamin Tissoires 	)
371*501ea61eSBenjamin Tissoires };
372*501ea61eSBenjamin Tissoires 
373*501ea61eSBenjamin Tissoires static const __u8 disabled_rdesc_pen[] = {
374*501ea61eSBenjamin Tissoires 	FixedSizeVendorReport(PEN_REPORT_LENGTH)
375*501ea61eSBenjamin Tissoires };
376*501ea61eSBenjamin Tissoires 
377*501ea61eSBenjamin Tissoires static const __u8 disabled_rdesc_pad[] = {
378*501ea61eSBenjamin Tissoires 	FixedSizeVendorReport(PAD_REPORT_LENGTH)
379*501ea61eSBenjamin Tissoires };
380*501ea61eSBenjamin Tissoires 
381*501ea61eSBenjamin Tissoires SEC(HID_BPF_RDESC_FIXUP)
382*501ea61eSBenjamin Tissoires int BPF_PROG(hid_fix_rdesc, struct hid_bpf_ctx *hctx)
383*501ea61eSBenjamin Tissoires {
384*501ea61eSBenjamin Tissoires 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, HID_MAX_DESCRIPTOR_SIZE /* size */);
385*501ea61eSBenjamin Tissoires 	__s32 rdesc_size = hctx->size;
386*501ea61eSBenjamin Tissoires 	__u8 have_fw_id;
387*501ea61eSBenjamin Tissoires 
388*501ea61eSBenjamin Tissoires 	if (!data)
389*501ea61eSBenjamin Tissoires 		return 0; /* EPERM check */
390*501ea61eSBenjamin Tissoires 
391*501ea61eSBenjamin Tissoires 	/* If we have a firmware ID and it matches our expected prefix, we
392*501ea61eSBenjamin Tissoires 	 * disable the default pad/pen nodes. They won't send events
393*501ea61eSBenjamin Tissoires 	 * but cause duplicate devices.
394*501ea61eSBenjamin Tissoires 	 */
395*501ea61eSBenjamin Tissoires 	have_fw_id = __builtin_memcmp(UDEV_PROP_HUION_FIRMWARE_ID,
396*501ea61eSBenjamin Tissoires 				      EXPECTED_FIRMWARE_ID,
397*501ea61eSBenjamin Tissoires 				      sizeof(EXPECTED_FIRMWARE_ID) - 1) == 0;
398*501ea61eSBenjamin Tissoires 	if (rdesc_size == PAD_REPORT_DESCRIPTOR_LENGTH) {
399*501ea61eSBenjamin Tissoires 		if (have_fw_id) {
400*501ea61eSBenjamin Tissoires 			__builtin_memcpy(data, disabled_rdesc_pad, sizeof(disabled_rdesc_pad));
401*501ea61eSBenjamin Tissoires 			return sizeof(disabled_rdesc_pad);
402*501ea61eSBenjamin Tissoires 		}
403*501ea61eSBenjamin Tissoires 
404*501ea61eSBenjamin Tissoires 		__builtin_memcpy(data, fixed_rdesc_pad, sizeof(fixed_rdesc_pad));
405*501ea61eSBenjamin Tissoires 		return sizeof(fixed_rdesc_pad);
406*501ea61eSBenjamin Tissoires 	}
407*501ea61eSBenjamin Tissoires 	if (rdesc_size == PEN_REPORT_DESCRIPTOR_LENGTH) {
408*501ea61eSBenjamin Tissoires 		if (have_fw_id) {
409*501ea61eSBenjamin Tissoires 			__builtin_memcpy(data, disabled_rdesc_pen, sizeof(disabled_rdesc_pen));
410*501ea61eSBenjamin Tissoires 			return sizeof(disabled_rdesc_pen);
411*501ea61eSBenjamin Tissoires 		}
412*501ea61eSBenjamin Tissoires 
413*501ea61eSBenjamin Tissoires 		__builtin_memcpy(data, fixed_rdesc_pen, sizeof(fixed_rdesc_pen));
414*501ea61eSBenjamin Tissoires 		return sizeof(fixed_rdesc_pen);
415*501ea61eSBenjamin Tissoires 	}
416*501ea61eSBenjamin Tissoires 	/* Always fix the vendor mode so the tablet will work even if nothing sets
417*501ea61eSBenjamin Tissoires 	 * the udev property (e.g. huion-switcher run manually)
418*501ea61eSBenjamin Tissoires 	 */
419*501ea61eSBenjamin Tissoires 	if (rdesc_size == VENDOR_REPORT_DESCRIPTOR_LENGTH) {
420*501ea61eSBenjamin Tissoires 		__builtin_memcpy(data, fixed_rdesc_vendor, sizeof(fixed_rdesc_vendor));
421*501ea61eSBenjamin Tissoires 		return sizeof(fixed_rdesc_vendor);
422*501ea61eSBenjamin Tissoires 	}
423*501ea61eSBenjamin Tissoires 	return 0;
424*501ea61eSBenjamin Tissoires }
425*501ea61eSBenjamin Tissoires 
426*501ea61eSBenjamin Tissoires SEC(HID_BPF_DEVICE_EVENT)
427*501ea61eSBenjamin Tissoires int BPF_PROG(inspiroy_2_fix_events, struct hid_bpf_ctx *hctx)
428*501ea61eSBenjamin Tissoires {
429*501ea61eSBenjamin Tissoires 	__u8 *data = hid_bpf_get_data(hctx, 0 /* offset */, 10 /* size */);
430*501ea61eSBenjamin Tissoires 
431*501ea61eSBenjamin Tissoires 	if (!data)
432*501ea61eSBenjamin Tissoires 		return 0; /* EPERM check */
433*501ea61eSBenjamin Tissoires 
434*501ea61eSBenjamin Tissoires 	/* Only sent if tablet is in default mode */
435*501ea61eSBenjamin Tissoires 	if (data[0] == PAD_REPORT_ID) {
436*501ea61eSBenjamin Tissoires 		/* Nicely enough, this device only supports one button down at a time so
437*501ea61eSBenjamin Tissoires 		 * the reports are easy to match. Buttons numbered from the top
438*501ea61eSBenjamin Tissoires 		 *   Button released: 03 00 00 00 00 00 00 00
439*501ea61eSBenjamin Tissoires 		 *   Button 1: 03 00 05 00 00 00 00 00 -> b
440*501ea61eSBenjamin Tissoires 		 *   Button 2: 03 07 11 00 00 00 00 00 -> Ctrl Shift N
441*501ea61eSBenjamin Tissoires 		 *   Button 3: 03 00 08 00 00 00 00 00 -> e
442*501ea61eSBenjamin Tissoires 		 *   Button 4: 03 00 0c 00 00 00 00 00 -> i
443*501ea61eSBenjamin Tissoires 		 *   Button 5: 03 00 2c 00 00 00 00 00 -> space
444*501ea61eSBenjamin Tissoires 		 *   Button 6: 03 01 08 00 00 00 00 00 -> Ctrl E
445*501ea61eSBenjamin Tissoires 		 *   Button 7: 03 01 16 00 00 00 00 00 -> Ctrl S
446*501ea61eSBenjamin Tissoires 		 *   Button 8: 03 05 1d 00 00 00 00 00 -> Ctrl Alt Z
447*501ea61eSBenjamin Tissoires 		 *
448*501ea61eSBenjamin Tissoires 		 *   Wheel down: 03 01 2d 00 00 00 00 00 -> Ctrl -
449*501ea61eSBenjamin Tissoires 		 *   Wheel up:   03 01 2e 00 00 00 00 00 -> Ctrl =
450*501ea61eSBenjamin Tissoires 		 */
451*501ea61eSBenjamin Tissoires 		__u8 button = 0;
452*501ea61eSBenjamin Tissoires 		__u8 wheel = 0;
453*501ea61eSBenjamin Tissoires 
454*501ea61eSBenjamin Tissoires 		switch (data[1] << 8 | data[2]) {
455*501ea61eSBenjamin Tissoires 		case 0x0000:
456*501ea61eSBenjamin Tissoires 			break;
457*501ea61eSBenjamin Tissoires 		case 0x0005:
458*501ea61eSBenjamin Tissoires 			button = 1;
459*501ea61eSBenjamin Tissoires 			break;
460*501ea61eSBenjamin Tissoires 		case 0x0711:
461*501ea61eSBenjamin Tissoires 			button = 2;
462*501ea61eSBenjamin Tissoires 			break;
463*501ea61eSBenjamin Tissoires 		case 0x0008:
464*501ea61eSBenjamin Tissoires 			button = 3;
465*501ea61eSBenjamin Tissoires 			break;
466*501ea61eSBenjamin Tissoires 		case 0x000c:
467*501ea61eSBenjamin Tissoires 			button = 4;
468*501ea61eSBenjamin Tissoires 			break;
469*501ea61eSBenjamin Tissoires 		case 0x002c:
470*501ea61eSBenjamin Tissoires 			button = 5;
471*501ea61eSBenjamin Tissoires 			break;
472*501ea61eSBenjamin Tissoires 		case 0x0108:
473*501ea61eSBenjamin Tissoires 			button = 6;
474*501ea61eSBenjamin Tissoires 			break;
475*501ea61eSBenjamin Tissoires 		case 0x0116:
476*501ea61eSBenjamin Tissoires 			button = 7;
477*501ea61eSBenjamin Tissoires 			break;
478*501ea61eSBenjamin Tissoires 		case 0x051d:
479*501ea61eSBenjamin Tissoires 			button = 8;
480*501ea61eSBenjamin Tissoires 			break;
481*501ea61eSBenjamin Tissoires 		case 0x012d:
482*501ea61eSBenjamin Tissoires 			wheel = -1;
483*501ea61eSBenjamin Tissoires 			break;
484*501ea61eSBenjamin Tissoires 		case 0x012e:
485*501ea61eSBenjamin Tissoires 			wheel = 1;
486*501ea61eSBenjamin Tissoires 			break;
487*501ea61eSBenjamin Tissoires 		}
488*501ea61eSBenjamin Tissoires 
489*501ea61eSBenjamin Tissoires 		__u8 report[6] = {PAD_REPORT_ID, 0x0, 0x0, 0x0, wheel, button};
490*501ea61eSBenjamin Tissoires 
491*501ea61eSBenjamin Tissoires 		__builtin_memcpy(data, report, sizeof(report));
492*501ea61eSBenjamin Tissoires 		return sizeof(report);
493*501ea61eSBenjamin Tissoires 	}
494*501ea61eSBenjamin Tissoires 
495*501ea61eSBenjamin Tissoires 	/* Nothing to do for the PEN_REPORT_ID, it's already mapped */
496*501ea61eSBenjamin Tissoires 
497*501ea61eSBenjamin Tissoires 	/* Only sent if tablet is in raw mode */
498*501ea61eSBenjamin Tissoires 	if (data[0] == VENDOR_REPORT_ID) {
499*501ea61eSBenjamin Tissoires 		/* Pad reports */
500*501ea61eSBenjamin Tissoires 		if (data[1] & 0x20) {
501*501ea61eSBenjamin Tissoires 			/* See fixed_rdesc_pad */
502*501ea61eSBenjamin Tissoires 			struct pad_report {
503*501ea61eSBenjamin Tissoires 				__u8 report_id;
504*501ea61eSBenjamin Tissoires 				__u8 btn_stylus;
505*501ea61eSBenjamin Tissoires 				__u8 x;
506*501ea61eSBenjamin Tissoires 				__u8 y;
507*501ea61eSBenjamin Tissoires 				__u16 buttons;
508*501ea61eSBenjamin Tissoires 				__u8 wheel;
509*501ea61eSBenjamin Tissoires 			} __attribute__((packed)) *pad_report;
510*501ea61eSBenjamin Tissoires 			__u8 wheel = 0;
511*501ea61eSBenjamin Tissoires 
512*501ea61eSBenjamin Tissoires 			/* Wheel report */
513*501ea61eSBenjamin Tissoires 			if (data[1] == 0xf1) {
514*501ea61eSBenjamin Tissoires 				if (data[5] == 2)
515*501ea61eSBenjamin Tissoires 					wheel = 0xff;
516*501ea61eSBenjamin Tissoires 				else
517*501ea61eSBenjamin Tissoires 					wheel = data[5];
518*501ea61eSBenjamin Tissoires 			} else {
519*501ea61eSBenjamin Tissoires 				/* data[4] and data[5] are the buttons, mapped correctly */
520*501ea61eSBenjamin Tissoires 				last_button_state = data[4] | (data[5] << 8);
521*501ea61eSBenjamin Tissoires 				wheel = 0; // wheel
522*501ea61eSBenjamin Tissoires 			}
523*501ea61eSBenjamin Tissoires 
524*501ea61eSBenjamin Tissoires 			pad_report = (struct pad_report *)data;
525*501ea61eSBenjamin Tissoires 
526*501ea61eSBenjamin Tissoires 			pad_report->report_id = PAD_REPORT_ID;
527*501ea61eSBenjamin Tissoires 			pad_report->btn_stylus = 0;
528*501ea61eSBenjamin Tissoires 			pad_report->x = 0;
529*501ea61eSBenjamin Tissoires 			pad_report->y = 0;
530*501ea61eSBenjamin Tissoires 			pad_report->buttons = last_button_state;
531*501ea61eSBenjamin Tissoires 			pad_report->wheel = wheel;
532*501ea61eSBenjamin Tissoires 
533*501ea61eSBenjamin Tissoires 			return sizeof(struct pad_report);
534*501ea61eSBenjamin Tissoires 		}
535*501ea61eSBenjamin Tissoires 
536*501ea61eSBenjamin Tissoires 		/* Pen reports need nothing done */
537*501ea61eSBenjamin Tissoires 	}
538*501ea61eSBenjamin Tissoires 
539*501ea61eSBenjamin Tissoires 	return 0;
540*501ea61eSBenjamin Tissoires }
541*501ea61eSBenjamin Tissoires 
542*501ea61eSBenjamin Tissoires HID_BPF_OPS(inspiroy_2) = {
543*501ea61eSBenjamin Tissoires 	.hid_device_event = (void *)inspiroy_2_fix_events,
544*501ea61eSBenjamin Tissoires 	.hid_rdesc_fixup = (void *)hid_fix_rdesc,
545*501ea61eSBenjamin Tissoires };
546*501ea61eSBenjamin Tissoires 
547*501ea61eSBenjamin Tissoires SEC("syscall")
548*501ea61eSBenjamin Tissoires int probe(struct hid_bpf_probe_args *ctx)
549*501ea61eSBenjamin Tissoires {
550*501ea61eSBenjamin Tissoires 	switch (ctx->rdesc_size) {
551*501ea61eSBenjamin Tissoires 	case PAD_REPORT_DESCRIPTOR_LENGTH:
552*501ea61eSBenjamin Tissoires 	case PEN_REPORT_DESCRIPTOR_LENGTH:
553*501ea61eSBenjamin Tissoires 	case VENDOR_REPORT_DESCRIPTOR_LENGTH:
554*501ea61eSBenjamin Tissoires 		ctx->retval = 0;
555*501ea61eSBenjamin Tissoires 		break;
556*501ea61eSBenjamin Tissoires 	default:
557*501ea61eSBenjamin Tissoires 		ctx->retval = -EINVAL;
558*501ea61eSBenjamin Tissoires 	}
559*501ea61eSBenjamin Tissoires 
560*501ea61eSBenjamin Tissoires 	return 0;
561*501ea61eSBenjamin Tissoires }
562*501ea61eSBenjamin Tissoires 
563*501ea61eSBenjamin Tissoires char _license[] SEC("license") = "GPL";
564