diff options
author | Chase Douglas <chase.douglas@canonical.com> | 2010-08-31 21:56:23 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2010-09-03 12:20:47 -0400 |
commit | a462230e16acc8664145216da3c928d03556691a (patch) | |
tree | c6c4464789f324d8bee320bcacf253fdcf51f166 /drivers/hid/hid-magicmouse.c | |
parent | 6de048bf1dd2ad35fe9b2326bf9d6d23fb2fff7a (diff) |
HID: magicmouse: enable Magic Trackpad support
The trackpad speaks a similar, but different, protocol from the magic
mouse. However, only small code tweaks here and there are needed to make
basic multitouch work.
Extra logic is required for single-touch emulation of the touchpad. The
changes made here take the approach that only one finger may emulate the
single pointer when multiple fingers have touched the screen. Once that
finger is raised, all touches must be raised before any further single
touch events can be sent.
Sometimes the magic trackpad sends two distinct touch reports as one big
report. Simply splitting the packet in two and resending them through
magicmouse_raw_event ensures they are handled properly.
I also added myself to the copyright statement.
Signed-off-by: Chase Douglas <chase.douglas@canonical.com>
Acked-by: Michael Poole <mdpoole@troilus.org>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-magicmouse.c')
-rw-r--r-- | drivers/hid/hid-magicmouse.c | 192 |
1 files changed, 153 insertions, 39 deletions
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c index 402682e0c2f4..8791a084693e 100644 --- a/drivers/hid/hid-magicmouse.c +++ b/drivers/hid/hid-magicmouse.c | |||
@@ -2,6 +2,7 @@ | |||
2 | * Apple "Magic" Wireless Mouse driver | 2 | * Apple "Magic" Wireless Mouse driver |
3 | * | 3 | * |
4 | * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> | 4 | * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org> |
5 | * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com> | ||
5 | */ | 6 | */ |
6 | 7 | ||
7 | /* | 8 | /* |
@@ -53,7 +54,9 @@ static bool report_undeciphered; | |||
53 | module_param(report_undeciphered, bool, 0644); | 54 | module_param(report_undeciphered, bool, 0644); |
54 | MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); | 55 | MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); |
55 | 56 | ||
56 | #define TOUCH_REPORT_ID 0x29 | 57 | #define TRACKPAD_REPORT_ID 0x28 |
58 | #define MOUSE_REPORT_ID 0x29 | ||
59 | #define DOUBLE_REPORT_ID 0xf7 | ||
57 | /* These definitions are not precise, but they're close enough. (Bits | 60 | /* These definitions are not precise, but they're close enough. (Bits |
58 | * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem | 61 | * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem |
59 | * to be some kind of bit mask -- 0x20 may be a near-field reading, | 62 | * to be some kind of bit mask -- 0x20 may be a near-field reading, |
@@ -67,6 +70,15 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie | |||
67 | 70 | ||
68 | #define SCROLL_ACCEL_DEFAULT 7 | 71 | #define SCROLL_ACCEL_DEFAULT 7 |
69 | 72 | ||
73 | /* Single touch emulation should only begin when no touches are currently down. | ||
74 | * This is true when single_touch_id is equal to NO_TOUCHES. If multiple touches | ||
75 | * are down and the touch providing for single touch emulation is lifted, | ||
76 | * single_touch_id is equal to SINGLE_TOUCH_UP. While single touch emulation is | ||
77 | * occuring, single_touch_id corresponds with the tracking id of the touch used. | ||
78 | */ | ||
79 | #define NO_TOUCHES -1 | ||
80 | #define SINGLE_TOUCH_UP -2 | ||
81 | |||
70 | /** | 82 | /** |
71 | * struct magicmouse_sc - Tracks Magic Mouse-specific data. | 83 | * struct magicmouse_sc - Tracks Magic Mouse-specific data. |
72 | * @input: Input device through which we report events. | 84 | * @input: Input device through which we report events. |
@@ -93,6 +105,7 @@ struct magicmouse_sc { | |||
93 | u8 size; | 105 | u8 size; |
94 | } touches[16]; | 106 | } touches[16]; |
95 | int tracking_ids[16]; | 107 | int tracking_ids[16]; |
108 | int single_touch_id; | ||
96 | }; | 109 | }; |
97 | 110 | ||
98 | static int magicmouse_firm_touch(struct magicmouse_sc *msc) | 111 | static int magicmouse_firm_touch(struct magicmouse_sc *msc) |
@@ -158,15 +171,29 @@ static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state) | |||
158 | static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) | 171 | static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata) |
159 | { | 172 | { |
160 | struct input_dev *input = msc->input; | 173 | struct input_dev *input = msc->input; |
161 | int id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; | 174 | int id, x, y, size, orientation, touch_major, touch_minor, state, down; |
162 | int x = (tdata[1] << 28 | tdata[0] << 20) >> 20; | 175 | |
163 | int y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); | 176 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
164 | int size = tdata[5] & 0x3f; | 177 | id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; |
165 | int orientation = (tdata[6] >> 2) - 32; | 178 | x = (tdata[1] << 28 | tdata[0] << 20) >> 20; |
166 | int touch_major = tdata[3]; | 179 | y = -((tdata[2] << 24 | tdata[1] << 16) >> 20); |
167 | int touch_minor = tdata[4]; | 180 | size = tdata[5] & 0x3f; |
168 | int state = tdata[7] & TOUCH_STATE_MASK; | 181 | orientation = (tdata[6] >> 2) - 32; |
169 | int down = state != TOUCH_STATE_NONE; | 182 | touch_major = tdata[3]; |
183 | touch_minor = tdata[4]; | ||
184 | state = tdata[7] & TOUCH_STATE_MASK; | ||
185 | down = state != TOUCH_STATE_NONE; | ||
186 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
187 | id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; | ||
188 | x = (tdata[1] << 27 | tdata[0] << 19) >> 19; | ||
189 | y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); | ||
190 | size = tdata[6] & 0x3f; | ||
191 | orientation = (tdata[7] >> 2) - 32; | ||
192 | touch_major = tdata[4]; | ||
193 | touch_minor = tdata[5]; | ||
194 | state = tdata[8] & TOUCH_STATE_MASK; | ||
195 | down = state != TOUCH_STATE_NONE; | ||
196 | } | ||
170 | 197 | ||
171 | /* Store tracking ID and other fields. */ | 198 | /* Store tracking ID and other fields. */ |
172 | msc->tracking_ids[raw_id] = id; | 199 | msc->tracking_ids[raw_id] = id; |
@@ -217,6 +244,13 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda | |||
217 | } | 244 | } |
218 | } | 245 | } |
219 | 246 | ||
247 | if (down) { | ||
248 | msc->ntouches++; | ||
249 | if (msc->single_touch_id == NO_TOUCHES) | ||
250 | msc->single_touch_id = id; | ||
251 | } else if (msc->single_touch_id == id) | ||
252 | msc->single_touch_id = SINGLE_TOUCH_UP; | ||
253 | |||
220 | /* Generate the input events for this touch. */ | 254 | /* Generate the input events for this touch. */ |
221 | if (report_touches && down) { | 255 | if (report_touches && down) { |
222 | input_report_abs(input, ABS_MT_TRACKING_ID, id); | 256 | input_report_abs(input, ABS_MT_TRACKING_ID, id); |
@@ -226,14 +260,15 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda | |||
226 | input_report_abs(input, ABS_MT_POSITION_X, x); | 260 | input_report_abs(input, ABS_MT_POSITION_X, x); |
227 | input_report_abs(input, ABS_MT_POSITION_Y, y); | 261 | input_report_abs(input, ABS_MT_POSITION_Y, y); |
228 | 262 | ||
229 | if (report_undeciphered) | 263 | if (report_undeciphered) { |
230 | input_event(input, EV_MSC, MSC_RAW, tdata[7]); | 264 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) |
265 | input_event(input, EV_MSC, MSC_RAW, tdata[7]); | ||
266 | else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
267 | input_event(input, EV_MSC, MSC_RAW, tdata[8]); | ||
268 | } | ||
231 | 269 | ||
232 | input_mt_sync(input); | 270 | input_mt_sync(input); |
233 | } | 271 | } |
234 | |||
235 | if (down) | ||
236 | msc->ntouches++; | ||
237 | } | 272 | } |
238 | 273 | ||
239 | static int magicmouse_raw_event(struct hid_device *hdev, | 274 | static int magicmouse_raw_event(struct hid_device *hdev, |
@@ -241,7 +276,7 @@ static int magicmouse_raw_event(struct hid_device *hdev, | |||
241 | { | 276 | { |
242 | struct magicmouse_sc *msc = hid_get_drvdata(hdev); | 277 | struct magicmouse_sc *msc = hid_get_drvdata(hdev); |
243 | struct input_dev *input = msc->input; | 278 | struct input_dev *input = msc->input; |
244 | int x, y, ii, clicks, npoints; | 279 | int x = 0, y = 0, ii, clicks = 0, npoints; |
245 | 280 | ||
246 | switch (data[0]) { | 281 | switch (data[0]) { |
247 | case 0x10: | 282 | case 0x10: |
@@ -251,7 +286,30 @@ static int magicmouse_raw_event(struct hid_device *hdev, | |||
251 | y = (__s16)(data[4] | data[5] << 8); | 286 | y = (__s16)(data[4] | data[5] << 8); |
252 | clicks = data[1]; | 287 | clicks = data[1]; |
253 | break; | 288 | break; |
254 | case TOUCH_REPORT_ID: | 289 | case TRACKPAD_REPORT_ID: |
290 | /* Expect four bytes of prefix, and N*9 bytes of touch data. */ | ||
291 | if (size < 4 || ((size - 4) % 9) != 0) | ||
292 | return 0; | ||
293 | npoints = (size - 4) / 9; | ||
294 | msc->ntouches = 0; | ||
295 | for (ii = 0; ii < npoints; ii++) | ||
296 | magicmouse_emit_touch(msc, ii, data + ii * 9 + 4); | ||
297 | |||
298 | /* We don't need an MT sync here because trackpad emits a | ||
299 | * BTN_TOUCH event in a new frame when all touches are released. | ||
300 | */ | ||
301 | if (msc->ntouches == 0) | ||
302 | msc->single_touch_id = NO_TOUCHES; | ||
303 | |||
304 | clicks = data[1]; | ||
305 | |||
306 | /* The following bits provide a device specific timestamp. They | ||
307 | * are unused here. | ||
308 | * | ||
309 | * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; | ||
310 | */ | ||
311 | break; | ||
312 | case MOUSE_REPORT_ID: | ||
255 | /* Expect six bytes of prefix, and N*8 bytes of touch data. */ | 313 | /* Expect six bytes of prefix, and N*8 bytes of touch data. */ |
256 | if (size < 6 || ((size - 6) % 8) != 0) | 314 | if (size < 6 || ((size - 6) % 8) != 0) |
257 | return 0; | 315 | return 0; |
@@ -277,6 +335,14 @@ static int magicmouse_raw_event(struct hid_device *hdev, | |||
277 | * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; | 335 | * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10; |
278 | */ | 336 | */ |
279 | break; | 337 | break; |
338 | case DOUBLE_REPORT_ID: | ||
339 | /* Sometimes the trackpad sends two touch reports in one | ||
340 | * packet. | ||
341 | */ | ||
342 | magicmouse_raw_event(hdev, report, data + 2, data[1]); | ||
343 | magicmouse_raw_event(hdev, report, data + 2 + data[1], | ||
344 | size - 2 - data[1]); | ||
345 | break; | ||
280 | case 0x20: /* Theoretically battery status (0-100), but I have | 346 | case 0x20: /* Theoretically battery status (0-100), but I have |
281 | * never seen it -- maybe it is only upon request. | 347 | * never seen it -- maybe it is only upon request. |
282 | */ | 348 | */ |
@@ -288,9 +354,25 @@ static int magicmouse_raw_event(struct hid_device *hdev, | |||
288 | return 0; | 354 | return 0; |
289 | } | 355 | } |
290 | 356 | ||
291 | magicmouse_emit_buttons(msc, clicks & 3); | 357 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
292 | input_report_rel(input, REL_X, x); | 358 | magicmouse_emit_buttons(msc, clicks & 3); |
293 | input_report_rel(input, REL_Y, y); | 359 | input_report_rel(input, REL_X, x); |
360 | input_report_rel(input, REL_Y, y); | ||
361 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
362 | input_report_key(input, BTN_MOUSE, clicks & 1); | ||
363 | input_report_key(input, BTN_TOUCH, msc->ntouches > 0); | ||
364 | input_report_key(input, BTN_TOOL_FINGER, msc->ntouches == 1); | ||
365 | input_report_key(input, BTN_TOOL_DOUBLETAP, msc->ntouches == 2); | ||
366 | input_report_key(input, BTN_TOOL_TRIPLETAP, msc->ntouches == 3); | ||
367 | input_report_key(input, BTN_TOOL_QUADTAP, msc->ntouches == 4); | ||
368 | if (msc->single_touch_id >= 0) { | ||
369 | input_report_abs(input, ABS_X, | ||
370 | msc->touches[msc->single_touch_id].x); | ||
371 | input_report_abs(input, ABS_Y, | ||
372 | msc->touches[msc->single_touch_id].y); | ||
373 | } | ||
374 | } | ||
375 | |||
294 | input_sync(input); | 376 | input_sync(input); |
295 | return 1; | 377 | return 1; |
296 | } | 378 | } |
@@ -326,18 +408,27 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h | |||
326 | input->dev.parent = hdev->dev.parent; | 408 | input->dev.parent = hdev->dev.parent; |
327 | 409 | ||
328 | __set_bit(EV_KEY, input->evbit); | 410 | __set_bit(EV_KEY, input->evbit); |
329 | __set_bit(BTN_LEFT, input->keybit); | 411 | |
330 | __set_bit(BTN_RIGHT, input->keybit); | 412 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
331 | if (emulate_3button) | 413 | __set_bit(BTN_LEFT, input->keybit); |
332 | __set_bit(BTN_MIDDLE, input->keybit); | 414 | __set_bit(BTN_RIGHT, input->keybit); |
333 | __set_bit(BTN_TOOL_FINGER, input->keybit); | 415 | if (emulate_3button) |
334 | 416 | __set_bit(BTN_MIDDLE, input->keybit); | |
335 | __set_bit(EV_REL, input->evbit); | 417 | |
336 | __set_bit(REL_X, input->relbit); | 418 | __set_bit(EV_REL, input->evbit); |
337 | __set_bit(REL_Y, input->relbit); | 419 | __set_bit(REL_X, input->relbit); |
338 | if (emulate_scroll_wheel) { | 420 | __set_bit(REL_Y, input->relbit); |
339 | __set_bit(REL_WHEEL, input->relbit); | 421 | if (emulate_scroll_wheel) { |
340 | __set_bit(REL_HWHEEL, input->relbit); | 422 | __set_bit(REL_WHEEL, input->relbit); |
423 | __set_bit(REL_HWHEEL, input->relbit); | ||
424 | } | ||
425 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
426 | __set_bit(BTN_MOUSE, input->keybit); | ||
427 | __set_bit(BTN_TOOL_FINGER, input->keybit); | ||
428 | __set_bit(BTN_TOOL_DOUBLETAP, input->keybit); | ||
429 | __set_bit(BTN_TOOL_TRIPLETAP, input->keybit); | ||
430 | __set_bit(BTN_TOOL_QUADTAP, input->keybit); | ||
431 | __set_bit(BTN_TOUCH, input->keybit); | ||
341 | } | 432 | } |
342 | 433 | ||
343 | if (report_touches) { | 434 | if (report_touches) { |
@@ -347,16 +438,26 @@ static void magicmouse_setup_input(struct input_dev *input, struct hid_device *h | |||
347 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); | 438 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255, 4, 0); |
348 | input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); | 439 | input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255, 4, 0); |
349 | input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); | 440 | input_set_abs_params(input, ABS_MT_ORIENTATION, -32, 31, 1, 0); |
350 | input_set_abs_params(input, ABS_MT_POSITION_X, -1100, 1358, | 441 | |
351 | 4, 0); | ||
352 | /* Note: Touch Y position from the device is inverted relative | 442 | /* Note: Touch Y position from the device is inverted relative |
353 | * to how pointer motion is reported (and relative to how USB | 443 | * to how pointer motion is reported (and relative to how USB |
354 | * HID recommends the coordinates work). This driver keeps | 444 | * HID recommends the coordinates work). This driver keeps |
355 | * the origin at the same position, and just uses the additive | 445 | * the origin at the same position, and just uses the additive |
356 | * inverse of the reported Y. | 446 | * inverse of the reported Y. |
357 | */ | 447 | */ |
358 | input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, 2047, | 448 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
359 | 4, 0); | 449 | input_set_abs_params(input, ABS_MT_POSITION_X, -1100, |
450 | 1358, 4, 0); | ||
451 | input_set_abs_params(input, ABS_MT_POSITION_Y, -1589, | ||
452 | 2047, 4, 0); | ||
453 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
454 | input_set_abs_params(input, ABS_X, -2909, 3167, 4, 0); | ||
455 | input_set_abs_params(input, ABS_Y, -2456, 2565, 4, 0); | ||
456 | input_set_abs_params(input, ABS_MT_POSITION_X, -2909, | ||
457 | 3167, 4, 0); | ||
458 | input_set_abs_params(input, ABS_MT_POSITION_Y, -2456, | ||
459 | 2565, 4, 0); | ||
460 | } | ||
360 | } | 461 | } |
361 | 462 | ||
362 | if (report_undeciphered) { | 463 | if (report_undeciphered) { |
@@ -385,6 +486,8 @@ static int magicmouse_probe(struct hid_device *hdev, | |||
385 | msc->quirks = id->driver_data; | 486 | msc->quirks = id->driver_data; |
386 | hid_set_drvdata(hdev, msc); | 487 | hid_set_drvdata(hdev, msc); |
387 | 488 | ||
489 | msc->single_touch_id = NO_TOUCHES; | ||
490 | |||
388 | ret = hid_parse(hdev); | 491 | ret = hid_parse(hdev); |
389 | if (ret) { | 492 | if (ret) { |
390 | dev_err(&hdev->dev, "magicmouse hid parse failed\n"); | 493 | dev_err(&hdev->dev, "magicmouse hid parse failed\n"); |
@@ -400,7 +503,16 @@ static int magicmouse_probe(struct hid_device *hdev, | |||
400 | /* we are handling the input ourselves */ | 503 | /* we are handling the input ourselves */ |
401 | hidinput_disconnect(hdev); | 504 | hidinput_disconnect(hdev); |
402 | 505 | ||
403 | report = hid_register_report(hdev, HID_INPUT_REPORT, TOUCH_REPORT_ID); | 506 | if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) |
507 | report = hid_register_report(hdev, HID_INPUT_REPORT, | ||
508 | MOUSE_REPORT_ID); | ||
509 | else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ | ||
510 | report = hid_register_report(hdev, HID_INPUT_REPORT, | ||
511 | TRACKPAD_REPORT_ID); | ||
512 | report = hid_register_report(hdev, HID_INPUT_REPORT, | ||
513 | DOUBLE_REPORT_ID); | ||
514 | } | ||
515 | |||
404 | if (!report) { | 516 | if (!report) { |
405 | dev_err(&hdev->dev, "unable to register touch report\n"); | 517 | dev_err(&hdev->dev, "unable to register touch report\n"); |
406 | ret = -ENOMEM; | 518 | ret = -ENOMEM; |
@@ -451,8 +563,10 @@ static void magicmouse_remove(struct hid_device *hdev) | |||
451 | } | 563 | } |
452 | 564 | ||
453 | static const struct hid_device_id magic_mice[] = { | 565 | static const struct hid_device_id magic_mice[] = { |
454 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MAGICMOUSE), | 566 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, |
455 | .driver_data = 0 }, | 567 | USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, |
568 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, | ||
569 | USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, | ||
456 | { } | 570 | { } |
457 | }; | 571 | }; |
458 | MODULE_DEVICE_TABLE(hid, magic_mice); | 572 | MODULE_DEVICE_TABLE(hid, magic_mice); |