Commit f7716563 authored by Jiri Kosina's avatar Jiri Kosina
Browse files

Merge branch 'for-5.17/magicmouse' into for-linus

- proper batter reporting for hid-magicmouse USB-connected devices (José Expósito)
parents 50ae0cfc 33812fc7
Loading
Loading
Loading
Loading
+89 −6
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state fie
#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 +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)
@@ -752,6 +755,44 @@ static void magicmouse_enable_mt_work(struct work_struct *work)
		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)
{
@@ -759,11 +800,6 @@ static int magicmouse_probe(struct hid_device *hdev,
	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");
@@ -789,6 +825,16 @@ static int magicmouse_probe(struct hid_device *hdev,
		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;
@@ -841,6 +887,7 @@ static int magicmouse_probe(struct hid_device *hdev,

	return 0;
err_stop_hw:
	del_timer_sync(&msc->battery_timer);
	hid_hw_stop(hdev);
	return ret;
}
@@ -849,17 +896,52 @@ static void magicmouse_remove(struct hid_device *hdev)
{
	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 +957,7 @@ static struct hid_driver magicmouse_driver = {
	.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,