summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/hid/Makefile2
-rw-r--r--drivers/hid/hid-uclogic-core.c493
-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.h117
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
108obj-$(CONFIG_HID_TIVO) += hid-tivo.o 108obj-$(CONFIG_HID_TIVO) += hid-tivo.o
109obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o 109obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o
110obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o 110obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o
111hid-uclogic-objs := hid-uclogic-core.o \
112 hid-uclogic-rdesc.o
111obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o 113obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
112obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o 114obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
113obj-$(CONFIG_HID_LED) += hid-led.o 115obj-$(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 */
26enum 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 */
35struct 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
43static __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
130static 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
145static 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 */
197static 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
271cleanup:
272 kfree(buf);
273 return rc;
274}
275
276/**
277 * Enable actual button mode.
278 *
279 * @hdev: HID device
280 */
281static 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
334cleanup:
335 kfree(str_buf);
336 return rc;
337}
338
339static 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
434static 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
453static 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};
478MODULE_DEVICE_TABLE(hid, uclogic_devices);
479
480static 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};
489module_hid_driver(uclogic_driver);
490
491MODULE_AUTHOR("Martin Rusko");
492MODULE_AUTHOR("Nikolai Kondrashov");
493MODULE_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 */
28static __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
63const 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 */
70static __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
140const 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 */
144static __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 */ 217const 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 */
221static __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 */ 265const 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 */
269static __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 */ 342const 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) */
348static __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
388const 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) */
391static __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
428const 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) */
428static __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 */ 454const 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) */
455static __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
503const 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) */
501static __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 */ 531const 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 */
529enum uclogic_ph_id { 535const __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 */
543static 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 */ 585const size_t uclogic_rdesc_pen_template_size =
590static 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 */
611enum 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/**
620struct 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 */
635const __u8 uclogic_rdesc_buttonpad_arr[] = {
636 UCLOGIC_RDESC_BUTTONPAD_BYTES(20)
626}; 637};
627 638const size_t uclogic_rdesc_buttonpad_size =
628static __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
715static 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
730static 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 */
782static 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
868cleanup:
869 kfree(buf);
870 return rc;
871}
872
873/**
874 * Enable actual button mode.
875 *
876 * @hdev: HID device
877 */
878static 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
931cleanup:
932 kfree(str_buf);
933 return rc;
934}
935
936static 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
1031static 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
1050static 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};
1075MODULE_DEVICE_TABLE(hid, uclogic_devices);
1076
1077static 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};
1086module_hid_driver(uclogic_driver);
1087
1088MODULE_AUTHOR("Martin Rusko");
1089MODULE_AUTHOR("Nikolai Kondrashov");
1090MODULE_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 */
26extern __u8 uclogic_rdesc_wp4030u_fixed_arr[];
27extern const size_t uclogic_rdesc_wp4030u_fixed_size;
28
29/* Fixed WP5540U report descriptor */
30extern __u8 uclogic_rdesc_wp5540u_fixed_arr[];
31extern const size_t uclogic_rdesc_wp5540u_fixed_size;
32
33/* Fixed WP8060U report descriptor */
34extern __u8 uclogic_rdesc_wp8060u_fixed_arr[];
35extern 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 */
41extern __u8 uclogic_rdesc_wp1062_fixed_arr[];
42extern 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 */
48extern __u8 uclogic_rdesc_pf1209_fixed_arr[];
49extern 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) */
57extern __u8 uclogic_rdesc_twhl850_fixed0_arr[];
58extern const size_t uclogic_rdesc_twhl850_fixed0_size;
59
60/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
61extern __u8 uclogic_rdesc_twhl850_fixed1_arr[];
62extern const size_t uclogic_rdesc_twhl850_fixed1_size;
63
64/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
65extern __u8 uclogic_rdesc_twhl850_fixed2_arr[];
66extern 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) */
73extern __u8 uclogic_rdesc_twha60_fixed0_arr[];
74extern const size_t uclogic_rdesc_twha60_fixed0_size;
75
76/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
77extern __u8 uclogic_rdesc_twha60_fixed1_arr[];
78extern 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 */
84extern __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 */
90enum 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 */
107extern const __u8 uclogic_rdesc_pen_template_arr[];
108extern const size_t uclogic_rdesc_pen_template_size;
109
110/* Fixed report descriptor for (tweaked) buttonpad reports */
111extern const __u8 uclogic_rdesc_buttonpad_arr[];
112extern 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 */