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