1// SPDX-License-Identifier: GPL-2.0+
2
3/*
4 * Quirks for I2C-HID devices that do not supply proper descriptors
5 *
6 * Copyright (c) 2018 Julian Sax <jsbc@gmx.de>
7 *
8 */
9
10#include <linux/types.h>
11#include <linux/dmi.h>
12#include <linux/mod_devicetable.h>
13#include <linux/hid.h>
14
15#include "i2c-hid.h"
16#include "../hid-ids.h"
17
18
19struct i2c_hid_desc_override {
20 union {
21 struct i2c_hid_desc *i2c_hid_desc;
22 uint8_t *i2c_hid_desc_buffer;
23 };
24 uint8_t *hid_report_desc;
25 unsigned int hid_report_desc_size;
26 uint8_t *i2c_name;
27};
28
29
30/*
31 * descriptors for the SIPODEV SP1064 touchpad
32 *
33 * This device does not supply any descriptors and on windows a filter
34 * driver operates between the i2c-hid layer and the device and injects
35 * these descriptors when the device is prompted. The descriptors were
36 * extracted by listening to the i2c-hid traffic that occurs between the
37 * windows filter driver and the windows i2c-hid driver.
38 */
39
40static const struct i2c_hid_desc_override sipodev_desc = {
41 .i2c_hid_desc_buffer = (uint8_t [])
42 {0x1e, 0x00, /* Length of descriptor */
43 0x00, 0x01, /* Version of descriptor */
44 0xdb, 0x01, /* Length of report descriptor */
45 0x21, 0x00, /* Location of report descriptor */
46 0x24, 0x00, /* Location of input report */
47 0x1b, 0x00, /* Max input report length */
48 0x25, 0x00, /* Location of output report */
49 0x11, 0x00, /* Max output report length */
50 0x22, 0x00, /* Location of command register */
51 0x23, 0x00, /* Location of data register */
52 0x11, 0x09, /* Vendor ID */
53 0x88, 0x52, /* Product ID */
54 0x06, 0x00, /* Version ID */
55 0x00, 0x00, 0x00, 0x00 /* Reserved */
56 },
57
58 .hid_report_desc = (uint8_t [])
59 {0x05, 0x01, /* Usage Page (Desktop), */
60 0x09, 0x02, /* Usage (Mouse), */
61 0xA1, 0x01, /* Collection (Application), */
62 0x85, 0x01, /* Report ID (1), */
63 0x09, 0x01, /* Usage (Pointer), */
64 0xA1, 0x00, /* Collection (Physical), */
65 0x05, 0x09, /* Usage Page (Button), */
66 0x19, 0x01, /* Usage Minimum (01h), */
67 0x29, 0x02, /* Usage Maximum (02h), */
68 0x25, 0x01, /* Logical Maximum (1), */
69 0x75, 0x01, /* Report Size (1), */
70 0x95, 0x02, /* Report Count (2), */
71 0x81, 0x02, /* Input (Variable), */
72 0x95, 0x06, /* Report Count (6), */
73 0x81, 0x01, /* Input (Constant), */
74 0x05, 0x01, /* Usage Page (Desktop), */
75 0x09, 0x30, /* Usage (X), */
76 0x09, 0x31, /* Usage (Y), */
77 0x15, 0x81, /* Logical Minimum (-127), */
78 0x25, 0x7F, /* Logical Maximum (127), */
79 0x75, 0x08, /* Report Size (8), */
80 0x95, 0x02, /* Report Count (2), */
81 0x81, 0x06, /* Input (Variable, Relative), */
82 0xC0, /* End Collection, */
83 0xC0, /* End Collection, */
84 0x05, 0x0D, /* Usage Page (Digitizer), */
85 0x09, 0x05, /* Usage (Touchpad), */
86 0xA1, 0x01, /* Collection (Application), */
87 0x85, 0x04, /* Report ID (4), */
88 0x05, 0x0D, /* Usage Page (Digitizer), */
89 0x09, 0x22, /* Usage (Finger), */
90 0xA1, 0x02, /* Collection (Logical), */
91 0x15, 0x00, /* Logical Minimum (0), */
92 0x25, 0x01, /* Logical Maximum (1), */
93 0x09, 0x47, /* Usage (Touch Valid), */
94 0x09, 0x42, /* Usage (Tip Switch), */
95 0x95, 0x02, /* Report Count (2), */
96 0x75, 0x01, /* Report Size (1), */
97 0x81, 0x02, /* Input (Variable), */
98 0x95, 0x01, /* Report Count (1), */
99 0x75, 0x03, /* Report Size (3), */
100 0x25, 0x05, /* Logical Maximum (5), */
101 0x09, 0x51, /* Usage (Contact Identifier), */
102 0x81, 0x02, /* Input (Variable), */
103 0x75, 0x01, /* Report Size (1), */
104 0x95, 0x03, /* Report Count (3), */
105 0x81, 0x03, /* Input (Constant, Variable), */
106 0x05, 0x01, /* Usage Page (Desktop), */
107 0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
108 0x75, 0x10, /* Report Size (16), */
109 0x55, 0x0E, /* Unit Exponent (14), */
110 0x65, 0x11, /* Unit (Centimeter), */
111 0x09, 0x30, /* Usage (X), */
112 0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
113 0x95, 0x01, /* Report Count (1), */
114 0x81, 0x02, /* Input (Variable), */
115 0x46, 0xBC, 0x02, /* Physical Maximum (700), */
116 0x26, 0x34, 0x05, /* Logical Maximum (1332), */
117 0x09, 0x31, /* Usage (Y), */
118 0x81, 0x02, /* Input (Variable), */
119 0xC0, /* End Collection, */
120 0x05, 0x0D, /* Usage Page (Digitizer), */
121 0x09, 0x22, /* Usage (Finger), */
122 0xA1, 0x02, /* Collection (Logical), */
123 0x25, 0x01, /* Logical Maximum (1), */
124 0x09, 0x47, /* Usage (Touch Valid), */
125 0x09, 0x42, /* Usage (Tip Switch), */
126 0x95, 0x02, /* Report Count (2), */
127 0x75, 0x01, /* Report Size (1), */
128 0x81, 0x02, /* Input (Variable), */
129 0x95, 0x01, /* Report Count (1), */
130 0x75, 0x03, /* Report Size (3), */
131 0x25, 0x05, /* Logical Maximum (5), */
132 0x09, 0x51, /* Usage (Contact Identifier), */
133 0x81, 0x02, /* Input (Variable), */
134 0x75, 0x01, /* Report Size (1), */
135 0x95, 0x03, /* Report Count (3), */
136 0x81, 0x03, /* Input (Constant, Variable), */
137 0x05, 0x01, /* Usage Page (Desktop), */
138 0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
139 0x75, 0x10, /* Report Size (16), */
140 0x09, 0x30, /* Usage (X), */
141 0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
142 0x95, 0x01, /* Report Count (1), */
143 0x81, 0x02, /* Input (Variable), */
144 0x46, 0xBC, 0x02, /* Physical Maximum (700), */
145 0x26, 0x34, 0x05, /* Logical Maximum (1332), */
146 0x09, 0x31, /* Usage (Y), */
147 0x81, 0x02, /* Input (Variable), */
148 0xC0, /* End Collection, */
149 0x05, 0x0D, /* Usage Page (Digitizer), */
150 0x09, 0x22, /* Usage (Finger), */
151 0xA1, 0x02, /* Collection (Logical), */
152 0x25, 0x01, /* Logical Maximum (1), */
153 0x09, 0x47, /* Usage (Touch Valid), */
154 0x09, 0x42, /* Usage (Tip Switch), */
155 0x95, 0x02, /* Report Count (2), */
156 0x75, 0x01, /* Report Size (1), */
157 0x81, 0x02, /* Input (Variable), */
158 0x95, 0x01, /* Report Count (1), */
159 0x75, 0x03, /* Report Size (3), */
160 0x25, 0x05, /* Logical Maximum (5), */
161 0x09, 0x51, /* Usage (Contact Identifier), */
162 0x81, 0x02, /* Input (Variable), */
163 0x75, 0x01, /* Report Size (1), */
164 0x95, 0x03, /* Report Count (3), */
165 0x81, 0x03, /* Input (Constant, Variable), */
166 0x05, 0x01, /* Usage Page (Desktop), */
167 0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
168 0x75, 0x10, /* Report Size (16), */
169 0x09, 0x30, /* Usage (X), */
170 0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
171 0x95, 0x01, /* Report Count (1), */
172 0x81, 0x02, /* Input (Variable), */
173 0x46, 0xBC, 0x02, /* Physical Maximum (700), */
174 0x26, 0x34, 0x05, /* Logical Maximum (1332), */
175 0x09, 0x31, /* Usage (Y), */
176 0x81, 0x02, /* Input (Variable), */
177 0xC0, /* End Collection, */
178 0x05, 0x0D, /* Usage Page (Digitizer), */
179 0x09, 0x22, /* Usage (Finger), */
180 0xA1, 0x02, /* Collection (Logical), */
181 0x25, 0x01, /* Logical Maximum (1), */
182 0x09, 0x47, /* Usage (Touch Valid), */
183 0x09, 0x42, /* Usage (Tip Switch), */
184 0x95, 0x02, /* Report Count (2), */
185 0x75, 0x01, /* Report Size (1), */
186 0x81, 0x02, /* Input (Variable), */
187 0x95, 0x01, /* Report Count (1), */
188 0x75, 0x03, /* Report Size (3), */
189 0x25, 0x05, /* Logical Maximum (5), */
190 0x09, 0x51, /* Usage (Contact Identifier), */
191 0x81, 0x02, /* Input (Variable), */
192 0x75, 0x01, /* Report Size (1), */
193 0x95, 0x03, /* Report Count (3), */
194 0x81, 0x03, /* Input (Constant, Variable), */
195 0x05, 0x01, /* Usage Page (Desktop), */
196 0x26, 0x44, 0x0A, /* Logical Maximum (2628), */
197 0x75, 0x10, /* Report Size (16), */
198 0x09, 0x30, /* Usage (X), */
199 0x46, 0x1A, 0x04, /* Physical Maximum (1050), */
200 0x95, 0x01, /* Report Count (1), */
201 0x81, 0x02, /* Input (Variable), */
202 0x46, 0xBC, 0x02, /* Physical Maximum (700), */
203 0x26, 0x34, 0x05, /* Logical Maximum (1332), */
204 0x09, 0x31, /* Usage (Y), */
205 0x81, 0x02, /* Input (Variable), */
206 0xC0, /* End Collection, */
207 0x05, 0x0D, /* Usage Page (Digitizer), */
208 0x55, 0x0C, /* Unit Exponent (12), */
209 0x66, 0x01, 0x10, /* Unit (Seconds), */
210 0x47, 0xFF, 0xFF, 0x00, 0x00,/* Physical Maximum (65535), */
211 0x27, 0xFF, 0xFF, 0x00, 0x00,/* Logical Maximum (65535), */
212 0x75, 0x10, /* Report Size (16), */
213 0x95, 0x01, /* Report Count (1), */
214 0x09, 0x56, /* Usage (Scan Time), */
215 0x81, 0x02, /* Input (Variable), */
216 0x09, 0x54, /* Usage (Contact Count), */
217 0x25, 0x7F, /* Logical Maximum (127), */
218 0x75, 0x08, /* Report Size (8), */
219 0x81, 0x02, /* Input (Variable), */
220 0x05, 0x09, /* Usage Page (Button), */
221 0x09, 0x01, /* Usage (01h), */
222 0x25, 0x01, /* Logical Maximum (1), */
223 0x75, 0x01, /* Report Size (1), */
224 0x95, 0x01, /* Report Count (1), */
225 0x81, 0x02, /* Input (Variable), */
226 0x95, 0x07, /* Report Count (7), */
227 0x81, 0x03, /* Input (Constant, Variable), */
228 0x05, 0x0D, /* Usage Page (Digitizer), */
229 0x85, 0x02, /* Report ID (2), */
230 0x09, 0x55, /* Usage (Contact Count Maximum), */
231 0x09, 0x59, /* Usage (59h), */
232 0x75, 0x04, /* Report Size (4), */
233 0x95, 0x02, /* Report Count (2), */
234 0x25, 0x0F, /* Logical Maximum (15), */
235 0xB1, 0x02, /* Feature (Variable), */
236 0x05, 0x0D, /* Usage Page (Digitizer), */
237 0x85, 0x07, /* Report ID (7), */
238 0x09, 0x60, /* Usage (60h), */
239 0x75, 0x01, /* Report Size (1), */
240 0x95, 0x01, /* Report Count (1), */
241 0x25, 0x01, /* Logical Maximum (1), */
242 0xB1, 0x02, /* Feature (Variable), */
243 0x95, 0x07, /* Report Count (7), */
244 0xB1, 0x03, /* Feature (Constant, Variable), */
245 0x85, 0x06, /* Report ID (6), */
246 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
247 0x09, 0xC5, /* Usage (C5h), */
248 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
249 0x75, 0x08, /* Report Size (8), */
250 0x96, 0x00, 0x01, /* Report Count (256), */
251 0xB1, 0x02, /* Feature (Variable), */
252 0xC0, /* End Collection, */
253 0x06, 0x00, 0xFF, /* Usage Page (FF00h), */
254 0x09, 0x01, /* Usage (01h), */
255 0xA1, 0x01, /* Collection (Application), */
256 0x85, 0x0D, /* Report ID (13), */
257 0x26, 0xFF, 0x00, /* Logical Maximum (255), */
258 0x19, 0x01, /* Usage Minimum (01h), */
259 0x29, 0x02, /* Usage Maximum (02h), */
260 0x75, 0x08, /* Report Size (8), */
261 0x95, 0x02, /* Report Count (2), */
262 0xB1, 0x02, /* Feature (Variable), */
263 0xC0, /* End Collection, */
264 0x05, 0x0D, /* Usage Page (Digitizer), */
265 0x09, 0x0E, /* Usage (Configuration), */
266 0xA1, 0x01, /* Collection (Application), */
267 0x85, 0x03, /* Report ID (3), */
268 0x09, 0x22, /* Usage (Finger), */
269 0xA1, 0x02, /* Collection (Logical), */
270 0x09, 0x52, /* Usage (Device Mode), */
271 0x25, 0x0A, /* Logical Maximum (10), */
272 0x95, 0x01, /* Report Count (1), */
273 0xB1, 0x02, /* Feature (Variable), */
274 0xC0, /* End Collection, */
275 0x09, 0x22, /* Usage (Finger), */
276 0xA1, 0x00, /* Collection (Physical), */
277 0x85, 0x05, /* Report ID (5), */
278 0x09, 0x57, /* Usage (57h), */
279 0x09, 0x58, /* Usage (58h), */
280 0x75, 0x01, /* Report Size (1), */
281 0x95, 0x02, /* Report Count (2), */
282 0x25, 0x01, /* Logical Maximum (1), */
283 0xB1, 0x02, /* Feature (Variable), */
284 0x95, 0x06, /* Report Count (6), */
285 0xB1, 0x03, /* Feature (Constant, Variable),*/
286 0xC0, /* End Collection, */
287 0xC0 /* End Collection */
288 },
289 .hid_report_desc_size = 475,
290 .i2c_name = "SYNA3602:00"
291};
292
293
294static const struct dmi_system_id i2c_hid_dmi_desc_override_table[] = {
295 {
296 .ident = "Teclast F6 Pro",
297 .matches = {
298 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
299 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F6 Pro"),
300 },
301 .driver_data = (void *)&sipodev_desc
302 },
303 {
304 .ident = "Teclast F7",
305 .matches = {
306 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TECLAST"),
307 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "F7"),
308 },
309 .driver_data = (void *)&sipodev_desc
310 },
311 {
312 .ident = "Trekstor Primebook C13",
313 .matches = {
314 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
315 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C13"),
316 },
317 .driver_data = (void *)&sipodev_desc
318 },
319 {
320 .ident = "Trekstor Primebook C11",
321 .matches = {
322 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
323 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11"),
324 },
325 .driver_data = (void *)&sipodev_desc
326 },
327 {
328 /*
329 * There are at least 2 Primebook C11B versions, the older
330 * version has a product-name of "Primebook C11B", and a
331 * bios version / release / firmware revision of:
332 * V2.1.2 / 05/03/2018 / 18.2
333 * The new version has "PRIMEBOOK C11B" as product-name and a
334 * bios version / release / firmware revision of:
335 * CFALKSW05_BIOS_V1.1.2 / 11/19/2018 / 19.2
336 * Only the older version needs this quirk, note the newer
337 * version will not match as it has a different product-name.
338 */
339 .ident = "Trekstor Primebook C11B",
340 .matches = {
341 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
342 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Primebook C11B"),
343 },
344 .driver_data = (void *)&sipodev_desc
345 },
346 {
347 .ident = "Trekstor SURFBOOK E11B",
348 .matches = {
349 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "TREKSTOR"),
350 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SURFBOOK E11B"),
351 },
352 .driver_data = (void *)&sipodev_desc
353 },
354 {
355 .ident = "Direkt-Tek DTLAPY116-2",
356 .matches = {
357 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
358 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY116-2"),
359 },
360 .driver_data = (void *)&sipodev_desc
361 },
362 {
363 .ident = "Direkt-Tek DTLAPY133-1",
364 .matches = {
365 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Direkt-Tek"),
366 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "DTLAPY133-1"),
367 },
368 .driver_data = (void *)&sipodev_desc
369 },
370 {
371 .ident = "Mediacom Flexbook Edge 11",
372 .matches = {
373 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
374 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook edge11 - M-FBE11"),
375 },
376 .driver_data = (void *)&sipodev_desc
377 },
378 {
379 .ident = "Mediacom FlexBook edge 13",
380 .matches = {
381 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "MEDIACOM"),
382 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "FlexBook_edge13-M-FBE13"),
383 },
384 .driver_data = (void *)&sipodev_desc
385 },
386 {
387 .ident = "Odys Winbook 13",
388 .matches = {
389 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "AXDIA International GmbH"),
390 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "WINBOOK 13"),
391 },
392 .driver_data = (void *)&sipodev_desc
393 },
394 {
395 .ident = "iBall Aer3",
396 .matches = {
397 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "iBall"),
398 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Aer3"),
399 },
400 .driver_data = (void *)&sipodev_desc
401 },
402 {
403 .ident = "Schneider SCL142ALM",
404 .matches = {
405 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "SCHNEIDER"),
406 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SCL142ALM"),
407 },
408 .driver_data = (void *)&sipodev_desc
409 },
410 {
411 .ident = "Vero K147",
412 .matches = {
413 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "VERO"),
414 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "K147"),
415 },
416 .driver_data = (void *)&sipodev_desc
417 },
418 { } /* Terminate list */
419};
420
421static const struct hid_device_id i2c_hid_elan_flipped_quirks = {
422 HID_DEVICE(BUS_I2C, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_ELAN, 0x2dcd),
423 HID_QUIRK_X_INVERT | HID_QUIRK_Y_INVERT
424};
425
426/*
427 * This list contains devices which have specific issues based on the system
428 * they're on and not just the device itself. The driver_data will have a
429 * specific hid device to match against.
430 */
431static const struct dmi_system_id i2c_hid_dmi_quirk_table[] = {
432 {
433 .ident = "DynaBook K50/FR",
434 .matches = {
435 DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Dynabook Inc."),
436 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "dynabook K50/FR"),
437 },
438 .driver_data = (void *)&i2c_hid_elan_flipped_quirks,
439 },
440 { } /* Terminate list */
441};
442
443
444struct i2c_hid_desc *i2c_hid_get_dmi_i2c_hid_desc_override(uint8_t *i2c_name)
445{
446 struct i2c_hid_desc_override *override;
447 const struct dmi_system_id *system_id;
448
449 system_id = dmi_first_match(list: i2c_hid_dmi_desc_override_table);
450 if (!system_id)
451 return NULL;
452
453 override = system_id->driver_data;
454 if (strcmp(override->i2c_name, i2c_name))
455 return NULL;
456
457 return override->i2c_hid_desc;
458}
459
460char *i2c_hid_get_dmi_hid_report_desc_override(uint8_t *i2c_name,
461 unsigned int *size)
462{
463 struct i2c_hid_desc_override *override;
464 const struct dmi_system_id *system_id;
465
466 system_id = dmi_first_match(list: i2c_hid_dmi_desc_override_table);
467 if (!system_id)
468 return NULL;
469
470 override = system_id->driver_data;
471 if (strcmp(override->i2c_name, i2c_name))
472 return NULL;
473
474 *size = override->hid_report_desc_size;
475 return override->hid_report_desc;
476}
477
478u32 i2c_hid_get_dmi_quirks(const u16 vendor, const u16 product)
479{
480 u32 quirks = 0;
481 const struct dmi_system_id *system_id =
482 dmi_first_match(list: i2c_hid_dmi_quirk_table);
483
484 if (system_id) {
485 const struct hid_device_id *device_id =
486 (struct hid_device_id *)(system_id->driver_data);
487
488 if (device_id && device_id->vendor == vendor &&
489 device_id->product == product)
490 quirks = device_id->driver_data;
491 }
492
493 return quirks;
494}
495

source code of linux/drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c