aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-uclogic-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-uclogic-core.c')
-rw-r--r--drivers/hid/hid-uclogic-core.c428
1 files changed, 102 insertions, 326 deletions
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,