| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Apple "Magic" Wireless Mouse driver |
|---|
| 3 | 4 | * |
|---|
| .. | .. |
|---|
| 6 | 7 | */ |
|---|
| 7 | 8 | |
|---|
| 8 | 9 | /* |
|---|
| 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 | 10 | */ |
|---|
| 14 | 11 | |
|---|
| 15 | 12 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt |
|---|
| .. | .. |
|---|
| 54 | 51 | MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event"); |
|---|
| 55 | 52 | |
|---|
| 56 | 53 | #define TRACKPAD_REPORT_ID 0x28 |
|---|
| 54 | +#define TRACKPAD2_USB_REPORT_ID 0x02 |
|---|
| 55 | +#define TRACKPAD2_BT_REPORT_ID 0x31 |
|---|
| 57 | 56 | #define MOUSE_REPORT_ID 0x29 |
|---|
| 58 | 57 | #define DOUBLE_REPORT_ID 0xf7 |
|---|
| 59 | 58 | /* These definitions are not precise, but they're close enough. (Bits |
|---|
| .. | .. |
|---|
| 90 | 89 | #define TRACKPAD_MAX_Y 2565 |
|---|
| 91 | 90 | #define TRACKPAD_RES_Y \ |
|---|
| 92 | 91 | ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100)) |
|---|
| 92 | + |
|---|
| 93 | +#define TRACKPAD2_DIMENSION_X (float)16000 |
|---|
| 94 | +#define TRACKPAD2_MIN_X -3678 |
|---|
| 95 | +#define TRACKPAD2_MAX_X 3934 |
|---|
| 96 | +#define TRACKPAD2_RES_X \ |
|---|
| 97 | + ((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100)) |
|---|
| 98 | +#define TRACKPAD2_DIMENSION_Y (float)11490 |
|---|
| 99 | +#define TRACKPAD2_MIN_Y -2478 |
|---|
| 100 | +#define TRACKPAD2_MAX_Y 2587 |
|---|
| 101 | +#define TRACKPAD2_RES_Y \ |
|---|
| 102 | + ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100)) |
|---|
| 93 | 103 | |
|---|
| 94 | 104 | /** |
|---|
| 95 | 105 | * struct magicmouse_sc - Tracks Magic Mouse-specific data. |
|---|
| .. | .. |
|---|
| 183 | 193 | { |
|---|
| 184 | 194 | struct input_dev *input = msc->input; |
|---|
| 185 | 195 | int id, x, y, size, orientation, touch_major, touch_minor, state, down; |
|---|
| 196 | + int pressure = 0; |
|---|
| 186 | 197 | |
|---|
| 187 | 198 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
|---|
| 188 | 199 | id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf; |
|---|
| .. | .. |
|---|
| 194 | 205 | touch_minor = tdata[4]; |
|---|
| 195 | 206 | state = tdata[7] & TOUCH_STATE_MASK; |
|---|
| 196 | 207 | down = state != TOUCH_STATE_NONE; |
|---|
| 208 | + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 209 | + id = tdata[8] & 0xf; |
|---|
| 210 | + x = (tdata[1] << 27 | tdata[0] << 19) >> 19; |
|---|
| 211 | + y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19); |
|---|
| 212 | + size = tdata[6]; |
|---|
| 213 | + orientation = (tdata[8] >> 5) - 4; |
|---|
| 214 | + touch_major = tdata[4]; |
|---|
| 215 | + touch_minor = tdata[5]; |
|---|
| 216 | + pressure = tdata[7]; |
|---|
| 217 | + state = tdata[3] & 0xC0; |
|---|
| 218 | + down = state == 0x80; |
|---|
| 197 | 219 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 198 | 220 | id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf; |
|---|
| 199 | 221 | x = (tdata[1] << 27 | tdata[0] << 19) >> 19; |
|---|
| .. | .. |
|---|
| 215 | 237 | /* If requested, emulate a scroll wheel by detecting small |
|---|
| 216 | 238 | * vertical touch motions. |
|---|
| 217 | 239 | */ |
|---|
| 218 | | - if (emulate_scroll_wheel) { |
|---|
| 240 | + if (emulate_scroll_wheel && (input->id.product != |
|---|
| 241 | + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2)) { |
|---|
| 219 | 242 | unsigned long now = jiffies; |
|---|
| 220 | 243 | int step_x = msc->touches[id].scroll_x - x; |
|---|
| 221 | 244 | int step_y = msc->touches[id].scroll_y - y; |
|---|
| .. | .. |
|---|
| 269 | 292 | input_report_abs(input, ABS_MT_POSITION_X, x); |
|---|
| 270 | 293 | input_report_abs(input, ABS_MT_POSITION_Y, y); |
|---|
| 271 | 294 | |
|---|
| 295 | + if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) |
|---|
| 296 | + input_report_abs(input, ABS_MT_PRESSURE, pressure); |
|---|
| 297 | + |
|---|
| 272 | 298 | if (report_undeciphered) { |
|---|
| 273 | 299 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) |
|---|
| 274 | 300 | input_event(input, EV_MSC, MSC_RAW, tdata[7]); |
|---|
| 275 | | - else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 301 | + else if (input->id.product != |
|---|
| 302 | + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) |
|---|
| 276 | 303 | input_event(input, EV_MSC, MSC_RAW, tdata[8]); |
|---|
| 277 | 304 | } |
|---|
| 278 | 305 | } |
|---|
| .. | .. |
|---|
| 287 | 314 | |
|---|
| 288 | 315 | switch (data[0]) { |
|---|
| 289 | 316 | case TRACKPAD_REPORT_ID: |
|---|
| 317 | + case TRACKPAD2_BT_REPORT_ID: |
|---|
| 290 | 318 | /* Expect four bytes of prefix, and N*9 bytes of touch data. */ |
|---|
| 291 | 319 | if (size < 4 || ((size - 4) % 9) != 0) |
|---|
| 292 | 320 | return 0; |
|---|
| .. | .. |
|---|
| 307 | 335 | * |
|---|
| 308 | 336 | * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10; |
|---|
| 309 | 337 | */ |
|---|
| 338 | + break; |
|---|
| 339 | + case TRACKPAD2_USB_REPORT_ID: |
|---|
| 340 | + /* Expect twelve bytes of prefix and N*9 bytes of touch data. */ |
|---|
| 341 | + if (size < 12 || ((size - 12) % 9) != 0) |
|---|
| 342 | + return 0; |
|---|
| 343 | + npoints = (size - 12) / 9; |
|---|
| 344 | + if (npoints > 15) { |
|---|
| 345 | + hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n", |
|---|
| 346 | + size); |
|---|
| 347 | + return 0; |
|---|
| 348 | + } |
|---|
| 349 | + msc->ntouches = 0; |
|---|
| 350 | + for (ii = 0; ii < npoints; ii++) |
|---|
| 351 | + magicmouse_emit_touch(msc, ii, data + ii * 9 + 12); |
|---|
| 352 | + |
|---|
| 353 | + clicks = data[1]; |
|---|
| 310 | 354 | break; |
|---|
| 311 | 355 | case MOUSE_REPORT_ID: |
|---|
| 312 | 356 | /* Expect six bytes of prefix, and N*8 bytes of touch data. */ |
|---|
| .. | .. |
|---|
| 343 | 387 | magicmouse_raw_event(hdev, report, data + 2, data[1]); |
|---|
| 344 | 388 | magicmouse_raw_event(hdev, report, data + 2 + data[1], |
|---|
| 345 | 389 | size - 2 - data[1]); |
|---|
| 346 | | - break; |
|---|
| 390 | + return 0; |
|---|
| 347 | 391 | default: |
|---|
| 348 | 392 | return 0; |
|---|
| 349 | 393 | } |
|---|
| .. | .. |
|---|
| 352 | 396 | magicmouse_emit_buttons(msc, clicks & 3); |
|---|
| 353 | 397 | input_report_rel(input, REL_X, x); |
|---|
| 354 | 398 | input_report_rel(input, REL_Y, y); |
|---|
| 399 | + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 400 | + input_mt_sync_frame(input); |
|---|
| 401 | + input_report_key(input, BTN_MOUSE, clicks & 1); |
|---|
| 355 | 402 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 356 | 403 | input_report_key(input, BTN_MOUSE, clicks & 1); |
|---|
| 357 | 404 | input_mt_report_pointer_emulation(input, true); |
|---|
| .. | .. |
|---|
| 364 | 411 | static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev) |
|---|
| 365 | 412 | { |
|---|
| 366 | 413 | int error; |
|---|
| 414 | + int mt_flags = 0; |
|---|
| 367 | 415 | |
|---|
| 368 | 416 | __set_bit(EV_KEY, input->evbit); |
|---|
| 369 | 417 | |
|---|
| .. | .. |
|---|
| 380 | 428 | __set_bit(REL_WHEEL, input->relbit); |
|---|
| 381 | 429 | __set_bit(REL_HWHEEL, input->relbit); |
|---|
| 382 | 430 | } |
|---|
| 431 | + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 432 | + /* setting the device name to ensure the same driver settings |
|---|
| 433 | + * get loaded, whether connected through bluetooth or USB |
|---|
| 434 | + */ |
|---|
| 435 | + input->name = "Apple Inc. Magic Trackpad 2"; |
|---|
| 436 | + |
|---|
| 437 | + __clear_bit(EV_MSC, input->evbit); |
|---|
| 438 | + __clear_bit(BTN_0, input->keybit); |
|---|
| 439 | + __clear_bit(BTN_RIGHT, input->keybit); |
|---|
| 440 | + __clear_bit(BTN_MIDDLE, input->keybit); |
|---|
| 441 | + __set_bit(BTN_MOUSE, input->keybit); |
|---|
| 442 | + __set_bit(INPUT_PROP_BUTTONPAD, input->propbit); |
|---|
| 443 | + __set_bit(BTN_TOOL_FINGER, input->keybit); |
|---|
| 444 | + |
|---|
| 445 | + mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | |
|---|
| 446 | + INPUT_MT_TRACK; |
|---|
| 383 | 447 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 384 | 448 | /* input->keybit is initialized with incorrect button info |
|---|
| 385 | 449 | * for Magic Trackpad. There really is only one physical |
|---|
| .. | .. |
|---|
| 402 | 466 | |
|---|
| 403 | 467 | __set_bit(EV_ABS, input->evbit); |
|---|
| 404 | 468 | |
|---|
| 405 | | - error = input_mt_init_slots(input, 16, 0); |
|---|
| 469 | + error = input_mt_init_slots(input, 16, mt_flags); |
|---|
| 406 | 470 | if (error) |
|---|
| 407 | 471 | return error; |
|---|
| 408 | 472 | input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2, |
|---|
| 409 | 473 | 4, 0); |
|---|
| 410 | 474 | input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2, |
|---|
| 411 | 475 | 4, 0); |
|---|
| 412 | | - input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); |
|---|
| 413 | 476 | |
|---|
| 414 | 477 | /* Note: Touch Y position from the device is inverted relative |
|---|
| 415 | 478 | * to how pointer motion is reported (and relative to how USB |
|---|
| .. | .. |
|---|
| 418 | 481 | * inverse of the reported Y. |
|---|
| 419 | 482 | */ |
|---|
| 420 | 483 | if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) { |
|---|
| 484 | + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); |
|---|
| 421 | 485 | input_set_abs_params(input, ABS_MT_POSITION_X, |
|---|
| 422 | 486 | MOUSE_MIN_X, MOUSE_MAX_X, 4, 0); |
|---|
| 423 | 487 | input_set_abs_params(input, ABS_MT_POSITION_Y, |
|---|
| .. | .. |
|---|
| 427 | 491 | MOUSE_RES_X); |
|---|
| 428 | 492 | input_abs_set_res(input, ABS_MT_POSITION_Y, |
|---|
| 429 | 493 | MOUSE_RES_Y); |
|---|
| 494 | + } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 495 | + input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0); |
|---|
| 496 | + input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0); |
|---|
| 497 | + input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0); |
|---|
| 498 | + input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X, |
|---|
| 499 | + TRACKPAD2_MAX_X, 0, 0); |
|---|
| 500 | + input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y, |
|---|
| 501 | + TRACKPAD2_MAX_Y, 0, 0); |
|---|
| 502 | + input_set_abs_params(input, ABS_MT_POSITION_X, |
|---|
| 503 | + TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0); |
|---|
| 504 | + input_set_abs_params(input, ABS_MT_POSITION_Y, |
|---|
| 505 | + TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0); |
|---|
| 506 | + |
|---|
| 507 | + input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X); |
|---|
| 508 | + input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y); |
|---|
| 509 | + input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X); |
|---|
| 510 | + input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y); |
|---|
| 430 | 511 | } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 512 | + input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0); |
|---|
| 431 | 513 | input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X, |
|---|
| 432 | 514 | TRACKPAD_MAX_X, 4, 0); |
|---|
| 433 | 515 | input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y, |
|---|
| .. | .. |
|---|
| 447 | 529 | |
|---|
| 448 | 530 | input_set_events_per_packet(input, 60); |
|---|
| 449 | 531 | |
|---|
| 450 | | - if (report_undeciphered) { |
|---|
| 532 | + if (report_undeciphered && |
|---|
| 533 | + input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 451 | 534 | __set_bit(EV_MSC, input->evbit); |
|---|
| 452 | 535 | __set_bit(MSC_RAW, input->mscbit); |
|---|
| 453 | 536 | } |
|---|
| .. | .. |
|---|
| 471 | 554 | msc->input = hi->input; |
|---|
| 472 | 555 | |
|---|
| 473 | 556 | /* Magic Trackpad does not give relative data after switching to MT */ |
|---|
| 474 | | - if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD && |
|---|
| 557 | + if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD || |
|---|
| 558 | + hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) && |
|---|
| 475 | 559 | field->flags & HID_MAIN_ITEM_RELATIVE) |
|---|
| 476 | 560 | return -1; |
|---|
| 477 | 561 | |
|---|
| .. | .. |
|---|
| 500 | 584 | static int magicmouse_probe(struct hid_device *hdev, |
|---|
| 501 | 585 | const struct hid_device_id *id) |
|---|
| 502 | 586 | { |
|---|
| 503 | | - const u8 feature[] = { 0xd7, 0x01 }; |
|---|
| 587 | + const u8 *feature; |
|---|
| 588 | + const u8 feature_mt[] = { 0xD7, 0x01 }; |
|---|
| 589 | + const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 }; |
|---|
| 590 | + const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 }; |
|---|
| 504 | 591 | u8 *buf; |
|---|
| 505 | 592 | struct magicmouse_sc *msc; |
|---|
| 506 | 593 | struct hid_report *report; |
|---|
| 507 | 594 | int ret; |
|---|
| 595 | + int feature_size; |
|---|
| 596 | + |
|---|
| 597 | + if (id->vendor == USB_VENDOR_ID_APPLE && |
|---|
| 598 | + id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && |
|---|
| 599 | + hdev->type != HID_TYPE_USBMOUSE) |
|---|
| 600 | + return -ENODEV; |
|---|
| 508 | 601 | |
|---|
| 509 | 602 | msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL); |
|---|
| 510 | 603 | if (msc == NULL) { |
|---|
| .. | .. |
|---|
| 538 | 631 | if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE) |
|---|
| 539 | 632 | report = hid_register_report(hdev, HID_INPUT_REPORT, |
|---|
| 540 | 633 | MOUSE_REPORT_ID, 0); |
|---|
| 541 | | - else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 634 | + else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 635 | + if (id->vendor == BT_VENDOR_ID_APPLE) |
|---|
| 636 | + report = hid_register_report(hdev, HID_INPUT_REPORT, |
|---|
| 637 | + TRACKPAD2_BT_REPORT_ID, 0); |
|---|
| 638 | + else /* USB_VENDOR_ID_APPLE */ |
|---|
| 639 | + report = hid_register_report(hdev, HID_INPUT_REPORT, |
|---|
| 640 | + TRACKPAD2_USB_REPORT_ID, 0); |
|---|
| 641 | + } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */ |
|---|
| 542 | 642 | report = hid_register_report(hdev, HID_INPUT_REPORT, |
|---|
| 543 | 643 | TRACKPAD_REPORT_ID, 0); |
|---|
| 544 | 644 | report = hid_register_report(hdev, HID_INPUT_REPORT, |
|---|
| .. | .. |
|---|
| 552 | 652 | } |
|---|
| 553 | 653 | report->size = 6; |
|---|
| 554 | 654 | |
|---|
| 555 | | - buf = kmemdup(feature, sizeof(feature), GFP_KERNEL); |
|---|
| 655 | + if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) { |
|---|
| 656 | + if (id->vendor == BT_VENDOR_ID_APPLE) { |
|---|
| 657 | + feature_size = sizeof(feature_mt_trackpad2_bt); |
|---|
| 658 | + feature = feature_mt_trackpad2_bt; |
|---|
| 659 | + } else { /* USB_VENDOR_ID_APPLE */ |
|---|
| 660 | + feature_size = sizeof(feature_mt_trackpad2_usb); |
|---|
| 661 | + feature = feature_mt_trackpad2_usb; |
|---|
| 662 | + } |
|---|
| 663 | + } else { |
|---|
| 664 | + feature_size = sizeof(feature_mt); |
|---|
| 665 | + feature = feature_mt; |
|---|
| 666 | + } |
|---|
| 667 | + |
|---|
| 668 | + buf = kmemdup(feature, feature_size, GFP_KERNEL); |
|---|
| 556 | 669 | if (!buf) { |
|---|
| 557 | 670 | ret = -ENOMEM; |
|---|
| 558 | 671 | goto err_stop_hw; |
|---|
| .. | .. |
|---|
| 566 | 679 | * but there seems to be no other way of switching the mode. |
|---|
| 567 | 680 | * Thus the super-ugly hacky success check below. |
|---|
| 568 | 681 | */ |
|---|
| 569 | | - ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature), |
|---|
| 682 | + ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size, |
|---|
| 570 | 683 | HID_FEATURE_REPORT, HID_REQ_SET_REPORT); |
|---|
| 571 | 684 | kfree(buf); |
|---|
| 572 | | - if (ret != -EIO && ret != sizeof(feature)) { |
|---|
| 685 | + if (ret != -EIO && ret != feature_size) { |
|---|
| 573 | 686 | hid_err(hdev, "unable to request touch data (%d)\n", ret); |
|---|
| 574 | 687 | goto err_stop_hw; |
|---|
| 575 | 688 | } |
|---|
| .. | .. |
|---|
| 585 | 698 | USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 }, |
|---|
| 586 | 699 | { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE, |
|---|
| 587 | 700 | USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 }, |
|---|
| 701 | + { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE, |
|---|
| 702 | + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, |
|---|
| 703 | + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, |
|---|
| 704 | + USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 }, |
|---|
| 588 | 705 | { } |
|---|
| 589 | 706 | }; |
|---|
| 590 | 707 | MODULE_DEVICE_TABLE(hid, magic_mice); |
|---|