diff options
-rw-r--r-- | drivers/hid/Makefile | 2 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-core.c | 493 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-rdesc.c (renamed from drivers/hid/hid-uclogic.c) | 673 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-rdesc.h | 117 |
4 files changed, 745 insertions, 540 deletions
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index a57e1088133a..fb75366ea776 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -108,6 +108,8 @@ obj-$(CONFIG_HID_THRUSTMASTER) += hid-tmff.o | |||
108 | obj-$(CONFIG_HID_TIVO) += hid-tivo.o | 108 | obj-$(CONFIG_HID_TIVO) += hid-tivo.o |
109 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o | 109 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o |
110 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o | 110 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o |
111 | hid-uclogic-objs := hid-uclogic-core.o \ | ||
112 | hid-uclogic-rdesc.o | ||
111 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o | 113 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o |
112 | obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o | 114 | obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o |
113 | obj-$(CONFIG_HID_LED) += hid-led.o | 115 | obj-$(CONFIG_HID_LED) += hid-led.o |
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c new file mode 100644 index 000000000000..4042183ee9a3 --- /dev/null +++ b/drivers/hid/hid-uclogic-core.c | |||
@@ -0,0 +1,493 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
2 | /* | ||
3 | * HID driver for UC-Logic devices not fully compliant with HID standard | ||
4 | * | ||
5 | * Copyright (c) 2010-2014 Nikolai Kondrashov | ||
6 | * Copyright (c) 2013 Martin Rusko | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License as published by the Free | ||
12 | * Software Foundation; either version 2 of the License, or (at your option) | ||
13 | * any later version. | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/hid.h> | ||
18 | #include <linux/module.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include "usbhid/usbhid.h" | ||
21 | #include "hid-uclogic-rdesc.h" | ||
22 | |||
23 | #include "hid-ids.h" | ||
24 | |||
25 | /* Parameter indices */ | ||
26 | enum uclogic_prm { | ||
27 | UCLOGIC_PRM_X_LM = 1, | ||
28 | UCLOGIC_PRM_Y_LM = 2, | ||
29 | UCLOGIC_PRM_PRESSURE_LM = 4, | ||
30 | UCLOGIC_PRM_RESOLUTION = 5, | ||
31 | UCLOGIC_PRM_NUM | ||
32 | }; | ||
33 | |||
34 | /* Driver data */ | ||
35 | struct uclogic_drvdata { | ||
36 | __u8 *rdesc; | ||
37 | unsigned int rsize; | ||
38 | bool invert_pen_inrange; | ||
39 | bool ignore_pen_usage; | ||
40 | bool has_virtual_pad_interface; | ||
41 | }; | ||
42 | |||
43 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | ||
44 | unsigned int *rsize) | ||
45 | { | ||
46 | struct usb_interface *iface = to_usb_interface(hdev->dev.parent); | ||
47 | __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; | ||
48 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
49 | |||
50 | if (drvdata->rdesc != NULL) { | ||
51 | rdesc = drvdata->rdesc; | ||
52 | *rsize = drvdata->rsize; | ||
53 | return rdesc; | ||
54 | } | ||
55 | |||
56 | switch (hdev->product) { | ||
57 | case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: | ||
58 | if (*rsize == UCLOGIC_RDESC_PF1209_ORIG_SIZE) { | ||
59 | rdesc = uclogic_rdesc_pf1209_fixed_arr; | ||
60 | *rsize = uclogic_rdesc_pf1209_fixed_size; | ||
61 | } | ||
62 | break; | ||
63 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: | ||
64 | if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { | ||
65 | rdesc = uclogic_rdesc_wp4030u_fixed_arr; | ||
66 | *rsize = uclogic_rdesc_wp4030u_fixed_size; | ||
67 | } | ||
68 | break; | ||
69 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: | ||
70 | if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { | ||
71 | rdesc = uclogic_rdesc_wp5540u_fixed_arr; | ||
72 | *rsize = uclogic_rdesc_wp5540u_fixed_size; | ||
73 | } | ||
74 | break; | ||
75 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: | ||
76 | if (*rsize == UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE) { | ||
77 | rdesc = uclogic_rdesc_wp8060u_fixed_arr; | ||
78 | *rsize = uclogic_rdesc_wp8060u_fixed_size; | ||
79 | } | ||
80 | break; | ||
81 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: | ||
82 | if (*rsize == UCLOGIC_RDESC_WP1062_ORIG_SIZE) { | ||
83 | rdesc = uclogic_rdesc_wp1062_fixed_arr; | ||
84 | *rsize = uclogic_rdesc_wp1062_fixed_size; | ||
85 | } | ||
86 | break; | ||
87 | case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: | ||
88 | switch (iface_num) { | ||
89 | case 0: | ||
90 | if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG0_SIZE) { | ||
91 | rdesc = uclogic_rdesc_twhl850_fixed0_arr; | ||
92 | *rsize = uclogic_rdesc_twhl850_fixed0_size; | ||
93 | } | ||
94 | break; | ||
95 | case 1: | ||
96 | if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG1_SIZE) { | ||
97 | rdesc = uclogic_rdesc_twhl850_fixed1_arr; | ||
98 | *rsize = uclogic_rdesc_twhl850_fixed1_size; | ||
99 | } | ||
100 | break; | ||
101 | case 2: | ||
102 | if (*rsize == UCLOGIC_RDESC_TWHL850_ORIG2_SIZE) { | ||
103 | rdesc = uclogic_rdesc_twhl850_fixed2_arr; | ||
104 | *rsize = uclogic_rdesc_twhl850_fixed2_size; | ||
105 | } | ||
106 | break; | ||
107 | } | ||
108 | break; | ||
109 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
110 | switch (iface_num) { | ||
111 | case 0: | ||
112 | if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG0_SIZE) { | ||
113 | rdesc = uclogic_rdesc_twha60_fixed0_arr; | ||
114 | *rsize = uclogic_rdesc_twha60_fixed0_size; | ||
115 | } | ||
116 | break; | ||
117 | case 1: | ||
118 | if (*rsize == UCLOGIC_RDESC_TWHA60_ORIG1_SIZE) { | ||
119 | rdesc = uclogic_rdesc_twha60_fixed1_arr; | ||
120 | *rsize = uclogic_rdesc_twha60_fixed1_size; | ||
121 | } | ||
122 | break; | ||
123 | } | ||
124 | break; | ||
125 | } | ||
126 | |||
127 | return rdesc; | ||
128 | } | ||
129 | |||
130 | static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
131 | struct hid_field *field, struct hid_usage *usage, | ||
132 | unsigned long **bit, int *max) | ||
133 | { | ||
134 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
135 | |||
136 | /* discard the unused pen interface */ | ||
137 | if ((drvdata->ignore_pen_usage) && | ||
138 | (field->application == HID_DG_PEN)) | ||
139 | return -1; | ||
140 | |||
141 | /* let hid-core decide what to do */ | ||
142 | return 0; | ||
143 | } | ||
144 | |||
145 | static int uclogic_input_configured(struct hid_device *hdev, | ||
146 | struct hid_input *hi) | ||
147 | { | ||
148 | char *name; | ||
149 | const char *suffix = NULL; | ||
150 | struct hid_field *field; | ||
151 | size_t len; | ||
152 | |||
153 | /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ | ||
154 | if (!hi->report) | ||
155 | return 0; | ||
156 | |||
157 | field = hi->report->field[0]; | ||
158 | |||
159 | switch (field->application) { | ||
160 | case HID_GD_KEYBOARD: | ||
161 | suffix = "Keyboard"; | ||
162 | break; | ||
163 | case HID_GD_MOUSE: | ||
164 | suffix = "Mouse"; | ||
165 | break; | ||
166 | case HID_GD_KEYPAD: | ||
167 | suffix = "Pad"; | ||
168 | break; | ||
169 | case HID_DG_PEN: | ||
170 | suffix = "Pen"; | ||
171 | break; | ||
172 | case HID_CP_CONSUMER_CONTROL: | ||
173 | suffix = "Consumer Control"; | ||
174 | break; | ||
175 | case HID_GD_SYSTEM_CONTROL: | ||
176 | suffix = "System Control"; | ||
177 | break; | ||
178 | } | ||
179 | |||
180 | if (suffix) { | ||
181 | len = strlen(hdev->name) + 2 + strlen(suffix); | ||
182 | name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); | ||
183 | if (name) { | ||
184 | snprintf(name, len, "%s %s", hdev->name, suffix); | ||
185 | hi->input->name = name; | ||
186 | } | ||
187 | } | ||
188 | |||
189 | return 0; | ||
190 | } | ||
191 | |||
192 | /** | ||
193 | * Enable fully-functional tablet mode and determine device parameters. | ||
194 | * | ||
195 | * @hdev: HID device | ||
196 | */ | ||
197 | static int uclogic_tablet_enable(struct hid_device *hdev) | ||
198 | { | ||
199 | int rc; | ||
200 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
201 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
202 | __le16 *buf = NULL; | ||
203 | size_t len; | ||
204 | s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; | ||
205 | s32 resolution; | ||
206 | |||
207 | /* | ||
208 | * Read string descriptor containing tablet parameters. The specific | ||
209 | * string descriptor and data were discovered by sniffing the Windows | ||
210 | * driver traffic. | ||
211 | * NOTE: This enables fully-functional tablet mode. | ||
212 | */ | ||
213 | len = UCLOGIC_PRM_NUM * sizeof(*buf); | ||
214 | buf = kmalloc(len, GFP_KERNEL); | ||
215 | if (buf == NULL) { | ||
216 | rc = -ENOMEM; | ||
217 | goto cleanup; | ||
218 | } | ||
219 | rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
220 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, | ||
221 | (USB_DT_STRING << 8) + 0x64, | ||
222 | 0x0409, buf, len, | ||
223 | USB_CTRL_GET_TIMEOUT); | ||
224 | if (rc == -EPIPE) { | ||
225 | hid_err(hdev, "device parameters not found\n"); | ||
226 | rc = -ENODEV; | ||
227 | goto cleanup; | ||
228 | } else if (rc < 0) { | ||
229 | hid_err(hdev, "failed to get device parameters: %d\n", rc); | ||
230 | rc = -ENODEV; | ||
231 | goto cleanup; | ||
232 | } else if (rc != len) { | ||
233 | hid_err(hdev, "invalid device parameters\n"); | ||
234 | rc = -ENODEV; | ||
235 | goto cleanup; | ||
236 | } | ||
237 | |||
238 | /* Extract device parameters */ | ||
239 | params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = | ||
240 | le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); | ||
241 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = | ||
242 | le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); | ||
243 | params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = | ||
244 | le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); | ||
245 | resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); | ||
246 | if (resolution == 0) { | ||
247 | params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; | ||
248 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; | ||
249 | } else { | ||
250 | params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = | ||
251 | params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * | ||
252 | 1000 / resolution; | ||
253 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = | ||
254 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * | ||
255 | 1000 / resolution; | ||
256 | } | ||
257 | |||
258 | /* Format fixed report descriptor */ | ||
259 | drvdata->rdesc = uclogic_rdesc_template_apply( | ||
260 | uclogic_rdesc_pen_template_arr, | ||
261 | uclogic_rdesc_pen_template_size, | ||
262 | params, ARRAY_SIZE(params)); | ||
263 | if (drvdata->rdesc == NULL) { | ||
264 | rc = -ENOMEM; | ||
265 | goto cleanup; | ||
266 | } | ||
267 | drvdata->rsize = uclogic_rdesc_pen_template_size; | ||
268 | |||
269 | rc = 0; | ||
270 | |||
271 | cleanup: | ||
272 | kfree(buf); | ||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * Enable actual button mode. | ||
278 | * | ||
279 | * @hdev: HID device | ||
280 | */ | ||
281 | static int uclogic_button_enable(struct hid_device *hdev) | ||
282 | { | ||
283 | int rc; | ||
284 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
285 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
286 | char *str_buf; | ||
287 | size_t str_len = 16; | ||
288 | unsigned char *rdesc; | ||
289 | size_t rdesc_len; | ||
290 | |||
291 | str_buf = kzalloc(str_len, GFP_KERNEL); | ||
292 | if (str_buf == NULL) { | ||
293 | rc = -ENOMEM; | ||
294 | goto cleanup; | ||
295 | } | ||
296 | |||
297 | /* Enable abstract keyboard mode */ | ||
298 | rc = usb_string(usb_dev, 0x7b, str_buf, str_len); | ||
299 | if (rc == -EPIPE) { | ||
300 | hid_info(hdev, "button mode setting not found\n"); | ||
301 | rc = 0; | ||
302 | goto cleanup; | ||
303 | } else if (rc < 0) { | ||
304 | hid_err(hdev, "failed to enable abstract keyboard\n"); | ||
305 | goto cleanup; | ||
306 | } else if (strncmp(str_buf, "HK On", rc)) { | ||
307 | hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", | ||
308 | str_buf); | ||
309 | rc = -EINVAL; | ||
310 | goto cleanup; | ||
311 | } | ||
312 | |||
313 | /* Re-allocate fixed report descriptor */ | ||
314 | rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size; | ||
315 | rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); | ||
316 | if (!rdesc) { | ||
317 | rc = -ENOMEM; | ||
318 | goto cleanup; | ||
319 | } | ||
320 | |||
321 | memcpy(rdesc, drvdata->rdesc, drvdata->rsize); | ||
322 | |||
323 | /* Append the buttonpad descriptor */ | ||
324 | memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr, | ||
325 | uclogic_rdesc_buttonpad_size); | ||
326 | |||
327 | /* clean up old rdesc and use the new one */ | ||
328 | drvdata->rsize = rdesc_len; | ||
329 | devm_kfree(&hdev->dev, drvdata->rdesc); | ||
330 | drvdata->rdesc = rdesc; | ||
331 | |||
332 | rc = 0; | ||
333 | |||
334 | cleanup: | ||
335 | kfree(str_buf); | ||
336 | return rc; | ||
337 | } | ||
338 | |||
339 | static int uclogic_probe(struct hid_device *hdev, | ||
340 | const struct hid_device_id *id) | ||
341 | { | ||
342 | int rc; | ||
343 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
344 | struct usb_device *udev = hid_to_usb_dev(hdev); | ||
345 | struct uclogic_drvdata *drvdata; | ||
346 | |||
347 | /* | ||
348 | * libinput requires the pad interface to be on a different node | ||
349 | * than the pen, so use QUIRK_MULTI_INPUT for all tablets. | ||
350 | */ | ||
351 | hdev->quirks |= HID_QUIRK_MULTI_INPUT; | ||
352 | |||
353 | /* Allocate and assign driver data */ | ||
354 | drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
355 | if (drvdata == NULL) | ||
356 | return -ENOMEM; | ||
357 | |||
358 | hid_set_drvdata(hdev, drvdata); | ||
359 | |||
360 | switch (id->product) { | ||
361 | case USB_DEVICE_ID_HUION_TABLET: | ||
362 | case USB_DEVICE_ID_YIYNOVA_TABLET: | ||
363 | case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: | ||
364 | case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: | ||
365 | case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: | ||
366 | /* If this is the pen interface */ | ||
367 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | ||
368 | rc = uclogic_tablet_enable(hdev); | ||
369 | if (rc) { | ||
370 | hid_err(hdev, "tablet enabling failed\n"); | ||
371 | return rc; | ||
372 | } | ||
373 | drvdata->invert_pen_inrange = true; | ||
374 | |||
375 | rc = uclogic_button_enable(hdev); | ||
376 | drvdata->has_virtual_pad_interface = !rc; | ||
377 | } else { | ||
378 | drvdata->ignore_pen_usage = true; | ||
379 | } | ||
380 | break; | ||
381 | case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: | ||
382 | case USB_DEVICE_ID_UGEE_TABLET_EX07S: | ||
383 | /* If this is the pen interface */ | ||
384 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { | ||
385 | rc = uclogic_tablet_enable(hdev); | ||
386 | if (rc) { | ||
387 | hid_err(hdev, "tablet enabling failed\n"); | ||
388 | return rc; | ||
389 | } | ||
390 | drvdata->invert_pen_inrange = true; | ||
391 | } else { | ||
392 | drvdata->ignore_pen_usage = true; | ||
393 | } | ||
394 | break; | ||
395 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
396 | /* | ||
397 | * If it is the three-interface version, which is known to | ||
398 | * respond to initialization. | ||
399 | */ | ||
400 | if (udev->config->desc.bNumInterfaces == 3) { | ||
401 | /* If it is the pen interface */ | ||
402 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | ||
403 | rc = uclogic_tablet_enable(hdev); | ||
404 | if (rc) { | ||
405 | hid_err(hdev, "tablet enabling failed\n"); | ||
406 | return rc; | ||
407 | } | ||
408 | drvdata->invert_pen_inrange = true; | ||
409 | |||
410 | rc = uclogic_button_enable(hdev); | ||
411 | drvdata->has_virtual_pad_interface = !rc; | ||
412 | } else { | ||
413 | drvdata->ignore_pen_usage = true; | ||
414 | } | ||
415 | } | ||
416 | break; | ||
417 | } | ||
418 | |||
419 | rc = hid_parse(hdev); | ||
420 | if (rc) { | ||
421 | hid_err(hdev, "parse failed\n"); | ||
422 | return rc; | ||
423 | } | ||
424 | |||
425 | rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
426 | if (rc) { | ||
427 | hid_err(hdev, "hw start failed\n"); | ||
428 | return rc; | ||
429 | } | ||
430 | |||
431 | return 0; | ||
432 | } | ||
433 | |||
434 | static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
435 | u8 *data, int size) | ||
436 | { | ||
437 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
438 | |||
439 | if ((report->type == HID_INPUT_REPORT) && | ||
440 | (report->id == UCLOGIC_RDESC_PEN_ID) && | ||
441 | (size >= 2)) { | ||
442 | if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) | ||
443 | /* Change to virtual frame button report ID */ | ||
444 | data[0] = 0xf7; | ||
445 | else if (drvdata->invert_pen_inrange) | ||
446 | /* Invert the in-range bit */ | ||
447 | data[1] ^= 0x40; | ||
448 | } | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
453 | static const struct hid_device_id uclogic_devices[] = { | ||
454 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
455 | USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, | ||
456 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
457 | USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, | ||
458 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
459 | USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, | ||
460 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
461 | USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, | ||
462 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
463 | USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | ||
464 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
465 | USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | ||
466 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
467 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | ||
468 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, | ||
469 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, | ||
470 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, | ||
471 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, | ||
472 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, | ||
473 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, | ||
474 | { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, | ||
475 | { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, | ||
476 | { } | ||
477 | }; | ||
478 | MODULE_DEVICE_TABLE(hid, uclogic_devices); | ||
479 | |||
480 | static struct hid_driver uclogic_driver = { | ||
481 | .name = "uclogic", | ||
482 | .id_table = uclogic_devices, | ||
483 | .probe = uclogic_probe, | ||
484 | .report_fixup = uclogic_report_fixup, | ||
485 | .raw_event = uclogic_raw_event, | ||
486 | .input_mapping = uclogic_input_mapping, | ||
487 | .input_configured = uclogic_input_configured, | ||
488 | }; | ||
489 | module_hid_driver(uclogic_driver); | ||
490 | |||
491 | MODULE_AUTHOR("Martin Rusko"); | ||
492 | MODULE_AUTHOR("Nikolai Kondrashov"); | ||
493 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic-rdesc.c index fdc4d1ea7049..e9053d28f4e0 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic-rdesc.c | |||
@@ -1,7 +1,9 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0+ | ||
1 | /* | 2 | /* |
2 | * HID driver for UC-Logic devices not fully compliant with HID standard | 3 | * HID driver for UC-Logic devices not fully compliant with HID standard |
4 | * - original and fixed report descriptors | ||
3 | * | 5 | * |
4 | * Copyright (c) 2010-2014 Nikolai Kondrashov | 6 | * Copyright (c) 2010-2017 Nikolai Kondrashov |
5 | * Copyright (c) 2013 Martin Rusko | 7 | * Copyright (c) 2013 Martin Rusko |
6 | */ | 8 | */ |
7 | 9 | ||
@@ -12,20 +14,12 @@ | |||
12 | * any later version. | 14 | * any later version. |
13 | */ | 15 | */ |
14 | 16 | ||
15 | #include <linux/device.h> | 17 | #include "hid-uclogic-rdesc.h" |
16 | #include <linux/hid.h> | 18 | #include <linux/slab.h> |
17 | #include <linux/module.h> | ||
18 | #include <linux/usb.h> | ||
19 | #include <asm/unaligned.h> | 19 | #include <asm/unaligned.h> |
20 | #include "usbhid/usbhid.h" | ||
21 | |||
22 | #include "hid-ids.h" | ||
23 | |||
24 | /* Size of the original descriptor of WPXXXXU tablets */ | ||
25 | #define WPXXXXU_RDESC_ORIG_SIZE 212 | ||
26 | 20 | ||
27 | /* Fixed WP4030U report descriptor */ | 21 | /* Fixed WP4030U report descriptor */ |
28 | static __u8 wp4030u_rdesc_fixed[] = { | 22 | __u8 uclogic_rdesc_wp4030u_fixed_arr[] = { |
29 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 23 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
30 | 0x09, 0x02, /* Usage (Pen), */ | 24 | 0x09, 0x02, /* Usage (Pen), */ |
31 | 0xA1, 0x01, /* Collection (Application), */ | 25 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -66,8 +60,11 @@ static __u8 wp4030u_rdesc_fixed[] = { | |||
66 | 0xC0 /* End Collection */ | 60 | 0xC0 /* End Collection */ |
67 | }; | 61 | }; |
68 | 62 | ||
63 | const size_t uclogic_rdesc_wp4030u_fixed_size = | ||
64 | sizeof(uclogic_rdesc_wp4030u_fixed_arr); | ||
65 | |||
69 | /* Fixed WP5540U report descriptor */ | 66 | /* Fixed WP5540U report descriptor */ |
70 | static __u8 wp5540u_rdesc_fixed[] = { | 67 | __u8 uclogic_rdesc_wp5540u_fixed_arr[] = { |
71 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 68 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
72 | 0x09, 0x02, /* Usage (Pen), */ | 69 | 0x09, 0x02, /* Usage (Pen), */ |
73 | 0xA1, 0x01, /* Collection (Application), */ | 70 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -140,8 +137,11 @@ static __u8 wp5540u_rdesc_fixed[] = { | |||
140 | 0xC0 /* End Collection */ | 137 | 0xC0 /* End Collection */ |
141 | }; | 138 | }; |
142 | 139 | ||
140 | const size_t uclogic_rdesc_wp5540u_fixed_size = | ||
141 | sizeof(uclogic_rdesc_wp5540u_fixed_arr); | ||
142 | |||
143 | /* Fixed WP8060U report descriptor */ | 143 | /* Fixed WP8060U report descriptor */ |
144 | static __u8 wp8060u_rdesc_fixed[] = { | 144 | __u8 uclogic_rdesc_wp8060u_fixed_arr[] = { |
145 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 145 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
146 | 0x09, 0x02, /* Usage (Pen), */ | 146 | 0x09, 0x02, /* Usage (Pen), */ |
147 | 0xA1, 0x01, /* Collection (Application), */ | 147 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -214,11 +214,11 @@ static __u8 wp8060u_rdesc_fixed[] = { | |||
214 | 0xC0 /* End Collection */ | 214 | 0xC0 /* End Collection */ |
215 | }; | 215 | }; |
216 | 216 | ||
217 | /* Size of the original descriptor of WP1062 tablet */ | 217 | const size_t uclogic_rdesc_wp8060u_fixed_size = |
218 | #define WP1062_RDESC_ORIG_SIZE 254 | 218 | sizeof(uclogic_rdesc_wp8060u_fixed_arr); |
219 | 219 | ||
220 | /* Fixed WP1062 report descriptor */ | 220 | /* Fixed WP1062 report descriptor */ |
221 | static __u8 wp1062_rdesc_fixed[] = { | 221 | __u8 uclogic_rdesc_wp1062_fixed_arr[] = { |
222 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 222 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
223 | 0x09, 0x02, /* Usage (Pen), */ | 223 | 0x09, 0x02, /* Usage (Pen), */ |
224 | 0xA1, 0x01, /* Collection (Application), */ | 224 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -262,11 +262,11 @@ static __u8 wp1062_rdesc_fixed[] = { | |||
262 | 0xC0 /* End Collection */ | 262 | 0xC0 /* End Collection */ |
263 | }; | 263 | }; |
264 | 264 | ||
265 | /* Size of the original descriptor of PF1209 tablet */ | 265 | const size_t uclogic_rdesc_wp1062_fixed_size = |
266 | #define PF1209_RDESC_ORIG_SIZE 234 | 266 | sizeof(uclogic_rdesc_wp1062_fixed_arr); |
267 | 267 | ||
268 | /* Fixed PF1209 report descriptor */ | 268 | /* Fixed PF1209 report descriptor */ |
269 | static __u8 pf1209_rdesc_fixed[] = { | 269 | __u8 uclogic_rdesc_pf1209_fixed_arr[] = { |
270 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 270 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
271 | 0x09, 0x02, /* Usage (Pen), */ | 271 | 0x09, 0x02, /* Usage (Pen), */ |
272 | 0xA1, 0x01, /* Collection (Application), */ | 272 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -339,13 +339,11 @@ static __u8 pf1209_rdesc_fixed[] = { | |||
339 | 0xC0 /* End Collection */ | 339 | 0xC0 /* End Collection */ |
340 | }; | 340 | }; |
341 | 341 | ||
342 | /* Size of the original descriptors of TWHL850 tablet */ | 342 | const size_t uclogic_rdesc_pf1209_fixed_size = |
343 | #define TWHL850_RDESC_ORIG_SIZE0 182 | 343 | sizeof(uclogic_rdesc_pf1209_fixed_arr); |
344 | #define TWHL850_RDESC_ORIG_SIZE1 161 | ||
345 | #define TWHL850_RDESC_ORIG_SIZE2 92 | ||
346 | 344 | ||
347 | /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ | 345 | /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ |
348 | static __u8 twhl850_rdesc_fixed0[] = { | 346 | __u8 uclogic_rdesc_twhl850_fixed0_arr[] = { |
349 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 347 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
350 | 0x09, 0x02, /* Usage (Pen), */ | 348 | 0x09, 0x02, /* Usage (Pen), */ |
351 | 0xA1, 0x01, /* Collection (Application), */ | 349 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -387,8 +385,11 @@ static __u8 twhl850_rdesc_fixed0[] = { | |||
387 | 0xC0 /* End Collection */ | 385 | 0xC0 /* End Collection */ |
388 | }; | 386 | }; |
389 | 387 | ||
388 | const size_t uclogic_rdesc_twhl850_fixed0_size = | ||
389 | sizeof(uclogic_rdesc_twhl850_fixed0_arr); | ||
390 | |||
390 | /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ | 391 | /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ |
391 | static __u8 twhl850_rdesc_fixed1[] = { | 392 | __u8 uclogic_rdesc_twhl850_fixed1_arr[] = { |
392 | 0x05, 0x01, /* Usage Page (Desktop), */ | 393 | 0x05, 0x01, /* Usage Page (Desktop), */ |
393 | 0x09, 0x02, /* Usage (Mouse), */ | 394 | 0x09, 0x02, /* Usage (Mouse), */ |
394 | 0xA1, 0x01, /* Collection (Application), */ | 395 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -424,8 +425,11 @@ static __u8 twhl850_rdesc_fixed1[] = { | |||
424 | 0xC0 /* End Collection */ | 425 | 0xC0 /* End Collection */ |
425 | }; | 426 | }; |
426 | 427 | ||
428 | const size_t uclogic_rdesc_twhl850_fixed1_size = | ||
429 | sizeof(uclogic_rdesc_twhl850_fixed1_arr); | ||
430 | |||
427 | /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ | 431 | /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ |
428 | static __u8 twhl850_rdesc_fixed2[] = { | 432 | __u8 uclogic_rdesc_twhl850_fixed2_arr[] = { |
429 | 0x05, 0x01, /* Usage Page (Desktop), */ | 433 | 0x05, 0x01, /* Usage Page (Desktop), */ |
430 | 0x09, 0x06, /* Usage (Keyboard), */ | 434 | 0x09, 0x06, /* Usage (Keyboard), */ |
431 | 0xA1, 0x01, /* Collection (Application), */ | 435 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -447,12 +451,11 @@ static __u8 twhl850_rdesc_fixed2[] = { | |||
447 | 0xC0 /* End Collection */ | 451 | 0xC0 /* End Collection */ |
448 | }; | 452 | }; |
449 | 453 | ||
450 | /* Size of the original descriptors of TWHA60 tablet */ | 454 | const size_t uclogic_rdesc_twhl850_fixed2_size = |
451 | #define TWHA60_RDESC_ORIG_SIZE0 254 | 455 | sizeof(uclogic_rdesc_twhl850_fixed2_arr); |
452 | #define TWHA60_RDESC_ORIG_SIZE1 139 | ||
453 | 456 | ||
454 | /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ | 457 | /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ |
455 | static __u8 twha60_rdesc_fixed0[] = { | 458 | __u8 uclogic_rdesc_twha60_fixed0_arr[] = { |
456 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 459 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
457 | 0x09, 0x02, /* Usage (Pen), */ | 460 | 0x09, 0x02, /* Usage (Pen), */ |
458 | 0xA1, 0x01, /* Collection (Application), */ | 461 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -497,8 +500,11 @@ static __u8 twha60_rdesc_fixed0[] = { | |||
497 | 0xC0 /* End Collection */ | 500 | 0xC0 /* End Collection */ |
498 | }; | 501 | }; |
499 | 502 | ||
503 | const size_t uclogic_rdesc_twha60_fixed0_size = | ||
504 | sizeof(uclogic_rdesc_twha60_fixed0_arr); | ||
505 | |||
500 | /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ | 506 | /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ |
501 | static __u8 twha60_rdesc_fixed1[] = { | 507 | __u8 uclogic_rdesc_twha60_fixed1_arr[] = { |
502 | 0x05, 0x01, /* Usage Page (Desktop), */ | 508 | 0x05, 0x01, /* Usage Page (Desktop), */ |
503 | 0x09, 0x06, /* Usage (Keyboard), */ | 509 | 0x09, 0x06, /* Usage (Keyboard), */ |
504 | 0xA1, 0x01, /* Collection (Application), */ | 510 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -522,25 +528,11 @@ static __u8 twha60_rdesc_fixed1[] = { | |||
522 | 0xC0 /* End Collection */ | 528 | 0xC0 /* End Collection */ |
523 | }; | 529 | }; |
524 | 530 | ||
525 | /* Report descriptor template placeholder head */ | 531 | const size_t uclogic_rdesc_twha60_fixed1_size = |
526 | #define UCLOGIC_PH_HEAD 0xFE, 0xED, 0x1D | 532 | sizeof(uclogic_rdesc_twha60_fixed1_arr); |
527 | 533 | ||
528 | /* Report descriptor template placeholder IDs */ | 534 | /* Fixed report descriptor template for (tweaked) pen reports */ |
529 | enum uclogic_ph_id { | 535 | const __u8 uclogic_rdesc_pen_template_arr[] = { |
530 | UCLOGIC_PH_ID_X_LM, | ||
531 | UCLOGIC_PH_ID_X_PM, | ||
532 | UCLOGIC_PH_ID_Y_LM, | ||
533 | UCLOGIC_PH_ID_Y_PM, | ||
534 | UCLOGIC_PH_ID_PRESSURE_LM, | ||
535 | UCLOGIC_PH_ID_NUM | ||
536 | }; | ||
537 | |||
538 | /* Report descriptor template placeholder */ | ||
539 | #define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID | ||
540 | #define UCLOGIC_PEN_REPORT_ID 0x07 | ||
541 | |||
542 | /* Fixed report descriptor template */ | ||
543 | static const __u8 uclogic_tablet_rdesc_template[] = { | ||
544 | 0x05, 0x0D, /* Usage Page (Digitizer), */ | 536 | 0x05, 0x0D, /* Usage Page (Digitizer), */ |
545 | 0x09, 0x02, /* Usage (Pen), */ | 537 | 0x09, 0x02, /* Usage (Pen), */ |
546 | 0xA1, 0x01, /* Collection (Application), */ | 538 | 0xA1, 0x01, /* Collection (Application), */ |
@@ -570,521 +562,122 @@ static const __u8 uclogic_tablet_rdesc_template[] = { | |||
570 | 0x55, 0xFD, /* Unit Exponent (-3), */ | 562 | 0x55, 0xFD, /* Unit Exponent (-3), */ |
571 | 0x34, /* Physical Minimum (0), */ | 563 | 0x34, /* Physical Minimum (0), */ |
572 | 0x09, 0x30, /* Usage (X), */ | 564 | 0x09, 0x30, /* Usage (X), */ |
573 | 0x27, UCLOGIC_PH(X_LM), /* Logical Maximum (PLACEHOLDER), */ | 565 | 0x27, UCLOGIC_RDESC_PEN_PH(X_LM), |
574 | 0x47, UCLOGIC_PH(X_PM), /* Physical Maximum (PLACEHOLDER), */ | 566 | /* Logical Maximum (PLACEHOLDER), */ |
567 | 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), | ||
568 | /* Physical Maximum (PLACEHOLDER), */ | ||
575 | 0x81, 0x02, /* Input (Variable), */ | 569 | 0x81, 0x02, /* Input (Variable), */ |
576 | 0x09, 0x31, /* Usage (Y), */ | 570 | 0x09, 0x31, /* Usage (Y), */ |
577 | 0x27, UCLOGIC_PH(Y_LM), /* Logical Maximum (PLACEHOLDER), */ | 571 | 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), |
578 | 0x47, UCLOGIC_PH(Y_PM), /* Physical Maximum (PLACEHOLDER), */ | 572 | /* Logical Maximum (PLACEHOLDER), */ |
573 | 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM), | ||
574 | /* Physical Maximum (PLACEHOLDER), */ | ||
579 | 0x81, 0x02, /* Input (Variable), */ | 575 | 0x81, 0x02, /* Input (Variable), */ |
580 | 0xB4, /* Pop, */ | 576 | 0xB4, /* Pop, */ |
581 | 0x09, 0x30, /* Usage (Tip Pressure), */ | 577 | 0x09, 0x30, /* Usage (Tip Pressure), */ |
582 | 0x27, | 578 | 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), |
583 | UCLOGIC_PH(PRESSURE_LM),/* Logical Maximum (PLACEHOLDER), */ | 579 | /* Logical Maximum (PLACEHOLDER), */ |
584 | 0x81, 0x02, /* Input (Variable), */ | 580 | 0x81, 0x02, /* Input (Variable), */ |
585 | 0xC0, /* End Collection, */ | 581 | 0xC0, /* End Collection, */ |
586 | 0xC0 /* End Collection */ | 582 | 0xC0 /* End Collection */ |
587 | }; | 583 | }; |
588 | 584 | ||
589 | /* Fixed virtual pad report descriptor */ | 585 | const size_t uclogic_rdesc_pen_template_size = |
590 | static const __u8 uclogic_buttonpad_rdesc[] = { | 586 | sizeof(uclogic_rdesc_pen_template_arr); |
591 | 0x05, 0x01, /* Usage Page (Desktop), */ | ||
592 | 0x09, 0x07, /* Usage (Keypad), */ | ||
593 | 0xA1, 0x01, /* Collection (Application), */ | ||
594 | 0x85, 0xF7, /* Report ID (247), */ | ||
595 | 0x05, 0x0D, /* Usage Page (Digitizers), */ | ||
596 | 0x09, 0x39, /* Usage (Tablet Function Keys), */ | ||
597 | 0xA0, /* Collection (Physical), */ | ||
598 | 0x05, 0x09, /* Usage Page (Button), */ | ||
599 | 0x75, 0x01, /* Report Size (1), */ | ||
600 | 0x95, 0x18, /* Report Count (24), */ | ||
601 | 0x81, 0x03, /* Input (Constant, Variable), */ | ||
602 | 0x19, 0x01, /* Usage Minimum (01h), */ | ||
603 | 0x29, 0x08, /* Usage Maximum (08h), */ | ||
604 | 0x95, 0x08, /* Report Count (8), */ | ||
605 | 0x81, 0x02, /* Input (Variable), */ | ||
606 | 0xC0, /* End Collection */ | ||
607 | 0xC0 /* End Collection */ | ||
608 | }; | ||
609 | |||
610 | /* Parameter indices */ | ||
611 | enum uclogic_prm { | ||
612 | UCLOGIC_PRM_X_LM = 1, | ||
613 | UCLOGIC_PRM_Y_LM = 2, | ||
614 | UCLOGIC_PRM_PRESSURE_LM = 4, | ||
615 | UCLOGIC_PRM_RESOLUTION = 5, | ||
616 | UCLOGIC_PRM_NUM | ||
617 | }; | ||
618 | 587 | ||
619 | /* Driver data */ | 588 | /** |
620 | struct uclogic_drvdata { | 589 | * Expand to the contents of a generic buttonpad report descriptor. |
621 | __u8 *rdesc; | 590 | * |
622 | unsigned int rsize; | 591 | * @_padding: Padding from the end of button bits at bit 44, until |
623 | bool invert_pen_inrange; | 592 | * the end of the report, in bits. |
624 | bool ignore_pen_usage; | 593 | */ |
625 | bool has_virtual_pad_interface; | 594 | #define UCLOGIC_RDESC_BUTTONPAD_BYTES(_padding) \ |
595 | 0x05, 0x01, /* Usage Page (Desktop), */ \ | ||
596 | 0x09, 0x07, /* Usage (Keypad), */ \ | ||
597 | 0xA1, 0x01, /* Collection (Application), */ \ | ||
598 | 0x85, 0xF7, /* Report ID (247), */ \ | ||
599 | 0x14, /* Logical Minimum (0), */ \ | ||
600 | 0x25, 0x01, /* Logical Maximum (1), */ \ | ||
601 | 0x75, 0x01, /* Report Size (1), */ \ | ||
602 | 0x05, 0x0D, /* Usage Page (Digitizer), */ \ | ||
603 | 0x09, 0x39, /* Usage (Tablet Function Keys), */ \ | ||
604 | 0xA0, /* Collection (Physical), */ \ | ||
605 | 0x09, 0x44, /* Usage (Barrel Switch), */ \ | ||
606 | 0x95, 0x01, /* Report Count (1), */ \ | ||
607 | 0x81, 0x02, /* Input (Variable), */ \ | ||
608 | 0x05, 0x01, /* Usage Page (Desktop), */ \ | ||
609 | 0x09, 0x30, /* Usage (X), */ \ | ||
610 | 0x09, 0x31, /* Usage (Y), */ \ | ||
611 | 0x95, 0x02, /* Report Count (2), */ \ | ||
612 | 0x81, 0x02, /* Input (Variable), */ \ | ||
613 | 0x95, 0x15, /* Report Count (21), */ \ | ||
614 | 0x81, 0x01, /* Input (Constant), */ \ | ||
615 | 0x05, 0x09, /* Usage Page (Button), */ \ | ||
616 | 0x19, 0x01, /* Usage Minimum (01h), */ \ | ||
617 | 0x29, 0x0A, /* Usage Maximum (0Ah), */ \ | ||
618 | 0x95, 0x0A, /* Report Count (10), */ \ | ||
619 | 0x81, 0x02, /* Input (Variable), */ \ | ||
620 | 0xC0, /* End Collection, */ \ | ||
621 | 0x05, 0x01, /* Usage Page (Desktop), */ \ | ||
622 | 0x09, 0x05, /* Usage (Gamepad), */ \ | ||
623 | 0xA0, /* Collection (Physical), */ \ | ||
624 | 0x05, 0x09, /* Usage Page (Button), */ \ | ||
625 | 0x19, 0x01, /* Usage Minimum (01h), */ \ | ||
626 | 0x29, 0x02, /* Usage Maximum (02h), */ \ | ||
627 | 0x95, 0x02, /* Report Count (2), */ \ | ||
628 | 0x81, 0x02, /* Input (Variable), */ \ | ||
629 | 0x95, _padding, /* Report Count (_padding), */ \ | ||
630 | 0x81, 0x01, /* Input (Constant), */ \ | ||
631 | 0xC0, /* End Collection, */ \ | ||
632 | 0xC0 /* End Collection */ | ||
633 | |||
634 | /* Fixed report descriptor for (tweaked) buttonpad reports */ | ||
635 | const __u8 uclogic_rdesc_buttonpad_arr[] = { | ||
636 | UCLOGIC_RDESC_BUTTONPAD_BYTES(20) | ||
626 | }; | 637 | }; |
627 | 638 | const size_t uclogic_rdesc_buttonpad_size = | |
628 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 639 | sizeof(uclogic_rdesc_buttonpad_arr); |
629 | unsigned int *rsize) | ||
630 | { | ||
631 | struct usb_interface *iface = to_usb_interface(hdev->dev.parent); | ||
632 | __u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber; | ||
633 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
634 | |||
635 | if (drvdata->rdesc != NULL) { | ||
636 | rdesc = drvdata->rdesc; | ||
637 | *rsize = drvdata->rsize; | ||
638 | return rdesc; | ||
639 | } | ||
640 | |||
641 | switch (hdev->product) { | ||
642 | case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209: | ||
643 | if (*rsize == PF1209_RDESC_ORIG_SIZE) { | ||
644 | rdesc = pf1209_rdesc_fixed; | ||
645 | *rsize = sizeof(pf1209_rdesc_fixed); | ||
646 | } | ||
647 | break; | ||
648 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U: | ||
649 | if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { | ||
650 | rdesc = wp4030u_rdesc_fixed; | ||
651 | *rsize = sizeof(wp4030u_rdesc_fixed); | ||
652 | } | ||
653 | break; | ||
654 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U: | ||
655 | if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { | ||
656 | rdesc = wp5540u_rdesc_fixed; | ||
657 | *rsize = sizeof(wp5540u_rdesc_fixed); | ||
658 | } | ||
659 | break; | ||
660 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U: | ||
661 | if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) { | ||
662 | rdesc = wp8060u_rdesc_fixed; | ||
663 | *rsize = sizeof(wp8060u_rdesc_fixed); | ||
664 | } | ||
665 | break; | ||
666 | case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062: | ||
667 | if (*rsize == WP1062_RDESC_ORIG_SIZE) { | ||
668 | rdesc = wp1062_rdesc_fixed; | ||
669 | *rsize = sizeof(wp1062_rdesc_fixed); | ||
670 | } | ||
671 | break; | ||
672 | case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850: | ||
673 | switch (iface_num) { | ||
674 | case 0: | ||
675 | if (*rsize == TWHL850_RDESC_ORIG_SIZE0) { | ||
676 | rdesc = twhl850_rdesc_fixed0; | ||
677 | *rsize = sizeof(twhl850_rdesc_fixed0); | ||
678 | } | ||
679 | break; | ||
680 | case 1: | ||
681 | if (*rsize == TWHL850_RDESC_ORIG_SIZE1) { | ||
682 | rdesc = twhl850_rdesc_fixed1; | ||
683 | *rsize = sizeof(twhl850_rdesc_fixed1); | ||
684 | } | ||
685 | break; | ||
686 | case 2: | ||
687 | if (*rsize == TWHL850_RDESC_ORIG_SIZE2) { | ||
688 | rdesc = twhl850_rdesc_fixed2; | ||
689 | *rsize = sizeof(twhl850_rdesc_fixed2); | ||
690 | } | ||
691 | break; | ||
692 | } | ||
693 | break; | ||
694 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
695 | switch (iface_num) { | ||
696 | case 0: | ||
697 | if (*rsize == TWHA60_RDESC_ORIG_SIZE0) { | ||
698 | rdesc = twha60_rdesc_fixed0; | ||
699 | *rsize = sizeof(twha60_rdesc_fixed0); | ||
700 | } | ||
701 | break; | ||
702 | case 1: | ||
703 | if (*rsize == TWHA60_RDESC_ORIG_SIZE1) { | ||
704 | rdesc = twha60_rdesc_fixed1; | ||
705 | *rsize = sizeof(twha60_rdesc_fixed1); | ||
706 | } | ||
707 | break; | ||
708 | } | ||
709 | break; | ||
710 | } | ||
711 | |||
712 | return rdesc; | ||
713 | } | ||
714 | |||
715 | static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, | ||
716 | struct hid_field *field, struct hid_usage *usage, | ||
717 | unsigned long **bit, int *max) | ||
718 | { | ||
719 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
720 | |||
721 | /* discard the unused pen interface */ | ||
722 | if ((drvdata->ignore_pen_usage) && | ||
723 | (field->application == HID_DG_PEN)) | ||
724 | return -1; | ||
725 | |||
726 | /* let hid-core decide what to do */ | ||
727 | return 0; | ||
728 | } | ||
729 | |||
730 | static int uclogic_input_configured(struct hid_device *hdev, | ||
731 | struct hid_input *hi) | ||
732 | { | ||
733 | char *name; | ||
734 | const char *suffix = NULL; | ||
735 | struct hid_field *field; | ||
736 | size_t len; | ||
737 | |||
738 | /* no report associated (HID_QUIRK_MULTI_INPUT not set) */ | ||
739 | if (!hi->report) | ||
740 | return 0; | ||
741 | |||
742 | field = hi->report->field[0]; | ||
743 | |||
744 | switch (field->application) { | ||
745 | case HID_GD_KEYBOARD: | ||
746 | suffix = "Keyboard"; | ||
747 | break; | ||
748 | case HID_GD_MOUSE: | ||
749 | suffix = "Mouse"; | ||
750 | break; | ||
751 | case HID_GD_KEYPAD: | ||
752 | suffix = "Pad"; | ||
753 | break; | ||
754 | case HID_DG_PEN: | ||
755 | suffix = "Pen"; | ||
756 | break; | ||
757 | case HID_CP_CONSUMER_CONTROL: | ||
758 | suffix = "Consumer Control"; | ||
759 | break; | ||
760 | case HID_GD_SYSTEM_CONTROL: | ||
761 | suffix = "System Control"; | ||
762 | break; | ||
763 | } | ||
764 | |||
765 | if (suffix) { | ||
766 | len = strlen(hdev->name) + 2 + strlen(suffix); | ||
767 | name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL); | ||
768 | if (name) { | ||
769 | snprintf(name, len, "%s %s", hdev->name, suffix); | ||
770 | hi->input->name = name; | ||
771 | } | ||
772 | } | ||
773 | |||
774 | return 0; | ||
775 | } | ||
776 | 640 | ||
777 | /** | 641 | /** |
778 | * Enable fully-functional tablet mode and determine device parameters. | 642 | * uclogic_rdesc_template_apply() - apply report descriptor parameters to a |
643 | * report descriptor template, creating a report descriptor. Copies the | ||
644 | * template over to the new report descriptor and replaces every occurrence of | ||
645 | * UCLOGIC_RDESC_PH_HEAD, followed by an index byte, with the value from the | ||
646 | * parameter list at that index. | ||
647 | * | ||
648 | * @template_ptr: Pointer to the template buffer. | ||
649 | * @template_size: Size of the template buffer. | ||
650 | * @param_list: List of template parameters. | ||
651 | * @param_num: Number of parameters in the list. | ||
779 | * | 652 | * |
780 | * @hdev: HID device | 653 | * Returns: |
654 | * Kmalloc-allocated pointer to the created report descriptor, | ||
655 | * or NULL if allocation failed. | ||
781 | */ | 656 | */ |
782 | static int uclogic_tablet_enable(struct hid_device *hdev) | 657 | __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, |
658 | size_t template_size, | ||
659 | const s32 *param_list, | ||
660 | size_t param_num) | ||
783 | { | 661 | { |
784 | int rc; | 662 | static const __u8 head[] = {UCLOGIC_RDESC_PH_HEAD}; |
785 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | 663 | __u8 *rdesc_ptr; |
786 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
787 | __le16 *buf = NULL; | ||
788 | size_t len; | ||
789 | s32 params[UCLOGIC_PH_ID_NUM]; | ||
790 | s32 resolution; | ||
791 | __u8 *p; | 664 | __u8 *p; |
792 | s32 v; | 665 | s32 v; |
793 | 666 | ||
794 | /* | 667 | rdesc_ptr = kmemdup(template_ptr, template_size, GFP_KERNEL); |
795 | * Read string descriptor containing tablet parameters. The specific | 668 | if (rdesc_ptr == NULL) |
796 | * string descriptor and data were discovered by sniffing the Windows | 669 | return NULL; |
797 | * driver traffic. | ||
798 | * NOTE: This enables fully-functional tablet mode. | ||
799 | */ | ||
800 | len = UCLOGIC_PRM_NUM * sizeof(*buf); | ||
801 | buf = kmalloc(len, GFP_KERNEL); | ||
802 | if (buf == NULL) { | ||
803 | rc = -ENOMEM; | ||
804 | goto cleanup; | ||
805 | } | ||
806 | rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
807 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, | ||
808 | (USB_DT_STRING << 8) + 0x64, | ||
809 | 0x0409, buf, len, | ||
810 | USB_CTRL_GET_TIMEOUT); | ||
811 | if (rc == -EPIPE) { | ||
812 | hid_err(hdev, "device parameters not found\n"); | ||
813 | rc = -ENODEV; | ||
814 | goto cleanup; | ||
815 | } else if (rc < 0) { | ||
816 | hid_err(hdev, "failed to get device parameters: %d\n", rc); | ||
817 | rc = -ENODEV; | ||
818 | goto cleanup; | ||
819 | } else if (rc != len) { | ||
820 | hid_err(hdev, "invalid device parameters\n"); | ||
821 | rc = -ENODEV; | ||
822 | goto cleanup; | ||
823 | } | ||
824 | |||
825 | /* Extract device parameters */ | ||
826 | params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); | ||
827 | params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); | ||
828 | params[UCLOGIC_PH_ID_PRESSURE_LM] = | ||
829 | le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); | ||
830 | resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); | ||
831 | if (resolution == 0) { | ||
832 | params[UCLOGIC_PH_ID_X_PM] = 0; | ||
833 | params[UCLOGIC_PH_ID_Y_PM] = 0; | ||
834 | } else { | ||
835 | params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] * | ||
836 | 1000 / resolution; | ||
837 | params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] * | ||
838 | 1000 / resolution; | ||
839 | } | ||
840 | 670 | ||
841 | /* Allocate fixed report descriptor */ | 671 | for (p = rdesc_ptr; p + sizeof(head) < rdesc_ptr + template_size;) { |
842 | drvdata->rdesc = devm_kzalloc(&hdev->dev, | 672 | if (memcmp(p, head, sizeof(head)) == 0 && |
843 | sizeof(uclogic_tablet_rdesc_template), | 673 | p[sizeof(head)] < param_num) { |
844 | GFP_KERNEL); | 674 | v = param_list[p[sizeof(head)]]; |
845 | if (drvdata->rdesc == NULL) { | ||
846 | rc = -ENOMEM; | ||
847 | goto cleanup; | ||
848 | } | ||
849 | drvdata->rsize = sizeof(uclogic_tablet_rdesc_template); | ||
850 | |||
851 | /* Format fixed report descriptor */ | ||
852 | memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template, | ||
853 | drvdata->rsize); | ||
854 | for (p = drvdata->rdesc; | ||
855 | p <= drvdata->rdesc + drvdata->rsize - 4;) { | ||
856 | if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && | ||
857 | p[3] < ARRAY_SIZE(params)) { | ||
858 | v = params[p[3]]; | ||
859 | put_unaligned(cpu_to_le32(v), (s32 *)p); | 675 | put_unaligned(cpu_to_le32(v), (s32 *)p); |
860 | p += 4; | 676 | p += sizeof(head) + 1; |
861 | } else { | 677 | } else { |
862 | p++; | 678 | p++; |
863 | } | 679 | } |
864 | } | 680 | } |
865 | 681 | ||
866 | rc = 0; | 682 | return rdesc_ptr; |
867 | |||
868 | cleanup: | ||
869 | kfree(buf); | ||
870 | return rc; | ||
871 | } | ||
872 | |||
873 | /** | ||
874 | * Enable actual button mode. | ||
875 | * | ||
876 | * @hdev: HID device | ||
877 | */ | ||
878 | static int uclogic_button_enable(struct hid_device *hdev) | ||
879 | { | ||
880 | int rc; | ||
881 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
882 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
883 | char *str_buf; | ||
884 | size_t str_len = 16; | ||
885 | unsigned char *rdesc; | ||
886 | size_t rdesc_len; | ||
887 | |||
888 | str_buf = kzalloc(str_len, GFP_KERNEL); | ||
889 | if (str_buf == NULL) { | ||
890 | rc = -ENOMEM; | ||
891 | goto cleanup; | ||
892 | } | ||
893 | |||
894 | /* Enable abstract keyboard mode */ | ||
895 | rc = usb_string(usb_dev, 0x7b, str_buf, str_len); | ||
896 | if (rc == -EPIPE) { | ||
897 | hid_info(hdev, "button mode setting not found\n"); | ||
898 | rc = 0; | ||
899 | goto cleanup; | ||
900 | } else if (rc < 0) { | ||
901 | hid_err(hdev, "failed to enable abstract keyboard\n"); | ||
902 | goto cleanup; | ||
903 | } else if (strncmp(str_buf, "HK On", rc)) { | ||
904 | hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", | ||
905 | str_buf); | ||
906 | rc = -EINVAL; | ||
907 | goto cleanup; | ||
908 | } | ||
909 | |||
910 | /* Re-allocate fixed report descriptor */ | ||
911 | rdesc_len = drvdata->rsize + sizeof(uclogic_buttonpad_rdesc); | ||
912 | rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); | ||
913 | if (!rdesc) { | ||
914 | rc = -ENOMEM; | ||
915 | goto cleanup; | ||
916 | } | ||
917 | |||
918 | memcpy(rdesc, drvdata->rdesc, drvdata->rsize); | ||
919 | |||
920 | /* Append the buttonpad descriptor */ | ||
921 | memcpy(rdesc + drvdata->rsize, uclogic_buttonpad_rdesc, | ||
922 | sizeof(uclogic_buttonpad_rdesc)); | ||
923 | |||
924 | /* clean up old rdesc and use the new one */ | ||
925 | drvdata->rsize = rdesc_len; | ||
926 | devm_kfree(&hdev->dev, drvdata->rdesc); | ||
927 | drvdata->rdesc = rdesc; | ||
928 | |||
929 | rc = 0; | ||
930 | |||
931 | cleanup: | ||
932 | kfree(str_buf); | ||
933 | return rc; | ||
934 | } | ||
935 | |||
936 | static int uclogic_probe(struct hid_device *hdev, | ||
937 | const struct hid_device_id *id) | ||
938 | { | ||
939 | int rc; | ||
940 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
941 | struct usb_device *udev = hid_to_usb_dev(hdev); | ||
942 | struct uclogic_drvdata *drvdata; | ||
943 | |||
944 | /* | ||
945 | * libinput requires the pad interface to be on a different node | ||
946 | * than the pen, so use QUIRK_MULTI_INPUT for all tablets. | ||
947 | */ | ||
948 | hdev->quirks |= HID_QUIRK_MULTI_INPUT; | ||
949 | |||
950 | /* Allocate and assign driver data */ | ||
951 | drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); | ||
952 | if (drvdata == NULL) | ||
953 | return -ENOMEM; | ||
954 | |||
955 | hid_set_drvdata(hdev, drvdata); | ||
956 | |||
957 | switch (id->product) { | ||
958 | case USB_DEVICE_ID_HUION_TABLET: | ||
959 | case USB_DEVICE_ID_YIYNOVA_TABLET: | ||
960 | case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: | ||
961 | case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: | ||
962 | case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: | ||
963 | /* If this is the pen interface */ | ||
964 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | ||
965 | rc = uclogic_tablet_enable(hdev); | ||
966 | if (rc) { | ||
967 | hid_err(hdev, "tablet enabling failed\n"); | ||
968 | return rc; | ||
969 | } | ||
970 | drvdata->invert_pen_inrange = true; | ||
971 | |||
972 | rc = uclogic_button_enable(hdev); | ||
973 | drvdata->has_virtual_pad_interface = !rc; | ||
974 | } else { | ||
975 | drvdata->ignore_pen_usage = true; | ||
976 | } | ||
977 | break; | ||
978 | case USB_DEVICE_ID_UGTIZER_TABLET_GP0610: | ||
979 | case USB_DEVICE_ID_UGEE_TABLET_EX07S: | ||
980 | /* If this is the pen interface */ | ||
981 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { | ||
982 | rc = uclogic_tablet_enable(hdev); | ||
983 | if (rc) { | ||
984 | hid_err(hdev, "tablet enabling failed\n"); | ||
985 | return rc; | ||
986 | } | ||
987 | drvdata->invert_pen_inrange = true; | ||
988 | } else { | ||
989 | drvdata->ignore_pen_usage = true; | ||
990 | } | ||
991 | break; | ||
992 | case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60: | ||
993 | /* | ||
994 | * If it is the three-interface version, which is known to | ||
995 | * respond to initialization. | ||
996 | */ | ||
997 | if (udev->config->desc.bNumInterfaces == 3) { | ||
998 | /* If it is the pen interface */ | ||
999 | if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { | ||
1000 | rc = uclogic_tablet_enable(hdev); | ||
1001 | if (rc) { | ||
1002 | hid_err(hdev, "tablet enabling failed\n"); | ||
1003 | return rc; | ||
1004 | } | ||
1005 | drvdata->invert_pen_inrange = true; | ||
1006 | |||
1007 | rc = uclogic_button_enable(hdev); | ||
1008 | drvdata->has_virtual_pad_interface = !rc; | ||
1009 | } else { | ||
1010 | drvdata->ignore_pen_usage = true; | ||
1011 | } | ||
1012 | } | ||
1013 | break; | ||
1014 | } | ||
1015 | |||
1016 | rc = hid_parse(hdev); | ||
1017 | if (rc) { | ||
1018 | hid_err(hdev, "parse failed\n"); | ||
1019 | return rc; | ||
1020 | } | ||
1021 | |||
1022 | rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
1023 | if (rc) { | ||
1024 | hid_err(hdev, "hw start failed\n"); | ||
1025 | return rc; | ||
1026 | } | ||
1027 | |||
1028 | return 0; | ||
1029 | } | 683 | } |
1030 | |||
1031 | static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
1032 | u8 *data, int size) | ||
1033 | { | ||
1034 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
1035 | |||
1036 | if ((report->type == HID_INPUT_REPORT) && | ||
1037 | (report->id == UCLOGIC_PEN_REPORT_ID) && | ||
1038 | (size >= 2)) { | ||
1039 | if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) | ||
1040 | /* Change to virtual frame button report ID */ | ||
1041 | data[0] = 0xf7; | ||
1042 | else if (drvdata->invert_pen_inrange) | ||
1043 | /* Invert the in-range bit */ | ||
1044 | data[1] ^= 0x40; | ||
1045 | } | ||
1046 | |||
1047 | return 0; | ||
1048 | } | ||
1049 | |||
1050 | static const struct hid_device_id uclogic_devices[] = { | ||
1051 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1052 | USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, | ||
1053 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1054 | USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) }, | ||
1055 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1056 | USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) }, | ||
1057 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1058 | USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) }, | ||
1059 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1060 | USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | ||
1061 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1062 | USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | ||
1063 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, | ||
1064 | USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | ||
1065 | { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, | ||
1066 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, | ||
1067 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, | ||
1068 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, | ||
1069 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, | ||
1070 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, | ||
1071 | { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, | ||
1072 | { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, | ||
1073 | { } | ||
1074 | }; | ||
1075 | MODULE_DEVICE_TABLE(hid, uclogic_devices); | ||
1076 | |||
1077 | static struct hid_driver uclogic_driver = { | ||
1078 | .name = "uclogic", | ||
1079 | .id_table = uclogic_devices, | ||
1080 | .probe = uclogic_probe, | ||
1081 | .report_fixup = uclogic_report_fixup, | ||
1082 | .raw_event = uclogic_raw_event, | ||
1083 | .input_mapping = uclogic_input_mapping, | ||
1084 | .input_configured = uclogic_input_configured, | ||
1085 | }; | ||
1086 | module_hid_driver(uclogic_driver); | ||
1087 | |||
1088 | MODULE_AUTHOR("Martin Rusko"); | ||
1089 | MODULE_AUTHOR("Nikolai Kondrashov"); | ||
1090 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h new file mode 100644 index 000000000000..610575879522 --- /dev/null +++ b/drivers/hid/hid-uclogic-rdesc.h | |||
@@ -0,0 +1,117 @@ | |||
1 | /* SPDX-License-Identifier: GPL-2.0+ */ | ||
2 | /* | ||
3 | * HID driver for UC-Logic devices not fully compliant with HID standard | ||
4 | * - original and fixed report descriptors | ||
5 | * | ||
6 | * Copyright (c) 2010-2018 Nikolai Kondrashov | ||
7 | * Copyright (c) 2013 Martin Rusko | ||
8 | */ | ||
9 | |||
10 | /* | ||
11 | * This program is free software; you can redistribute it and/or modify it | ||
12 | * under the terms of the GNU General Public License as published by the Free | ||
13 | * Software Foundation; either version 2 of the License, or (at your option) | ||
14 | * any later version. | ||
15 | */ | ||
16 | |||
17 | #ifndef _HID_UCLOGIC_RDESC_H | ||
18 | #define _HID_UCLOGIC_RDESC_H | ||
19 | |||
20 | #include <linux/usb.h> | ||
21 | |||
22 | /* Size of the original descriptor of WPXXXXU tablets */ | ||
23 | #define UCLOGIC_RDESC_WPXXXXU_ORIG_SIZE 212 | ||
24 | |||
25 | /* Fixed WP4030U report descriptor */ | ||
26 | extern __u8 uclogic_rdesc_wp4030u_fixed_arr[]; | ||
27 | extern const size_t uclogic_rdesc_wp4030u_fixed_size; | ||
28 | |||
29 | /* Fixed WP5540U report descriptor */ | ||
30 | extern __u8 uclogic_rdesc_wp5540u_fixed_arr[]; | ||
31 | extern const size_t uclogic_rdesc_wp5540u_fixed_size; | ||
32 | |||
33 | /* Fixed WP8060U report descriptor */ | ||
34 | extern __u8 uclogic_rdesc_wp8060u_fixed_arr[]; | ||
35 | extern const size_t uclogic_rdesc_wp8060u_fixed_size; | ||
36 | |||
37 | /* Size of the original descriptor of WP1062 tablet */ | ||
38 | #define UCLOGIC_RDESC_WP1062_ORIG_SIZE 254 | ||
39 | |||
40 | /* Fixed WP1062 report descriptor */ | ||
41 | extern __u8 uclogic_rdesc_wp1062_fixed_arr[]; | ||
42 | extern const size_t uclogic_rdesc_wp1062_fixed_size; | ||
43 | |||
44 | /* Size of the original descriptor of PF1209 tablet */ | ||
45 | #define UCLOGIC_RDESC_PF1209_ORIG_SIZE 234 | ||
46 | |||
47 | /* Fixed PF1209 report descriptor */ | ||
48 | extern __u8 uclogic_rdesc_pf1209_fixed_arr[]; | ||
49 | extern const size_t uclogic_rdesc_pf1209_fixed_size; | ||
50 | |||
51 | /* Size of the original descriptors of TWHL850 tablet */ | ||
52 | #define UCLOGIC_RDESC_TWHL850_ORIG0_SIZE 182 | ||
53 | #define UCLOGIC_RDESC_TWHL850_ORIG1_SIZE 161 | ||
54 | #define UCLOGIC_RDESC_TWHL850_ORIG2_SIZE 92 | ||
55 | |||
56 | /* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */ | ||
57 | extern __u8 uclogic_rdesc_twhl850_fixed0_arr[]; | ||
58 | extern const size_t uclogic_rdesc_twhl850_fixed0_size; | ||
59 | |||
60 | /* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */ | ||
61 | extern __u8 uclogic_rdesc_twhl850_fixed1_arr[]; | ||
62 | extern const size_t uclogic_rdesc_twhl850_fixed1_size; | ||
63 | |||
64 | /* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */ | ||
65 | extern __u8 uclogic_rdesc_twhl850_fixed2_arr[]; | ||
66 | extern const size_t uclogic_rdesc_twhl850_fixed2_size; | ||
67 | |||
68 | /* Size of the original descriptors of TWHA60 tablet */ | ||
69 | #define UCLOGIC_RDESC_TWHA60_ORIG0_SIZE 254 | ||
70 | #define UCLOGIC_RDESC_TWHA60_ORIG1_SIZE 139 | ||
71 | |||
72 | /* Fixed TWHA60 report descriptor, interface 0 (stylus) */ | ||
73 | extern __u8 uclogic_rdesc_twha60_fixed0_arr[]; | ||
74 | extern const size_t uclogic_rdesc_twha60_fixed0_size; | ||
75 | |||
76 | /* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */ | ||
77 | extern __u8 uclogic_rdesc_twha60_fixed1_arr[]; | ||
78 | extern const size_t uclogic_rdesc_twha60_fixed1_size; | ||
79 | |||
80 | /* Report descriptor template placeholder head */ | ||
81 | #define UCLOGIC_RDESC_PH_HEAD 0xFE, 0xED, 0x1D | ||
82 | |||
83 | /* Apply report descriptor parameters to a report descriptor template */ | ||
84 | extern __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, | ||
85 | size_t template_size, | ||
86 | const s32 *param_list, | ||
87 | size_t param_num); | ||
88 | |||
89 | /* Pen report descriptor template placeholder IDs */ | ||
90 | enum uclogic_rdesc_pen_ph_id { | ||
91 | UCLOGIC_RDESC_PEN_PH_ID_X_LM, | ||
92 | UCLOGIC_RDESC_PEN_PH_ID_X_PM, | ||
93 | UCLOGIC_RDESC_PEN_PH_ID_Y_LM, | ||
94 | UCLOGIC_RDESC_PEN_PH_ID_Y_PM, | ||
95 | UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM, | ||
96 | UCLOGIC_RDESC_PEN_PH_ID_NUM | ||
97 | }; | ||
98 | |||
99 | /* Report descriptor pen template placeholder */ | ||
100 | #define UCLOGIC_RDESC_PEN_PH(_ID) \ | ||
101 | UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID | ||
102 | |||
103 | /* Report ID for pen reports */ | ||
104 | #define UCLOGIC_RDESC_PEN_ID 0x07 | ||
105 | |||
106 | /* Fixed report descriptor template for (tweaked) pen reports */ | ||
107 | extern const __u8 uclogic_rdesc_pen_template_arr[]; | ||
108 | extern const size_t uclogic_rdesc_pen_template_size; | ||
109 | |||
110 | /* Fixed report descriptor for (tweaked) buttonpad reports */ | ||
111 | extern const __u8 uclogic_rdesc_buttonpad_arr[]; | ||
112 | extern const size_t uclogic_rdesc_buttonpad_size; | ||
113 | |||
114 | /* Report ID for tweaked buttonpad reports */ | ||
115 | #define UCLOGIC_RDESC_BUTTONPAD_ID 0xf7 | ||
116 | |||
117 | #endif /* _HID_UCLOGIC_RDESC_H */ | ||