diff options
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-core.c | 428 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-params.c | 806 | ||||
-rw-r--r-- | drivers/hid/hid-uclogic-params.h | 180 |
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 | |||
109 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o | 109 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o |
110 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o | 110 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o |
111 | hid-uclogic-objs := hid-uclogic-core.o \ | 111 | hid-uclogic-objs := hid-uclogic-core.o \ |
112 | hid-uclogic-rdesc.o | 112 | hid-uclogic-rdesc.o \ |
113 | hid-uclogic-params.o | ||
113 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o | 114 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o |
114 | obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o | 115 | obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o |
115 | obj-$(CONFIG_HID_LED) += hid-led.o | 116 | obj-$(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 */ | ||
26 | enum uclogic_prm { | ||
27 | UCLOGIC_PRM_X_LM = 1, | ||
28 | UCLOGIC_PRM_Y_LM = 2, | ||
29 | UCLOGIC_PRM_PRESSURE_LM = 4, | ||
30 | UCLOGIC_PRM_RESOLUTION = 5, | ||
31 | UCLOGIC_PRM_NUM | ||
32 | }; | ||
33 | |||
34 | /* Driver data */ | 24 | /* Driver data */ |
35 | struct uclogic_drvdata { | 25 | struct 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 | ||
43 | static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc, | 37 | static __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 | ||
130 | static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi, | 49 | static 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 | */ | ||
197 | static int uclogic_tablet_enable(struct hid_device *hdev) | ||
198 | { | ||
199 | int rc; | ||
200 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
201 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
202 | __le16 *buf = NULL; | ||
203 | size_t len; | ||
204 | s32 params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; | ||
205 | s32 resolution; | ||
206 | |||
207 | /* | ||
208 | * Read string descriptor containing tablet parameters. The specific | ||
209 | * string descriptor and data were discovered by sniffing the Windows | ||
210 | * driver traffic. | ||
211 | * NOTE: This enables fully-functional tablet mode. | ||
212 | */ | ||
213 | len = UCLOGIC_PRM_NUM * sizeof(*buf); | ||
214 | buf = kmalloc(len, GFP_KERNEL); | ||
215 | if (buf == NULL) { | ||
216 | rc = -ENOMEM; | ||
217 | goto cleanup; | ||
218 | } | ||
219 | rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | ||
220 | USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, | ||
221 | (USB_DT_STRING << 8) + 0x64, | ||
222 | 0x0409, buf, len, | ||
223 | USB_CTRL_GET_TIMEOUT); | ||
224 | if (rc == -EPIPE) { | ||
225 | hid_err(hdev, "device parameters not found\n"); | ||
226 | rc = -ENODEV; | ||
227 | goto cleanup; | ||
228 | } else if (rc < 0) { | ||
229 | hid_err(hdev, "failed to get device parameters: %d\n", rc); | ||
230 | rc = -ENODEV; | ||
231 | goto cleanup; | ||
232 | } else if (rc != len) { | ||
233 | hid_err(hdev, "invalid device parameters\n"); | ||
234 | rc = -ENODEV; | ||
235 | goto cleanup; | ||
236 | } | ||
237 | |||
238 | /* Extract device parameters */ | ||
239 | params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = | ||
240 | le16_to_cpu(buf[UCLOGIC_PRM_X_LM]); | ||
241 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = | ||
242 | le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]); | ||
243 | params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = | ||
244 | le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]); | ||
245 | resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]); | ||
246 | if (resolution == 0) { | ||
247 | params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; | ||
248 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; | ||
249 | } else { | ||
250 | params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = | ||
251 | params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * | ||
252 | 1000 / resolution; | ||
253 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = | ||
254 | params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * | ||
255 | 1000 / resolution; | ||
256 | } | ||
257 | |||
258 | /* Format fixed report descriptor */ | ||
259 | drvdata->rdesc = uclogic_rdesc_template_apply( | ||
260 | uclogic_rdesc_pen_template_arr, | ||
261 | uclogic_rdesc_pen_template_size, | ||
262 | params, ARRAY_SIZE(params)); | ||
263 | if (drvdata->rdesc == NULL) { | ||
264 | rc = -ENOMEM; | ||
265 | goto cleanup; | ||
266 | } | ||
267 | drvdata->rsize = uclogic_rdesc_pen_template_size; | ||
268 | |||
269 | rc = 0; | ||
270 | |||
271 | cleanup: | ||
272 | kfree(buf); | ||
273 | return rc; | ||
274 | } | ||
275 | |||
276 | /** | ||
277 | * Enable actual button mode. | ||
278 | * | ||
279 | * @hdev: HID device | ||
280 | */ | ||
281 | static int uclogic_button_enable(struct hid_device *hdev) | ||
282 | { | ||
283 | int rc; | ||
284 | struct usb_device *usb_dev = hid_to_usb_dev(hdev); | ||
285 | struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev); | ||
286 | char *str_buf; | ||
287 | size_t str_len = 16; | ||
288 | unsigned char *rdesc; | ||
289 | size_t rdesc_len; | ||
290 | |||
291 | str_buf = kzalloc(str_len, GFP_KERNEL); | ||
292 | if (str_buf == NULL) { | ||
293 | rc = -ENOMEM; | ||
294 | goto cleanup; | ||
295 | } | ||
296 | |||
297 | /* Enable abstract keyboard mode */ | ||
298 | rc = usb_string(usb_dev, 0x7b, str_buf, str_len); | ||
299 | if (rc == -EPIPE) { | ||
300 | hid_info(hdev, "button mode setting not found\n"); | ||
301 | rc = 0; | ||
302 | goto cleanup; | ||
303 | } else if (rc < 0) { | ||
304 | hid_err(hdev, "failed to enable abstract keyboard\n"); | ||
305 | goto cleanup; | ||
306 | } else if (strncmp(str_buf, "HK On", rc)) { | ||
307 | hid_info(hdev, "invalid answer when requesting buttons: '%s'\n", | ||
308 | str_buf); | ||
309 | rc = -EINVAL; | ||
310 | goto cleanup; | ||
311 | } | ||
312 | |||
313 | /* Re-allocate fixed report descriptor */ | ||
314 | rdesc_len = drvdata->rsize + uclogic_rdesc_buttonpad_size; | ||
315 | rdesc = devm_kzalloc(&hdev->dev, rdesc_len, GFP_KERNEL); | ||
316 | if (!rdesc) { | ||
317 | rc = -ENOMEM; | ||
318 | goto cleanup; | ||
319 | } | ||
320 | |||
321 | memcpy(rdesc, drvdata->rdesc, drvdata->rsize); | ||
322 | |||
323 | /* Append the buttonpad descriptor */ | ||
324 | memcpy(rdesc + drvdata->rsize, uclogic_rdesc_buttonpad_arr, | ||
325 | uclogic_rdesc_buttonpad_size); | ||
326 | |||
327 | /* clean up old rdesc and use the new one */ | ||
328 | drvdata->rsize = rdesc_len; | ||
329 | devm_kfree(&hdev->dev, drvdata->rdesc); | ||
330 | drvdata->rdesc = rdesc; | ||
331 | |||
332 | rc = 0; | ||
333 | |||
334 | cleanup: | ||
335 | kfree(str_buf); | ||
336 | return rc; | ||
337 | } | ||
338 | |||
339 | static int uclogic_probe(struct hid_device *hdev, | 114 | static 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; |
174 | failure: | ||
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 | ||
434 | static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report, | 181 | static 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 | ||
211 | static 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 | |||
453 | static const struct hid_device_id uclogic_devices[] = { | 220 | static 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 | }; |
478 | MODULE_DEVICE_TABLE(hid, uclogic_devices); | 253 | MODULE_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 | */ | ||
31 | const 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 | */ | ||
62 | static 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 | |||
101 | cleanup: | ||
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 | */ | ||
113 | static 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 | */ | ||
135 | static 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; | ||
223 | finish: | ||
224 | *pfound = found; | ||
225 | rc = 0; | ||
226 | cleanup: | ||
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 | */ | ||
239 | static 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 | */ | ||
260 | static 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 | */ | ||
298 | static 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; | ||
348 | cleanup: | ||
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 | */ | ||
360 | void 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(¶ms->pen); | ||
366 | uclogic_params_frame_cleanup(¶ms->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 | */ | ||
389 | int 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 | */ | ||
456 | static 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 | */ | ||
482 | static 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; | ||
524 | cleanup: | ||
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 | */ | ||
538 | static 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 | */ | ||
557 | static 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 | |||
608 | output: | ||
609 | /* Output parameters */ | ||
610 | memcpy(params, &p, sizeof(*params)); | ||
611 | memset(&p, 0, sizeof(p)); | ||
612 | rc = 0; | ||
613 | cleanup: | ||
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 | */ | ||
631 | int 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; | ||
803 | cleanup: | ||
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 */ | ||
23 | enum 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 */ | ||
31 | extern 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 | */ | ||
42 | struct 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 | */ | ||
67 | struct 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 | */ | ||
98 | struct 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 */ | ||
139 | extern 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. */ | ||
173 | extern 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 */ | ||
178 | extern void uclogic_params_cleanup(struct uclogic_params *params); | ||
179 | |||
180 | #endif /* _HID_UCLOGIC_PARAMS_H */ | ||