1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HID driver for THQ PS3 uDraw tablet |
4 | * |
5 | * Copyright (C) 2016 Red Hat Inc. All Rights Reserved |
6 | */ |
7 | |
8 | #include <linux/device.h> |
9 | #include <linux/hid.h> |
10 | #include <linux/module.h> |
11 | #include "hid-ids.h" |
12 | |
13 | MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>" ); |
14 | MODULE_DESCRIPTION("PS3 uDraw tablet driver" ); |
15 | MODULE_LICENSE("GPL" ); |
16 | |
17 | /* |
18 | * Protocol information from: |
19 | * https://brandonw.net/udraw/ |
20 | * and the source code of: |
21 | * https://vvvv.org/contribution/udraw-hid |
22 | */ |
23 | |
24 | /* |
25 | * The device is setup with multiple input devices: |
26 | * - the touch area which works as a touchpad |
27 | * - the tablet area which works as a touchpad/drawing tablet |
28 | * - a joypad with a d-pad, and 7 buttons |
29 | * - an accelerometer device |
30 | */ |
31 | |
32 | enum { |
33 | TOUCH_NONE, |
34 | TOUCH_PEN, |
35 | TOUCH_FINGER, |
36 | TOUCH_TWOFINGER |
37 | }; |
38 | |
39 | enum { |
40 | AXIS_X, |
41 | AXIS_Y, |
42 | AXIS_Z |
43 | }; |
44 | |
45 | /* |
46 | * Accelerometer min/max values |
47 | * in order, X, Y and Z |
48 | */ |
49 | static struct { |
50 | int min; |
51 | int max; |
52 | } accel_limits[] = { |
53 | [AXIS_X] = { .min: 490, .max: 534 }, |
54 | [AXIS_Y] = { .min: 490, .max: 534 }, |
55 | [AXIS_Z] = { .min: 492, .max: 536 } |
56 | }; |
57 | |
58 | #define DEVICE_NAME "THQ uDraw Game Tablet for PS3" |
59 | /* resolution in pixels */ |
60 | #define RES_X 1920 |
61 | #define RES_Y 1080 |
62 | /* size in mm */ |
63 | #define WIDTH 160 |
64 | #define HEIGHT 90 |
65 | #define PRESSURE_OFFSET 113 |
66 | #define MAX_PRESSURE (255 - PRESSURE_OFFSET) |
67 | |
68 | struct udraw { |
69 | struct input_dev *joy_input_dev; |
70 | struct input_dev *touch_input_dev; |
71 | struct input_dev *pen_input_dev; |
72 | struct input_dev *accel_input_dev; |
73 | struct hid_device *hdev; |
74 | |
75 | /* |
76 | * The device's two-finger support is pretty unreliable, as |
77 | * the device could report a single touch when the two fingers |
78 | * are too close together, and the distance between fingers, even |
79 | * though reported is not in the same unit as the touches. |
80 | * |
81 | * We'll make do without it, and try to report the first touch |
82 | * as reliably as possible. |
83 | */ |
84 | int last_one_finger_x; |
85 | int last_one_finger_y; |
86 | int last_two_finger_x; |
87 | int last_two_finger_y; |
88 | }; |
89 | |
90 | static int clamp_accel(int axis, int offset) |
91 | { |
92 | axis = clamp(axis, |
93 | accel_limits[offset].min, |
94 | accel_limits[offset].max); |
95 | axis = (axis - accel_limits[offset].min) / |
96 | ((accel_limits[offset].max - |
97 | accel_limits[offset].min) * 0xFF); |
98 | return axis; |
99 | } |
100 | |
101 | static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report, |
102 | u8 *data, int len) |
103 | { |
104 | struct udraw *udraw = hid_get_drvdata(hdev); |
105 | int touch; |
106 | int x, y, z; |
107 | |
108 | if (len != 27) |
109 | return 0; |
110 | |
111 | if (data[11] == 0x00) |
112 | touch = TOUCH_NONE; |
113 | else if (data[11] == 0x40) |
114 | touch = TOUCH_PEN; |
115 | else if (data[11] == 0x80) |
116 | touch = TOUCH_FINGER; |
117 | else |
118 | touch = TOUCH_TWOFINGER; |
119 | |
120 | /* joypad */ |
121 | input_report_key(dev: udraw->joy_input_dev, BTN_WEST, value: data[0] & 1); |
122 | input_report_key(dev: udraw->joy_input_dev, BTN_SOUTH, value: !!(data[0] & 2)); |
123 | input_report_key(dev: udraw->joy_input_dev, BTN_EAST, value: !!(data[0] & 4)); |
124 | input_report_key(dev: udraw->joy_input_dev, BTN_NORTH, value: !!(data[0] & 8)); |
125 | |
126 | input_report_key(dev: udraw->joy_input_dev, BTN_SELECT, value: !!(data[1] & 1)); |
127 | input_report_key(dev: udraw->joy_input_dev, BTN_START, value: !!(data[1] & 2)); |
128 | input_report_key(dev: udraw->joy_input_dev, BTN_MODE, value: !!(data[1] & 16)); |
129 | |
130 | x = y = 0; |
131 | switch (data[2]) { |
132 | case 0x0: |
133 | y = -127; |
134 | break; |
135 | case 0x1: |
136 | y = -127; |
137 | x = 127; |
138 | break; |
139 | case 0x2: |
140 | x = 127; |
141 | break; |
142 | case 0x3: |
143 | y = 127; |
144 | x = 127; |
145 | break; |
146 | case 0x4: |
147 | y = 127; |
148 | break; |
149 | case 0x5: |
150 | y = 127; |
151 | x = -127; |
152 | break; |
153 | case 0x6: |
154 | x = -127; |
155 | break; |
156 | case 0x7: |
157 | y = -127; |
158 | x = -127; |
159 | break; |
160 | default: |
161 | break; |
162 | } |
163 | |
164 | input_report_abs(dev: udraw->joy_input_dev, ABS_X, value: x); |
165 | input_report_abs(dev: udraw->joy_input_dev, ABS_Y, value: y); |
166 | |
167 | input_sync(dev: udraw->joy_input_dev); |
168 | |
169 | /* For pen and touchpad */ |
170 | x = y = 0; |
171 | if (touch != TOUCH_NONE) { |
172 | if (data[15] != 0x0F) |
173 | x = data[15] * 256 + data[17]; |
174 | if (data[16] != 0x0F) |
175 | y = data[16] * 256 + data[18]; |
176 | } |
177 | |
178 | if (touch == TOUCH_FINGER) { |
179 | /* Save the last one-finger touch */ |
180 | udraw->last_one_finger_x = x; |
181 | udraw->last_one_finger_y = y; |
182 | udraw->last_two_finger_x = -1; |
183 | udraw->last_two_finger_y = -1; |
184 | } else if (touch == TOUCH_TWOFINGER) { |
185 | /* |
186 | * We have a problem because x/y is the one for the |
187 | * second finger but we want the first finger given |
188 | * to user-space otherwise it'll look as if it jumped. |
189 | * |
190 | * See the udraw struct definition for why this was |
191 | * implemented this way. |
192 | */ |
193 | if (udraw->last_two_finger_x == -1) { |
194 | /* Save the position of the 2nd finger */ |
195 | udraw->last_two_finger_x = x; |
196 | udraw->last_two_finger_y = y; |
197 | |
198 | x = udraw->last_one_finger_x; |
199 | y = udraw->last_one_finger_y; |
200 | } else { |
201 | /* |
202 | * Offset the 2-finger coords using the |
203 | * saved data from the first finger |
204 | */ |
205 | x = x - (udraw->last_two_finger_x |
206 | - udraw->last_one_finger_x); |
207 | y = y - (udraw->last_two_finger_y |
208 | - udraw->last_one_finger_y); |
209 | } |
210 | } |
211 | |
212 | /* touchpad */ |
213 | if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) { |
214 | input_report_key(dev: udraw->touch_input_dev, BTN_TOUCH, value: 1); |
215 | input_report_key(dev: udraw->touch_input_dev, BTN_TOOL_FINGER, |
216 | value: touch == TOUCH_FINGER); |
217 | input_report_key(dev: udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, |
218 | value: touch == TOUCH_TWOFINGER); |
219 | |
220 | input_report_abs(dev: udraw->touch_input_dev, ABS_X, value: x); |
221 | input_report_abs(dev: udraw->touch_input_dev, ABS_Y, value: y); |
222 | } else { |
223 | input_report_key(dev: udraw->touch_input_dev, BTN_TOUCH, value: 0); |
224 | input_report_key(dev: udraw->touch_input_dev, BTN_TOOL_FINGER, value: 0); |
225 | input_report_key(dev: udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, value: 0); |
226 | } |
227 | input_sync(dev: udraw->touch_input_dev); |
228 | |
229 | /* pen */ |
230 | if (touch == TOUCH_PEN) { |
231 | int level; |
232 | |
233 | level = clamp(data[13] - PRESSURE_OFFSET, |
234 | 0, MAX_PRESSURE); |
235 | |
236 | input_report_key(dev: udraw->pen_input_dev, BTN_TOUCH, value: (level != 0)); |
237 | input_report_key(dev: udraw->pen_input_dev, BTN_TOOL_PEN, value: 1); |
238 | input_report_abs(dev: udraw->pen_input_dev, ABS_PRESSURE, value: level); |
239 | input_report_abs(dev: udraw->pen_input_dev, ABS_X, value: x); |
240 | input_report_abs(dev: udraw->pen_input_dev, ABS_Y, value: y); |
241 | } else { |
242 | input_report_key(dev: udraw->pen_input_dev, BTN_TOUCH, value: 0); |
243 | input_report_key(dev: udraw->pen_input_dev, BTN_TOOL_PEN, value: 0); |
244 | input_report_abs(dev: udraw->pen_input_dev, ABS_PRESSURE, value: 0); |
245 | } |
246 | input_sync(dev: udraw->pen_input_dev); |
247 | |
248 | /* accel */ |
249 | x = (data[19] + (data[20] << 8)); |
250 | x = clamp_accel(axis: x, offset: AXIS_X); |
251 | y = (data[21] + (data[22] << 8)); |
252 | y = clamp_accel(axis: y, offset: AXIS_Y); |
253 | z = (data[23] + (data[24] << 8)); |
254 | z = clamp_accel(axis: z, offset: AXIS_Z); |
255 | input_report_abs(dev: udraw->accel_input_dev, ABS_X, value: x); |
256 | input_report_abs(dev: udraw->accel_input_dev, ABS_Y, value: y); |
257 | input_report_abs(dev: udraw->accel_input_dev, ABS_Z, value: z); |
258 | input_sync(dev: udraw->accel_input_dev); |
259 | |
260 | /* let hidraw and hiddev handle the report */ |
261 | return 0; |
262 | } |
263 | |
264 | static int udraw_open(struct input_dev *dev) |
265 | { |
266 | struct udraw *udraw = input_get_drvdata(dev); |
267 | |
268 | return hid_hw_open(hdev: udraw->hdev); |
269 | } |
270 | |
271 | static void udraw_close(struct input_dev *dev) |
272 | { |
273 | struct udraw *udraw = input_get_drvdata(dev); |
274 | |
275 | hid_hw_close(hdev: udraw->hdev); |
276 | } |
277 | |
278 | static struct input_dev *allocate_and_setup(struct hid_device *hdev, |
279 | const char *name) |
280 | { |
281 | struct input_dev *input_dev; |
282 | |
283 | input_dev = devm_input_allocate_device(&hdev->dev); |
284 | if (!input_dev) |
285 | return NULL; |
286 | |
287 | input_dev->name = name; |
288 | input_dev->phys = hdev->phys; |
289 | input_dev->dev.parent = &hdev->dev; |
290 | input_dev->open = udraw_open; |
291 | input_dev->close = udraw_close; |
292 | input_dev->uniq = hdev->uniq; |
293 | input_dev->id.bustype = hdev->bus; |
294 | input_dev->id.vendor = hdev->vendor; |
295 | input_dev->id.product = hdev->product; |
296 | input_dev->id.version = hdev->version; |
297 | input_set_drvdata(dev: input_dev, data: hid_get_drvdata(hdev)); |
298 | |
299 | return input_dev; |
300 | } |
301 | |
302 | static bool udraw_setup_touch(struct udraw *udraw, |
303 | struct hid_device *hdev) |
304 | { |
305 | struct input_dev *input_dev; |
306 | |
307 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad" ); |
308 | if (!input_dev) |
309 | return false; |
310 | |
311 | input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); |
312 | |
313 | input_set_abs_params(dev: input_dev, ABS_X, min: 0, RES_X, fuzz: 1, flat: 0); |
314 | input_abs_set_res(dev: input_dev, ABS_X, RES_X / WIDTH); |
315 | input_set_abs_params(dev: input_dev, ABS_Y, min: 0, RES_Y, fuzz: 1, flat: 0); |
316 | input_abs_set_res(dev: input_dev, ABS_Y, RES_Y / HEIGHT); |
317 | |
318 | set_bit(BTN_TOUCH, addr: input_dev->keybit); |
319 | set_bit(BTN_TOOL_FINGER, addr: input_dev->keybit); |
320 | set_bit(BTN_TOOL_DOUBLETAP, addr: input_dev->keybit); |
321 | |
322 | set_bit(INPUT_PROP_POINTER, addr: input_dev->propbit); |
323 | |
324 | udraw->touch_input_dev = input_dev; |
325 | |
326 | return true; |
327 | } |
328 | |
329 | static bool udraw_setup_pen(struct udraw *udraw, |
330 | struct hid_device *hdev) |
331 | { |
332 | struct input_dev *input_dev; |
333 | |
334 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen" ); |
335 | if (!input_dev) |
336 | return false; |
337 | |
338 | input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); |
339 | |
340 | input_set_abs_params(dev: input_dev, ABS_X, min: 0, RES_X, fuzz: 1, flat: 0); |
341 | input_abs_set_res(dev: input_dev, ABS_X, RES_X / WIDTH); |
342 | input_set_abs_params(dev: input_dev, ABS_Y, min: 0, RES_Y, fuzz: 1, flat: 0); |
343 | input_abs_set_res(dev: input_dev, ABS_Y, RES_Y / HEIGHT); |
344 | input_set_abs_params(dev: input_dev, ABS_PRESSURE, |
345 | min: 0, MAX_PRESSURE, fuzz: 0, flat: 0); |
346 | |
347 | set_bit(BTN_TOUCH, addr: input_dev->keybit); |
348 | set_bit(BTN_TOOL_PEN, addr: input_dev->keybit); |
349 | |
350 | set_bit(INPUT_PROP_POINTER, addr: input_dev->propbit); |
351 | |
352 | udraw->pen_input_dev = input_dev; |
353 | |
354 | return true; |
355 | } |
356 | |
357 | static bool udraw_setup_accel(struct udraw *udraw, |
358 | struct hid_device *hdev) |
359 | { |
360 | struct input_dev *input_dev; |
361 | |
362 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer" ); |
363 | if (!input_dev) |
364 | return false; |
365 | |
366 | input_dev->evbit[0] = BIT(EV_ABS); |
367 | |
368 | /* 1G accel is reported as ~256, so clamp to 2G */ |
369 | input_set_abs_params(dev: input_dev, ABS_X, min: -512, max: 512, fuzz: 0, flat: 0); |
370 | input_set_abs_params(dev: input_dev, ABS_Y, min: -512, max: 512, fuzz: 0, flat: 0); |
371 | input_set_abs_params(dev: input_dev, ABS_Z, min: -512, max: 512, fuzz: 0, flat: 0); |
372 | |
373 | set_bit(INPUT_PROP_ACCELEROMETER, addr: input_dev->propbit); |
374 | |
375 | udraw->accel_input_dev = input_dev; |
376 | |
377 | return true; |
378 | } |
379 | |
380 | static bool udraw_setup_joypad(struct udraw *udraw, |
381 | struct hid_device *hdev) |
382 | { |
383 | struct input_dev *input_dev; |
384 | |
385 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad" ); |
386 | if (!input_dev) |
387 | return false; |
388 | |
389 | input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); |
390 | |
391 | set_bit(BTN_SOUTH, addr: input_dev->keybit); |
392 | set_bit(BTN_NORTH, addr: input_dev->keybit); |
393 | set_bit(BTN_EAST, addr: input_dev->keybit); |
394 | set_bit(BTN_WEST, addr: input_dev->keybit); |
395 | set_bit(BTN_SELECT, addr: input_dev->keybit); |
396 | set_bit(BTN_START, addr: input_dev->keybit); |
397 | set_bit(BTN_MODE, addr: input_dev->keybit); |
398 | |
399 | input_set_abs_params(dev: input_dev, ABS_X, min: -127, max: 127, fuzz: 0, flat: 0); |
400 | input_set_abs_params(dev: input_dev, ABS_Y, min: -127, max: 127, fuzz: 0, flat: 0); |
401 | |
402 | udraw->joy_input_dev = input_dev; |
403 | |
404 | return true; |
405 | } |
406 | |
407 | static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id) |
408 | { |
409 | struct udraw *udraw; |
410 | int ret; |
411 | |
412 | udraw = devm_kzalloc(dev: &hdev->dev, size: sizeof(struct udraw), GFP_KERNEL); |
413 | if (!udraw) |
414 | return -ENOMEM; |
415 | |
416 | udraw->hdev = hdev; |
417 | udraw->last_two_finger_x = -1; |
418 | udraw->last_two_finger_y = -1; |
419 | |
420 | hid_set_drvdata(hdev, data: udraw); |
421 | |
422 | ret = hid_parse(hdev); |
423 | if (ret) { |
424 | hid_err(hdev, "parse failed\n" ); |
425 | return ret; |
426 | } |
427 | |
428 | if (!udraw_setup_joypad(udraw, hdev) || |
429 | !udraw_setup_touch(udraw, hdev) || |
430 | !udraw_setup_pen(udraw, hdev) || |
431 | !udraw_setup_accel(udraw, hdev)) { |
432 | hid_err(hdev, "could not allocate interfaces\n" ); |
433 | return -ENOMEM; |
434 | } |
435 | |
436 | ret = input_register_device(udraw->joy_input_dev) || |
437 | input_register_device(udraw->touch_input_dev) || |
438 | input_register_device(udraw->pen_input_dev) || |
439 | input_register_device(udraw->accel_input_dev); |
440 | if (ret) { |
441 | hid_err(hdev, "failed to register interfaces\n" ); |
442 | return ret; |
443 | } |
444 | |
445 | ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); |
446 | if (ret) { |
447 | hid_err(hdev, "hw start failed\n" ); |
448 | return ret; |
449 | } |
450 | |
451 | return 0; |
452 | } |
453 | |
454 | static const struct hid_device_id udraw_devices[] = { |
455 | { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, |
456 | { } |
457 | }; |
458 | MODULE_DEVICE_TABLE(hid, udraw_devices); |
459 | |
460 | static struct hid_driver udraw_driver = { |
461 | .name = "hid-udraw" , |
462 | .id_table = udraw_devices, |
463 | .raw_event = udraw_raw_event, |
464 | .probe = udraw_probe, |
465 | }; |
466 | module_hid_driver(udraw_driver); |
467 | |