1/*
2 *  HID driver for UC-Logic devices not fully compliant with HID standard
3 *
4 *  Copyright (c) 2010-2014 Nikolai Kondrashov
5 *  Copyright (c) 2013 Martin Rusko
6 */
7
8/*
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 */
14
15#include <linux/device.h>
16#include <linux/hid.h>
17#include <linux/module.h>
18#include <linux/usb.h>
19#include <asm/unaligned.h>
20#include "usbhid/usbhid.h"
21
22#include "hid-ids.h"
23
24/*
25 * See WPXXXXU model descriptions, device and HID report descriptors at
26 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP4030U
27 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP5540U
28 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP8060U
29 */
30
31/* Size of the original descriptor of WPXXXXU tablets */
32#define WPXXXXU_RDESC_ORIG_SIZE	212
33
34/* Fixed WP4030U report descriptor */
35static __u8 wp4030u_rdesc_fixed[] = {
36	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
37	0x09, 0x02,         /*  Usage (Pen),                        */
38	0xA1, 0x01,         /*  Collection (Application),           */
39	0x85, 0x09,         /*      Report ID (9),                  */
40	0x09, 0x20,         /*      Usage (Stylus),                 */
41	0xA0,               /*      Collection (Physical),          */
42	0x75, 0x01,         /*          Report Size (1),            */
43	0x09, 0x42,         /*          Usage (Tip Switch),         */
44	0x09, 0x44,         /*          Usage (Barrel Switch),      */
45	0x09, 0x46,         /*          Usage (Tablet Pick),        */
46	0x14,               /*          Logical Minimum (0),        */
47	0x25, 0x01,         /*          Logical Maximum (1),        */
48	0x95, 0x03,         /*          Report Count (3),           */
49	0x81, 0x02,         /*          Input (Variable),           */
50	0x95, 0x05,         /*          Report Count (5),           */
51	0x81, 0x01,         /*          Input (Constant),           */
52	0x75, 0x10,         /*          Report Size (16),           */
53	0x95, 0x01,         /*          Report Count (1),           */
54	0x14,               /*          Logical Minimum (0),        */
55	0xA4,               /*          Push,                       */
56	0x05, 0x01,         /*          Usage Page (Desktop),       */
57	0x55, 0xFD,         /*          Unit Exponent (-3),         */
58	0x65, 0x13,         /*          Unit (Inch),                */
59	0x34,               /*          Physical Minimum (0),       */
60	0x09, 0x30,         /*          Usage (X),                  */
61	0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
62	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
63	0x81, 0x02,         /*          Input (Variable),           */
64	0x09, 0x31,         /*          Usage (Y),                  */
65	0x46, 0xB8, 0x0B,   /*          Physical Maximum (3000),    */
66	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
67	0x81, 0x02,         /*          Input (Variable),           */
68	0xB4,               /*          Pop,                        */
69	0x09, 0x30,         /*          Usage (Tip Pressure),       */
70	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
71	0x81, 0x02,         /*          Input (Variable),           */
72	0xC0,               /*      End Collection,                 */
73	0xC0                /*  End Collection                      */
74};
75
76/* Fixed WP5540U report descriptor */
77static __u8 wp5540u_rdesc_fixed[] = {
78	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
79	0x09, 0x02,         /*  Usage (Pen),                        */
80	0xA1, 0x01,         /*  Collection (Application),           */
81	0x85, 0x09,         /*      Report ID (9),                  */
82	0x09, 0x20,         /*      Usage (Stylus),                 */
83	0xA0,               /*      Collection (Physical),          */
84	0x75, 0x01,         /*          Report Size (1),            */
85	0x09, 0x42,         /*          Usage (Tip Switch),         */
86	0x09, 0x44,         /*          Usage (Barrel Switch),      */
87	0x09, 0x46,         /*          Usage (Tablet Pick),        */
88	0x14,               /*          Logical Minimum (0),        */
89	0x25, 0x01,         /*          Logical Maximum (1),        */
90	0x95, 0x03,         /*          Report Count (3),           */
91	0x81, 0x02,         /*          Input (Variable),           */
92	0x95, 0x05,         /*          Report Count (5),           */
93	0x81, 0x01,         /*          Input (Constant),           */
94	0x75, 0x10,         /*          Report Size (16),           */
95	0x95, 0x01,         /*          Report Count (1),           */
96	0x14,               /*          Logical Minimum (0),        */
97	0xA4,               /*          Push,                       */
98	0x05, 0x01,         /*          Usage Page (Desktop),       */
99	0x55, 0xFD,         /*          Unit Exponent (-3),         */
100	0x65, 0x13,         /*          Unit (Inch),                */
101	0x34,               /*          Physical Minimum (0),       */
102	0x09, 0x30,         /*          Usage (X),                  */
103	0x46, 0x7C, 0x15,   /*          Physical Maximum (5500),    */
104	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
105	0x81, 0x02,         /*          Input (Variable),           */
106	0x09, 0x31,         /*          Usage (Y),                  */
107	0x46, 0xA0, 0x0F,   /*          Physical Maximum (4000),    */
108	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
109	0x81, 0x02,         /*          Input (Variable),           */
110	0xB4,               /*          Pop,                        */
111	0x09, 0x30,         /*          Usage (Tip Pressure),       */
112	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
113	0x81, 0x02,         /*          Input (Variable),           */
114	0xC0,               /*      End Collection,                 */
115	0xC0,               /*  End Collection,                     */
116	0x05, 0x01,         /*  Usage Page (Desktop),               */
117	0x09, 0x02,         /*  Usage (Mouse),                      */
118	0xA1, 0x01,         /*  Collection (Application),           */
119	0x85, 0x08,         /*      Report ID (8),                  */
120	0x09, 0x01,         /*      Usage (Pointer),                */
121	0xA0,               /*      Collection (Physical),          */
122	0x75, 0x01,         /*          Report Size (1),            */
123	0x05, 0x09,         /*          Usage Page (Button),        */
124	0x19, 0x01,         /*          Usage Minimum (01h),        */
125	0x29, 0x03,         /*          Usage Maximum (03h),        */
126	0x14,               /*          Logical Minimum (0),        */
127	0x25, 0x01,         /*          Logical Maximum (1),        */
128	0x95, 0x03,         /*          Report Count (3),           */
129	0x81, 0x02,         /*          Input (Variable),           */
130	0x95, 0x05,         /*          Report Count (5),           */
131	0x81, 0x01,         /*          Input (Constant),           */
132	0x05, 0x01,         /*          Usage Page (Desktop),       */
133	0x75, 0x08,         /*          Report Size (8),            */
134	0x09, 0x30,         /*          Usage (X),                  */
135	0x09, 0x31,         /*          Usage (Y),                  */
136	0x15, 0x81,         /*          Logical Minimum (-127),     */
137	0x25, 0x7F,         /*          Logical Maximum (127),      */
138	0x95, 0x02,         /*          Report Count (2),           */
139	0x81, 0x06,         /*          Input (Variable, Relative), */
140	0x09, 0x38,         /*          Usage (Wheel),              */
141	0x15, 0xFF,         /*          Logical Minimum (-1),       */
142	0x25, 0x01,         /*          Logical Maximum (1),        */
143	0x95, 0x01,         /*          Report Count (1),           */
144	0x81, 0x06,         /*          Input (Variable, Relative), */
145	0x81, 0x01,         /*          Input (Constant),           */
146	0xC0,               /*      End Collection,                 */
147	0xC0                /*  End Collection                      */
148};
149
150/* Fixed WP8060U report descriptor */
151static __u8 wp8060u_rdesc_fixed[] = {
152	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
153	0x09, 0x02,         /*  Usage (Pen),                        */
154	0xA1, 0x01,         /*  Collection (Application),           */
155	0x85, 0x09,         /*      Report ID (9),                  */
156	0x09, 0x20,         /*      Usage (Stylus),                 */
157	0xA0,               /*      Collection (Physical),          */
158	0x75, 0x01,         /*          Report Size (1),            */
159	0x09, 0x42,         /*          Usage (Tip Switch),         */
160	0x09, 0x44,         /*          Usage (Barrel Switch),      */
161	0x09, 0x46,         /*          Usage (Tablet Pick),        */
162	0x14,               /*          Logical Minimum (0),        */
163	0x25, 0x01,         /*          Logical Maximum (1),        */
164	0x95, 0x03,         /*          Report Count (3),           */
165	0x81, 0x02,         /*          Input (Variable),           */
166	0x95, 0x05,         /*          Report Count (5),           */
167	0x81, 0x01,         /*          Input (Constant),           */
168	0x75, 0x10,         /*          Report Size (16),           */
169	0x95, 0x01,         /*          Report Count (1),           */
170	0x14,               /*          Logical Minimum (0),        */
171	0xA4,               /*          Push,                       */
172	0x05, 0x01,         /*          Usage Page (Desktop),       */
173	0x55, 0xFD,         /*          Unit Exponent (-3),         */
174	0x65, 0x13,         /*          Unit (Inch),                */
175	0x34,               /*          Physical Minimum (0),       */
176	0x09, 0x30,         /*          Usage (X),                  */
177	0x46, 0x40, 0x1F,   /*          Physical Maximum (8000),    */
178	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
179	0x81, 0x02,         /*          Input (Variable),           */
180	0x09, 0x31,         /*          Usage (Y),                  */
181	0x46, 0x70, 0x17,   /*          Physical Maximum (6000),    */
182	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
183	0x81, 0x02,         /*          Input (Variable),           */
184	0xB4,               /*          Pop,                        */
185	0x09, 0x30,         /*          Usage (Tip Pressure),       */
186	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
187	0x81, 0x02,         /*          Input (Variable),           */
188	0xC0,               /*      End Collection,                 */
189	0xC0,               /*  End Collection,                     */
190	0x05, 0x01,         /*  Usage Page (Desktop),               */
191	0x09, 0x02,         /*  Usage (Mouse),                      */
192	0xA1, 0x01,         /*  Collection (Application),           */
193	0x85, 0x08,         /*      Report ID (8),                  */
194	0x09, 0x01,         /*      Usage (Pointer),                */
195	0xA0,               /*      Collection (Physical),          */
196	0x75, 0x01,         /*          Report Size (1),            */
197	0x05, 0x09,         /*          Usage Page (Button),        */
198	0x19, 0x01,         /*          Usage Minimum (01h),        */
199	0x29, 0x03,         /*          Usage Maximum (03h),        */
200	0x14,               /*          Logical Minimum (0),        */
201	0x25, 0x01,         /*          Logical Maximum (1),        */
202	0x95, 0x03,         /*          Report Count (3),           */
203	0x81, 0x02,         /*          Input (Variable),           */
204	0x95, 0x05,         /*          Report Count (5),           */
205	0x81, 0x01,         /*          Input (Constant),           */
206	0x05, 0x01,         /*          Usage Page (Desktop),       */
207	0x75, 0x08,         /*          Report Size (8),            */
208	0x09, 0x30,         /*          Usage (X),                  */
209	0x09, 0x31,         /*          Usage (Y),                  */
210	0x15, 0x81,         /*          Logical Minimum (-127),     */
211	0x25, 0x7F,         /*          Logical Maximum (127),      */
212	0x95, 0x02,         /*          Report Count (2),           */
213	0x81, 0x06,         /*          Input (Variable, Relative), */
214	0x09, 0x38,         /*          Usage (Wheel),              */
215	0x15, 0xFF,         /*          Logical Minimum (-1),       */
216	0x25, 0x01,         /*          Logical Maximum (1),        */
217	0x95, 0x01,         /*          Report Count (1),           */
218	0x81, 0x06,         /*          Input (Variable, Relative), */
219	0x81, 0x01,         /*          Input (Constant),           */
220	0xC0,               /*      End Collection,                 */
221	0xC0                /*  End Collection                      */
222};
223
224/*
225 * See WP1062 description, device and HID report descriptors at
226 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_WP1062
227 */
228
229/* Size of the original descriptor of WP1062 tablet */
230#define WP1062_RDESC_ORIG_SIZE	254
231
232/* Fixed WP1062 report descriptor */
233static __u8 wp1062_rdesc_fixed[] = {
234	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
235	0x09, 0x02,         /*  Usage (Pen),                        */
236	0xA1, 0x01,         /*  Collection (Application),           */
237	0x85, 0x09,         /*      Report ID (9),                  */
238	0x09, 0x20,         /*      Usage (Stylus),                 */
239	0xA0,               /*      Collection (Physical),          */
240	0x75, 0x01,         /*          Report Size (1),            */
241	0x09, 0x42,         /*          Usage (Tip Switch),         */
242	0x09, 0x44,         /*          Usage (Barrel Switch),      */
243	0x09, 0x46,         /*          Usage (Tablet Pick),        */
244	0x14,               /*          Logical Minimum (0),        */
245	0x25, 0x01,         /*          Logical Maximum (1),        */
246	0x95, 0x03,         /*          Report Count (3),           */
247	0x81, 0x02,         /*          Input (Variable),           */
248	0x95, 0x04,         /*          Report Count (4),           */
249	0x81, 0x01,         /*          Input (Constant),           */
250	0x09, 0x32,         /*          Usage (In Range),           */
251	0x95, 0x01,         /*          Report Count (1),           */
252	0x81, 0x02,         /*          Input (Variable),           */
253	0x75, 0x10,         /*          Report Size (16),           */
254	0x95, 0x01,         /*          Report Count (1),           */
255	0x14,               /*          Logical Minimum (0),        */
256	0xA4,               /*          Push,                       */
257	0x05, 0x01,         /*          Usage Page (Desktop),       */
258	0x55, 0xFD,         /*          Unit Exponent (-3),         */
259	0x65, 0x13,         /*          Unit (Inch),                */
260	0x34,               /*          Physical Minimum (0),       */
261	0x09, 0x30,         /*          Usage (X),                  */
262	0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
263	0x26, 0x20, 0x4E,   /*          Logical Maximum (20000),    */
264	0x81, 0x02,         /*          Input (Variable),           */
265	0x09, 0x31,         /*          Usage (Y),                  */
266	0x46, 0xB7, 0x19,   /*          Physical Maximum (6583),    */
267	0x26, 0x6E, 0x33,   /*          Logical Maximum (13166),    */
268	0x81, 0x02,         /*          Input (Variable),           */
269	0xB4,               /*          Pop,                        */
270	0x09, 0x30,         /*          Usage (Tip Pressure),       */
271	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
272	0x81, 0x02,         /*          Input (Variable),           */
273	0xC0,               /*      End Collection,                 */
274	0xC0                /*  End Collection                      */
275};
276
277/*
278 * See PF1209 description, device and HID report descriptors at
279 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_PF1209
280 */
281
282/* Size of the original descriptor of PF1209 tablet */
283#define PF1209_RDESC_ORIG_SIZE	234
284
285/* Fixed PF1209 report descriptor */
286static __u8 pf1209_rdesc_fixed[] = {
287	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
288	0x09, 0x02,         /*  Usage (Pen),                        */
289	0xA1, 0x01,         /*  Collection (Application),           */
290	0x85, 0x09,         /*      Report ID (9),                  */
291	0x09, 0x20,         /*      Usage (Stylus),                 */
292	0xA0,               /*      Collection (Physical),          */
293	0x75, 0x01,         /*          Report Size (1),            */
294	0x09, 0x42,         /*          Usage (Tip Switch),         */
295	0x09, 0x44,         /*          Usage (Barrel Switch),      */
296	0x09, 0x46,         /*          Usage (Tablet Pick),        */
297	0x14,               /*          Logical Minimum (0),        */
298	0x25, 0x01,         /*          Logical Maximum (1),        */
299	0x95, 0x03,         /*          Report Count (3),           */
300	0x81, 0x02,         /*          Input (Variable),           */
301	0x95, 0x05,         /*          Report Count (5),           */
302	0x81, 0x01,         /*          Input (Constant),           */
303	0x75, 0x10,         /*          Report Size (16),           */
304	0x95, 0x01,         /*          Report Count (1),           */
305	0x14,               /*          Logical Minimum (0),        */
306	0xA4,               /*          Push,                       */
307	0x05, 0x01,         /*          Usage Page (Desktop),       */
308	0x55, 0xFD,         /*          Unit Exponent (-3),         */
309	0x65, 0x13,         /*          Unit (Inch),                */
310	0x34,               /*          Physical Minimum (0),       */
311	0x09, 0x30,         /*          Usage (X),                  */
312	0x46, 0xE0, 0x2E,   /*          Physical Maximum (12000),   */
313	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
314	0x81, 0x02,         /*          Input (Variable),           */
315	0x09, 0x31,         /*          Usage (Y),                  */
316	0x46, 0x28, 0x23,   /*          Physical Maximum (9000),    */
317	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
318	0x81, 0x02,         /*          Input (Variable),           */
319	0xB4,               /*          Pop,                        */
320	0x09, 0x30,         /*          Usage (Tip Pressure),       */
321	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
322	0x81, 0x02,         /*          Input (Variable),           */
323	0xC0,               /*      End Collection,                 */
324	0xC0,               /*  End Collection,                     */
325	0x05, 0x01,         /*  Usage Page (Desktop),               */
326	0x09, 0x02,         /*  Usage (Mouse),                      */
327	0xA1, 0x01,         /*  Collection (Application),           */
328	0x85, 0x08,         /*      Report ID (8),                  */
329	0x09, 0x01,         /*      Usage (Pointer),                */
330	0xA0,               /*      Collection (Physical),          */
331	0x75, 0x01,         /*          Report Size (1),            */
332	0x05, 0x09,         /*          Usage Page (Button),        */
333	0x19, 0x01,         /*          Usage Minimum (01h),        */
334	0x29, 0x03,         /*          Usage Maximum (03h),        */
335	0x14,               /*          Logical Minimum (0),        */
336	0x25, 0x01,         /*          Logical Maximum (1),        */
337	0x95, 0x03,         /*          Report Count (3),           */
338	0x81, 0x02,         /*          Input (Variable),           */
339	0x95, 0x05,         /*          Report Count (5),           */
340	0x81, 0x01,         /*          Input (Constant),           */
341	0x05, 0x01,         /*          Usage Page (Desktop),       */
342	0x75, 0x08,         /*          Report Size (8),            */
343	0x09, 0x30,         /*          Usage (X),                  */
344	0x09, 0x31,         /*          Usage (Y),                  */
345	0x15, 0x81,         /*          Logical Minimum (-127),     */
346	0x25, 0x7F,         /*          Logical Maximum (127),      */
347	0x95, 0x02,         /*          Report Count (2),           */
348	0x81, 0x06,         /*          Input (Variable, Relative), */
349	0x09, 0x38,         /*          Usage (Wheel),              */
350	0x15, 0xFF,         /*          Logical Minimum (-1),       */
351	0x25, 0x01,         /*          Logical Maximum (1),        */
352	0x95, 0x01,         /*          Report Count (1),           */
353	0x81, 0x06,         /*          Input (Variable, Relative), */
354	0x81, 0x01,         /*          Input (Constant),           */
355	0xC0,               /*      End Collection,                 */
356	0xC0                /*  End Collection                      */
357};
358
359/*
360 * See TWHL850 description, device and HID report descriptors at
361 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Wireless_Tablet_TWHL850
362 */
363
364/* Size of the original descriptors of TWHL850 tablet */
365#define TWHL850_RDESC_ORIG_SIZE0	182
366#define TWHL850_RDESC_ORIG_SIZE1	161
367#define TWHL850_RDESC_ORIG_SIZE2	92
368
369/* Fixed PID 0522 tablet report descriptor, interface 0 (stylus) */
370static __u8 twhl850_rdesc_fixed0[] = {
371	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
372	0x09, 0x02,         /*  Usage (Pen),                        */
373	0xA1, 0x01,         /*  Collection (Application),           */
374	0x85, 0x09,         /*      Report ID (9),                  */
375	0x09, 0x20,         /*      Usage (Stylus),                 */
376	0xA0,               /*      Collection (Physical),          */
377	0x14,               /*          Logical Minimum (0),        */
378	0x25, 0x01,         /*          Logical Maximum (1),        */
379	0x75, 0x01,         /*          Report Size (1),            */
380	0x95, 0x03,         /*          Report Count (3),           */
381	0x09, 0x42,         /*          Usage (Tip Switch),         */
382	0x09, 0x44,         /*          Usage (Barrel Switch),      */
383	0x09, 0x46,         /*          Usage (Tablet Pick),        */
384	0x81, 0x02,         /*          Input (Variable),           */
385	0x81, 0x03,         /*          Input (Constant, Variable), */
386	0x95, 0x01,         /*          Report Count (1),           */
387	0x09, 0x32,         /*          Usage (In Range),           */
388	0x81, 0x02,         /*          Input (Variable),           */
389	0x81, 0x03,         /*          Input (Constant, Variable), */
390	0x75, 0x10,         /*          Report Size (16),           */
391	0xA4,               /*          Push,                       */
392	0x05, 0x01,         /*          Usage Page (Desktop),       */
393	0x65, 0x13,         /*          Unit (Inch),                */
394	0x55, 0xFD,         /*          Unit Exponent (-3),         */
395	0x34,               /*          Physical Minimum (0),       */
396	0x09, 0x30,         /*          Usage (X),                  */
397	0x46, 0x40, 0x1F,   /*          Physical Maximum (8000),    */
398	0x26, 0x00, 0x7D,   /*          Logical Maximum (32000),    */
399	0x81, 0x02,         /*          Input (Variable),           */
400	0x09, 0x31,         /*          Usage (Y),                  */
401	0x46, 0x88, 0x13,   /*          Physical Maximum (5000),    */
402	0x26, 0x20, 0x4E,   /*          Logical Maximum (20000),    */
403	0x81, 0x02,         /*          Input (Variable),           */
404	0xB4,               /*          Pop,                        */
405	0x09, 0x30,         /*          Usage (Tip Pressure),       */
406	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
407	0x81, 0x02,         /*          Input (Variable),           */
408	0xC0,               /*      End Collection,                 */
409	0xC0                /*  End Collection                      */
410};
411
412/* Fixed PID 0522 tablet report descriptor, interface 1 (mouse) */
413static __u8 twhl850_rdesc_fixed1[] = {
414	0x05, 0x01,         /*  Usage Page (Desktop),               */
415	0x09, 0x02,         /*  Usage (Mouse),                      */
416	0xA1, 0x01,         /*  Collection (Application),           */
417	0x85, 0x01,         /*      Report ID (1),                  */
418	0x09, 0x01,         /*      Usage (Pointer),                */
419	0xA0,               /*      Collection (Physical),          */
420	0x05, 0x09,         /*          Usage Page (Button),        */
421	0x75, 0x01,         /*          Report Size (1),            */
422	0x95, 0x03,         /*          Report Count (3),           */
423	0x19, 0x01,         /*          Usage Minimum (01h),        */
424	0x29, 0x03,         /*          Usage Maximum (03h),        */
425	0x14,               /*          Logical Minimum (0),        */
426	0x25, 0x01,         /*          Logical Maximum (1),        */
427	0x81, 0x02,         /*          Input (Variable),           */
428	0x95, 0x05,         /*          Report Count (5),           */
429	0x81, 0x03,         /*          Input (Constant, Variable), */
430	0x05, 0x01,         /*          Usage Page (Desktop),       */
431	0x09, 0x30,         /*          Usage (X),                  */
432	0x09, 0x31,         /*          Usage (Y),                  */
433	0x16, 0x00, 0x80,   /*          Logical Minimum (-32768),   */
434	0x26, 0xFF, 0x7F,   /*          Logical Maximum (32767),    */
435	0x75, 0x10,         /*          Report Size (16),           */
436	0x95, 0x02,         /*          Report Count (2),           */
437	0x81, 0x06,         /*          Input (Variable, Relative), */
438	0x09, 0x38,         /*          Usage (Wheel),              */
439	0x15, 0xFF,         /*          Logical Minimum (-1),       */
440	0x25, 0x01,         /*          Logical Maximum (1),        */
441	0x95, 0x01,         /*          Report Count (1),           */
442	0x75, 0x08,         /*          Report Size (8),            */
443	0x81, 0x06,         /*          Input (Variable, Relative), */
444	0x81, 0x03,         /*          Input (Constant, Variable), */
445	0xC0,               /*      End Collection,                 */
446	0xC0                /*  End Collection                      */
447};
448
449/* Fixed PID 0522 tablet report descriptor, interface 2 (frame buttons) */
450static __u8 twhl850_rdesc_fixed2[] = {
451	0x05, 0x01,         /*  Usage Page (Desktop),               */
452	0x09, 0x06,         /*  Usage (Keyboard),                   */
453	0xA1, 0x01,         /*  Collection (Application),           */
454	0x85, 0x03,         /*      Report ID (3),                  */
455	0x05, 0x07,         /*      Usage Page (Keyboard),          */
456	0x14,               /*      Logical Minimum (0),            */
457	0x19, 0xE0,         /*      Usage Minimum (KB Leftcontrol), */
458	0x29, 0xE7,         /*      Usage Maximum (KB Right GUI),   */
459	0x25, 0x01,         /*      Logical Maximum (1),            */
460	0x75, 0x01,         /*      Report Size (1),                */
461	0x95, 0x08,         /*      Report Count (8),               */
462	0x81, 0x02,         /*      Input (Variable),               */
463	0x18,               /*      Usage Minimum (None),           */
464	0x29, 0xFF,         /*      Usage Maximum (FFh),            */
465	0x26, 0xFF, 0x00,   /*      Logical Maximum (255),          */
466	0x75, 0x08,         /*      Report Size (8),                */
467	0x95, 0x06,         /*      Report Count (6),               */
468	0x80,               /*      Input,                          */
469	0xC0                /*  End Collection                      */
470};
471
472/*
473 * See TWHA60 description, device and HID report descriptors at
474 * http://sf.net/apps/mediawiki/digimend/?title=UC-Logic_Tablet_TWHA60
475 */
476
477/* Size of the original descriptors of TWHA60 tablet */
478#define TWHA60_RDESC_ORIG_SIZE0 254
479#define TWHA60_RDESC_ORIG_SIZE1 139
480
481/* Fixed TWHA60 report descriptor, interface 0 (stylus) */
482static __u8 twha60_rdesc_fixed0[] = {
483	0x05, 0x0D,         /*  Usage Page (Digitizer),             */
484	0x09, 0x02,         /*  Usage (Pen),                        */
485	0xA1, 0x01,         /*  Collection (Application),           */
486	0x85, 0x09,         /*      Report ID (9),                  */
487	0x09, 0x20,         /*      Usage (Stylus),                 */
488	0xA0,               /*      Collection (Physical),          */
489	0x75, 0x01,         /*          Report Size (1),            */
490	0x09, 0x42,         /*          Usage (Tip Switch),         */
491	0x09, 0x44,         /*          Usage (Barrel Switch),      */
492	0x09, 0x46,         /*          Usage (Tablet Pick),        */
493	0x14,               /*          Logical Minimum (0),        */
494	0x25, 0x01,         /*          Logical Maximum (1),        */
495	0x95, 0x03,         /*          Report Count (3),           */
496	0x81, 0x02,         /*          Input (Variable),           */
497	0x95, 0x04,         /*          Report Count (4),           */
498	0x81, 0x01,         /*          Input (Constant),           */
499	0x09, 0x32,         /*          Usage (In Range),           */
500	0x95, 0x01,         /*          Report Count (1),           */
501	0x81, 0x02,         /*          Input (Variable),           */
502	0x75, 0x10,         /*          Report Size (16),           */
503	0x95, 0x01,         /*          Report Count (1),           */
504	0x14,               /*          Logical Minimum (0),        */
505	0xA4,               /*          Push,                       */
506	0x05, 0x01,         /*          Usage Page (Desktop),       */
507	0x55, 0xFD,         /*          Unit Exponent (-3),         */
508	0x65, 0x13,         /*          Unit (Inch),                */
509	0x34,               /*          Physical Minimum (0),       */
510	0x09, 0x30,         /*          Usage (X),                  */
511	0x46, 0x10, 0x27,   /*          Physical Maximum (10000),   */
512	0x27, 0x3F, 0x9C,
513		0x00, 0x00, /*          Logical Maximum (39999),    */
514	0x81, 0x02,         /*          Input (Variable),           */
515	0x09, 0x31,         /*          Usage (Y),                  */
516	0x46, 0x6A, 0x18,   /*          Physical Maximum (6250),    */
517	0x26, 0xA7, 0x61,   /*          Logical Maximum (24999),    */
518	0x81, 0x02,         /*          Input (Variable),           */
519	0xB4,               /*          Pop,                        */
520	0x09, 0x30,         /*          Usage (Tip Pressure),       */
521	0x26, 0xFF, 0x03,   /*          Logical Maximum (1023),     */
522	0x81, 0x02,         /*          Input (Variable),           */
523	0xC0,               /*      End Collection,                 */
524	0xC0                /*  End Collection                      */
525};
526
527/* Fixed TWHA60 report descriptor, interface 1 (frame buttons) */
528static __u8 twha60_rdesc_fixed1[] = {
529	0x05, 0x01, /*  Usage Page (Desktop),       */
530	0x09, 0x06, /*  Usage (Keyboard),           */
531	0xA1, 0x01, /*  Collection (Application),   */
532	0x85, 0x05, /*      Report ID (5),          */
533	0x05, 0x07, /*      Usage Page (Keyboard),  */
534	0x14,       /*      Logical Minimum (0),    */
535	0x25, 0x01, /*      Logical Maximum (1),    */
536	0x75, 0x01, /*      Report Size (1),        */
537	0x95, 0x08, /*      Report Count (8),       */
538	0x81, 0x01, /*      Input (Constant),       */
539	0x95, 0x0C, /*      Report Count (12),      */
540	0x19, 0x3A, /*      Usage Minimum (KB F1),  */
541	0x29, 0x45, /*      Usage Maximum (KB F12), */
542	0x81, 0x02, /*      Input (Variable),       */
543	0x95, 0x0C, /*      Report Count (12),      */
544	0x19, 0x68, /*      Usage Minimum (KB F13), */
545	0x29, 0x73, /*      Usage Maximum (KB F24), */
546	0x81, 0x02, /*      Input (Variable),       */
547	0x95, 0x08, /*      Report Count (8),       */
548	0x81, 0x01, /*      Input (Constant),       */
549	0xC0        /*  End Collection              */
550};
551
552/* Report descriptor template placeholder head */
553#define UCLOGIC_PH_HEAD	0xFE, 0xED, 0x1D
554
555/* Report descriptor template placeholder IDs */
556enum uclogic_ph_id {
557	UCLOGIC_PH_ID_X_LM,
558	UCLOGIC_PH_ID_X_PM,
559	UCLOGIC_PH_ID_Y_LM,
560	UCLOGIC_PH_ID_Y_PM,
561	UCLOGIC_PH_ID_PRESSURE_LM,
562	UCLOGIC_PH_ID_NUM
563};
564
565/* Report descriptor template placeholder */
566#define UCLOGIC_PH(_ID) UCLOGIC_PH_HEAD, UCLOGIC_PH_ID_##_ID
567#define UCLOGIC_PEN_REPORT_ID	0x07
568
569/* Fixed report descriptor template */
570static const __u8 uclogic_tablet_rdesc_template[] = {
571	0x05, 0x0D,             /*  Usage Page (Digitizer),                 */
572	0x09, 0x02,             /*  Usage (Pen),                            */
573	0xA1, 0x01,             /*  Collection (Application),               */
574	0x85, 0x07,             /*      Report ID (7),                      */
575	0x09, 0x20,             /*      Usage (Stylus),                     */
576	0xA0,                   /*      Collection (Physical),              */
577	0x14,                   /*          Logical Minimum (0),            */
578	0x25, 0x01,             /*          Logical Maximum (1),            */
579	0x75, 0x01,             /*          Report Size (1),                */
580	0x09, 0x42,             /*          Usage (Tip Switch),             */
581	0x09, 0x44,             /*          Usage (Barrel Switch),          */
582	0x09, 0x46,             /*          Usage (Tablet Pick),            */
583	0x95, 0x03,             /*          Report Count (3),               */
584	0x81, 0x02,             /*          Input (Variable),               */
585	0x95, 0x03,             /*          Report Count (3),               */
586	0x81, 0x03,             /*          Input (Constant, Variable),     */
587	0x09, 0x32,             /*          Usage (In Range),               */
588	0x95, 0x01,             /*          Report Count (1),               */
589	0x81, 0x02,             /*          Input (Variable),               */
590	0x95, 0x01,             /*          Report Count (1),               */
591	0x81, 0x03,             /*          Input (Constant, Variable),     */
592	0x75, 0x10,             /*          Report Size (16),               */
593	0x95, 0x01,             /*          Report Count (1),               */
594	0xA4,                   /*          Push,                           */
595	0x05, 0x01,             /*          Usage Page (Desktop),           */
596	0x65, 0x13,             /*          Unit (Inch),                    */
597	0x55, 0xFD,             /*          Unit Exponent (-3),             */
598	0x34,                   /*          Physical Minimum (0),           */
599	0x09, 0x30,             /*          Usage (X),                      */
600	0x27, UCLOGIC_PH(X_LM), /*          Logical Maximum (PLACEHOLDER),  */
601	0x47, UCLOGIC_PH(X_PM), /*          Physical Maximum (PLACEHOLDER), */
602	0x81, 0x02,             /*          Input (Variable),               */
603	0x09, 0x31,             /*          Usage (Y),                      */
604	0x27, UCLOGIC_PH(Y_LM), /*          Logical Maximum (PLACEHOLDER),  */
605	0x47, UCLOGIC_PH(Y_PM), /*          Physical Maximum (PLACEHOLDER), */
606	0x81, 0x02,             /*          Input (Variable),               */
607	0xB4,                   /*          Pop,                            */
608	0x09, 0x30,             /*          Usage (Tip Pressure),           */
609	0x27,
610	UCLOGIC_PH(PRESSURE_LM),/*          Logical Maximum (PLACEHOLDER),  */
611	0x81, 0x02,             /*          Input (Variable),               */
612	0xC0,                   /*      End Collection,                     */
613	0xC0                    /*  End Collection                          */
614};
615
616/* Parameter indices */
617enum uclogic_prm {
618	UCLOGIC_PRM_X_LM	= 1,
619	UCLOGIC_PRM_Y_LM	= 2,
620	UCLOGIC_PRM_PRESSURE_LM	= 4,
621	UCLOGIC_PRM_RESOLUTION	= 5,
622	UCLOGIC_PRM_NUM
623};
624
625/* Driver data */
626struct uclogic_drvdata {
627	__u8 *rdesc;
628	unsigned int rsize;
629	bool invert_pen_inrange;
630	bool ignore_pen_usage;
631};
632
633static __u8 *uclogic_report_fixup(struct hid_device *hdev, __u8 *rdesc,
634					unsigned int *rsize)
635{
636	struct usb_interface *iface = to_usb_interface(hdev->dev.parent);
637	__u8 iface_num = iface->cur_altsetting->desc.bInterfaceNumber;
638	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
639
640	switch (hdev->product) {
641	case USB_DEVICE_ID_UCLOGIC_TABLET_PF1209:
642		if (*rsize == PF1209_RDESC_ORIG_SIZE) {
643			rdesc = pf1209_rdesc_fixed;
644			*rsize = sizeof(pf1209_rdesc_fixed);
645		}
646		break;
647	case USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U:
648		if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
649			rdesc = wp4030u_rdesc_fixed;
650			*rsize = sizeof(wp4030u_rdesc_fixed);
651		}
652		break;
653	case USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U:
654		if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
655			rdesc = wp5540u_rdesc_fixed;
656			*rsize = sizeof(wp5540u_rdesc_fixed);
657		}
658		break;
659	case USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U:
660		if (*rsize == WPXXXXU_RDESC_ORIG_SIZE) {
661			rdesc = wp8060u_rdesc_fixed;
662			*rsize = sizeof(wp8060u_rdesc_fixed);
663		}
664		break;
665	case USB_DEVICE_ID_UCLOGIC_TABLET_WP1062:
666		if (*rsize == WP1062_RDESC_ORIG_SIZE) {
667			rdesc = wp1062_rdesc_fixed;
668			*rsize = sizeof(wp1062_rdesc_fixed);
669		}
670		break;
671	case USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850:
672		switch (iface_num) {
673		case 0:
674			if (*rsize == TWHL850_RDESC_ORIG_SIZE0) {
675				rdesc = twhl850_rdesc_fixed0;
676				*rsize = sizeof(twhl850_rdesc_fixed0);
677			}
678			break;
679		case 1:
680			if (*rsize == TWHL850_RDESC_ORIG_SIZE1) {
681				rdesc = twhl850_rdesc_fixed1;
682				*rsize = sizeof(twhl850_rdesc_fixed1);
683			}
684			break;
685		case 2:
686			if (*rsize == TWHL850_RDESC_ORIG_SIZE2) {
687				rdesc = twhl850_rdesc_fixed2;
688				*rsize = sizeof(twhl850_rdesc_fixed2);
689			}
690			break;
691		}
692		break;
693	case USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60:
694		switch (iface_num) {
695		case 0:
696			if (*rsize == TWHA60_RDESC_ORIG_SIZE0) {
697				rdesc = twha60_rdesc_fixed0;
698				*rsize = sizeof(twha60_rdesc_fixed0);
699			}
700			break;
701		case 1:
702			if (*rsize == TWHA60_RDESC_ORIG_SIZE1) {
703				rdesc = twha60_rdesc_fixed1;
704				*rsize = sizeof(twha60_rdesc_fixed1);
705			}
706			break;
707		}
708		break;
709	default:
710		if (drvdata->rdesc != NULL) {
711			rdesc = drvdata->rdesc;
712			*rsize = drvdata->rsize;
713		}
714	}
715
716	return rdesc;
717}
718
719static int uclogic_input_mapping(struct hid_device *hdev, struct hid_input *hi,
720		struct hid_field *field, struct hid_usage *usage,
721		unsigned long **bit, int *max)
722{
723	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
724
725	/* discard the unused pen interface */
726	if ((drvdata->ignore_pen_usage) &&
727	    (field->application == HID_DG_PEN))
728		return -1;
729
730	/* let hid-core decide what to do */
731	return 0;
732}
733
734static void uclogic_input_configured(struct hid_device *hdev,
735		struct hid_input *hi)
736{
737	char *name;
738	const char *suffix = NULL;
739	struct hid_field *field;
740	size_t len;
741
742	/* no report associated (HID_QUIRK_MULTI_INPUT not set) */
743	if (!hi->report)
744		return;
745
746	field = hi->report->field[0];
747
748	switch (field->application) {
749	case HID_GD_KEYBOARD:
750		suffix = "Keyboard";
751		break;
752	case HID_GD_MOUSE:
753		suffix = "Mouse";
754		break;
755	case HID_GD_KEYPAD:
756		suffix = "Pad";
757		break;
758	case HID_DG_PEN:
759		suffix = "Pen";
760		break;
761	case HID_CP_CONSUMER_CONTROL:
762		suffix = "Consumer Control";
763		break;
764	case HID_GD_SYSTEM_CONTROL:
765		suffix = "System Control";
766		break;
767	}
768
769	if (suffix) {
770		len = strlen(hdev->name) + 2 + strlen(suffix);
771		name = devm_kzalloc(&hi->input->dev, len, GFP_KERNEL);
772		if (name) {
773			snprintf(name, len, "%s %s", hdev->name, suffix);
774			hi->input->name = name;
775		}
776	}
777}
778
779/**
780 * Enable fully-functional tablet mode and determine device parameters.
781 *
782 * @hdev:	HID device
783 */
784static int uclogic_tablet_enable(struct hid_device *hdev)
785{
786	int rc;
787	struct usb_device *usb_dev = hid_to_usb_dev(hdev);
788	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
789	__le16 *buf = NULL;
790	size_t len;
791	s32 params[UCLOGIC_PH_ID_NUM];
792	s32 resolution;
793	__u8 *p;
794	s32 v;
795
796	/*
797	 * Read string descriptor containing tablet parameters. The specific
798	 * string descriptor and data were discovered by sniffing the Windows
799	 * driver traffic.
800	 * NOTE: This enables fully-functional tablet mode.
801	 */
802	len = UCLOGIC_PRM_NUM * sizeof(*buf);
803	buf = kmalloc(len, GFP_KERNEL);
804	if (buf == NULL) {
805		hid_err(hdev, "failed to allocate parameter buffer\n");
806		rc = -ENOMEM;
807		goto cleanup;
808	}
809	rc = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
810				USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
811				(USB_DT_STRING << 8) + 0x64,
812				0x0409, buf, len,
813				USB_CTRL_GET_TIMEOUT);
814	if (rc == -EPIPE) {
815		hid_err(hdev, "device parameters not found\n");
816		rc = -ENODEV;
817		goto cleanup;
818	} else if (rc < 0) {
819		hid_err(hdev, "failed to get device parameters: %d\n", rc);
820		rc = -ENODEV;
821		goto cleanup;
822	} else if (rc != len) {
823		hid_err(hdev, "invalid device parameters\n");
824		rc = -ENODEV;
825		goto cleanup;
826	}
827
828	/* Extract device parameters */
829	params[UCLOGIC_PH_ID_X_LM] = le16_to_cpu(buf[UCLOGIC_PRM_X_LM]);
830	params[UCLOGIC_PH_ID_Y_LM] = le16_to_cpu(buf[UCLOGIC_PRM_Y_LM]);
831	params[UCLOGIC_PH_ID_PRESSURE_LM] =
832		le16_to_cpu(buf[UCLOGIC_PRM_PRESSURE_LM]);
833	resolution = le16_to_cpu(buf[UCLOGIC_PRM_RESOLUTION]);
834	if (resolution == 0) {
835		params[UCLOGIC_PH_ID_X_PM] = 0;
836		params[UCLOGIC_PH_ID_Y_PM] = 0;
837	} else {
838		params[UCLOGIC_PH_ID_X_PM] = params[UCLOGIC_PH_ID_X_LM] *
839						1000 / resolution;
840		params[UCLOGIC_PH_ID_Y_PM] = params[UCLOGIC_PH_ID_Y_LM] *
841						1000 / resolution;
842	}
843
844	/* Allocate fixed report descriptor */
845	drvdata->rdesc = devm_kzalloc(&hdev->dev,
846				sizeof(uclogic_tablet_rdesc_template),
847				GFP_KERNEL);
848	if (drvdata->rdesc == NULL) {
849		hid_err(hdev, "failed to allocate fixed rdesc\n");
850		rc = -ENOMEM;
851		goto cleanup;
852	}
853	drvdata->rsize = sizeof(uclogic_tablet_rdesc_template);
854
855	/* Format fixed report descriptor */
856	memcpy(drvdata->rdesc, uclogic_tablet_rdesc_template,
857		drvdata->rsize);
858	for (p = drvdata->rdesc;
859	     p <= drvdata->rdesc + drvdata->rsize - 4;) {
860		if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D &&
861		    p[3] < ARRAY_SIZE(params)) {
862			v = params[p[3]];
863			put_unaligned(cpu_to_le32(v), (s32 *)p);
864			p += 4;
865		} else {
866			p++;
867		}
868	}
869
870	rc = 0;
871
872cleanup:
873	kfree(buf);
874	return rc;
875}
876
877static int uclogic_probe(struct hid_device *hdev,
878		const struct hid_device_id *id)
879{
880	int rc;
881	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
882	struct uclogic_drvdata *drvdata;
883
884	/*
885	 * libinput requires the pad interface to be on a different node
886	 * than the pen, so use QUIRK_MULTI_INPUT for all tablets.
887	 */
888	hdev->quirks |= HID_QUIRK_MULTI_INPUT;
889	hdev->quirks |= HID_QUIRK_NO_EMPTY_INPUT;
890
891	/* Allocate and assign driver data */
892	drvdata = devm_kzalloc(&hdev->dev, sizeof(*drvdata), GFP_KERNEL);
893	if (drvdata == NULL)
894		return -ENOMEM;
895
896	hid_set_drvdata(hdev, drvdata);
897
898	switch (id->product) {
899	case USB_DEVICE_ID_HUION_TABLET:
900		/* If this is the pen interface */
901		if (intf->cur_altsetting->desc.bInterfaceNumber == 0) {
902			rc = uclogic_tablet_enable(hdev);
903			if (rc) {
904				hid_err(hdev, "tablet enabling failed\n");
905				return rc;
906			}
907			drvdata->invert_pen_inrange = true;
908		} else {
909			drvdata->ignore_pen_usage = true;
910		}
911		break;
912	}
913
914	rc = hid_parse(hdev);
915	if (rc) {
916		hid_err(hdev, "parse failed\n");
917		return rc;
918	}
919
920	rc = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
921	if (rc) {
922		hid_err(hdev, "hw start failed\n");
923		return rc;
924	}
925
926	return 0;
927}
928
929static int uclogic_raw_event(struct hid_device *hdev, struct hid_report *report,
930			u8 *data, int size)
931{
932	struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
933
934	if ((drvdata->invert_pen_inrange) &&
935	    (report->type == HID_INPUT_REPORT) &&
936	    (report->id == UCLOGIC_PEN_REPORT_ID) &&
937	    (size >= 2))
938		/* Invert the in-range bit */
939		data[1] ^= 0x40;
940
941	return 0;
942}
943
944static const struct hid_device_id uclogic_devices[] = {
945	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
946				USB_DEVICE_ID_UCLOGIC_TABLET_PF1209) },
947	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
948				USB_DEVICE_ID_UCLOGIC_TABLET_WP4030U) },
949	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
950				USB_DEVICE_ID_UCLOGIC_TABLET_WP5540U) },
951	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
952				USB_DEVICE_ID_UCLOGIC_TABLET_WP8060U) },
953	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
954				USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) },
955	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
956				USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) },
957	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC,
958				USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) },
959	{ HID_USB_DEVICE(USB_VENDOR_ID_HUION, USB_DEVICE_ID_HUION_TABLET) },
960	{ HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_HUION_TABLET) },
961	{ }
962};
963MODULE_DEVICE_TABLE(hid, uclogic_devices);
964
965static struct hid_driver uclogic_driver = {
966	.name = "uclogic",
967	.id_table = uclogic_devices,
968	.probe = uclogic_probe,
969	.report_fixup = uclogic_report_fixup,
970	.raw_event = uclogic_raw_event,
971	.input_mapping = uclogic_input_mapping,
972	.input_configured = uclogic_input_configured,
973};
974module_hid_driver(uclogic_driver);
975
976MODULE_AUTHOR("Martin Rusko");
977MODULE_AUTHOR("Nikolai Kondrashov");
978MODULE_LICENSE("GPL");
979