diff options
author | Bastien Nocera <hadess@hadess.net> | 2016-11-15 07:02:05 -0500 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2016-11-15 08:20:05 -0500 |
commit | 0edffe655a52d7ce7c093212bc0cce6576084a8e (patch) | |
tree | 43fa3fb92c61d1790a0d03760b1d5bc0f423764d | |
parent | c1f4c2b28c11f6d042d15100b36a4ebba07c48b2 (diff) |
HID: udraw-ps3: Add support for the uDraw tablet for PS3
This adds support for the THQ uDraw tablet for the PS3, as
4 separate device nodes, so that user-space can easily consume
events coming from the hardware.
Note that the touchpad two-finger support is fairly unreliable,
and a right-click can only be achieved with a two-finger tap
with the two fingers slightly apart (about 1cm should be enough).
Tested-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Bastien Nocera <hadess@hadess.net>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r-- | MAINTAINERS | 6 | ||||
-rw-r--r-- | drivers/hid/Kconfig | 7 | ||||
-rw-r--r-- | drivers/hid/Makefile | 1 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 3 | ||||
-rw-r--r-- | drivers/hid/hid-udraw-ps3.c | 474 |
6 files changed, 492 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 411e3b87b8c2..cbc1533f5b82 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -12340,6 +12340,12 @@ S: Maintained | |||
12340 | F: Documentation/filesystems/udf.txt | 12340 | F: Documentation/filesystems/udf.txt |
12341 | F: fs/udf/ | 12341 | F: fs/udf/ |
12342 | 12342 | ||
12343 | UDRAW TABLET | ||
12344 | M: Bastien Nocera <hadess@hadess.net> | ||
12345 | L: linux-input@vger.kernel.org | ||
12346 | S: Maintained | ||
12347 | F: drivers/hid/hid-udraw.c | ||
12348 | |||
12343 | UFS FILESYSTEM | 12349 | UFS FILESYSTEM |
12344 | M: Evgeniy Dushistov <dushistov@mail.ru> | 12350 | M: Evgeniy Dushistov <dushistov@mail.ru> |
12345 | S: Maintained | 12351 | S: Maintained |
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index cd4599c0523b..91025b3ff18d 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig | |||
@@ -861,6 +861,13 @@ config THRUSTMASTER_FF | |||
861 | a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT | 861 | a THRUSTMASTER Dual Trigger 3-in-1 or a THRUSTMASTER Ferrari GT |
862 | Rumble Force or Force Feedback Wheel. | 862 | Rumble Force or Force Feedback Wheel. |
863 | 863 | ||
864 | config HID_UDRAW_PS3 | ||
865 | tristate "THQ PS3 uDraw tablet" | ||
866 | depends on HID | ||
867 | ---help--- | ||
868 | Say Y here if you want to use the THQ uDraw gaming tablet for | ||
869 | the PS3. | ||
870 | |||
864 | config HID_WACOM | 871 | config HID_WACOM |
865 | tristate "Wacom Intuos/Graphire tablet support (USB)" | 872 | tristate "Wacom Intuos/Graphire tablet support (USB)" |
866 | depends on HID | 873 | depends on HID |
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index 86b2b5785fd2..b4ed502050b7 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -96,6 +96,7 @@ obj-$(CONFIG_HID_TIVO) += hid-tivo.o | |||
96 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o | 96 | obj-$(CONFIG_HID_TOPSEED) += hid-topseed.o |
97 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o | 97 | obj-$(CONFIG_HID_TWINHAN) += hid-twinhan.o |
98 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o | 98 | obj-$(CONFIG_HID_UCLOGIC) += hid-uclogic.o |
99 | obj-$(CONFIG_HID_UDRAW_PS3) += hid-udraw-ps3.o | ||
99 | obj-$(CONFIG_HID_LED) += hid-led.o | 100 | obj-$(CONFIG_HID_LED) += hid-led.o |
100 | obj-$(CONFIG_HID_XINMO) += hid-xinmo.o | 101 | obj-$(CONFIG_HID_XINMO) += hid-xinmo.o |
101 | obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o | 102 | obj-$(CONFIG_HID_ZEROPLUS) += hid-zpff.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 2b89c701076f..3611ec77ddb9 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -2086,6 +2086,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
2086 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, | 2086 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_WP1062) }, |
2087 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, | 2087 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_WIRELESS_TABLET_TWHL850) }, |
2088 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, | 2088 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UCLOGIC_TABLET_TWHA60) }, |
2089 | { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, | ||
2089 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, | 2090 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_YIYNOVA_TABLET) }, |
2090 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, | 2091 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_81) }, |
2091 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, | 2092 | { HID_USB_DEVICE(USB_VENDOR_ID_UCLOGIC, USB_DEVICE_ID_UGEE_TABLET_45) }, |
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 575aa65436d1..e8166568a900 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -959,6 +959,9 @@ | |||
959 | #define USB_VENDOR_ID_THINGM 0x27b8 | 959 | #define USB_VENDOR_ID_THINGM 0x27b8 |
960 | #define USB_DEVICE_ID_BLINK1 0x01ed | 960 | #define USB_DEVICE_ID_BLINK1 0x01ed |
961 | 961 | ||
962 | #define USB_VENDOR_ID_THQ 0x20d6 | ||
963 | #define USB_DEVICE_ID_THQ_PS3_UDRAW 0xcb17 | ||
964 | |||
962 | #define USB_VENDOR_ID_THRUSTMASTER 0x044f | 965 | #define USB_VENDOR_ID_THRUSTMASTER 0x044f |
963 | 966 | ||
964 | #define USB_VENDOR_ID_TIVO 0x150a | 967 | #define USB_VENDOR_ID_TIVO 0x150a |
diff --git a/drivers/hid/hid-udraw-ps3.c b/drivers/hid/hid-udraw-ps3.c new file mode 100644 index 000000000000..1f68b0b5f12e --- /dev/null +++ b/drivers/hid/hid-udraw-ps3.c | |||
@@ -0,0 +1,474 @@ | |||
1 | /* | ||
2 | * HID driver for THQ PS3 uDraw tablet | ||
3 | * | ||
4 | * Copyright (C) 2016 Red Hat Inc. All Rights Reserved | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | #include <linux/device.h> | ||
17 | #include <linux/hid.h> | ||
18 | #include <linux/module.h> | ||
19 | #include "hid-ids.h" | ||
20 | |||
21 | MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>"); | ||
22 | MODULE_DESCRIPTION("PS3 uDraw tablet driver"); | ||
23 | MODULE_LICENSE("GPL"); | ||
24 | |||
25 | /* | ||
26 | * Protocol information from: | ||
27 | * http://brandonw.net/udraw/ | ||
28 | * and the source code of: | ||
29 | * https://vvvv.org/contribution/udraw-hid | ||
30 | */ | ||
31 | |||
32 | /* | ||
33 | * The device is setup with multiple input devices: | ||
34 | * - the touch area which works as a touchpad | ||
35 | * - the tablet area which works as a touchpad/drawing tablet | ||
36 | * - a joypad with a d-pad, and 7 buttons | ||
37 | * - an accelerometer device | ||
38 | */ | ||
39 | |||
40 | enum { | ||
41 | TOUCH_NONE, | ||
42 | TOUCH_PEN, | ||
43 | TOUCH_FINGER, | ||
44 | TOUCH_TWOFINGER | ||
45 | }; | ||
46 | |||
47 | enum { | ||
48 | AXIS_X, | ||
49 | AXIS_Y, | ||
50 | AXIS_Z | ||
51 | }; | ||
52 | |||
53 | /* | ||
54 | * Accelerometer min/max values | ||
55 | * in order, X, Y and Z | ||
56 | */ | ||
57 | struct { | ||
58 | int min; | ||
59 | int max; | ||
60 | } accel_limits[] = { | ||
61 | [AXIS_X] = { 490, 534 }, | ||
62 | [AXIS_Y] = { 490, 534 }, | ||
63 | [AXIS_Z] = { 492, 536 } | ||
64 | }; | ||
65 | |||
66 | #define DEVICE_NAME "THQ uDraw Game Tablet for PS3" | ||
67 | /* resolution in pixels */ | ||
68 | #define RES_X 1920 | ||
69 | #define RES_Y 1080 | ||
70 | /* size in mm */ | ||
71 | #define WIDTH 160 | ||
72 | #define HEIGHT 90 | ||
73 | #define PRESSURE_OFFSET 113 | ||
74 | #define MAX_PRESSURE (255 - PRESSURE_OFFSET) | ||
75 | |||
76 | struct udraw { | ||
77 | struct input_dev *joy_input_dev; | ||
78 | struct input_dev *touch_input_dev; | ||
79 | struct input_dev *pen_input_dev; | ||
80 | struct input_dev *accel_input_dev; | ||
81 | struct hid_device *hdev; | ||
82 | |||
83 | /* | ||
84 | * The device's two-finger support is pretty unreliable, as | ||
85 | * the device could report a single touch when the two fingers | ||
86 | * are too close together, and the distance between fingers, even | ||
87 | * though reported is not in the same unit as the touches. | ||
88 | * | ||
89 | * We'll make do without it, and try to report the first touch | ||
90 | * as reliably as possible. | ||
91 | */ | ||
92 | int last_one_finger_x; | ||
93 | int last_one_finger_y; | ||
94 | int last_two_finger_x; | ||
95 | int last_two_finger_y; | ||
96 | }; | ||
97 | |||
98 | static int clamp_accel(int axis, int offset) | ||
99 | { | ||
100 | axis = clamp(axis, | ||
101 | accel_limits[offset].min, | ||
102 | accel_limits[offset].max); | ||
103 | axis = (axis - accel_limits[offset].min) / | ||
104 | ((accel_limits[offset].max - | ||
105 | accel_limits[offset].min) * 0xFF); | ||
106 | return axis; | ||
107 | } | ||
108 | |||
109 | static int udraw_raw_event(struct hid_device *hdev, struct hid_report *report, | ||
110 | u8 *data, int len) | ||
111 | { | ||
112 | struct udraw *udraw = hid_get_drvdata(hdev); | ||
113 | int touch; | ||
114 | int x, y, z; | ||
115 | |||
116 | if (len != 27) | ||
117 | return 0; | ||
118 | |||
119 | if (data[11] == 0x00) | ||
120 | touch = TOUCH_NONE; | ||
121 | else if (data[11] == 0x40) | ||
122 | touch = TOUCH_PEN; | ||
123 | else if (data[11] == 0x80) | ||
124 | touch = TOUCH_FINGER; | ||
125 | else | ||
126 | touch = TOUCH_TWOFINGER; | ||
127 | |||
128 | /* joypad */ | ||
129 | input_report_key(udraw->joy_input_dev, BTN_WEST, data[0] & 1); | ||
130 | input_report_key(udraw->joy_input_dev, BTN_SOUTH, !!(data[0] & 2)); | ||
131 | input_report_key(udraw->joy_input_dev, BTN_EAST, !!(data[0] & 4)); | ||
132 | input_report_key(udraw->joy_input_dev, BTN_NORTH, !!(data[0] & 8)); | ||
133 | |||
134 | input_report_key(udraw->joy_input_dev, BTN_SELECT, !!(data[1] & 1)); | ||
135 | input_report_key(udraw->joy_input_dev, BTN_START, !!(data[1] & 2)); | ||
136 | input_report_key(udraw->joy_input_dev, BTN_MODE, !!(data[1] & 16)); | ||
137 | |||
138 | x = y = 0; | ||
139 | switch (data[2]) { | ||
140 | case 0x0: | ||
141 | y = -127; | ||
142 | break; | ||
143 | case 0x1: | ||
144 | y = -127; | ||
145 | x = 127; | ||
146 | break; | ||
147 | case 0x2: | ||
148 | x = 127; | ||
149 | break; | ||
150 | case 0x3: | ||
151 | y = 127; | ||
152 | x = 127; | ||
153 | break; | ||
154 | case 0x4: | ||
155 | y = 127; | ||
156 | break; | ||
157 | case 0x5: | ||
158 | y = 127; | ||
159 | x = -127; | ||
160 | break; | ||
161 | case 0x6: | ||
162 | x = -127; | ||
163 | break; | ||
164 | case 0x7: | ||
165 | y = -127; | ||
166 | x = -127; | ||
167 | break; | ||
168 | default: | ||
169 | break; | ||
170 | } | ||
171 | |||
172 | input_report_abs(udraw->joy_input_dev, ABS_X, x); | ||
173 | input_report_abs(udraw->joy_input_dev, ABS_Y, y); | ||
174 | |||
175 | input_sync(udraw->joy_input_dev); | ||
176 | |||
177 | /* For pen and touchpad */ | ||
178 | x = y = 0; | ||
179 | if (touch != TOUCH_NONE) { | ||
180 | if (data[15] != 0x0F) | ||
181 | x = data[15] * 256 + data[17]; | ||
182 | if (data[16] != 0x0F) | ||
183 | y = data[16] * 256 + data[18]; | ||
184 | } | ||
185 | |||
186 | if (touch == TOUCH_FINGER) { | ||
187 | /* Save the last one-finger touch */ | ||
188 | udraw->last_one_finger_x = x; | ||
189 | udraw->last_one_finger_y = y; | ||
190 | udraw->last_two_finger_x = -1; | ||
191 | udraw->last_two_finger_y = -1; | ||
192 | } else if (touch == TOUCH_TWOFINGER) { | ||
193 | /* | ||
194 | * We have a problem because x/y is the one for the | ||
195 | * second finger but we want the first finger given | ||
196 | * to user-space otherwise it'll look as if it jumped. | ||
197 | * | ||
198 | * See the udraw struct definition for why this was | ||
199 | * implemented this way. | ||
200 | */ | ||
201 | if (udraw->last_two_finger_x == -1) { | ||
202 | /* Save the position of the 2nd finger */ | ||
203 | udraw->last_two_finger_x = x; | ||
204 | udraw->last_two_finger_y = y; | ||
205 | |||
206 | x = udraw->last_one_finger_x; | ||
207 | y = udraw->last_one_finger_y; | ||
208 | } else { | ||
209 | /* | ||
210 | * Offset the 2-finger coords using the | ||
211 | * saved data from the first finger | ||
212 | */ | ||
213 | x = x - (udraw->last_two_finger_x | ||
214 | - udraw->last_one_finger_x); | ||
215 | y = y - (udraw->last_two_finger_y | ||
216 | - udraw->last_one_finger_y); | ||
217 | } | ||
218 | } | ||
219 | |||
220 | /* touchpad */ | ||
221 | if (touch == TOUCH_FINGER || touch == TOUCH_TWOFINGER) { | ||
222 | input_report_key(udraw->touch_input_dev, BTN_TOUCH, 1); | ||
223 | input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, | ||
224 | touch == TOUCH_FINGER); | ||
225 | input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, | ||
226 | touch == TOUCH_TWOFINGER); | ||
227 | |||
228 | input_report_abs(udraw->touch_input_dev, ABS_X, x); | ||
229 | input_report_abs(udraw->touch_input_dev, ABS_Y, y); | ||
230 | } else { | ||
231 | input_report_key(udraw->touch_input_dev, BTN_TOUCH, 0); | ||
232 | input_report_key(udraw->touch_input_dev, BTN_TOOL_FINGER, 0); | ||
233 | input_report_key(udraw->touch_input_dev, BTN_TOOL_DOUBLETAP, 0); | ||
234 | } | ||
235 | input_sync(udraw->touch_input_dev); | ||
236 | |||
237 | /* pen */ | ||
238 | if (touch == TOUCH_PEN) { | ||
239 | int level; | ||
240 | |||
241 | level = clamp(data[13] - PRESSURE_OFFSET, | ||
242 | 0, MAX_PRESSURE); | ||
243 | |||
244 | input_report_key(udraw->pen_input_dev, BTN_TOUCH, (level != 0)); | ||
245 | input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 1); | ||
246 | input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, level); | ||
247 | input_report_abs(udraw->pen_input_dev, ABS_X, x); | ||
248 | input_report_abs(udraw->pen_input_dev, ABS_Y, y); | ||
249 | } else { | ||
250 | input_report_key(udraw->pen_input_dev, BTN_TOUCH, 0); | ||
251 | input_report_key(udraw->pen_input_dev, BTN_TOOL_PEN, 0); | ||
252 | input_report_abs(udraw->pen_input_dev, ABS_PRESSURE, 0); | ||
253 | } | ||
254 | input_sync(udraw->pen_input_dev); | ||
255 | |||
256 | /* accel */ | ||
257 | x = (data[19] + (data[20] << 8)); | ||
258 | x = clamp_accel(x, AXIS_X); | ||
259 | y = (data[21] + (data[22] << 8)); | ||
260 | y = clamp_accel(y, AXIS_Y); | ||
261 | z = (data[23] + (data[24] << 8)); | ||
262 | z = clamp_accel(z, AXIS_Z); | ||
263 | input_report_abs(udraw->accel_input_dev, ABS_X, x); | ||
264 | input_report_abs(udraw->accel_input_dev, ABS_Y, y); | ||
265 | input_report_abs(udraw->accel_input_dev, ABS_Z, z); | ||
266 | input_sync(udraw->accel_input_dev); | ||
267 | |||
268 | /* let hidraw and hiddev handle the report */ | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | static int udraw_open(struct input_dev *dev) | ||
273 | { | ||
274 | struct udraw *udraw = input_get_drvdata(dev); | ||
275 | |||
276 | return hid_hw_open(udraw->hdev); | ||
277 | } | ||
278 | |||
279 | static void udraw_close(struct input_dev *dev) | ||
280 | { | ||
281 | struct udraw *udraw = input_get_drvdata(dev); | ||
282 | |||
283 | hid_hw_close(udraw->hdev); | ||
284 | } | ||
285 | |||
286 | static struct input_dev *allocate_and_setup(struct hid_device *hdev, | ||
287 | const char *name) | ||
288 | { | ||
289 | struct input_dev *input_dev; | ||
290 | |||
291 | input_dev = devm_input_allocate_device(&hdev->dev); | ||
292 | if (!input_dev) | ||
293 | return NULL; | ||
294 | |||
295 | input_dev->name = name; | ||
296 | input_dev->phys = hdev->phys; | ||
297 | input_dev->dev.parent = &hdev->dev; | ||
298 | input_dev->open = udraw_open; | ||
299 | input_dev->close = udraw_close; | ||
300 | input_dev->uniq = hdev->uniq; | ||
301 | input_dev->id.bustype = hdev->bus; | ||
302 | input_dev->id.vendor = hdev->vendor; | ||
303 | input_dev->id.product = hdev->product; | ||
304 | input_dev->id.version = hdev->version; | ||
305 | input_set_drvdata(input_dev, hid_get_drvdata(hdev)); | ||
306 | |||
307 | return input_dev; | ||
308 | } | ||
309 | |||
310 | static bool udraw_setup_touch(struct udraw *udraw, | ||
311 | struct hid_device *hdev) | ||
312 | { | ||
313 | struct input_dev *input_dev; | ||
314 | |||
315 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Touchpad"); | ||
316 | if (!input_dev) | ||
317 | return false; | ||
318 | |||
319 | input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); | ||
320 | |||
321 | input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); | ||
322 | input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); | ||
323 | input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); | ||
324 | input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); | ||
325 | |||
326 | set_bit(BTN_TOUCH, input_dev->keybit); | ||
327 | set_bit(BTN_TOOL_FINGER, input_dev->keybit); | ||
328 | set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit); | ||
329 | |||
330 | set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
331 | |||
332 | udraw->touch_input_dev = input_dev; | ||
333 | |||
334 | return true; | ||
335 | } | ||
336 | |||
337 | static bool udraw_setup_pen(struct udraw *udraw, | ||
338 | struct hid_device *hdev) | ||
339 | { | ||
340 | struct input_dev *input_dev; | ||
341 | |||
342 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Pen"); | ||
343 | if (!input_dev) | ||
344 | return false; | ||
345 | |||
346 | input_dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY); | ||
347 | |||
348 | input_set_abs_params(input_dev, ABS_X, 0, RES_X, 1, 0); | ||
349 | input_abs_set_res(input_dev, ABS_X, RES_X / WIDTH); | ||
350 | input_set_abs_params(input_dev, ABS_Y, 0, RES_Y, 1, 0); | ||
351 | input_abs_set_res(input_dev, ABS_Y, RES_Y / HEIGHT); | ||
352 | input_set_abs_params(input_dev, ABS_PRESSURE, | ||
353 | 0, MAX_PRESSURE, 0, 0); | ||
354 | |||
355 | set_bit(BTN_TOUCH, input_dev->keybit); | ||
356 | set_bit(BTN_TOOL_PEN, input_dev->keybit); | ||
357 | |||
358 | set_bit(INPUT_PROP_POINTER, input_dev->propbit); | ||
359 | |||
360 | udraw->pen_input_dev = input_dev; | ||
361 | |||
362 | return true; | ||
363 | } | ||
364 | |||
365 | static bool udraw_setup_accel(struct udraw *udraw, | ||
366 | struct hid_device *hdev) | ||
367 | { | ||
368 | struct input_dev *input_dev; | ||
369 | |||
370 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Accelerometer"); | ||
371 | if (!input_dev) | ||
372 | return false; | ||
373 | |||
374 | input_dev->evbit[0] = BIT(EV_ABS); | ||
375 | |||
376 | /* 1G accel is reported as ~256, so clamp to 2G */ | ||
377 | input_set_abs_params(input_dev, ABS_X, -512, 512, 0, 0); | ||
378 | input_set_abs_params(input_dev, ABS_Y, -512, 512, 0, 0); | ||
379 | input_set_abs_params(input_dev, ABS_Z, -512, 512, 0, 0); | ||
380 | |||
381 | set_bit(INPUT_PROP_ACCELEROMETER, input_dev->propbit); | ||
382 | |||
383 | udraw->accel_input_dev = input_dev; | ||
384 | |||
385 | return true; | ||
386 | } | ||
387 | |||
388 | static bool udraw_setup_joypad(struct udraw *udraw, | ||
389 | struct hid_device *hdev) | ||
390 | { | ||
391 | struct input_dev *input_dev; | ||
392 | |||
393 | input_dev = allocate_and_setup(hdev, DEVICE_NAME " Joypad"); | ||
394 | if (!input_dev) | ||
395 | return false; | ||
396 | |||
397 | input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); | ||
398 | |||
399 | set_bit(BTN_SOUTH, input_dev->keybit); | ||
400 | set_bit(BTN_NORTH, input_dev->keybit); | ||
401 | set_bit(BTN_EAST, input_dev->keybit); | ||
402 | set_bit(BTN_WEST, input_dev->keybit); | ||
403 | set_bit(BTN_SELECT, input_dev->keybit); | ||
404 | set_bit(BTN_START, input_dev->keybit); | ||
405 | set_bit(BTN_MODE, input_dev->keybit); | ||
406 | |||
407 | input_set_abs_params(input_dev, ABS_X, -127, 127, 0, 0); | ||
408 | input_set_abs_params(input_dev, ABS_Y, -127, 127, 0, 0); | ||
409 | |||
410 | udraw->joy_input_dev = input_dev; | ||
411 | |||
412 | return true; | ||
413 | } | ||
414 | |||
415 | static int udraw_probe(struct hid_device *hdev, const struct hid_device_id *id) | ||
416 | { | ||
417 | struct udraw *udraw; | ||
418 | int ret; | ||
419 | |||
420 | udraw = devm_kzalloc(&hdev->dev, sizeof(struct udraw), GFP_KERNEL); | ||
421 | if (!udraw) | ||
422 | return -ENOMEM; | ||
423 | |||
424 | udraw->hdev = hdev; | ||
425 | udraw->last_two_finger_x = -1; | ||
426 | udraw->last_two_finger_y = -1; | ||
427 | |||
428 | hid_set_drvdata(hdev, udraw); | ||
429 | |||
430 | ret = hid_parse(hdev); | ||
431 | if (ret) { | ||
432 | hid_err(hdev, "parse failed\n"); | ||
433 | return ret; | ||
434 | } | ||
435 | |||
436 | if (!udraw_setup_joypad(udraw, hdev) || | ||
437 | !udraw_setup_touch(udraw, hdev) || | ||
438 | !udraw_setup_pen(udraw, hdev) || | ||
439 | !udraw_setup_accel(udraw, hdev)) { | ||
440 | hid_err(hdev, "could not allocate interfaces\n"); | ||
441 | return -ENOMEM; | ||
442 | } | ||
443 | |||
444 | ret = input_register_device(udraw->joy_input_dev) || | ||
445 | input_register_device(udraw->touch_input_dev) || | ||
446 | input_register_device(udraw->pen_input_dev) || | ||
447 | input_register_device(udraw->accel_input_dev); | ||
448 | if (ret) { | ||
449 | hid_err(hdev, "failed to register interfaces\n"); | ||
450 | return ret; | ||
451 | } | ||
452 | |||
453 | ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW | HID_CONNECT_DRIVER); | ||
454 | if (ret) { | ||
455 | hid_err(hdev, "hw start failed\n"); | ||
456 | return ret; | ||
457 | } | ||
458 | |||
459 | return 0; | ||
460 | } | ||
461 | |||
462 | static const struct hid_device_id udraw_devices[] = { | ||
463 | { HID_USB_DEVICE(USB_VENDOR_ID_THQ, USB_DEVICE_ID_THQ_PS3_UDRAW) }, | ||
464 | { } | ||
465 | }; | ||
466 | MODULE_DEVICE_TABLE(hid, udraw_devices); | ||
467 | |||
468 | static struct hid_driver udraw_driver = { | ||
469 | .name = "hid-udraw", | ||
470 | .id_table = udraw_devices, | ||
471 | .raw_event = udraw_raw_event, | ||
472 | .probe = udraw_probe, | ||
473 | }; | ||
474 | module_hid_driver(udraw_driver); | ||