aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hid/Makefile3
-rw-r--r--drivers/hid/hid-uclogic-core.c428
-rw-r--r--drivers/hid/hid-uclogic-params.c806
-rw-r--r--drivers/hid/hid-uclogic-params.h180
4 files changed, 1090 insertions, 327 deletions
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index fb75366ea776..9b3a747af60d 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -109,7 +109,8 @@ obj-$(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 \ 111hid-uclogic-objs := hid-uclogic-core.o \
112 hid-uclogic-rdesc.o 112 hid-uclogic-rdesc.o \
113 hid-uclogic-params.o
113obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o 114obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o
114obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o 115obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o
115obj-$(CONFIG_HID_LED) += hid-led.o 116obj-$(CONFIG_HID_LED) += hid-led.o
diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c
index 4042183ee9a3..72a3a43766cc 100644
--- a/drivers/hid/hid-uclogic-core.c
+++ b/drivers/hid/hid-uclogic-core.c
@@ -16,126 +16,48 @@
16#include <linux/device.h> 16#include <linux/device.h>
17#include <linux/hid.h> 17#include <linux/hid.h>
18#include <linux/module.h> 18#include <linux/module.h>
19#include <linux/usb.h>
20#include "usbhid/usbhid.h" 19#include "usbhid/usbhid.h"
21#include "hid-uclogic-rdesc.h" 20#include "hid-uclogic-params.h"
22 21
23#include "hid-ids.h" 22#include "hid-ids.h"
24 23
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 */ 24/* Driver data */
35struct uclogic_drvdata { 25struct uclogic_drvdata {
36 __u8 *rdesc; 26 /* Interface parameters */
37 unsigned int rsize; 27 struct uclogic_params params;
38 bool invert_pen_inrange; 28 /* Pointer to the replacement report descriptor. NULL if none. */
39 bool ignore_pen_usage; 29 __u8 *desc_ptr;
40 bool has_virtual_pad_interface; 30 /*
31 * Size of the replacement report descriptor.
32 * Only valid if desc_ptr is not NULL
33 */
34 unsigned int desc_size;
41}; 35};
42 36
43static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, 37static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
44 unsigned int *rsize) 38 unsigned int *rsize)
45{ 39{
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); 40 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
49 41
50 if (drvdata->rdesc != NULL) { 42 if (drvdata->desc_ptr != NULL) {
51 rdesc = drvdata->rdesc; 43 rdesc = drvdata->desc_ptr;
52 *rsize = drvdata->rsize; 44 *rsize = drvdata->desc_size;
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 } 45 }
126
127 return rdesc; 46 return rdesc;
128} 47}
129 48
130static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, 49static int uclogic_input_mapping(struct hid_device *hdev,
131 struct hid_field *field, struct hid_usage *usage, 50 struct hid_input *hi,
132 unsigned long **bit, int *max) 51 struct hid_field *field,
52 struct hid_usage *usage,
53 unsigned long **bit,
54 int *max)
133{ 55{
134 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 56 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
57 struct uclogic_params *params = &drvdata->params;
135 58
136 /* discard the unused pen interface */ 59 /* discard the unused pen interface */
137 if ((drvdata->ignore_pen_usage) && 60 if (params->pen_unused && (field->application == HID_DG_PEN))
138 (field->application == HID_DG_PEN))
139 return -1; 61 return -1;
140 62
141 /* let hid-core decide what to do */ 63 /* let hid-core decide what to do */
@@ -189,160 +111,12 @@ static int uclogic_input_configured(struct hid_device *hdev,
189 return 0; 111 return 0;
190} 112}
191 113
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, 114static int uclogic_probe(struct hid_device *hdev,
340 const struct hid_device_id *id) 115 const struct hid_device_id *id)
341{ 116{
342 int rc; 117 int rc;
343 struct usb_interface *intf = to_usb_interface(hdev->dev.parent); 118 struct uclogic_drvdata *drvdata = NULL;
344 struct usb_device *udev = hid_to_usb_dev(hdev); 119 bool params_initialized = false;
345 struct uclogic_drvdata *drvdata;
346 120
347 /* 121 /*
348 * libinput requires the pad interface to be on a different node 122 * libinput requires the pad interface to be on a different node
@@ -352,104 +126,97 @@ static int uclogic_probe(struct hid_device *hdev,
352 126
353 /* Allocate and assign driver data */ 127 /* Allocate and assign driver data */
354 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL); 128 drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
355 if (drvdata == NULL) 129 if (drvdata == NULL) {
356 return -ENOMEM; 130 rc = -ENOMEM;
357 131 goto failure;
132 }
358 hid_set_drvdata(hdev, drvdata); 133 hid_set_drvdata(hdev, drvdata);
359 134
360 switch (id->product) { 135 /* Initialize the device and retrieve interface parameters */
361 case USB_DEVICE_ID_HUION_TABLET: 136 rc = uclogic_params_init(&drvdata->params, hdev);
362 case USB_DEVICE_ID_YIYNOVA_TABLET: 137 if (rc != 0) {
363 case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81: 138 hid_err(hdev, "failed probing parameters: %d\n", rc);
364 case USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3: 139 goto failure;
365 case USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45: 140 }
366 /* If this is the pen interface */ 141 params_initialized = true;
367 if (intf->cur_altsetting->desc.bInterfaceNumber == 0) { 142 hid_dbg(hdev, "parameters:\n" UCLOGIC_PARAMS_FMT_STR,
368 rc = uclogic_tablet_enable(hdev); 143 UCLOGIC_PARAMS_FMT_ARGS(&drvdata->params));
369 if (rc) { 144 if (drvdata->params.invalid) {
370 hid_err(hdev, "tablet enabling failed\n"); 145 hid_info(hdev, "interface is invalid, ignoring\n");
371 return rc; 146 rc = -ENODEV;
372 } 147 goto failure;
373 drvdata->invert_pen_inrange = true; 148 }
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 149
410 rc = uclogic_button_enable(hdev); 150 /* Generate replacement report descriptor */
411 drvdata->has_virtual_pad_interface = !rc; 151 rc = uclogic_params_get_desc(&drvdata->params,
412 } else { 152 &drvdata->desc_ptr,
413 drvdata->ignore_pen_usage = true; 153 &drvdata->desc_size);
414 } 154 if (rc) {
415 } 155 hid_err(hdev,
416 break; 156 "failed generating replacement report descriptor: %d\n",
157 rc);
158 goto failure;
417 } 159 }
418 160
419 rc = hid_parse(hdev); 161 rc = hid_parse(hdev);
420 if (rc) { 162 if (rc) {
421 hid_err(hdev, "parse failed\n"); 163 hid_err(hdev, "parse failed\n");
422 return rc; 164 goto failure;
423 } 165 }
424 166
425 rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 167 rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
426 if (rc) { 168 if (rc) {
427 hid_err(hdev, "hw start failed\n"); 169 hid_err(hdev, "hw start failed\n");
428 return rc; 170 goto failure;
429 } 171 }
430 172
431 return 0; 173 return 0;
174failure:
175 /* Assume "remove" might not be called if "probe" failed */
176 if (params_initialized)
177 uclogic_params_cleanup(&drvdata->params);
178 return rc;
432} 179}
433 180
434static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, 181static int uclogic_raw_event(struct hid_device *hdev,
435 u8 *data, int size) 182 struct hid_report *report,
183 u8 *data, int size)
436{ 184{
437 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); 185 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
186 struct uclogic_params *params = &drvdata->params;
438 187
439 if ((report->type == HID_INPUT_REPORT) && 188 /* Tweak pen reports, if necessary */
440 (report->id == UCLOGIC_RDESC_PEN_ID) && 189 if (!params->pen_unused &&
190 (report->type == HID_INPUT_REPORT) &&
191 (report->id == params->pen.id) &&
441 (size >= 2)) { 192 (size >= 2)) {
442 if (drvdata->has_virtual_pad_interface && (data[1] & 0x20)) 193 /* If it's the "virtual" frame controls report */
443 /* Change to virtual frame button report ID */ 194 if (params->frame.id != 0 &&
444 data[0] = 0xf7; 195 data[1] & params->pen_frame_flag) {
445 else if (drvdata->invert_pen_inrange) 196 /* Change to virtual frame controls report ID */
197 data[0] = params->frame.id;
198 return 0;
199 }
200 /* If in-range reports are inverted */
201 if (params->pen.inrange ==
202 UCLOGIC_PARAMS_PEN_INRANGE_INVERTED) {
446 /* Invert the in-range bit */ 203 /* Invert the in-range bit */
447 data[1] ^= 0x40; 204 data[1] ^= 0x40;
205 }
448 } 206 }
449 207
450 return 0; 208 return 0;
451} 209}
452 210
211static void uclogic_remove(struct hid_device *hdev)
212{
213 struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
214
215 hid_hw_stop(hdev);
216 kfree(drvdata->desc_ptr);
217 uclogic_params_cleanup(&drvdata->params);
218}
219
453static const struct hid_device_id uclogic_devices[] = { 220static const struct hid_device_id uclogic_devices[] = {
454 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 221 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
455 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) }, 222 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
@@ -465,14 +232,22 @@ static const struct hid_device_id uclogic_devices[] = {
465 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, 232 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
466 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, 233 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
467 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, 234 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
468 { HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) }, 235 { HID_USB_DEVICE(USB_VENDOR_ID_HUION,
469 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) }, 236 USB_DEVICE_ID_HUION_TABLET) },
470 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, 237 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
471 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) }, 238 USB_DEVICE_ID_HUION_TABLET) },
472 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) }, 239 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
473 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) }, 240 USB_DEVICE_ID_YIYNOVA_TABLET) },
474 { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER, USB_DEVICE_ID_UGTIZER_TABLET_GP0610) }, 241 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
475 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_TABLET_EX07S) }, 242 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81) },
243 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
244 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45) },
245 { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
246 USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3) },
247 { HID_USB_DEVICE(USB_VENDOR_ID_UGTIZER,
248 USB_DEVICE_ID_UGTIZER_TABLET_GP0610) },
249 { HID_USB_DEVICE(USB_VENDOR_ID_UGEE,
250 USB_DEVICE_ID_UGEE_TABLET_EX07S) },
476 { } 251 { }
477}; 252};
478MODULE_DEVICE_TABLE(hid, uclogic_devices); 253MODULE_DEVICE_TABLE(hid, uclogic_devices);
@@ -481,6 +256,7 @@ static struct hid_driver uclogic_driver = {
481 .name = "uclogic", 256 .name = "uclogic",
482 .id_table = uclogic_devices, 257 .id_table = uclogic_devices,
483 .probe = uclogic_probe, 258 .probe = uclogic_probe,
259 .remove = uclogic_remove,
484 .report_fixup = uclogic_report_fixup, 260 .report_fixup = uclogic_report_fixup,
485 .raw_event = uclogic_raw_event, 261 .raw_event = uclogic_raw_event,
486 .input_mapping = uclogic_input_mapping, 262 .input_mapping = uclogic_input_mapping,
diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c
new file mode 100644
index 000000000000..2f8870d58f9a
--- /dev/null
+++ b/drivers/hid/hid-uclogic-params.c
@@ -0,0 +1,806 @@
1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * HID driver for UC-Logic devices not fully compliant with HID standard
4 * - tablet initialization and parameter retrieval
5 *
6 * Copyright (c) 2018 Nikolai Kondrashov
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 "hid-uclogic-params.h"
17#include "hid-uclogic-rdesc.h"
18#include "usbhid/usbhid.h"
19#include "hid-ids.h"
20#include <linux/ctype.h>
21#include <asm/unaligned.h>
22
23/**
24 * Convert a pen in-range reporting type to a string.
25 *
26 * @inrange: The in-range reporting type to convert.
27 *
28 * Returns:
29 * The string representing the type, or NULL if the type is unknown.
30 */
31const char *uclogic_params_pen_inrange_to_str(
32 enum uclogic_params_pen_inrange inrange)
33{
34 switch (inrange) {
35 case UCLOGIC_PARAMS_PEN_INRANGE_NORMAL:
36 return "normal";
37 case UCLOGIC_PARAMS_PEN_INRANGE_INVERTED:
38 return "inverted";
39 default:
40 return NULL;
41 }
42}
43
44/**
45 * uclogic_params_get_str_desc - retrieve a string descriptor from a HID
46 * device interface, putting it into a kmalloc-allocated buffer as is, without
47 * character encoding conversion.
48 *
49 * @pbuf: Location for the kmalloc-allocated buffer pointer containing
50 * the retrieved descriptor. Not modified in case of error.
51 * Can be NULL to have retrieved descriptor discarded.
52 * @hdev: The HID device of the tablet interface to retrieve the string
53 * descriptor from. Cannot be NULL.
54 * @idx: Index of the string descriptor to request from the device.
55 * @len: Length of the buffer to allocate and the data to retrieve.
56 *
57 * Returns:
58 * number of bytes retrieved (<= len),
59 * -EPIPE, if the descriptor was not found, or
60 * another negative errno code in case of other error.
61 */
62static int uclogic_params_get_str_desc(__u8 **pbuf, struct hid_device *hdev,
63 __u8 idx, size_t len)
64{
65 int rc;
66 struct usb_device *udev = hid_to_usb_dev(hdev);
67 __u8 *buf = NULL;
68
69 /* Check arguments */
70 if (hdev == NULL) {
71 rc = -EINVAL;
72 goto cleanup;
73 }
74
75 buf = kmalloc(len, GFP_KERNEL);
76 if (buf == NULL) {
77 rc = -ENOMEM;
78 goto cleanup;
79 }
80
81 rc = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
82 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
83 (USB_DT_STRING << 8) + idx,
84 0x0409, buf, len,
85 USB_CTRL_GET_TIMEOUT);
86 if (rc == -EPIPE) {
87 hid_dbg(hdev, "string descriptor #%hhu not found\n", idx);
88 goto cleanup;
89 } else if (rc < 0) {
90 hid_err(hdev,
91 "failed retrieving string descriptor #%hhu: %d\n",
92 idx, rc);
93 goto cleanup;
94 }
95
96 if (pbuf != NULL) {
97 *pbuf = buf;
98 buf = NULL;
99 }
100
101cleanup:
102 kfree(buf);
103 return rc;
104}
105
106/**
107 * uclogic_params_pen_cleanup - free resources used by struct
108 * uclogic_params_pen (tablet interface's pen input parameters).
109 * Can be called repeatedly.
110 *
111 * @pen: Pen input parameters to cleanup. Cannot be NULL.
112 */
113static void uclogic_params_pen_cleanup(struct uclogic_params_pen *pen)
114{
115 kfree(pen->desc_ptr);
116 memset(pen, 0, sizeof(*pen));
117}
118
119/**
120 * uclogic_params_pen_init() - initialize tablet interface pen
121 * input and retrieve its parameters from the device.
122 *
123 * @pen: Pointer to the pen parameters to initialize (to be
124 * cleaned up with uclogic_params_pen_cleanup()). Not modified in
125 * case of error, or if parameters are not found. Cannot be NULL.
126 * @pfound: Location for a flag which is set to true if the parameters
127 * were found, and to false if not (e.g. device was
128 * incompatible). Not modified in case of error. Cannot be NULL.
129 * @hdev: The HID device of the tablet interface to initialize and get
130 * parameters from. Cannot be NULL.
131 *
132 * Returns:
133 * Zero, if successful. A negative errno code on error.
134 */
135static int uclogic_params_pen_init(struct uclogic_params_pen *pen,
136 bool *pfound,
137 struct hid_device *hdev)
138{
139 int rc;
140 bool found = false;
141 /* Buffer for (part of) the string descriptor */
142 __u8 *buf = NULL;
143 /* Minimum descriptor length required, maximum seen so far is 18 */
144 const int len = 12;
145 s32 resolution;
146 /* Pen report descriptor template parameters */
147 s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM];
148 __u8 *desc_ptr = NULL;
149
150 /* Check arguments */
151 if (pen == NULL || pfound == NULL || hdev == NULL) {
152 rc = -EINVAL;
153 goto cleanup;
154 }
155
156 /*
157 * Read string descriptor containing pen input parameters.
158 * The specific string descriptor and data were discovered by sniffing
159 * the Windows driver traffic.
160 * NOTE: This enables fully-functional tablet mode.
161 */
162 rc = uclogic_params_get_str_desc(&buf, hdev, 100, len);
163 if (rc == -EPIPE) {
164 hid_dbg(hdev,
165 "string descriptor with pen parameters not found, assuming not compatible\n");
166 goto finish;
167 } else if (rc < 0) {
168 hid_err(hdev, "failed retrieving pen parameters: %d\n", rc);
169 goto cleanup;
170 } else if (rc != len) {
171 hid_dbg(hdev,
172 "string descriptor with pen parameters has invalid length (got %d, expected %d), assuming not compatible\n",
173 rc, len);
174 goto finish;
175 }
176
177 /*
178 * Fill report descriptor parameters from the string descriptor
179 */
180 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] =
181 get_unaligned_le16(buf + 2);
182 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] =
183 get_unaligned_le16(buf + 4);
184 desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] =
185 get_unaligned_le16(buf + 8);
186 resolution = get_unaligned_le16(buf + 10);
187 if (resolution == 0) {
188 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0;
189 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0;
190 } else {
191 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] =
192 desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 /
193 resolution;
194 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] =
195 desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 /
196 resolution;
197 }
198 kfree(buf);
199 buf = NULL;
200
201 /*
202 * Generate pen report descriptor
203 */
204 desc_ptr = uclogic_rdesc_template_apply(
205 uclogic_rdesc_pen_template_arr,
206 uclogic_rdesc_pen_template_size,
207 desc_params, ARRAY_SIZE(desc_params));
208 if (desc_ptr == NULL) {
209 rc = -ENOMEM;
210 goto cleanup;
211 }
212
213 /*
214 * Fill-in the parameters
215 */
216 memset(pen, 0, sizeof(*pen));
217 pen->desc_ptr = desc_ptr;
218 desc_ptr = NULL;
219 pen->desc_size = uclogic_rdesc_pen_template_size;
220 pen->id = UCLOGIC_RDESC_PEN_ID;
221 pen->inrange = UCLOGIC_PARAMS_PEN_INRANGE_INVERTED;
222 found = true;
223finish:
224 *pfound = found;
225 rc = 0;
226cleanup:
227 kfree(desc_ptr);
228 kfree(buf);
229 return rc;
230}
231
232/**
233 * uclogic_params_frame_cleanup - free resources used by struct
234 * uclogic_params_frame (tablet interface's frame controls input parameters).
235 * Can be called repeatedly.
236 *
237 * @frame: Frame controls input parameters to cleanup. Cannot be NULL.
238 */
239static void uclogic_params_frame_cleanup(struct uclogic_params_frame *frame)
240{
241 kfree(frame->desc_ptr);
242 memset(frame, 0, sizeof(*frame));
243}
244
245/**
246 * uclogic_params_frame_init_with_desc() - initialize tablet's frame control
247 * parameters with a static report descriptor.
248 *
249 * @frame: Pointer to the frame parameters to initialize (to be cleaned
250 * up with uclogic_params_frame_cleanup()). Not modified in case
251 * of error. Cannot be NULL.
252 * @desc_ptr: Report descriptor pointer. Can be NULL, if desc_size is zero.
253 * @desc_size: Report descriptor size.
254 * @id: Report ID used for frame reports, if they should be tweaked,
255 * zero if not.
256 *
257 * Returns:
258 * Zero, if successful. A negative errno code on error.
259 */
260static int uclogic_params_frame_init_with_desc(
261 struct uclogic_params_frame *frame,
262 const __u8 *desc_ptr,
263 size_t desc_size,
264 unsigned int id)
265{
266 __u8 *copy_desc_ptr;
267
268 if (frame == NULL || (desc_ptr == NULL && desc_size != 0))
269 return -EINVAL;
270
271 copy_desc_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
272 if (copy_desc_ptr == NULL)
273 return -ENOMEM;
274
275 memset(frame, 0, sizeof(*frame));
276 frame->desc_ptr = copy_desc_ptr;
277 frame->desc_size = desc_size;
278 frame->id = id;
279 return 0;
280}
281
282/**
283 * uclogic_params_frame_init_buttonpad() - initialize abstract buttonpad
284 * on a tablet interface.
285 *
286 * @frame: Pointer to the frame parameters to initialize (to be cleaned
287 * up with uclogic_params_frame_cleanup()). Not modified in case
288 * of error, or if parameters are not found. Cannot be NULL.
289 * @pfound: Location for a flag which is set to true if the parameters
290 * were found, and to false if not (e.g. device was
291 * incompatible). Not modified in case of error. Cannot be NULL.
292 * @hdev: The HID device of the tablet interface to initialize and get
293 * parameters from. Cannot be NULL.
294 *
295 * Returns:
296 * Zero, if successful. A negative errno code on error.
297 */
298static int uclogic_params_frame_init_buttonpad(
299 struct uclogic_params_frame *frame,
300 bool *pfound,
301 struct hid_device *hdev)
302{
303 int rc;
304 bool found = false;
305 struct usb_device *usb_dev = hid_to_usb_dev(hdev);
306 char *str_buf = NULL;
307 const size_t str_len = 16;
308
309 /* Check arguments */
310 if (frame == NULL || pfound == NULL || hdev == NULL) {
311 rc = -EINVAL;
312 goto cleanup;
313 }
314
315 /*
316 * Enable generic button mode
317 */
318 str_buf = kzalloc(str_len, GFP_KERNEL);
319 if (str_buf == NULL) {
320 rc = -ENOMEM;
321 goto cleanup;
322 }
323
324 rc = usb_string(usb_dev, 123, str_buf, str_len);
325 if (rc == -EPIPE) {
326 hid_dbg(hdev,
327 "generic button -enabling string descriptor not found\n");
328 } else if (rc < 0) {
329 goto cleanup;
330 } else if (strncmp(str_buf, "HK On", rc) != 0) {
331 hid_dbg(hdev,
332 "invalid response to enabling generic buttons: \"%s\"\n",
333 str_buf);
334 } else {
335 hid_dbg(hdev, "generic buttons enabled\n");
336 rc = uclogic_params_frame_init_with_desc(
337 frame,
338 uclogic_rdesc_buttonpad_arr,
339 uclogic_rdesc_buttonpad_size,
340 UCLOGIC_RDESC_BUTTONPAD_ID);
341 if (rc != 0)
342 goto cleanup;
343 found = true;
344 }
345
346 *pfound = found;
347 rc = 0;
348cleanup:
349 kfree(str_buf);
350 return rc;
351}
352
353/**
354 * uclogic_params_cleanup - free resources used by struct uclogic_params
355 * (tablet interface's parameters).
356 * Can be called repeatedly.
357 *
358 * @params: Input parameters to cleanup. Cannot be NULL.
359 */
360void uclogic_params_cleanup(struct uclogic_params *params)
361{
362 if (!params->invalid) {
363 kfree(params->desc_ptr);
364 if (!params->pen_unused)
365 uclogic_params_pen_cleanup(&params->pen);
366 uclogic_params_frame_cleanup(&params->frame);
367 memset(params, 0, sizeof(*params));
368 }
369}
370
371/**
372 * Get a replacement report descriptor for a tablet's interface.
373 *
374 * @params: The parameters of a tablet interface to get report
375 * descriptor for. Cannot be NULL.
376 * @pdesc: Location for the resulting, kmalloc-allocated report
377 * descriptor pointer, or for NULL, if there's no replacement
378 * report descriptor. Not modified in case of error. Cannot be
379 * NULL.
380 * @psize: Location for the resulting report descriptor size, not set if
381 * there's no replacement report descriptor. Not modified in case
382 * of error. Cannot be NULL.
383 *
384 * Returns:
385 * Zero, if successful.
386 * -EINVAL, if invalid arguments are supplied.
387 * -ENOMEM, if failed to allocate memory.
388 */
389int uclogic_params_get_desc(const struct uclogic_params *params,
390 __u8 **pdesc,
391 unsigned int *psize)
392{
393 bool common_present;
394 bool pen_present;
395 bool frame_present;
396 unsigned int size;
397 __u8 *desc = NULL;
398
399 /* Check arguments */
400 if (params == NULL || pdesc == NULL || psize == NULL)
401 return -EINVAL;
402
403 size = 0;
404
405 common_present = (params->desc_ptr != NULL);
406 pen_present = (!params->pen_unused && params->pen.desc_ptr != NULL);
407 frame_present = (params->frame.desc_ptr != NULL);
408
409 if (common_present)
410 size += params->desc_size;
411 if (pen_present)
412 size += params->pen.desc_size;
413 if (frame_present)
414 size += params->frame.desc_size;
415
416 if (common_present || pen_present || frame_present) {
417 __u8 *p;
418
419 desc = kmalloc(size, GFP_KERNEL);
420 if (desc == NULL)
421 return -ENOMEM;
422 p = desc;
423
424 if (common_present) {
425 memcpy(p, params->desc_ptr,
426 params->desc_size);
427 p += params->desc_size;
428 }
429 if (pen_present) {
430 memcpy(p, params->pen.desc_ptr,
431 params->pen.desc_size);
432 p += params->pen.desc_size;
433 }
434 if (frame_present) {
435 memcpy(p, params->frame.desc_ptr,
436 params->frame.desc_size);
437 p += params->frame.desc_size;
438 }
439
440 WARN_ON(p != desc + size);
441
442 *psize = size;
443 }
444
445 *pdesc = desc;
446 return 0;
447}
448
449/**
450 * uclogic_params_init_invalid() - initialize tablet interface parameters,
451 * specifying the interface is invalid.
452 *
453 * @params: Parameters to initialize (to be cleaned with
454 * uclogic_params_cleanup()). Cannot be NULL.
455 */
456static void uclogic_params_init_invalid(struct uclogic_params *params)
457{
458 params->invalid = true;
459}
460
461/**
462 * uclogic_params_init_with_opt_desc() - initialize tablet interface
463 * parameters with an optional replacement report descriptor. Only modify
464 * report descriptor, if the original report descriptor matches the expected
465 * size.
466 *
467 * @params: Parameters to initialize (to be cleaned with
468 * uclogic_params_cleanup()). Not modified in case of
469 * error. Cannot be NULL.
470 * @hdev: The HID device of the tablet interface create the
471 * parameters for. Cannot be NULL.
472 * @orig_desc_size: Expected size of the original report descriptor to
473 * be replaced.
474 * @desc_ptr: Pointer to the replacement report descriptor.
475 * Can be NULL, if desc_size is zero.
476 * @desc_size: Size of the replacement report descriptor.
477 *
478 * Returns:
479 * Zero, if successful. -EINVAL if an invalid argument was passed.
480 * -ENOMEM, if failed to allocate memory.
481 */
482static int uclogic_params_init_with_opt_desc(struct uclogic_params *params,
483 struct hid_device *hdev,
484 unsigned int orig_desc_size,
485 __u8 *desc_ptr,
486 unsigned int desc_size)
487{
488 __u8 *desc_copy_ptr = NULL;
489 unsigned int desc_copy_size;
490 int rc;
491
492 /* Check arguments */
493 if (params == NULL || hdev == NULL ||
494 (desc_ptr == NULL && desc_size != 0)) {
495 rc = -EINVAL;
496 goto cleanup;
497 }
498
499 /* Replace report descriptor, if it matches */
500 if (hdev->dev_rsize == orig_desc_size) {
501 hid_dbg(hdev,
502 "device report descriptor matches the expected size, replacing\n");
503 desc_copy_ptr = kmemdup(desc_ptr, desc_size, GFP_KERNEL);
504 if (desc_copy_ptr == NULL) {
505 rc = -ENOMEM;
506 goto cleanup;
507 }
508 desc_copy_size = desc_size;
509 } else {
510 hid_dbg(hdev,
511 "device report descriptor doesn't match the expected size (%u != %u), preserving\n",
512 hdev->dev_rsize, orig_desc_size);
513 desc_copy_ptr = NULL;
514 desc_copy_size = 0;
515 }
516
517 /* Output parameters */
518 memset(params, 0, sizeof(*params));
519 params->desc_ptr = desc_copy_ptr;
520 desc_copy_ptr = NULL;
521 params->desc_size = desc_copy_size;
522
523 rc = 0;
524cleanup:
525 kfree(desc_copy_ptr);
526 return rc;
527}
528
529/**
530 * uclogic_params_init_with_pen_unused() - initialize tablet interface
531 * parameters preserving original reports and generic HID processing, but
532 * disabling pen usage.
533 *
534 * @params: Parameters to initialize (to be cleaned with
535 * uclogic_params_cleanup()). Not modified in case of
536 * error. Cannot be NULL.
537 */
538static void uclogic_params_init_with_pen_unused(struct uclogic_params *params)
539{
540 memset(params, 0, sizeof(*params));
541 params->pen_unused = true;
542}
543
544/**
545 * uclogic_params_init() - initialize a Huion tablet interface and discover
546 * its parameters.
547 *
548 * @params: Parameters to fill in (to be cleaned with
549 * uclogic_params_cleanup()). Not modified in case of error.
550 * Cannot be NULL.
551 * @hdev: The HID device of the tablet interface to initialize and get
552 * parameters from. Cannot be NULL.
553 *
554 * Returns:
555 * Zero, if successful. A negative errno code on error.
556 */
557static int uclogic_params_huion_init(struct uclogic_params *params,
558 struct hid_device *hdev)
559{
560 int rc;
561 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
562 __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
563 bool found;
564 /* The resulting parameters (noop) */
565 struct uclogic_params p = {0, };
566
567 /* Check arguments */
568 if (params == NULL || hdev == NULL) {
569 rc = -EINVAL;
570 goto cleanup;
571 }
572
573 /* If it's not a pen interface */
574 if (bInterfaceNumber != 0) {
575 /* TODO: Consider marking the interface invalid */
576 uclogic_params_init_with_pen_unused(&p);
577 goto output;
578 }
579
580 /* Try to probe pen parameters */
581 rc = uclogic_params_pen_init(&p.pen, &found, hdev);
582 if (rc != 0) {
583 hid_err(hdev,
584 "failed probing pen parameters: %d\n", rc);
585 goto cleanup;
586 } else if (found) {
587 hid_dbg(hdev, "pen parameters found\n");
588 /* Try to probe buttonpad */
589 rc = uclogic_params_frame_init_buttonpad(
590 &p.frame,
591 &found, hdev);
592 if (rc != 0) {
593 hid_err(hdev, "v1 buttonpad probing failed: %d\n", rc);
594 goto cleanup;
595 }
596 hid_dbg(hdev, "buttonpad parameters%s found\n",
597 (found ? "" : " not"));
598 if (found) {
599 /* Set bitmask marking frame reports */
600 p.pen_frame_flag = 0x20;
601 }
602 goto output;
603 }
604 hid_dbg(hdev, "pen parameters not found\n");
605
606 uclogic_params_init_invalid(&p);
607
608output:
609 /* Output parameters */
610 memcpy(params, &p, sizeof(*params));
611 memset(&p, 0, sizeof(p));
612 rc = 0;
613cleanup:
614 uclogic_params_cleanup(&p);
615 return rc;
616}
617
618/**
619 * uclogic_params_init() - initialize a tablet interface and discover its
620 * parameters.
621 *
622 * @params: Parameters to fill in (to be cleaned with
623 * uclogic_params_cleanup()). Not modified in case of error.
624 * Cannot be NULL.
625 * @hdev: The HID device of the tablet interface to initialize and get
626 * parameters from. Cannot be NULL.
627 *
628 * Returns:
629 * Zero, if successful. A negative errno code on error.
630 */
631int uclogic_params_init(struct uclogic_params *params,
632 struct hid_device *hdev)
633{
634 int rc;
635 struct usb_device *udev = hid_to_usb_dev(hdev);
636 __u8 bNumInterfaces = udev->config->desc.bNumInterfaces;
637 struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
638 __u8 bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber;
639 bool found;
640 /* The resulting parameters (noop) */
641 struct uclogic_params p = {0, };
642
643 /* Check arguments */
644 if (params == NULL || hdev == NULL) {
645 rc = -EINVAL;
646 goto cleanup;
647 }
648
649 /*
650 * Set replacement report descriptor if the original matches the
651 * specified size. Otherwise keep interface unchanged.
652 */
653#define WITH_OPT_DESC(_orig_desc_token, _new_desc_token) \
654 uclogic_params_init_with_opt_desc( \
655 &p, hdev, \
656 UCLOGIC_RDESC_##_orig_desc_token##_SIZE, \
657 uclogic_rdesc_##_new_desc_token##_arr, \
658 uclogic_rdesc_##_new_desc_token##_size)
659
660#define VID_PID(_vid, _pid) \
661 (((__u32)(_vid) << 16) | ((__u32)(_pid) & U16_MAX))
662
663 /*
664 * Handle specific interfaces for specific tablets.
665 *
666 * Observe the following logic:
667 *
668 * If the interface is recognized as producing certain useful input:
669 * Mark interface as valid.
670 * Output interface parameters.
671 * Else, if the interface is recognized as *not* producing any useful
672 * input:
673 * Mark interface as invalid.
674 * Else:
675 * Mark interface as valid.
676 * Output noop parameters.
677 *
678 * Rule of thumb: it is better to disable a broken interface than let
679 * it spew garbage input.
680 */
681
682 switch (VID_PID(hdev->vendor, hdev->product)) {
683 case VID_PID(USB_VENDOR_ID_UCLOGIC,
684 USB_DEVICE_ID_UCLOGIC_TABLET_PF1209):
685 rc = WITH_OPT_DESC(PF1209_ORIG, pf1209_fixed);
686 if (rc != 0)
687 goto cleanup;
688 break;
689 case VID_PID(USB_VENDOR_ID_UCLOGIC,
690 USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U):
691 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp4030u_fixed);
692 if (rc != 0)
693 goto cleanup;
694 break;
695 case VID_PID(USB_VENDOR_ID_UCLOGIC,
696 USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U):
697 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp5540u_fixed);
698 if (rc != 0)
699 goto cleanup;
700 break;
701 case VID_PID(USB_VENDOR_ID_UCLOGIC,
702 USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U):
703 rc = WITH_OPT_DESC(WPXXXXU_ORIG, wp8060u_fixed);
704 if (rc != 0)
705 goto cleanup;
706 break;
707 case VID_PID(USB_VENDOR_ID_UCLOGIC,
708 USB_DEVICE_ID_UCLOGIC_TABLET_WP1062):
709 rc = WITH_OPT_DESC(WP1062_ORIG, wp1062_fixed);
710 if (rc != 0)
711 goto cleanup;
712 break;
713 case VID_PID(USB_VENDOR_ID_UCLOGIC,
714 USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850):
715 switch (bInterfaceNumber) {
716 case 0:
717 rc = WITH_OPT_DESC(TWHL850_ORIG0, twhl850_fixed0);
718 if (rc != 0)
719 goto cleanup;
720 break;
721 case 1:
722 rc = WITH_OPT_DESC(TWHL850_ORIG1, twhl850_fixed1);
723 if (rc != 0)
724 goto cleanup;
725 break;
726 case 2:
727 rc = WITH_OPT_DESC(TWHL850_ORIG2, twhl850_fixed2);
728 if (rc != 0)
729 goto cleanup;
730 break;
731 }
732 break;
733 case VID_PID(USB_VENDOR_ID_UCLOGIC,
734 USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60):
735 /*
736 * If it is not a three-interface version, which is known to
737 * respond to initialization.
738 */
739 if (bNumInterfaces != 3) {
740 switch (bInterfaceNumber) {
741 case 0:
742 rc = WITH_OPT_DESC(TWHA60_ORIG0,
743 twha60_fixed0);
744 if (rc != 0)
745 goto cleanup;
746 break;
747 case 1:
748 rc = WITH_OPT_DESC(TWHA60_ORIG1,
749 twha60_fixed1);
750 if (rc != 0)
751 goto cleanup;
752 break;
753 }
754 break;
755 }
756 /* FALL THROUGH */
757 case VID_PID(USB_VENDOR_ID_HUION,
758 USB_DEVICE_ID_HUION_TABLET):
759 case VID_PID(USB_VENDOR_ID_UCLOGIC,
760 USB_DEVICE_ID_HUION_TABLET):
761 case VID_PID(USB_VENDOR_ID_UCLOGIC,
762 USB_DEVICE_ID_YIYNOVA_TABLET):
763 case VID_PID(USB_VENDOR_ID_UCLOGIC,
764 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_81):
765 case VID_PID(USB_VENDOR_ID_UCLOGIC,
766 USB_DEVICE_ID_UCLOGIC_DRAWIMAGE_G3):
767 case VID_PID(USB_VENDOR_ID_UCLOGIC,
768 USB_DEVICE_ID_UCLOGIC_UGEE_TABLET_45):
769 rc = uclogic_params_huion_init(&p, hdev);
770 if (rc != 0)
771 goto cleanup;
772 break;
773 case VID_PID(USB_VENDOR_ID_UGTIZER,
774 USB_DEVICE_ID_UGTIZER_TABLET_GP0610):
775 case VID_PID(USB_VENDOR_ID_UGEE,
776 USB_DEVICE_ID_UGEE_TABLET_EX07S):
777 /* If this is the pen interface */
778 if (bInterfaceNumber == 1) {
779 /* Probe pen parameters */
780 rc = uclogic_params_pen_init(&p.pen, &found, hdev);
781 if (rc != 0) {
782 hid_err(hdev, "pen probing failed: %d\n", rc);
783 goto cleanup;
784 }
785 if (!found) {
786 hid_warn(hdev, "pen parameters not found");
787 uclogic_params_init_invalid(&p);
788 }
789 } else {
790 /* TODO: Consider marking the interface invalid */
791 uclogic_params_init_with_pen_unused(&p);
792 }
793 break;
794 }
795
796#undef VID_PID
797#undef WITH_OPT_DESC
798
799 /* Output parameters */
800 memcpy(params, &p, sizeof(*params));
801 memset(&p, 0, sizeof(p));
802 rc = 0;
803cleanup:
804 uclogic_params_cleanup(&p);
805 return rc;
806}
diff --git a/drivers/hid/hid-uclogic-params.h b/drivers/hid/hid-uclogic-params.h
new file mode 100644
index 000000000000..4c78d9dd0576
--- /dev/null
+++ b/drivers/hid/hid-uclogic-params.h
@@ -0,0 +1,180 @@
1/* SPDX-License-Identifier: GPL-2.0+ */
2/*
3 * HID driver for UC-Logic devices not fully compliant with HID standard
4 * - tablet initialization and parameter retrieval
5 *
6 * Copyright (c) 2018 Nikolai Kondrashov
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#ifndef _HID_UCLOGIC_PARAMS_H
17#define _HID_UCLOGIC_PARAMS_H
18
19#include <linux/usb.h>
20#include <linux/hid.h>
21
22/* Types of pen in-range reporting */
23enum uclogic_params_pen_inrange {
24 /* Normal reports: zero - out of proximity, one - in proximity */
25 UCLOGIC_PARAMS_PEN_INRANGE_NORMAL = 0,
26 /* Inverted reports: zero - in proximity, one - out of proximity */
27 UCLOGIC_PARAMS_PEN_INRANGE_INVERTED,
28};
29
30/* Convert a pen in-range reporting type to a string */
31extern const char *uclogic_params_pen_inrange_to_str(
32 enum uclogic_params_pen_inrange inrange);
33
34/*
35 * Tablet interface's pen input parameters.
36 *
37 * Must use declarative (descriptive) language, not imperative, to simplify
38 * understanding and maintain consistency.
39 *
40 * Noop (preserving functionality) when filled with zeroes.
41 */
42struct uclogic_params_pen {
43 /*
44 * Pointer to report descriptor describing the inputs.
45 * Allocated with kmalloc.
46 */
47 __u8 *desc_ptr;
48 /*
49 * Size of the report descriptor.
50 * Only valid, if "desc_ptr" is not NULL.
51 */
52 unsigned int desc_size;
53 /* Report ID, if reports should be tweaked, zero if not */
54 unsigned int id;
55 /* Type of in-range reporting, only valid if "id" is not zero */
56 enum uclogic_params_pen_inrange inrange;
57};
58
59/*
60 * Parameters of frame control inputs of a tablet interface.
61 *
62 * Must use declarative (descriptive) language, not imperative, to simplify
63 * understanding and maintain consistency.
64 *
65 * Noop (preserving functionality) when filled with zeroes.
66 */
67struct uclogic_params_frame {
68 /*
69 * Pointer to report descriptor describing the inputs.
70 * Allocated with kmalloc.
71 */
72 __u8 *desc_ptr;
73 /*
74 * Size of the report descriptor.
75 * Only valid, if "desc_ptr" is not NULL.
76 */
77 unsigned int desc_size;
78 /*
79 * Report ID, if reports should be tweaked, zero if not.
80 */
81 unsigned int id;
82};
83
84/*
85 * Tablet interface report parameters.
86 *
87 * Must use declarative (descriptive) language, not imperative, to simplify
88 * understanding and maintain consistency.
89 *
90 * When filled with zeros represents a "noop" configuration - passes all
91 * reports unchanged and lets the generic HID driver handle everything.
92 *
93 * The resulting device report descriptor is assembled from all the report
94 * descriptor parts referenced by the structure. No order of assembly should
95 * be assumed. The structure represents original device report descriptor if
96 * all the parts are NULL.
97 */
98struct uclogic_params {
99 /*
100 * True if the whole interface is invalid, false otherwise.
101 */
102 bool invalid;
103 /*
104 * Pointer to the common part of the replacement report descriptor,
105 * allocated with kmalloc. NULL if no common part is needed.
106 * Only valid, if "invalid" is false.
107 */
108 __u8 *desc_ptr;
109 /*
110 * Size of the common part of the replacement report descriptor.
111 * Only valid, if "desc_ptr" is not NULL.
112 */
113 unsigned int desc_size;
114 /*
115 * True, if pen usage in report descriptor is invalid, when present.
116 * Only valid, if "invalid" is false.
117 */
118 bool pen_unused;
119 /*
120 * Pen parameters and optional report descriptor part.
121 * Only valid if "pen_unused" is valid and false.
122 */
123 struct uclogic_params_pen pen;
124 /*
125 * Frame control parameters and optional report descriptor part.
126 * Only valid, if "invalid" is false.
127 */
128 struct uclogic_params_frame frame;
129 /*
130 * Bitmask matching frame controls "sub-report" flag in the second
131 * byte of the pen report, or zero if it's not expected.
132 * Only valid if both "pen" and "frame" are valid, and "frame.id" is
133 * not zero.
134 */
135 __u8 pen_frame_flag;
136};
137
138/* Initialize a tablet interface and discover its parameters */
139extern int uclogic_params_init(struct uclogic_params *params,
140 struct hid_device *hdev);
141
142/* Tablet interface parameters *printf format string */
143#define UCLOGIC_PARAMS_FMT_STR \
144 ".invalid = %s\n" \
145 ".desc_ptr = %p\n" \
146 ".desc_size = %u\n" \
147 ".pen_unused = %s\n" \
148 ".pen.desc_ptr = %p\n" \
149 ".pen.desc_size = %u\n" \
150 ".pen.id = %u\n" \
151 ".pen.inrange = %s\n" \
152 ".frame.desc_ptr = %p\n" \
153 ".frame.desc_size = %u\n" \
154 ".frame.id = %u\n" \
155 ".pen_frame_flag = 0x%02x\n"
156
157/* Tablet interface parameters *printf format arguments */
158#define UCLOGIC_PARAMS_FMT_ARGS(_params) \
159 ((_params)->invalid ? "true" : "false"), \
160 (_params)->desc_ptr, \
161 (_params)->desc_size, \
162 ((_params)->pen_unused ? "true" : "false"), \
163 (_params)->pen.desc_ptr, \
164 (_params)->pen.desc_size, \
165 (_params)->pen.id, \
166 uclogic_params_pen_inrange_to_str((_params)->pen.inrange), \
167 (_params)->frame.desc_ptr, \
168 (_params)->frame.desc_size, \
169 (_params)->frame.id, \
170 (_params)->pen_frame_flag
171
172/* Get a replacement report descriptor for a tablet's interface. */
173extern int uclogic_params_get_desc(const struct uclogic_params *params,
174 __u8 **pdesc,
175 unsigned int *psize);
176
177/* Free resources used by tablet interface's parameters */
178extern void uclogic_params_cleanup(struct uclogic_params *params);
179
180#endif /* _HID_UCLOGIC_PARAMS_H */