aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-magicmouse.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hid/hid-magicmouse.c')
-rw-r--r--drivers/hid/hid-magicmouse.c122
1 files changed, 111 insertions, 11 deletions
diff --git a/drivers/hid/hid-magicmouse.c b/drivers/hid/hid-magicmouse.c
index 686788ebf3e1..664a624a363d 100644
--- a/drivers/hid/hid-magicmouse.c
+++ b/drivers/hid/hid-magicmouse.c
@@ -51,12 +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,
@@ -140,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)
@@ -256,8 +261,11 @@ static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tda
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;
@@ -535,10 +543,22 @@ static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hd
__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);
@@ -735,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)
{
@@ -742,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");
@@ -772,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;
@@ -824,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;
}
@@ -832,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,
@@ -858,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,