xref: /linux/drivers/platform/x86/tuxedo/nb04/wmi_ab.c (revision 1193e205dbb6feca917dc8e1862ffcdf2194234b)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * This driver implements the WMI AB device found on TUXEDO notebooks with board
4  * vendor NB04.
5  *
6  * Copyright (C) 2024-2025 Werner Sembach <wse@tuxedocomputers.com>
7  */
8 
9 #include <linux/dmi.h>
10 #include <linux/hid.h>
11 #include <linux/minmax.h>
12 #include <linux/module.h>
13 #include <linux/wmi.h>
14 
15 #include "wmi_util.h"
16 
17 static const struct wmi_device_id tuxedo_nb04_wmi_ab_device_ids[] = {
18 	{ .guid_string = "80C9BAA6-AC48-4538-9234-9F81A55E7C85" },
19 	{ }
20 };
21 MODULE_DEVICE_TABLE(wmi, tuxedo_nb04_wmi_ab_device_ids);
22 
23 enum {
24 	LAMP_ARRAY_ATTRIBUTES_REPORT_ID		= 0x01,
25 	LAMP_ATTRIBUTES_REQUEST_REPORT_ID	= 0x02,
26 	LAMP_ATTRIBUTES_RESPONSE_REPORT_ID	= 0x03,
27 	LAMP_MULTI_UPDATE_REPORT_ID		= 0x04,
28 	LAMP_RANGE_UPDATE_REPORT_ID		= 0x05,
29 	LAMP_ARRAY_CONTROL_REPORT_ID		= 0x06,
30 };
31 
32 static u8 tux_report_descriptor[327] = {
33 	0x05, 0x59,			// Usage Page (Lighting and Illumination)
34 	0x09, 0x01,			// Usage (Lamp Array)
35 	0xa1, 0x01,			// Collection (Application)
36 	0x85, LAMP_ARRAY_ATTRIBUTES_REPORT_ID, //  Report ID (1)
37 	0x09, 0x02,			//  Usage (Lamp Array Attributes Report)
38 	0xa1, 0x02,			//  Collection (Logical)
39 	0x09, 0x03,			//   Usage (Lamp Count)
40 	0x15, 0x00,			//   Logical Minimum (0)
41 	0x27, 0xff, 0xff, 0x00, 0x00,	//   Logical Maximum (65535)
42 	0x75, 0x10,			//   Report Size (16)
43 	0x95, 0x01,			//   Report Count (1)
44 	0xb1, 0x03,			//   Feature (Cnst,Var,Abs)
45 	0x09, 0x04,			//   Usage (Bounding Box Width In Micrometers)
46 	0x09, 0x05,			//   Usage (Bounding Box Height In Micrometers)
47 	0x09, 0x06,			//   Usage (Bounding Box Depth In Micrometers)
48 	0x09, 0x07,			//   Usage (Lamp Array Kind)
49 	0x09, 0x08,			//   Usage (Min Update Interval In Microseconds)
50 	0x15, 0x00,			//   Logical Minimum (0)
51 	0x27, 0xff, 0xff, 0xff, 0x7f,	//   Logical Maximum (2147483647)
52 	0x75, 0x20,			//   Report Size (32)
53 	0x95, 0x05,			//   Report Count (5)
54 	0xb1, 0x03,			//   Feature (Cnst,Var,Abs)
55 	0xc0,				//  End Collection
56 	0x85, LAMP_ATTRIBUTES_REQUEST_REPORT_ID, //  Report ID (2)
57 	0x09, 0x20,			//  Usage (Lamp Attributes Request Report)
58 	0xa1, 0x02,			//  Collection (Logical)
59 	0x09, 0x21,			//   Usage (Lamp Id)
60 	0x15, 0x00,			//   Logical Minimum (0)
61 	0x27, 0xff, 0xff, 0x00, 0x00,	//   Logical Maximum (65535)
62 	0x75, 0x10,			//   Report Size (16)
63 	0x95, 0x01,			//   Report Count (1)
64 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
65 	0xc0,				//  End Collection
66 	0x85, LAMP_ATTRIBUTES_RESPONSE_REPORT_ID, //  Report ID (3)
67 	0x09, 0x22,			//  Usage (Lamp Attributes Response Report)
68 	0xa1, 0x02,			//  Collection (Logical)
69 	0x09, 0x21,			//   Usage (Lamp Id)
70 	0x15, 0x00,			//   Logical Minimum (0)
71 	0x27, 0xff, 0xff, 0x00, 0x00,	//   Logical Maximum (65535)
72 	0x75, 0x10,			//   Report Size (16)
73 	0x95, 0x01,			//   Report Count (1)
74 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
75 	0x09, 0x23,			//   Usage (Position X In Micrometers)
76 	0x09, 0x24,			//   Usage (Position Y In Micrometers)
77 	0x09, 0x25,			//   Usage (Position Z In Micrometers)
78 	0x09, 0x27,			//   Usage (Update Latency In Microseconds)
79 	0x09, 0x26,			//   Usage (Lamp Purposes)
80 	0x15, 0x00,			//   Logical Minimum (0)
81 	0x27, 0xff, 0xff, 0xff, 0x7f,	//   Logical Maximum (2147483647)
82 	0x75, 0x20,			//   Report Size (32)
83 	0x95, 0x05,			//   Report Count (5)
84 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
85 	0x09, 0x28,			//   Usage (Red Level Count)
86 	0x09, 0x29,			//   Usage (Green Level Count)
87 	0x09, 0x2a,			//   Usage (Blue Level Count)
88 	0x09, 0x2b,			//   Usage (Intensity Level Count)
89 	0x09, 0x2c,			//   Usage (Is Programmable)
90 	0x09, 0x2d,			//   Usage (Input Binding)
91 	0x15, 0x00,			//   Logical Minimum (0)
92 	0x26, 0xff, 0x00,		//   Logical Maximum (255)
93 	0x75, 0x08,			//   Report Size (8)
94 	0x95, 0x06,			//   Report Count (6)
95 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
96 	0xc0,				//  End Collection
97 	0x85, LAMP_MULTI_UPDATE_REPORT_ID, //  Report ID (4)
98 	0x09, 0x50,			//  Usage (Lamp Multi Update Report)
99 	0xa1, 0x02,			//  Collection (Logical)
100 	0x09, 0x03,			//   Usage (Lamp Count)
101 	0x09, 0x55,			//   Usage (Lamp Update Flags)
102 	0x15, 0x00,			//   Logical Minimum (0)
103 	0x25, 0x08,			//   Logical Maximum (8)
104 	0x75, 0x08,			//   Report Size (8)
105 	0x95, 0x02,			//   Report Count (2)
106 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
107 	0x09, 0x21,			//   Usage (Lamp Id)
108 	0x15, 0x00,			//   Logical Minimum (0)
109 	0x27, 0xff, 0xff, 0x00, 0x00,	//   Logical Maximum (65535)
110 	0x75, 0x10,			//   Report Size (16)
111 	0x95, 0x08,			//   Report Count (8)
112 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
113 	0x09, 0x51,			//   Usage (Red Update Channel)
114 	0x09, 0x52,			//   Usage (Green Update Channel)
115 	0x09, 0x53,			//   Usage (Blue Update Channel)
116 	0x09, 0x54,			//   Usage (Intensity Update Channel)
117 	0x09, 0x51,			//   Usage (Red Update Channel)
118 	0x09, 0x52,			//   Usage (Green Update Channel)
119 	0x09, 0x53,			//   Usage (Blue Update Channel)
120 	0x09, 0x54,			//   Usage (Intensity Update Channel)
121 	0x09, 0x51,			//   Usage (Red Update Channel)
122 	0x09, 0x52,			//   Usage (Green Update Channel)
123 	0x09, 0x53,			//   Usage (Blue Update Channel)
124 	0x09, 0x54,			//   Usage (Intensity Update Channel)
125 	0x09, 0x51,			//   Usage (Red Update Channel)
126 	0x09, 0x52,			//   Usage (Green Update Channel)
127 	0x09, 0x53,			//   Usage (Blue Update Channel)
128 	0x09, 0x54,			//   Usage (Intensity Update Channel)
129 	0x09, 0x51,			//   Usage (Red Update Channel)
130 	0x09, 0x52,			//   Usage (Green Update Channel)
131 	0x09, 0x53,			//   Usage (Blue Update Channel)
132 	0x09, 0x54,			//   Usage (Intensity Update Channel)
133 	0x09, 0x51,			//   Usage (Red Update Channel)
134 	0x09, 0x52,			//   Usage (Green Update Channel)
135 	0x09, 0x53,			//   Usage (Blue Update Channel)
136 	0x09, 0x54,			//   Usage (Intensity Update Channel)
137 	0x09, 0x51,			//   Usage (Red Update Channel)
138 	0x09, 0x52,			//   Usage (Green Update Channel)
139 	0x09, 0x53,			//   Usage (Blue Update Channel)
140 	0x09, 0x54,			//   Usage (Intensity Update Channel)
141 	0x09, 0x51,			//   Usage (Red Update Channel)
142 	0x09, 0x52,			//   Usage (Green Update Channel)
143 	0x09, 0x53,			//   Usage (Blue Update Channel)
144 	0x09, 0x54,			//   Usage (Intensity Update Channel)
145 	0x15, 0x00,			//   Logical Minimum (0)
146 	0x26, 0xff, 0x00,		//   Logical Maximum (255)
147 	0x75, 0x08,			//   Report Size (8)
148 	0x95, 0x20,			//   Report Count (32)
149 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
150 	0xc0,				//  End Collection
151 	0x85, LAMP_RANGE_UPDATE_REPORT_ID, //  Report ID (5)
152 	0x09, 0x60,			//  Usage (Lamp Range Update Report)
153 	0xa1, 0x02,			//  Collection (Logical)
154 	0x09, 0x55,			//   Usage (Lamp Update Flags)
155 	0x15, 0x00,			//   Logical Minimum (0)
156 	0x25, 0x08,			//   Logical Maximum (8)
157 	0x75, 0x08,			//   Report Size (8)
158 	0x95, 0x01,			//   Report Count (1)
159 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
160 	0x09, 0x61,			//   Usage (Lamp Id Start)
161 	0x09, 0x62,			//   Usage (Lamp Id End)
162 	0x15, 0x00,			//   Logical Minimum (0)
163 	0x27, 0xff, 0xff, 0x00, 0x00,	//   Logical Maximum (65535)
164 	0x75, 0x10,			//   Report Size (16)
165 	0x95, 0x02,			//   Report Count (2)
166 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
167 	0x09, 0x51,			//   Usage (Red Update Channel)
168 	0x09, 0x52,			//   Usage (Green Update Channel)
169 	0x09, 0x53,			//   Usage (Blue Update Channel)
170 	0x09, 0x54,			//   Usage (Intensity Update Channel)
171 	0x15, 0x00,			//   Logical Minimum (0)
172 	0x26, 0xff, 0x00,		//   Logical Maximum (255)
173 	0x75, 0x08,			//   Report Size (8)
174 	0x95, 0x04,			//   Report Count (4)
175 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
176 	0xc0,				//  End Collection
177 	0x85, LAMP_ARRAY_CONTROL_REPORT_ID, //  Report ID (6)
178 	0x09, 0x70,			//  Usage (Lamp Array Control Report)
179 	0xa1, 0x02,			//  Collection (Logical)
180 	0x09, 0x71,			//   Usage (Autonomous Mode)
181 	0x15, 0x00,			//   Logical Minimum (0)
182 	0x25, 0x01,			//   Logical Maximum (1)
183 	0x75, 0x08,			//   Report Size (8)
184 	0x95, 0x01,			//   Report Count (1)
185 	0xb1, 0x02,			//   Feature (Data,Var,Abs)
186 	0xc0,				//  End Collection
187 	0xc0				// End Collection
188 };
189 
190 struct tux_kbl_map_entry_t {
191 	u8 code;
192 	struct {
193 		u32 x;
194 		u32 y;
195 		u32 z;
196 	} pos;
197 };
198 
199 static const struct tux_kbl_map_entry_t sirius_16_ansii_kbl_map[] = {
200 	{ 0x29, {  25000,  53000, 5000 } },
201 	{ 0x3a, {  41700,  53000, 5000 } },
202 	{ 0x3b, {  58400,  53000, 5000 } },
203 	{ 0x3c, {  75100,  53000, 5000 } },
204 	{ 0x3d, {  91800,  53000, 5000 } },
205 	{ 0x3e, { 108500,  53000, 5000 } },
206 	{ 0x3f, { 125200,  53000, 5000 } },
207 	{ 0x40, { 141900,  53000, 5000 } },
208 	{ 0x41, { 158600,  53000, 5000 } },
209 	{ 0x42, { 175300,  53000, 5000 } },
210 	{ 0x43, { 192000,  53000, 5000 } },
211 	{ 0x44, { 208700,  53000, 5000 } },
212 	{ 0x45, { 225400,  53000, 5000 } },
213 	{ 0xf1, { 242100,  53000, 5000 } },
214 	{ 0x46, { 258800,  53000, 5000 } },
215 	{ 0x4c, { 275500,  53000, 5000 } },
216 	{ 0x4a, { 294500,  53000, 5000 } },
217 	{ 0x4d, { 311200,  53000, 5000 } },
218 	{ 0x4b, { 327900,  53000, 5000 } },
219 	{ 0x4e, { 344600,  53000, 5000 } },
220 	{ 0x35, {  24500,  67500, 5250 } },
221 	{ 0x1e, {  42500,  67500, 5250 } },
222 	{ 0x1f, {  61000,  67500, 5250 } },
223 	{ 0x20, {  79500,  67500, 5250 } },
224 	{ 0x21, {  98000,  67500, 5250 } },
225 	{ 0x22, { 116500,  67500, 5250 } },
226 	{ 0x23, { 135000,  67500, 5250 } },
227 	{ 0x24, { 153500,  67500, 5250 } },
228 	{ 0x25, { 172000,  67500, 5250 } },
229 	{ 0x26, { 190500,  67500, 5250 } },
230 	{ 0x27, { 209000,  67500, 5250 } },
231 	{ 0x2d, { 227500,  67500, 5250 } },
232 	{ 0x2e, { 246000,  67500, 5250 } },
233 	{ 0x2a, { 269500,  67500, 5250 } },
234 	{ 0x53, { 294500,  67500, 5250 } },
235 	{ 0x55, { 311200,  67500, 5250 } },
236 	{ 0x54, { 327900,  67500, 5250 } },
237 	{ 0x56, { 344600,  67500, 5250 } },
238 	{ 0x2b, {  31000,  85500, 5500 } },
239 	{ 0x14, {  51500,  85500, 5500 } },
240 	{ 0x1a, {  70000,  85500, 5500 } },
241 	{ 0x08, {  88500,  85500, 5500 } },
242 	{ 0x15, { 107000,  85500, 5500 } },
243 	{ 0x17, { 125500,  85500, 5500 } },
244 	{ 0x1c, { 144000,  85500, 5500 } },
245 	{ 0x18, { 162500,  85500, 5500 } },
246 	{ 0x0c, { 181000,  85500, 5500 } },
247 	{ 0x12, { 199500,  85500, 5500 } },
248 	{ 0x13, { 218000,  85500, 5500 } },
249 	{ 0x2f, { 236500,  85500, 5500 } },
250 	{ 0x30, { 255000,  85500, 5500 } },
251 	{ 0x31, { 273500,  85500, 5500 } },
252 	{ 0x5f, { 294500,  85500, 5500 } },
253 	{ 0x60, { 311200,  85500, 5500 } },
254 	{ 0x61, { 327900,  85500, 5500 } },
255 	{ 0x39, {  33000, 103500, 5750 } },
256 	{ 0x04, {  57000, 103500, 5750 } },
257 	{ 0x16, {  75500, 103500, 5750 } },
258 	{ 0x07, {  94000, 103500, 5750 } },
259 	{ 0x09, { 112500, 103500, 5750 } },
260 	{ 0x0a, { 131000, 103500, 5750 } },
261 	{ 0x0b, { 149500, 103500, 5750 } },
262 	{ 0x0d, { 168000, 103500, 5750 } },
263 	{ 0x0e, { 186500, 103500, 5750 } },
264 	{ 0x0f, { 205000, 103500, 5750 } },
265 	{ 0x33, { 223500, 103500, 5750 } },
266 	{ 0x34, { 242000, 103500, 5750 } },
267 	{ 0x28, { 267500, 103500, 5750 } },
268 	{ 0x5c, { 294500, 103500, 5750 } },
269 	{ 0x5d, { 311200, 103500, 5750 } },
270 	{ 0x5e, { 327900, 103500, 5750 } },
271 	{ 0x57, { 344600,  94500, 5625 } },
272 	{ 0xe1, {  37000, 121500, 6000 } },
273 	{ 0x1d, {  66000, 121500, 6000 } },
274 	{ 0x1b, {  84500, 121500, 6000 } },
275 	{ 0x06, { 103000, 121500, 6000 } },
276 	{ 0x19, { 121500, 121500, 6000 } },
277 	{ 0x05, { 140000, 121500, 6000 } },
278 	{ 0x11, { 158500, 121500, 6000 } },
279 	{ 0x10, { 177000, 121500, 6000 } },
280 	{ 0x36, { 195500, 121500, 6000 } },
281 	{ 0x37, { 214000, 121500, 6000 } },
282 	{ 0x38, { 232500, 121500, 6000 } },
283 	{ 0xe5, { 251500, 121500, 6000 } },
284 	{ 0x52, { 273500, 129000, 6125 } },
285 	{ 0x59, { 294500, 121500, 6000 } },
286 	{ 0x5a, { 311200, 121500, 6000 } },
287 	{ 0x5b, { 327900, 121500, 6000 } },
288 	{ 0xe0, {  28000, 139500, 6250 } },
289 	{ 0xfe, {  47500, 139500, 6250 } },
290 	{ 0xe3, {  66000, 139500, 6250 } },
291 	{ 0xe2, {  84500, 139500, 6250 } },
292 	{ 0x2c, { 140000, 139500, 6250 } },
293 	{ 0xe6, { 195500, 139500, 6250 } },
294 	{ 0x65, { 214000, 139500, 6250 } },
295 	{ 0xe4, { 234000, 139500, 6250 } },
296 	{ 0x50, { 255000, 147000, 6375 } },
297 	{ 0x51, { 273500, 147000, 6375 } },
298 	{ 0x4f, { 292000, 147000, 6375 } },
299 	{ 0x62, { 311200, 139500, 6250 } },
300 	{ 0x63, { 327900, 139500, 6250 } },
301 	{ 0x58, { 344600, 130500, 6125 } },
302 };
303 
304 static const struct tux_kbl_map_entry_t sirius_16_iso_kbl_map[] = {
305 	{ 0x29, {  25000,  53000, 5000 } },
306 	{ 0x3a, {  41700,  53000, 5000 } },
307 	{ 0x3b, {  58400,  53000, 5000 } },
308 	{ 0x3c, {  75100,  53000, 5000 } },
309 	{ 0x3d, {  91800,  53000, 5000 } },
310 	{ 0x3e, { 108500,  53000, 5000 } },
311 	{ 0x3f, { 125200,  53000, 5000 } },
312 	{ 0x40, { 141900,  53000, 5000 } },
313 	{ 0x41, { 158600,  53000, 5000 } },
314 	{ 0x42, { 175300,  53000, 5000 } },
315 	{ 0x43, { 192000,  53000, 5000 } },
316 	{ 0x44, { 208700,  53000, 5000 } },
317 	{ 0x45, { 225400,  53000, 5000 } },
318 	{ 0xf1, { 242100,  53000, 5000 } },
319 	{ 0x46, { 258800,  53000, 5000 } },
320 	{ 0x4c, { 275500,  53000, 5000 } },
321 	{ 0x4a, { 294500,  53000, 5000 } },
322 	{ 0x4d, { 311200,  53000, 5000 } },
323 	{ 0x4b, { 327900,  53000, 5000 } },
324 	{ 0x4e, { 344600,  53000, 5000 } },
325 	{ 0x35, {  24500,  67500, 5250 } },
326 	{ 0x1e, {  42500,  67500, 5250 } },
327 	{ 0x1f, {  61000,  67500, 5250 } },
328 	{ 0x20, {  79500,  67500, 5250 } },
329 	{ 0x21, {  98000,  67500, 5250 } },
330 	{ 0x22, { 116500,  67500, 5250 } },
331 	{ 0x23, { 135000,  67500, 5250 } },
332 	{ 0x24, { 153500,  67500, 5250 } },
333 	{ 0x25, { 172000,  67500, 5250 } },
334 	{ 0x26, { 190500,  67500, 5250 } },
335 	{ 0x27, { 209000,  67500, 5250 } },
336 	{ 0x2d, { 227500,  67500, 5250 } },
337 	{ 0x2e, { 246000,  67500, 5250 } },
338 	{ 0x2a, { 269500,  67500, 5250 } },
339 	{ 0x53, { 294500,  67500, 5250 } },
340 	{ 0x55, { 311200,  67500, 5250 } },
341 	{ 0x54, { 327900,  67500, 5250 } },
342 	{ 0x56, { 344600,  67500, 5250 } },
343 	{ 0x2b, {  31000,  85500, 5500 } },
344 	{ 0x14, {  51500,  85500, 5500 } },
345 	{ 0x1a, {  70000,  85500, 5500 } },
346 	{ 0x08, {  88500,  85500, 5500 } },
347 	{ 0x15, { 107000,  85500, 5500 } },
348 	{ 0x17, { 125500,  85500, 5500 } },
349 	{ 0x1c, { 144000,  85500, 5500 } },
350 	{ 0x18, { 162500,  85500, 5500 } },
351 	{ 0x0c, { 181000,  85500, 5500 } },
352 	{ 0x12, { 199500,  85500, 5500 } },
353 	{ 0x13, { 218000,  85500, 5500 } },
354 	{ 0x2f, { 234500,  85500, 5500 } },
355 	{ 0x30, { 251000,  85500, 5500 } },
356 	{ 0x5f, { 294500,  85500, 5500 } },
357 	{ 0x60, { 311200,  85500, 5500 } },
358 	{ 0x61, { 327900,  85500, 5500 } },
359 	{ 0x39, {  33000, 103500, 5750 } },
360 	{ 0x04, {  57000, 103500, 5750 } },
361 	{ 0x16, {  75500, 103500, 5750 } },
362 	{ 0x07, {  94000, 103500, 5750 } },
363 	{ 0x09, { 112500, 103500, 5750 } },
364 	{ 0x0a, { 131000, 103500, 5750 } },
365 	{ 0x0b, { 149500, 103500, 5750 } },
366 	{ 0x0d, { 168000, 103500, 5750 } },
367 	{ 0x0e, { 186500, 103500, 5750 } },
368 	{ 0x0f, { 205000, 103500, 5750 } },
369 	{ 0x33, { 223500, 103500, 5750 } },
370 	{ 0x34, { 240000, 103500, 5750 } },
371 	{ 0x32, { 256500, 103500, 5750 } },
372 	{ 0x28, { 271500,  94500, 5750 } },
373 	{ 0x5c, { 294500, 103500, 5750 } },
374 	{ 0x5d, { 311200, 103500, 5750 } },
375 	{ 0x5e, { 327900, 103500, 5750 } },
376 	{ 0x57, { 344600,  94500, 5625 } },
377 	{ 0xe1, {  28000, 121500, 6000 } },
378 	{ 0x64, {  47500, 121500, 6000 } },
379 	{ 0x1d, {  66000, 121500, 6000 } },
380 	{ 0x1b, {  84500, 121500, 6000 } },
381 	{ 0x06, { 103000, 121500, 6000 } },
382 	{ 0x19, { 121500, 121500, 6000 } },
383 	{ 0x05, { 140000, 121500, 6000 } },
384 	{ 0x11, { 158500, 121500, 6000 } },
385 	{ 0x10, { 177000, 121500, 6000 } },
386 	{ 0x36, { 195500, 121500, 6000 } },
387 	{ 0x37, { 214000, 121500, 6000 } },
388 	{ 0x38, { 232500, 121500, 6000 } },
389 	{ 0xe5, { 251500, 121500, 6000 } },
390 	{ 0x52, { 273500, 129000, 6125 } },
391 	{ 0x59, { 294500, 121500, 6000 } },
392 	{ 0x5a, { 311200, 121500, 6000 } },
393 	{ 0x5b, { 327900, 121500, 6000 } },
394 	{ 0xe0, {  28000, 139500, 6250 } },
395 	{ 0xfe, {  47500, 139500, 6250 } },
396 	{ 0xe3, {  66000, 139500, 6250 } },
397 	{ 0xe2, {  84500, 139500, 6250 } },
398 	{ 0x2c, { 140000, 139500, 6250 } },
399 	{ 0xe6, { 195500, 139500, 6250 } },
400 	{ 0x65, { 214000, 139500, 6250 } },
401 	{ 0xe4, { 234000, 139500, 6250 } },
402 	{ 0x50, { 255000, 147000, 6375 } },
403 	{ 0x51, { 273500, 147000, 6375 } },
404 	{ 0x4f, { 292000, 147000, 6375 } },
405 	{ 0x62, { 311200, 139500, 6250 } },
406 	{ 0x63, { 327900, 139500, 6250 } },
407 	{ 0x58, { 344600, 130500, 6125 } },
408 };
409 
410 struct tux_driver_data_t {
411 	struct hid_device *hdev;
412 };
413 
414 struct tux_hdev_driver_data_t {
415 	u8 lamp_count;
416 	const struct tux_kbl_map_entry_t *kbl_map;
417 	u8 next_lamp_id;
418 	union tux_wmi_xx_496in_80out_in_t next_kbl_set_multiple_keys_in;
419 };
420 
tux_ll_start(struct hid_device * hdev)421 static int tux_ll_start(struct hid_device *hdev)
422 {
423 	struct wmi_device *wdev = to_wmi_device(hdev->dev.parent);
424 	struct tux_hdev_driver_data_t *driver_data;
425 	union tux_wmi_xx_8in_80out_out_t out;
426 	union tux_wmi_xx_8in_80out_in_t in;
427 	u8 keyboard_type;
428 	int ret;
429 
430 	driver_data = devm_kzalloc(&hdev->dev, sizeof(*driver_data), GFP_KERNEL);
431 	if (!driver_data)
432 		return -ENOMEM;
433 
434 	in.get_device_status_in.device_type = TUX_GET_DEVICE_STATUS_DEVICE_ID_KEYBOARD;
435 	ret = tux_wmi_xx_8in_80out(wdev, TUX_GET_DEVICE_STATUS, &in, &out);
436 	if (ret)
437 		return ret;
438 
439 	keyboard_type = out.get_device_status_out.keyboard_physical_layout;
440 	if (keyboard_type == TUX_GET_DEVICE_STATUS_KEYBOARD_LAYOUT_ANSII) {
441 		driver_data->lamp_count = ARRAY_SIZE(sirius_16_ansii_kbl_map);
442 		driver_data->kbl_map = sirius_16_ansii_kbl_map;
443 	} else if (keyboard_type == TUX_GET_DEVICE_STATUS_KEYBOARD_LAYOUT_ISO) {
444 		driver_data->lamp_count = ARRAY_SIZE(sirius_16_iso_kbl_map);
445 		driver_data->kbl_map = sirius_16_iso_kbl_map;
446 	} else {
447 		return -EINVAL;
448 	}
449 	driver_data->next_lamp_id = 0;
450 
451 	dev_set_drvdata(&hdev->dev, driver_data);
452 
453 	return ret;
454 }
455 
tux_ll_stop(struct hid_device * hdev __always_unused)456 static void tux_ll_stop(struct hid_device *hdev __always_unused)
457 {
458 }
459 
tux_ll_open(struct hid_device * hdev __always_unused)460 static int tux_ll_open(struct hid_device *hdev __always_unused)
461 {
462 	return 0;
463 }
464 
tux_ll_close(struct hid_device * hdev __always_unused)465 static void tux_ll_close(struct hid_device *hdev __always_unused)
466 {
467 }
468 
tux_ll_parse(struct hid_device * hdev)469 static int tux_ll_parse(struct hid_device *hdev)
470 {
471 	return hid_parse_report(hdev, tux_report_descriptor,
472 				sizeof(tux_report_descriptor));
473 }
474 
475 struct __packed lamp_array_attributes_report_t {
476 	const u8 report_id;
477 	u16 lamp_count;
478 	u32 bounding_box_width_in_micrometers;
479 	u32 bounding_box_height_in_micrometers;
480 	u32 bounding_box_depth_in_micrometers;
481 	u32 lamp_array_kind;
482 	u32 min_update_interval_in_microseconds;
483 };
484 
handle_lamp_array_attributes_report(struct hid_device * hdev,struct lamp_array_attributes_report_t * rep)485 static int handle_lamp_array_attributes_report(struct hid_device *hdev,
486 					       struct lamp_array_attributes_report_t *rep)
487 {
488 	struct tux_hdev_driver_data_t *driver_data = dev_get_drvdata(&hdev->dev);
489 
490 	rep->lamp_count = driver_data->lamp_count;
491 	rep->bounding_box_width_in_micrometers = 368000;
492 	rep->bounding_box_height_in_micrometers = 266000;
493 	rep->bounding_box_depth_in_micrometers = 30000;
494 	/*
495 	 * LampArrayKindKeyboard, see "26.2.1 LampArrayKind Values" of
496 	 * "HID Usage Tables v1.5"
497 	 */
498 	rep->lamp_array_kind = 1;
499 	// Some guessed value for interval microseconds
500 	rep->min_update_interval_in_microseconds = 500;
501 
502 	return sizeof(*rep);
503 }
504 
505 struct __packed lamp_attributes_request_report_t {
506 	const u8 report_id;
507 	u16 lamp_id;
508 };
509 
handle_lamp_attributes_request_report(struct hid_device * hdev,struct lamp_attributes_request_report_t * rep)510 static int handle_lamp_attributes_request_report(struct hid_device *hdev,
511 						 struct lamp_attributes_request_report_t *rep)
512 {
513 	struct tux_hdev_driver_data_t *driver_data = dev_get_drvdata(&hdev->dev);
514 
515 	if (rep->lamp_id < driver_data->lamp_count)
516 		driver_data->next_lamp_id = rep->lamp_id;
517 	else
518 		driver_data->next_lamp_id = 0;
519 
520 	return sizeof(*rep);
521 }
522 
523 struct __packed lamp_attributes_response_report_t {
524 	const u8 report_id;
525 	u16 lamp_id;
526 	u32 position_x_in_micrometers;
527 	u32 position_y_in_micrometers;
528 	u32 position_z_in_micrometers;
529 	u32 update_latency_in_microseconds;
530 	u32 lamp_purpose;
531 	u8 red_level_count;
532 	u8 green_level_count;
533 	u8 blue_level_count;
534 	u8 intensity_level_count;
535 	u8 is_programmable;
536 	u8 input_binding;
537 };
538 
handle_lamp_attributes_response_report(struct hid_device * hdev,struct lamp_attributes_response_report_t * rep)539 static int handle_lamp_attributes_response_report(struct hid_device *hdev,
540 						  struct lamp_attributes_response_report_t *rep)
541 {
542 	struct tux_hdev_driver_data_t *driver_data = dev_get_drvdata(&hdev->dev);
543 	u16 lamp_id = driver_data->next_lamp_id;
544 
545 	rep->lamp_id = lamp_id;
546 	// Some guessed value for latency microseconds
547 	rep->update_latency_in_microseconds = 100;
548 	/*
549 	 * LampPurposeControl, see "26.3.1 LampPurposes Flags" of
550 	 * "HID Usage Tables v1.5"
551 	 */
552 	rep->lamp_purpose = 1;
553 	rep->red_level_count = 0xff;
554 	rep->green_level_count = 0xff;
555 	rep->blue_level_count = 0xff;
556 	rep->intensity_level_count = 0xff;
557 	rep->is_programmable = 1;
558 
559 	if (driver_data->kbl_map[lamp_id].code <= 0xe8) {
560 		rep->input_binding = driver_data->kbl_map[lamp_id].code;
561 	} else {
562 		/*
563 		 * Everything bigger is reserved/undefined, see
564 		 * "10 Keyboard/Keypad Page (0x07)" of "HID Usage Tables v1.5"
565 		 * and should return 0, see "26.8.3 Lamp Attributes" of the same
566 		 * document.
567 		 */
568 		rep->input_binding = 0;
569 	}
570 	rep->position_x_in_micrometers = driver_data->kbl_map[lamp_id].pos.x;
571 	rep->position_y_in_micrometers = driver_data->kbl_map[lamp_id].pos.y;
572 	rep->position_z_in_micrometers = driver_data->kbl_map[lamp_id].pos.z;
573 
574 	driver_data->next_lamp_id = (driver_data->next_lamp_id + 1) % driver_data->lamp_count;
575 
576 	return sizeof(*rep);
577 }
578 
579 #define LAMP_UPDATE_FLAGS_LAMP_UPDATE_COMPLETE	BIT(0)
580 
581 struct __packed lamp_rgbi_tuple_t {
582 	u8 red;
583 	u8 green;
584 	u8 blue;
585 	u8 intensity;
586 };
587 
588 #define LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX	8
589 
590 struct __packed lamp_multi_update_report_t {
591 	const u8 report_id;
592 	u8 lamp_count;
593 	u8 lamp_update_flags;
594 	u16 lamp_id[LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX];
595 	struct lamp_rgbi_tuple_t update_channels[LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX];
596 };
597 
handle_lamp_multi_update_report(struct hid_device * hdev,struct lamp_multi_update_report_t * rep)598 static int handle_lamp_multi_update_report(struct hid_device *hdev,
599 					   struct lamp_multi_update_report_t *rep)
600 {
601 	struct tux_hdev_driver_data_t *driver_data = dev_get_drvdata(&hdev->dev);
602 	union tux_wmi_xx_496in_80out_in_t *next = &driver_data->next_kbl_set_multiple_keys_in;
603 	struct tux_kbl_set_multiple_keys_in_rgb_config_t *rgb_configs_j;
604 	struct wmi_device *wdev = to_wmi_device(hdev->dev.parent);
605 	union tux_wmi_xx_496in_80out_out_t out;
606 	u8 key_id, key_id_j, intensity_i, red_i, green_i, blue_i;
607 	int ret;
608 
609 	/*
610 	 * Catching misformatted lamp_multi_update_report and fail silently
611 	 * according to "HID Usage Tables v1.5"
612 	 */
613 	for (unsigned int i = 0; i < rep->lamp_count; ++i) {
614 		if (rep->lamp_id[i] > driver_data->lamp_count) {
615 			hid_dbg(hdev, "Out of bounds lamp_id in lamp_multi_update_report. Skipping whole report!\n");
616 			return sizeof(*rep);
617 		}
618 
619 		for (unsigned int j = i + 1; j < rep->lamp_count; ++j) {
620 			if (rep->lamp_id[i] == rep->lamp_id[j]) {
621 				hid_dbg(hdev, "Duplicate lamp_id in lamp_multi_update_report. Skipping whole report!\n");
622 				return sizeof(*rep);
623 			}
624 		}
625 	}
626 
627 	for (unsigned int i = 0; i < rep->lamp_count; ++i) {
628 		key_id = driver_data->kbl_map[rep->lamp_id[i]].code;
629 
630 		for (unsigned int j = 0;
631 		     j < TUX_KBL_SET_MULTIPLE_KEYS_LIGHTING_SETTINGS_COUNT_MAX;
632 		     ++j) {
633 			rgb_configs_j = &next->kbl_set_multiple_keys_in.rgb_configs[j];
634 			key_id_j = rgb_configs_j->key_id;
635 			if (key_id_j != 0x00 && key_id_j != key_id)
636 				continue;
637 
638 			if (key_id_j == 0x00)
639 				next->kbl_set_multiple_keys_in.rgb_configs_cnt =
640 					j + 1;
641 			rgb_configs_j->key_id = key_id;
642 			/*
643 			 * While this driver respects update_channel.intensity
644 			 * according to "HID Usage Tables v1.5" also on RGB
645 			 * leds, the Microsoft MacroPad reference implementation
646 			 * (https://github.com/microsoft/RP2040MacropadHidSample
647 			 * 1d6c3ad) does not and ignores it. If it turns out
648 			 * that Windows writes intensity = 0 for RGB leds
649 			 * instead of intensity = 255, this driver should also
650 			 * ignore the update_channel.intensity.
651 			 */
652 			intensity_i = rep->update_channels[i].intensity;
653 			red_i = rep->update_channels[i].red;
654 			green_i = rep->update_channels[i].green;
655 			blue_i = rep->update_channels[i].blue;
656 			rgb_configs_j->red = red_i * intensity_i / 0xff;
657 			rgb_configs_j->green = green_i * intensity_i / 0xff;
658 			rgb_configs_j->blue = blue_i * intensity_i / 0xff;
659 
660 			break;
661 		}
662 	}
663 
664 	if (rep->lamp_update_flags & LAMP_UPDATE_FLAGS_LAMP_UPDATE_COMPLETE) {
665 		ret = tux_wmi_xx_496in_80out(wdev, TUX_KBL_SET_MULTIPLE_KEYS,
666 					     next, &out);
667 		memset(next, 0, sizeof(*next));
668 		if (ret)
669 			return ret;
670 	}
671 
672 	return sizeof(*rep);
673 }
674 
675 struct __packed lamp_range_update_report_t {
676 	const u8 report_id;
677 	u8 lamp_update_flags;
678 	u16 lamp_id_start;
679 	u16 lamp_id_end;
680 	struct lamp_rgbi_tuple_t update_channel;
681 };
682 
handle_lamp_range_update_report(struct hid_device * hdev,struct lamp_range_update_report_t * rep)683 static int handle_lamp_range_update_report(struct hid_device *hdev,
684 					   struct lamp_range_update_report_t *rep)
685 {
686 	struct tux_hdev_driver_data_t *driver_data = dev_get_drvdata(&hdev->dev);
687 	struct lamp_multi_update_report_t lamp_multi_update_report = {
688 		.report_id = LAMP_MULTI_UPDATE_REPORT_ID,
689 	};
690 	struct lamp_rgbi_tuple_t *update_channels_j;
691 	int ret;
692 
693 	/*
694 	 * Catching misformatted lamp_range_update_report and fail silently
695 	 * according to "HID Usage Tables v1.5"
696 	 */
697 	if (rep->lamp_id_start > rep->lamp_id_end) {
698 		hid_dbg(hdev, "lamp_id_start > lamp_id_end in lamp_range_update_report. Skipping whole report!\n");
699 		return sizeof(*rep);
700 	}
701 
702 	if (rep->lamp_id_end > driver_data->lamp_count - 1) {
703 		hid_dbg(hdev, "Out of bounds lamp_id_end in lamp_range_update_report. Skipping whole report!\n");
704 		return sizeof(*rep);
705 	}
706 
707 	/*
708 	 * Break handle_lamp_range_update_report call down to multiple
709 	 * handle_lamp_multi_update_report calls to easily ensure that mixing
710 	 * handle_lamp_range_update_report and handle_lamp_multi_update_report
711 	 * does not break things.
712 	 */
713 	for (unsigned int i = rep->lamp_id_start; i < rep->lamp_id_end + 1;
714 	     i = i + LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX) {
715 		lamp_multi_update_report.lamp_count =
716 			min(rep->lamp_id_end + 1 - i,
717 			    LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX);
718 		lamp_multi_update_report.lamp_update_flags =
719 			i + LAMP_MULTI_UPDATE_REPORT_LAMP_COUNT_MAX >=
720 			rep->lamp_id_end + 1 ?
721 				LAMP_UPDATE_FLAGS_LAMP_UPDATE_COMPLETE : 0;
722 
723 		for (unsigned int j = 0; j < lamp_multi_update_report.lamp_count; ++j) {
724 			lamp_multi_update_report.lamp_id[j] = i + j;
725 			update_channels_j =
726 				&lamp_multi_update_report.update_channels[j];
727 			update_channels_j->red = rep->update_channel.red;
728 			update_channels_j->green = rep->update_channel.green;
729 			update_channels_j->blue = rep->update_channel.blue;
730 			update_channels_j->intensity = rep->update_channel.intensity;
731 		}
732 
733 		ret = handle_lamp_multi_update_report(hdev, &lamp_multi_update_report);
734 		if (ret < 0)
735 			return ret;
736 		if (ret != sizeof(lamp_multi_update_report))
737 			return -EIO;
738 	}
739 
740 	return sizeof(*rep);
741 }
742 
743 struct __packed lamp_array_control_report_t {
744 	const u8 report_id;
745 	u8 autonomous_mode;
746 };
747 
handle_lamp_array_control_report(struct hid_device * hdev __always_unused,struct lamp_array_control_report_t * rep)748 static int handle_lamp_array_control_report(struct hid_device *hdev __always_unused,
749 					    struct lamp_array_control_report_t *rep)
750 {
751 	/*
752 	 * The keyboards firmware doesn't have any built in controls and the
753 	 * built in effects are not implemented so this is a NOOP.
754 	 * According to the HID Documentation (HID Usage Tables v1.5) this
755 	 * function is optional and can be removed from the HID Report
756 	 * Descriptor, but it should first be confirmed that userspace respects
757 	 * this possibility too. The Microsoft MacroPad reference implementation
758 	 * (https://github.com/microsoft/RP2040MacropadHidSample 1d6c3ad)
759 	 * already deviates from the spec at another point, see
760 	 * handle_lamp_*_update_report.
761 	 */
762 
763 	return sizeof(*rep);
764 }
765 
tux_ll_raw_request(struct hid_device * hdev,u8 reportnum,u8 * buf,size_t len,unsigned char rtype,int reqtype)766 static int tux_ll_raw_request(struct hid_device *hdev, u8 reportnum, u8 *buf,
767 			      size_t len, unsigned char rtype, int reqtype)
768 {
769 	if (rtype != HID_FEATURE_REPORT)
770 		return -EINVAL;
771 
772 	switch (reqtype) {
773 	case HID_REQ_GET_REPORT:
774 		switch (reportnum) {
775 		case LAMP_ARRAY_ATTRIBUTES_REPORT_ID:
776 			if (len != sizeof(struct lamp_array_attributes_report_t))
777 				return -EINVAL;
778 			return handle_lamp_array_attributes_report(hdev,
779 				(struct lamp_array_attributes_report_t *)buf);
780 		case LAMP_ATTRIBUTES_RESPONSE_REPORT_ID:
781 			if (len != sizeof(struct lamp_attributes_response_report_t))
782 				return -EINVAL;
783 			return handle_lamp_attributes_response_report(hdev,
784 				(struct lamp_attributes_response_report_t *)buf);
785 		}
786 		break;
787 	case HID_REQ_SET_REPORT:
788 		switch (reportnum) {
789 		case LAMP_ATTRIBUTES_REQUEST_REPORT_ID:
790 			if (len != sizeof(struct lamp_attributes_request_report_t))
791 				return -EINVAL;
792 			return handle_lamp_attributes_request_report(hdev,
793 				(struct lamp_attributes_request_report_t *)buf);
794 		case LAMP_MULTI_UPDATE_REPORT_ID:
795 			if (len != sizeof(struct lamp_multi_update_report_t))
796 				return -EINVAL;
797 			return handle_lamp_multi_update_report(hdev,
798 				(struct lamp_multi_update_report_t *)buf);
799 		case LAMP_RANGE_UPDATE_REPORT_ID:
800 			if (len != sizeof(struct lamp_range_update_report_t))
801 				return -EINVAL;
802 			return handle_lamp_range_update_report(hdev,
803 				(struct lamp_range_update_report_t *)buf);
804 		case LAMP_ARRAY_CONTROL_REPORT_ID:
805 			if (len != sizeof(struct lamp_array_control_report_t))
806 				return -EINVAL;
807 			return handle_lamp_array_control_report(hdev,
808 				(struct lamp_array_control_report_t *)buf);
809 		}
810 		break;
811 	}
812 
813 	return -EINVAL;
814 }
815 
816 static const struct hid_ll_driver tux_ll_driver = {
817 	.start = &tux_ll_start,
818 	.stop = &tux_ll_stop,
819 	.open = &tux_ll_open,
820 	.close = &tux_ll_close,
821 	.parse = &tux_ll_parse,
822 	.raw_request = &tux_ll_raw_request,
823 };
824 
tux_virt_lamparray_add_device(struct wmi_device * wdev,struct hid_device ** hdev_out)825 static int tux_virt_lamparray_add_device(struct wmi_device *wdev,
826 					 struct hid_device **hdev_out)
827 {
828 	struct hid_device *hdev;
829 	int ret;
830 
831 	dev_dbg(&wdev->dev, "Adding TUXEDO NB04 Virtual LampArray device.\n");
832 
833 	hdev = hid_allocate_device();
834 	if (IS_ERR(hdev))
835 		return PTR_ERR(hdev);
836 	*hdev_out = hdev;
837 
838 	strscpy(hdev->name, "TUXEDO NB04 RGB Lighting", sizeof(hdev->name));
839 
840 	hdev->ll_driver = &tux_ll_driver;
841 	hdev->bus = BUS_VIRTUAL;
842 	hdev->vendor = 0x21ba;
843 	hdev->product = 0x0400;
844 	hdev->dev.parent = &wdev->dev;
845 
846 	ret = hid_add_device(hdev);
847 	if (ret)
848 		hid_destroy_device(hdev);
849 	return ret;
850 }
851 
tux_probe(struct wmi_device * wdev,const void * context __always_unused)852 static int tux_probe(struct wmi_device *wdev, const void *context __always_unused)
853 {
854 	struct tux_driver_data_t *driver_data;
855 
856 	driver_data = devm_kzalloc(&wdev->dev, sizeof(*driver_data), GFP_KERNEL);
857 	if (!driver_data)
858 		return -ENOMEM;
859 
860 	dev_set_drvdata(&wdev->dev, driver_data);
861 
862 	return tux_virt_lamparray_add_device(wdev, &driver_data->hdev);
863 }
864 
tux_remove(struct wmi_device * wdev)865 static void tux_remove(struct wmi_device *wdev)
866 {
867 	struct tux_driver_data_t *driver_data = dev_get_drvdata(&wdev->dev);
868 
869 	hid_destroy_device(driver_data->hdev);
870 }
871 
872 static struct wmi_driver tuxedo_nb04_wmi_tux_driver = {
873 	.driver = {
874 		.name = "tuxedo_nb04_wmi_ab",
875 		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
876 	},
877 	.id_table = tuxedo_nb04_wmi_ab_device_ids,
878 	.probe = tux_probe,
879 	.remove = tux_remove,
880 	.no_singleton = true,
881 };
882 
883 /*
884  * We don't know if the WMI API is stable and how unique the GUID is for this
885  * ODM. To be on the safe side we therefore only run this driver on tested
886  * devices defined by this list.
887  */
888 static const struct dmi_system_id tested_devices_dmi_table[] __initconst = {
889 	{
890 		// TUXEDO Sirius 16 Gen1
891 		.matches = {
892 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
893 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "APX958"),
894 		},
895 	},
896 	{
897 		// TUXEDO Sirius 16 Gen2
898 		.matches = {
899 			DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TUXEDO"),
900 			DMI_EXACT_MATCH(DMI_BOARD_NAME, "AHP958"),
901 		},
902 	},
903 	{ }
904 };
905 
tuxedo_nb04_wmi_tux_init(void)906 static int __init tuxedo_nb04_wmi_tux_init(void)
907 {
908 	if (!dmi_check_system(tested_devices_dmi_table))
909 		return -ENODEV;
910 
911 	return wmi_driver_register(&tuxedo_nb04_wmi_tux_driver);
912 }
913 module_init(tuxedo_nb04_wmi_tux_init);
914 
tuxedo_nb04_wmi_tux_exit(void)915 static void __exit tuxedo_nb04_wmi_tux_exit(void)
916 {
917 	return wmi_driver_unregister(&tuxedo_nb04_wmi_tux_driver);
918 }
919 module_exit(tuxedo_nb04_wmi_tux_exit);
920 
921 MODULE_DESCRIPTION("Virtual HID LampArray interface for TUXEDO NB04 devices");
922 MODULE_AUTHOR("Werner Sembach <wse@tuxedocomputers.com>");
923 MODULE_LICENSE("GPL");
924