Merge branch 'for-5.17/magicmouse' into for-linus
authorJiri Kosina <jkosina@suse.cz>
Mon, 10 Jan 2022 08:58:34 +0000 (09:58 +0100)
committerJiri Kosina <jkosina@suse.cz>
Mon, 10 Jan 2022 08:58:34 +0000 (09:58 +0100)
- proper batter reporting for hid-magicmouse USB-connected devices (José Expósito)

1  2 
drivers/hid/hid-magicmouse.c

@@@ -51,14 -51,14 +51,16 @@@ static bool report_undeciphered
  module_param(report_undeciphered, bool, 0644);
  MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
  
 +#define TRACKPAD2_2021_BT_VERSION 0x110
 +
  #define TRACKPAD_REPORT_ID 0x28
  #define TRACKPAD2_USB_REPORT_ID 0x02
  #define TRACKPAD2_BT_REPORT_ID 0x31
  #define MOUSE_REPORT_ID    0x29
  #define MOUSE2_REPORT_ID   0x12
  #define DOUBLE_REPORT_ID   0xf7
+ #define USB_BATTERY_TIMEOUT_MS 60000
  /* These definitions are not precise, but they're close enough.  (Bits
   * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
   * to be some kind of bit mask -- 0x20 may be a near-field reading,
@@@ -142,6 -142,7 +144,7 @@@ struct magicmouse_sc 
  
        struct hid_device *hdev;
        struct delayed_work work;
+       struct timer_list battery_timer;
  };
  
  static int magicmouse_firm_touch(struct magicmouse_sc *msc)
@@@ -258,11 -259,8 +261,11 @@@ static void magicmouse_emit_touch(struc
                unsigned long now = jiffies;
                int step_x = msc->touches[id].scroll_x - x;
                int step_y = msc->touches[id].scroll_y - y;
 -              int step_hr = ((64 - (int)scroll_speed) * msc->scroll_accel) /
 -                            SCROLL_HR_STEPS;
 +              int step_hr =
 +                      max_t(int,
 +                            ((64 - (int)scroll_speed) * msc->scroll_accel) /
 +                                      SCROLL_HR_STEPS,
 +                            1);
                int step_x_hr = msc->touches[id].scroll_x_hr - x;
                int step_y_hr = msc->touches[id].scroll_y_hr - y;
  
@@@ -540,22 -538,10 +543,22 @@@ static int magicmouse_setup_input(struc
                        __set_bit(REL_HWHEEL_HI_RES, input->relbit);
                }
        } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) {
 -              /* setting the device name to ensure the same driver settings
 -               * get loaded, whether connected through bluetooth or USB
 +              /* If the trackpad has been connected to a Mac, the name is
 +               * automatically personalized, e.g., "José Expósito's Trackpad".
 +               * When connected through Bluetooth, the personalized name is
 +               * reported, however, when connected through USB the generic
 +               * name is reported.
 +               * Set the device name to ensure the same driver settings get
 +               * loaded, whether connected through bluetooth or USB.
                 */
 -              input->name = "Apple Inc. Magic Trackpad 2";
 +              if (hdev->vendor == BT_VENDOR_ID_APPLE) {
 +                      if (input->id.version == TRACKPAD2_2021_BT_VERSION)
 +                              input->name = "Apple Inc. Magic Trackpad";
 +                      else
 +                              input->name = "Apple Inc. Magic Trackpad 2";
 +              } else { /* USB_VENDOR_ID_APPLE */
 +                      input->name = hdev->name;
 +              }
  
                __clear_bit(EV_MSC, input->evbit);
                __clear_bit(BTN_0, input->keybit);
@@@ -752,6 -738,44 +755,44 @@@ static void magicmouse_enable_mt_work(s
                hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
  }
  
+ static int magicmouse_fetch_battery(struct hid_device *hdev)
+ {
+ #ifdef CONFIG_HID_BATTERY_STRENGTH
+       struct hid_report_enum *report_enum;
+       struct hid_report *report;
+       if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE ||
+           (hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
+            hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2))
+               return -1;
+       report_enum = &hdev->report_enum[hdev->battery_report_type];
+       report = report_enum->report_id_hash[hdev->battery_report_id];
+       if (!report || report->maxfield < 1)
+               return -1;
+       if (hdev->battery_capacity == hdev->battery_max)
+               return -1;
+       hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
+       return 0;
+ #else
+       return -1;
+ #endif
+ }
+ static void magicmouse_battery_timer_tick(struct timer_list *t)
+ {
+       struct magicmouse_sc *msc = from_timer(msc, t, battery_timer);
+       struct hid_device *hdev = msc->hdev;
+       if (magicmouse_fetch_battery(hdev) == 0) {
+               mod_timer(&msc->battery_timer,
+                         jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
+       }
+ }
  static int magicmouse_probe(struct hid_device *hdev,
        const struct hid_device_id *id)
  {
        struct hid_report *report;
        int ret;
  
-       if (id->vendor == USB_VENDOR_ID_APPLE &&
-           id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
-           hdev->type != HID_TYPE_USBMOUSE)
-               return -ENODEV;
        msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
        if (msc == NULL) {
                hid_err(hdev, "can't alloc magicmouse descriptor\n");
                return ret;
        }
  
+       timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
+       mod_timer(&msc->battery_timer,
+                 jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
+       magicmouse_fetch_battery(hdev);
+       if (id->vendor == USB_VENDOR_ID_APPLE &&
+           (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
+            (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 && hdev->type != HID_TYPE_USBMOUSE)))
+               return 0;
        if (!msc->input) {
                hid_err(hdev, "magicmouse input not registered\n");
                ret = -ENOMEM;
  
        return 0;
  err_stop_hw:
+       del_timer_sync(&msc->battery_timer);
        hid_hw_stop(hdev);
        return ret;
  }
@@@ -849,17 -879,52 +896,52 @@@ static void magicmouse_remove(struct hi
  {
        struct magicmouse_sc *msc = hid_get_drvdata(hdev);
  
-       if (msc)
+       if (msc) {
                cancel_delayed_work_sync(&msc->work);
+               del_timer_sync(&msc->battery_timer);
+       }
  
        hid_hw_stop(hdev);
  }
  
+ static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+                                    unsigned int *rsize)
+ {
+       /*
+        * Change the usage from:
+        *   0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1)  0
+        *   0x09, 0x0b,       // Usage (Vendor Usage 0x0b)           3
+        * To:
+        *   0x05, 0x01,       // Usage Page (Generic Desktop)        0
+        *   0x09, 0x02,       // Usage (Mouse)                       2
+        */
+       if (hdev->vendor == USB_VENDOR_ID_APPLE &&
+           (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
+            hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2) &&
+           *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
+               hid_info(hdev,
+                        "fixing up magicmouse battery report descriptor\n");
+               *rsize = *rsize - 1;
+               rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
+               if (!rdesc)
+                       return NULL;
+               rdesc[0] = 0x05;
+               rdesc[1] = 0x01;
+               rdesc[2] = 0x09;
+               rdesc[3] = 0x02;
+       }
+       return rdesc;
+ }
  static const struct hid_device_id magic_mice[] = {
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
                USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
        { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
                USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
+       { HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
+               USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
        { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
                USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
        { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
@@@ -875,6 -940,7 +957,7 @@@ static struct hid_driver magicmouse_dri
        .id_table = magic_mice,
        .probe = magicmouse_probe,
        .remove = magicmouse_remove,
+       .report_fixup = magicmouse_report_fixup,
        .raw_event = magicmouse_raw_event,
        .event = magicmouse_event,
        .input_mapping = magicmouse_input_mapping,