12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2128537ceSMichael Poole /*
3128537ceSMichael Poole * Apple "Magic" Wireless Mouse driver
4128537ceSMichael Poole *
5128537ceSMichael Poole * Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
6a462230eSChase Douglas * Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com>
7128537ceSMichael Poole */
8128537ceSMichael Poole
9128537ceSMichael Poole /*
10128537ceSMichael Poole */
11128537ceSMichael Poole
124291ee30SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
134291ee30SJoe Perches
14128537ceSMichael Poole #include <linux/device.h>
15128537ceSMichael Poole #include <linux/hid.h>
16a6d1bc1dSYufeng Shen #include <linux/input/mt.h>
17128537ceSMichael Poole #include <linux/module.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
19c0dc5582SJohn Chen #include <linux/workqueue.h>
20128537ceSMichael Poole
21128537ceSMichael Poole #include "hid-ids.h"
22128537ceSMichael Poole
2371b38bd4SMichael Poole static bool emulate_3button = true;
24128537ceSMichael Poole module_param(emulate_3button, bool, 0644);
25128537ceSMichael Poole MODULE_PARM_DESC(emulate_3button, "Emulate a middle button");
26128537ceSMichael Poole
27128537ceSMichael Poole static int middle_button_start = -350;
28128537ceSMichael Poole static int middle_button_stop = +350;
29128537ceSMichael Poole
3071b38bd4SMichael Poole static bool emulate_scroll_wheel = true;
31128537ceSMichael Poole module_param(emulate_scroll_wheel, bool, 0644);
32128537ceSMichael Poole MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
33128537ceSMichael Poole
340b778e76SChase Douglas static unsigned int scroll_speed = 32;
param_set_scroll_speed(const char * val,const struct kernel_param * kp)35e4dca7b7SKees Cook static int param_set_scroll_speed(const char *val,
36e4dca7b7SKees Cook const struct kernel_param *kp) {
370b778e76SChase Douglas unsigned long speed;
38dfc450b5SJingoo Han if (!val || kstrtoul(val, 0, &speed) || speed > 63)
390b778e76SChase Douglas return -EINVAL;
400b778e76SChase Douglas scroll_speed = speed;
410b778e76SChase Douglas return 0;
420b778e76SChase Douglas }
430b778e76SChase Douglas module_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644);
440b778e76SChase Douglas MODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)");
450b778e76SChase Douglas
469846f350SChase Douglas static bool scroll_acceleration = false;
479846f350SChase Douglas module_param(scroll_acceleration, bool, 0644);
489846f350SChase Douglas MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
499846f350SChase Douglas
5071b38bd4SMichael Poole static bool report_undeciphered;
51128537ceSMichael Poole module_param(report_undeciphered, bool, 0644);
52128537ceSMichael Poole MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
53128537ceSMichael Poole
545768701eSJosé Expósito #define TRACKPAD2_2021_BT_VERSION 0x110
555768701eSJosé Expósito
56a462230eSChase Douglas #define TRACKPAD_REPORT_ID 0x28
579d7b1866SSean O'Brien #define TRACKPAD2_USB_REPORT_ID 0x02
589d7b1866SSean O'Brien #define TRACKPAD2_BT_REPORT_ID 0x31
59a462230eSChase Douglas #define MOUSE_REPORT_ID 0x29
602b0c086cSJohn Chen #define MOUSE2_REPORT_ID 0x12
61a462230eSChase Douglas #define DOUBLE_REPORT_ID 0xf7
620b91b4e4SJosé Expósito #define USB_BATTERY_TIMEOUT_MS 60000
630b91b4e4SJosé Expósito
64128537ceSMichael Poole /* These definitions are not precise, but they're close enough. (Bits
65128537ceSMichael Poole * 0x03 seem to indicate the aspect ratio of the touch, bits 0x70 seem
66128537ceSMichael Poole * to be some kind of bit mask -- 0x20 may be a near-field reading,
67128537ceSMichael Poole * and 0x40 is actual contact, and 0x10 may be a start/stop or change
68128537ceSMichael Poole * indication.)
69128537ceSMichael Poole */
70128537ceSMichael Poole #define TOUCH_STATE_MASK 0xf0
71128537ceSMichael Poole #define TOUCH_STATE_NONE 0x00
72128537ceSMichael Poole #define TOUCH_STATE_START 0x30
73128537ceSMichael Poole #define TOUCH_STATE_DRAG 0x40
74128537ceSMichael Poole
75d4b9f10aSJosé Expósito /* Number of high-resolution events for each low-resolution detent. */
76d4b9f10aSJosé Expósito #define SCROLL_HR_STEPS 10
77d4b9f10aSJosé Expósito #define SCROLL_HR_MULT (120 / SCROLL_HR_STEPS)
789d60648cSJosé Expósito #define SCROLL_HR_THRESHOLD 90 /* units */
790b778e76SChase Douglas #define SCROLL_ACCEL_DEFAULT 7
800b778e76SChase Douglas
814f6fdf08SChase Douglas /* Touch surface information. Dimension is in hundredths of a mm, min and max
824f6fdf08SChase Douglas * are in units. */
834f6fdf08SChase Douglas #define MOUSE_DIMENSION_X (float)9056
844f6fdf08SChase Douglas #define MOUSE_MIN_X -1100
854f6fdf08SChase Douglas #define MOUSE_MAX_X 1258
864f6fdf08SChase Douglas #define MOUSE_RES_X ((MOUSE_MAX_X - MOUSE_MIN_X) / (MOUSE_DIMENSION_X / 100))
874f6fdf08SChase Douglas #define MOUSE_DIMENSION_Y (float)5152
884f6fdf08SChase Douglas #define MOUSE_MIN_Y -1589
894f6fdf08SChase Douglas #define MOUSE_MAX_Y 2047
904f6fdf08SChase Douglas #define MOUSE_RES_Y ((MOUSE_MAX_Y - MOUSE_MIN_Y) / (MOUSE_DIMENSION_Y / 100))
914f6fdf08SChase Douglas
924f6fdf08SChase Douglas #define TRACKPAD_DIMENSION_X (float)13000
934f6fdf08SChase Douglas #define TRACKPAD_MIN_X -2909
944f6fdf08SChase Douglas #define TRACKPAD_MAX_X 3167
954f6fdf08SChase Douglas #define TRACKPAD_RES_X \
964f6fdf08SChase Douglas ((TRACKPAD_MAX_X - TRACKPAD_MIN_X) / (TRACKPAD_DIMENSION_X / 100))
974f6fdf08SChase Douglas #define TRACKPAD_DIMENSION_Y (float)11000
984f6fdf08SChase Douglas #define TRACKPAD_MIN_Y -2456
994f6fdf08SChase Douglas #define TRACKPAD_MAX_Y 2565
1004f6fdf08SChase Douglas #define TRACKPAD_RES_Y \
1014f6fdf08SChase Douglas ((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
1024f6fdf08SChase Douglas
1039d7b1866SSean O'Brien #define TRACKPAD2_DIMENSION_X (float)16000
1049d7b1866SSean O'Brien #define TRACKPAD2_MIN_X -3678
1059d7b1866SSean O'Brien #define TRACKPAD2_MAX_X 3934
1069d7b1866SSean O'Brien #define TRACKPAD2_RES_X \
1079d7b1866SSean O'Brien ((TRACKPAD2_MAX_X - TRACKPAD2_MIN_X) / (TRACKPAD2_DIMENSION_X / 100))
1089d7b1866SSean O'Brien #define TRACKPAD2_DIMENSION_Y (float)11490
1099d7b1866SSean O'Brien #define TRACKPAD2_MIN_Y -2478
1109d7b1866SSean O'Brien #define TRACKPAD2_MAX_Y 2587
1119d7b1866SSean O'Brien #define TRACKPAD2_RES_Y \
1129d7b1866SSean O'Brien ((TRACKPAD2_MAX_Y - TRACKPAD2_MIN_Y) / (TRACKPAD2_DIMENSION_Y / 100))
1139d7b1866SSean O'Brien
114128537ceSMichael Poole /**
115128537ceSMichael Poole * struct magicmouse_sc - Tracks Magic Mouse-specific data.
116128537ceSMichael Poole * @input: Input device through which we report events.
117128537ceSMichael Poole * @quirks: Currently unused.
118128537ceSMichael Poole * @ntouches: Number of touches in most recent touch report.
119128537ceSMichael Poole * @scroll_accel: Number of consecutive scroll motions.
120128537ceSMichael Poole * @scroll_jiffies: Time of last scroll motion.
121128537ceSMichael Poole * @touches: Most recent data for a touch, indexed by tracking ID.
122128537ceSMichael Poole * @tracking_ids: Mapping of current touch input data to @touches.
123128537ceSMichael Poole */
124128537ceSMichael Poole struct magicmouse_sc {
125128537ceSMichael Poole struct input_dev *input;
126128537ceSMichael Poole unsigned long quirks;
127128537ceSMichael Poole
128128537ceSMichael Poole int ntouches;
129128537ceSMichael Poole int scroll_accel;
130128537ceSMichael Poole unsigned long scroll_jiffies;
131128537ceSMichael Poole
132128537ceSMichael Poole struct {
133128537ceSMichael Poole short x;
134128537ceSMichael Poole short y;
135c0426688SChase Douglas short scroll_x;
136128537ceSMichael Poole short scroll_y;
137d4b9f10aSJosé Expósito short scroll_x_hr;
138d4b9f10aSJosé Expósito short scroll_y_hr;
139128537ceSMichael Poole u8 size;
1409d60648cSJosé Expósito bool scroll_x_active;
1419d60648cSJosé Expósito bool scroll_y_active;
142128537ceSMichael Poole } touches[16];
143128537ceSMichael Poole int tracking_ids[16];
144c0dc5582SJohn Chen
145c0dc5582SJohn Chen struct hid_device *hdev;
146c0dc5582SJohn Chen struct delayed_work work;
1470b91b4e4SJosé Expósito struct timer_list battery_timer;
148128537ceSMichael Poole };
149128537ceSMichael Poole
magicmouse_firm_touch(struct magicmouse_sc * msc)150128537ceSMichael Poole static int magicmouse_firm_touch(struct magicmouse_sc *msc)
151128537ceSMichael Poole {
152128537ceSMichael Poole int touch = -1;
153128537ceSMichael Poole int ii;
154128537ceSMichael Poole
155128537ceSMichael Poole /* If there is only one "firm" touch, set touch to its
156128537ceSMichael Poole * tracking ID.
157128537ceSMichael Poole */
158128537ceSMichael Poole for (ii = 0; ii < msc->ntouches; ii++) {
159128537ceSMichael Poole int idx = msc->tracking_ids[ii];
160128537ceSMichael Poole if (msc->touches[idx].size < 8) {
161128537ceSMichael Poole /* Ignore this touch. */
162128537ceSMichael Poole } else if (touch >= 0) {
163128537ceSMichael Poole touch = -1;
164128537ceSMichael Poole break;
165128537ceSMichael Poole } else {
166128537ceSMichael Poole touch = idx;
167128537ceSMichael Poole }
168128537ceSMichael Poole }
169128537ceSMichael Poole
170128537ceSMichael Poole return touch;
171128537ceSMichael Poole }
172128537ceSMichael Poole
magicmouse_emit_buttons(struct magicmouse_sc * msc,int state)173128537ceSMichael Poole static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
174128537ceSMichael Poole {
175128537ceSMichael Poole int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 |
176128537ceSMichael Poole test_bit(BTN_RIGHT, msc->input->key) << 1 |
177128537ceSMichael Poole test_bit(BTN_MIDDLE, msc->input->key) << 2;
178128537ceSMichael Poole
179128537ceSMichael Poole if (emulate_3button) {
180128537ceSMichael Poole int id;
181128537ceSMichael Poole
182128537ceSMichael Poole /* If some button was pressed before, keep it held
183128537ceSMichael Poole * down. Otherwise, if there's exactly one firm
184128537ceSMichael Poole * touch, use that to override the mouse's guess.
185128537ceSMichael Poole */
186128537ceSMichael Poole if (state == 0) {
187128537ceSMichael Poole /* The button was released. */
188128537ceSMichael Poole } else if (last_state != 0) {
189128537ceSMichael Poole state = last_state;
190128537ceSMichael Poole } else if ((id = magicmouse_firm_touch(msc)) >= 0) {
191128537ceSMichael Poole int x = msc->touches[id].x;
192128537ceSMichael Poole if (x < middle_button_start)
193128537ceSMichael Poole state = 1;
194128537ceSMichael Poole else if (x > middle_button_stop)
195128537ceSMichael Poole state = 2;
196128537ceSMichael Poole else
197128537ceSMichael Poole state = 4;
198128537ceSMichael Poole } /* else: we keep the mouse's guess */
199128537ceSMichael Poole
200128537ceSMichael Poole input_report_key(msc->input, BTN_MIDDLE, state & 4);
201128537ceSMichael Poole }
202128537ceSMichael Poole
203128537ceSMichael Poole input_report_key(msc->input, BTN_LEFT, state & 1);
204128537ceSMichael Poole input_report_key(msc->input, BTN_RIGHT, state & 2);
205128537ceSMichael Poole
206128537ceSMichael Poole if (state != last_state)
2070b778e76SChase Douglas msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
208128537ceSMichael Poole }
209128537ceSMichael Poole
magicmouse_emit_touch(struct magicmouse_sc * msc,int raw_id,u8 * tdata)210128537ceSMichael Poole static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
211128537ceSMichael Poole {
212128537ceSMichael Poole struct input_dev *input = msc->input;
213a462230eSChase Douglas int id, x, y, size, orientation, touch_major, touch_minor, state, down;
2149d7b1866SSean O'Brien int pressure = 0;
215a462230eSChase Douglas
2162b0c086cSJohn Chen if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE ||
2172b0c086cSJohn Chen input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
218a462230eSChase Douglas id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
219a462230eSChase Douglas x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
220a462230eSChase Douglas y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
221a462230eSChase Douglas size = tdata[5] & 0x3f;
222a462230eSChase Douglas orientation = (tdata[6] >> 2) - 32;
223a462230eSChase Douglas touch_major = tdata[3];
224a462230eSChase Douglas touch_minor = tdata[4];
225a462230eSChase Douglas state = tdata[7] & TOUCH_STATE_MASK;
226a462230eSChase Douglas down = state != TOUCH_STATE_NONE;
227*6e8348ecSCallahan Kovacs } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
228*6e8348ecSCallahan Kovacs input->id.product ==
229*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
2309d7b1866SSean O'Brien id = tdata[8] & 0xf;
2319d7b1866SSean O'Brien x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
2329d7b1866SSean O'Brien y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
2339d7b1866SSean O'Brien size = tdata[6];
2349d7b1866SSean O'Brien orientation = (tdata[8] >> 5) - 4;
2359d7b1866SSean O'Brien touch_major = tdata[4];
2369d7b1866SSean O'Brien touch_minor = tdata[5];
2379d7b1866SSean O'Brien pressure = tdata[7];
2389d7b1866SSean O'Brien state = tdata[3] & 0xC0;
2399d7b1866SSean O'Brien down = state == 0x80;
240a462230eSChase Douglas } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
241a462230eSChase Douglas id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
242a462230eSChase Douglas x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
243a462230eSChase Douglas y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
244a462230eSChase Douglas size = tdata[6] & 0x3f;
245a462230eSChase Douglas orientation = (tdata[7] >> 2) - 32;
246a462230eSChase Douglas touch_major = tdata[4];
247a462230eSChase Douglas touch_minor = tdata[5];
248a462230eSChase Douglas state = tdata[8] & TOUCH_STATE_MASK;
249a462230eSChase Douglas down = state != TOUCH_STATE_NONE;
250a462230eSChase Douglas }
251128537ceSMichael Poole
252128537ceSMichael Poole /* Store tracking ID and other fields. */
253128537ceSMichael Poole msc->tracking_ids[raw_id] = id;
254128537ceSMichael Poole msc->touches[id].x = x;
255128537ceSMichael Poole msc->touches[id].y = y;
2566de048bfSChase Douglas msc->touches[id].size = size;
257128537ceSMichael Poole
258128537ceSMichael Poole /* If requested, emulate a scroll wheel by detecting small
259ef566d30SChase Douglas * vertical touch motions.
260128537ceSMichael Poole */
261*6e8348ecSCallahan Kovacs if (emulate_scroll_wheel &&
262*6e8348ecSCallahan Kovacs input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
263*6e8348ecSCallahan Kovacs input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
264128537ceSMichael Poole unsigned long now = jiffies;
265c0426688SChase Douglas int step_x = msc->touches[id].scroll_x - x;
266c0426688SChase Douglas int step_y = msc->touches[id].scroll_y - y;
267a1091118SClaudia Pellegrino int step_hr =
268a1091118SClaudia Pellegrino max_t(int,
269a1091118SClaudia Pellegrino ((64 - (int)scroll_speed) * msc->scroll_accel) /
270a1091118SClaudia Pellegrino SCROLL_HR_STEPS,
271a1091118SClaudia Pellegrino 1);
272d4b9f10aSJosé Expósito int step_x_hr = msc->touches[id].scroll_x_hr - x;
273d4b9f10aSJosé Expósito int step_y_hr = msc->touches[id].scroll_y_hr - y;
274128537ceSMichael Poole
275128537ceSMichael Poole /* Calculate and apply the scroll motion. */
2766de048bfSChase Douglas switch (state) {
277128537ceSMichael Poole case TOUCH_STATE_START:
278c0426688SChase Douglas msc->touches[id].scroll_x = x;
279128537ceSMichael Poole msc->touches[id].scroll_y = y;
280d4b9f10aSJosé Expósito msc->touches[id].scroll_x_hr = x;
281d4b9f10aSJosé Expósito msc->touches[id].scroll_y_hr = y;
2829d60648cSJosé Expósito msc->touches[id].scroll_x_active = false;
2839d60648cSJosé Expósito msc->touches[id].scroll_y_active = false;
2840b778e76SChase Douglas
2850b778e76SChase Douglas /* Reset acceleration after half a second. */
2860b778e76SChase Douglas if (scroll_acceleration && time_before(now,
2870b778e76SChase Douglas msc->scroll_jiffies + HZ / 2))
2880b778e76SChase Douglas msc->scroll_accel = max_t(int,
2890b778e76SChase Douglas msc->scroll_accel - 1, 1);
2900b778e76SChase Douglas else
2910b778e76SChase Douglas msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
2920b778e76SChase Douglas
293128537ceSMichael Poole break;
294128537ceSMichael Poole case TOUCH_STATE_DRAG:
295c0426688SChase Douglas step_x /= (64 - (int)scroll_speed) * msc->scroll_accel;
296c0426688SChase Douglas if (step_x != 0) {
297c0426688SChase Douglas msc->touches[id].scroll_x -= step_x *
2980b778e76SChase Douglas (64 - scroll_speed) * msc->scroll_accel;
299128537ceSMichael Poole msc->scroll_jiffies = now;
300c0426688SChase Douglas input_report_rel(input, REL_HWHEEL, -step_x);
301c0426688SChase Douglas }
302c0426688SChase Douglas
303c0426688SChase Douglas step_y /= (64 - (int)scroll_speed) * msc->scroll_accel;
304c0426688SChase Douglas if (step_y != 0) {
305c0426688SChase Douglas msc->touches[id].scroll_y -= step_y *
306c0426688SChase Douglas (64 - scroll_speed) * msc->scroll_accel;
307c0426688SChase Douglas msc->scroll_jiffies = now;
308c0426688SChase Douglas input_report_rel(input, REL_WHEEL, step_y);
309128537ceSMichael Poole }
310d4b9f10aSJosé Expósito
3119d60648cSJosé Expósito if (!msc->touches[id].scroll_x_active &&
3129d60648cSJosé Expósito abs(step_x_hr) > SCROLL_HR_THRESHOLD) {
3139d60648cSJosé Expósito msc->touches[id].scroll_x_active = true;
3149d60648cSJosé Expósito msc->touches[id].scroll_x_hr = x;
3159d60648cSJosé Expósito step_x_hr = 0;
3169d60648cSJosé Expósito }
3179d60648cSJosé Expósito
318d4b9f10aSJosé Expósito step_x_hr /= step_hr;
3199d60648cSJosé Expósito if (step_x_hr != 0 &&
3209d60648cSJosé Expósito msc->touches[id].scroll_x_active) {
321d4b9f10aSJosé Expósito msc->touches[id].scroll_x_hr -= step_x_hr *
322d4b9f10aSJosé Expósito step_hr;
323d4b9f10aSJosé Expósito input_report_rel(input,
324d4b9f10aSJosé Expósito REL_HWHEEL_HI_RES,
325d4b9f10aSJosé Expósito -step_x_hr * SCROLL_HR_MULT);
326d4b9f10aSJosé Expósito }
327d4b9f10aSJosé Expósito
3289d60648cSJosé Expósito if (!msc->touches[id].scroll_y_active &&
3299d60648cSJosé Expósito abs(step_y_hr) > SCROLL_HR_THRESHOLD) {
3309d60648cSJosé Expósito msc->touches[id].scroll_y_active = true;
3319d60648cSJosé Expósito msc->touches[id].scroll_y_hr = y;
3329d60648cSJosé Expósito step_y_hr = 0;
3339d60648cSJosé Expósito }
3349d60648cSJosé Expósito
335d4b9f10aSJosé Expósito step_y_hr /= step_hr;
3369d60648cSJosé Expósito if (step_y_hr != 0 &&
3379d60648cSJosé Expósito msc->touches[id].scroll_y_active) {
338d4b9f10aSJosé Expósito msc->touches[id].scroll_y_hr -= step_y_hr *
339d4b9f10aSJosé Expósito step_hr;
340d4b9f10aSJosé Expósito input_report_rel(input,
341d4b9f10aSJosé Expósito REL_WHEEL_HI_RES,
342d4b9f10aSJosé Expósito step_y_hr * SCROLL_HR_MULT);
343d4b9f10aSJosé Expósito }
344128537ceSMichael Poole break;
345128537ceSMichael Poole }
346128537ceSMichael Poole }
347128537ceSMichael Poole
348a6d1bc1dSYufeng Shen if (down)
349a462230eSChase Douglas msc->ntouches++;
350a6d1bc1dSYufeng Shen
351a6d1bc1dSYufeng Shen input_mt_slot(input, id);
352a6d1bc1dSYufeng Shen input_mt_report_slot_state(input, MT_TOOL_FINGER, down);
353a462230eSChase Douglas
354128537ceSMichael Poole /* Generate the input events for this touch. */
3556264307eSYufeng Shen if (down) {
356921990b7SHenrik Rydberg input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
357921990b7SHenrik Rydberg input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
3582d9ca4e9SHenrik Rydberg input_report_abs(input, ABS_MT_ORIENTATION, -orientation);
359128537ceSMichael Poole input_report_abs(input, ABS_MT_POSITION_X, x);
360128537ceSMichael Poole input_report_abs(input, ABS_MT_POSITION_Y, y);
361128537ceSMichael Poole
362*6e8348ecSCallahan Kovacs if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
363*6e8348ecSCallahan Kovacs input->id.product ==
364*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC)
3659d7b1866SSean O'Brien input_report_abs(input, ABS_MT_PRESSURE, pressure);
3669d7b1866SSean O'Brien
367a462230eSChase Douglas if (report_undeciphered) {
3682b0c086cSJohn Chen if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE ||
3692b0c086cSJohn Chen input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2)
370128537ceSMichael Poole input_event(input, EV_MSC, MSC_RAW, tdata[7]);
3719d7b1866SSean O'Brien else if (input->id.product !=
372*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
373*6e8348ecSCallahan Kovacs input->id.product !=
374*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC)
375a462230eSChase Douglas input_event(input, EV_MSC, MSC_RAW, tdata[8]);
376a462230eSChase Douglas }
377128537ceSMichael Poole }
378128537ceSMichael Poole }
379128537ceSMichael Poole
magicmouse_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)380128537ceSMichael Poole static int magicmouse_raw_event(struct hid_device *hdev,
381128537ceSMichael Poole struct hid_report *report, u8 *data, int size)
382128537ceSMichael Poole {
383128537ceSMichael Poole struct magicmouse_sc *msc = hid_get_drvdata(hdev);
384128537ceSMichael Poole struct input_dev *input = msc->input;
385a462230eSChase Douglas int x = 0, y = 0, ii, clicks = 0, npoints;
386128537ceSMichael Poole
387128537ceSMichael Poole switch (data[0]) {
388a462230eSChase Douglas case TRACKPAD_REPORT_ID:
3899d7b1866SSean O'Brien case TRACKPAD2_BT_REPORT_ID:
390a462230eSChase Douglas /* Expect four bytes of prefix, and N*9 bytes of touch data. */
391a462230eSChase Douglas if (size < 4 || ((size - 4) % 9) != 0)
392a462230eSChase Douglas return 0;
393a462230eSChase Douglas npoints = (size - 4) / 9;
394c54def7bSJiri Kosina if (npoints > 15) {
395c54def7bSJiri Kosina hid_warn(hdev, "invalid size value (%d) for TRACKPAD_REPORT_ID\n",
396c54def7bSJiri Kosina size);
397c54def7bSJiri Kosina return 0;
398c54def7bSJiri Kosina }
399a462230eSChase Douglas msc->ntouches = 0;
400a462230eSChase Douglas for (ii = 0; ii < npoints; ii++)
401a462230eSChase Douglas magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
402a462230eSChase Douglas
403a462230eSChase Douglas clicks = data[1];
404a462230eSChase Douglas
405a462230eSChase Douglas /* The following bits provide a device specific timestamp. They
406a462230eSChase Douglas * are unused here.
407a462230eSChase Douglas *
408a462230eSChase Douglas * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
409a462230eSChase Douglas */
410a462230eSChase Douglas break;
4119d7b1866SSean O'Brien case TRACKPAD2_USB_REPORT_ID:
4129d7b1866SSean O'Brien /* Expect twelve bytes of prefix and N*9 bytes of touch data. */
4139d7b1866SSean O'Brien if (size < 12 || ((size - 12) % 9) != 0)
4149d7b1866SSean O'Brien return 0;
4159d7b1866SSean O'Brien npoints = (size - 12) / 9;
4169d7b1866SSean O'Brien if (npoints > 15) {
4179d7b1866SSean O'Brien hid_warn(hdev, "invalid size value (%d) for TRACKPAD2_USB_REPORT_ID\n",
4189d7b1866SSean O'Brien size);
4199d7b1866SSean O'Brien return 0;
4209d7b1866SSean O'Brien }
4219d7b1866SSean O'Brien msc->ntouches = 0;
4229d7b1866SSean O'Brien for (ii = 0; ii < npoints; ii++)
4239d7b1866SSean O'Brien magicmouse_emit_touch(msc, ii, data + ii * 9 + 12);
4249d7b1866SSean O'Brien
4259d7b1866SSean O'Brien clicks = data[1];
4269d7b1866SSean O'Brien break;
427a462230eSChase Douglas case MOUSE_REPORT_ID:
428128537ceSMichael Poole /* Expect six bytes of prefix, and N*8 bytes of touch data. */
429128537ceSMichael Poole if (size < 6 || ((size - 6) % 8) != 0)
430128537ceSMichael Poole return 0;
4310228db70SChase Douglas npoints = (size - 6) / 8;
432c54def7bSJiri Kosina if (npoints > 15) {
433c54def7bSJiri Kosina hid_warn(hdev, "invalid size value (%d) for MOUSE_REPORT_ID\n",
434c54def7bSJiri Kosina size);
435c54def7bSJiri Kosina return 0;
436c54def7bSJiri Kosina }
4370228db70SChase Douglas msc->ntouches = 0;
4380228db70SChase Douglas for (ii = 0; ii < npoints; ii++)
439128537ceSMichael Poole magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
440e3612e86SChase Douglas
441128537ceSMichael Poole /* When emulating three-button mode, it is important
442128537ceSMichael Poole * to have the current touch information before
443128537ceSMichael Poole * generating a click event.
444128537ceSMichael Poole */
4457d876c05SMichael Poole x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
4467d876c05SMichael Poole y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
447128537ceSMichael Poole clicks = data[3];
448c61b7ceeSChase Douglas
449c61b7ceeSChase Douglas /* The following bits provide a device specific timestamp. They
450c61b7ceeSChase Douglas * are unused here.
451c61b7ceeSChase Douglas *
452c61b7ceeSChase Douglas * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
453c61b7ceeSChase Douglas */
454128537ceSMichael Poole break;
4552b0c086cSJohn Chen case MOUSE2_REPORT_ID:
4562b0c086cSJohn Chen /* Size is either 8 or (14 + 8 * N) */
4572b0c086cSJohn Chen if (size != 8 && (size < 14 || (size - 14) % 8 != 0))
4582b0c086cSJohn Chen return 0;
4592b0c086cSJohn Chen npoints = (size - 14) / 8;
4602b0c086cSJohn Chen if (npoints > 15) {
4612b0c086cSJohn Chen hid_warn(hdev, "invalid size value (%d) for MOUSE2_REPORT_ID\n",
4622b0c086cSJohn Chen size);
4632b0c086cSJohn Chen return 0;
4642b0c086cSJohn Chen }
4652b0c086cSJohn Chen msc->ntouches = 0;
4662b0c086cSJohn Chen for (ii = 0; ii < npoints; ii++)
4672b0c086cSJohn Chen magicmouse_emit_touch(msc, ii, data + ii * 8 + 14);
4682b0c086cSJohn Chen
4692b0c086cSJohn Chen /* When emulating three-button mode, it is important
4702b0c086cSJohn Chen * to have the current touch information before
4712b0c086cSJohn Chen * generating a click event.
4722b0c086cSJohn Chen */
4732b0c086cSJohn Chen x = (int)((data[3] << 24) | (data[2] << 16)) >> 16;
4742b0c086cSJohn Chen y = (int)((data[5] << 24) | (data[4] << 16)) >> 16;
4752b0c086cSJohn Chen clicks = data[1];
4762b0c086cSJohn Chen
4772b0c086cSJohn Chen /* The following bits provide a device specific timestamp. They
4782b0c086cSJohn Chen * are unused here.
4792b0c086cSJohn Chen *
4802b0c086cSJohn Chen * ts = data[11] >> 6 | data[12] << 2 | data[13] << 10;
4812b0c086cSJohn Chen */
4822b0c086cSJohn Chen break;
483a462230eSChase Douglas case DOUBLE_REPORT_ID:
484a462230eSChase Douglas /* Sometimes the trackpad sends two touch reports in one
485a462230eSChase Douglas * packet.
486a462230eSChase Douglas */
487a462230eSChase Douglas magicmouse_raw_event(hdev, report, data + 2, data[1]);
488a462230eSChase Douglas magicmouse_raw_event(hdev, report, data + 2 + data[1],
489a462230eSChase Douglas size - 2 - data[1]);
490bb5f0c85SJosé Expósito return 0;
491128537ceSMichael Poole default:
492128537ceSMichael Poole return 0;
493128537ceSMichael Poole }
494128537ceSMichael Poole
4952b0c086cSJohn Chen if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE ||
4962b0c086cSJohn Chen input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
497128537ceSMichael Poole magicmouse_emit_buttons(msc, clicks & 3);
498128537ceSMichael Poole input_report_rel(input, REL_X, x);
499128537ceSMichael Poole input_report_rel(input, REL_Y, y);
500*6e8348ecSCallahan Kovacs } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
501*6e8348ecSCallahan Kovacs input->id.product ==
502*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
5039d7b1866SSean O'Brien input_mt_sync_frame(input);
5049d7b1866SSean O'Brien input_report_key(input, BTN_MOUSE, clicks & 1);
505a462230eSChase Douglas } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
506a462230eSChase Douglas input_report_key(input, BTN_MOUSE, clicks & 1);
507a6d1bc1dSYufeng Shen input_mt_report_pointer_emulation(input, true);
508a462230eSChase Douglas }
509a462230eSChase Douglas
510128537ceSMichael Poole input_sync(input);
511128537ceSMichael Poole return 1;
512128537ceSMichael Poole }
513128537ceSMichael Poole
magicmouse_event(struct hid_device * hdev,struct hid_field * field,struct hid_usage * usage,__s32 value)5143dcc5f7bSJohn Chen static int magicmouse_event(struct hid_device *hdev, struct hid_field *field,
5153dcc5f7bSJohn Chen struct hid_usage *usage, __s32 value)
5163dcc5f7bSJohn Chen {
5173dcc5f7bSJohn Chen struct magicmouse_sc *msc = hid_get_drvdata(hdev);
5183dcc5f7bSJohn Chen if (msc->input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
5193dcc5f7bSJohn Chen field->report->id == MOUSE2_REPORT_ID) {
5203dcc5f7bSJohn Chen /*
5213dcc5f7bSJohn Chen * magic_mouse_raw_event has done all the work. Skip hidinput.
5223dcc5f7bSJohn Chen *
5233dcc5f7bSJohn Chen * Specifically, hidinput may modify BTN_LEFT and BTN_RIGHT,
5243dcc5f7bSJohn Chen * breaking emulate_3button.
5253dcc5f7bSJohn Chen */
5263dcc5f7bSJohn Chen return 1;
5273dcc5f7bSJohn Chen }
5283dcc5f7bSJohn Chen return 0;
5293dcc5f7bSJohn Chen }
5303dcc5f7bSJohn Chen
magicmouse_setup_input(struct input_dev * input,struct hid_device * hdev)531a6d1bc1dSYufeng Shen static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
532128537ceSMichael Poole {
533a6d1bc1dSYufeng Shen int error;
5349d7b1866SSean O'Brien int mt_flags = 0;
535a6d1bc1dSYufeng Shen
53671b38bd4SMichael Poole __set_bit(EV_KEY, input->evbit);
537a462230eSChase Douglas
5382b0c086cSJohn Chen if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE ||
5392b0c086cSJohn Chen input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
54071b38bd4SMichael Poole __set_bit(BTN_LEFT, input->keybit);
54171b38bd4SMichael Poole __set_bit(BTN_RIGHT, input->keybit);
542128537ceSMichael Poole if (emulate_3button)
54371b38bd4SMichael Poole __set_bit(BTN_MIDDLE, input->keybit);
544128537ceSMichael Poole
54571b38bd4SMichael Poole __set_bit(EV_REL, input->evbit);
54671b38bd4SMichael Poole __set_bit(REL_X, input->relbit);
54771b38bd4SMichael Poole __set_bit(REL_Y, input->relbit);
548c0426688SChase Douglas if (emulate_scroll_wheel) {
54971b38bd4SMichael Poole __set_bit(REL_WHEEL, input->relbit);
550c0426688SChase Douglas __set_bit(REL_HWHEEL, input->relbit);
551d4b9f10aSJosé Expósito __set_bit(REL_WHEEL_HI_RES, input->relbit);
552d4b9f10aSJosé Expósito __set_bit(REL_HWHEEL_HI_RES, input->relbit);
553c0426688SChase Douglas }
554*6e8348ecSCallahan Kovacs } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
555*6e8348ecSCallahan Kovacs input->id.product ==
556*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
5570aa45fccSJosé Expósito /* If the trackpad has been connected to a Mac, the name is
5580aa45fccSJosé Expósito * automatically personalized, e.g., "José Expósito's Trackpad".
5590aa45fccSJosé Expósito * When connected through Bluetooth, the personalized name is
5600aa45fccSJosé Expósito * reported, however, when connected through USB the generic
5610aa45fccSJosé Expósito * name is reported.
5620aa45fccSJosé Expósito * Set the device name to ensure the same driver settings get
5630aa45fccSJosé Expósito * loaded, whether connected through bluetooth or USB.
5649d7b1866SSean O'Brien */
5655768701eSJosé Expósito if (hdev->vendor == BT_VENDOR_ID_APPLE) {
5665768701eSJosé Expósito if (input->id.version == TRACKPAD2_2021_BT_VERSION)
5675768701eSJosé Expósito input->name = "Apple Inc. Magic Trackpad";
5685768701eSJosé Expósito else
5699d7b1866SSean O'Brien input->name = "Apple Inc. Magic Trackpad 2";
5705768701eSJosé Expósito } else { /* USB_VENDOR_ID_APPLE */
5710aa45fccSJosé Expósito input->name = hdev->name;
5725768701eSJosé Expósito }
5739d7b1866SSean O'Brien
5749d7b1866SSean O'Brien __clear_bit(EV_MSC, input->evbit);
5759d7b1866SSean O'Brien __clear_bit(BTN_0, input->keybit);
5769d7b1866SSean O'Brien __clear_bit(BTN_RIGHT, input->keybit);
5779d7b1866SSean O'Brien __clear_bit(BTN_MIDDLE, input->keybit);
5789d7b1866SSean O'Brien __set_bit(BTN_MOUSE, input->keybit);
5799d7b1866SSean O'Brien __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
5809d7b1866SSean O'Brien __set_bit(BTN_TOOL_FINGER, input->keybit);
5819d7b1866SSean O'Brien
5829d7b1866SSean O'Brien mt_flags = INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
5839d7b1866SSean O'Brien INPUT_MT_TRACK;
584a462230eSChase Douglas } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
585bca62142SDaniel van Vugt /* input->keybit is initialized with incorrect button info
586bca62142SDaniel van Vugt * for Magic Trackpad. There really is only one physical
587bca62142SDaniel van Vugt * button (BTN_LEFT == BTN_MOUSE). Make sure we don't
588bca62142SDaniel van Vugt * advertise buttons that don't exist...
589bca62142SDaniel van Vugt */
590bca62142SDaniel van Vugt __clear_bit(BTN_RIGHT, input->keybit);
591bca62142SDaniel van Vugt __clear_bit(BTN_MIDDLE, input->keybit);
592a462230eSChase Douglas __set_bit(BTN_MOUSE, input->keybit);
593a462230eSChase Douglas __set_bit(BTN_TOOL_FINGER, input->keybit);
594a462230eSChase Douglas __set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
595a462230eSChase Douglas __set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
596a462230eSChase Douglas __set_bit(BTN_TOOL_QUADTAP, input->keybit);
597a6d1bc1dSYufeng Shen __set_bit(BTN_TOOL_QUINTTAP, input->keybit);
598a462230eSChase Douglas __set_bit(BTN_TOUCH, input->keybit);
599503f7d53SChase Douglas __set_bit(INPUT_PROP_POINTER, input->propbit);
60053145c2eSDaniel Stone __set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
60153145c2eSDaniel Stone }
60253145c2eSDaniel Stone
6036264307eSYufeng Shen
60471b38bd4SMichael Poole __set_bit(EV_ABS, input->evbit);
605128537ceSMichael Poole
6069d7b1866SSean O'Brien error = input_mt_init_slots(input, 16, mt_flags);
607a6d1bc1dSYufeng Shen if (error)
608a6d1bc1dSYufeng Shen return error;
609e75561b3SYufeng Shen input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
610e75561b3SYufeng Shen 4, 0);
611e75561b3SYufeng Shen input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
612e75561b3SYufeng Shen 4, 0);
613a462230eSChase Douglas
614128537ceSMichael Poole /* Note: Touch Y position from the device is inverted relative
615128537ceSMichael Poole * to how pointer motion is reported (and relative to how USB
616128537ceSMichael Poole * HID recommends the coordinates work). This driver keeps
617128537ceSMichael Poole * the origin at the same position, and just uses the additive
618128537ceSMichael Poole * inverse of the reported Y.
619128537ceSMichael Poole */
6202b0c086cSJohn Chen if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE ||
6212b0c086cSJohn Chen input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
6229d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
6234f6fdf08SChase Douglas input_set_abs_params(input, ABS_MT_POSITION_X,
6244f6fdf08SChase Douglas MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
6254f6fdf08SChase Douglas input_set_abs_params(input, ABS_MT_POSITION_Y,
6264f6fdf08SChase Douglas MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
6274f6fdf08SChase Douglas
6284f6fdf08SChase Douglas input_abs_set_res(input, ABS_MT_POSITION_X,
6294f6fdf08SChase Douglas MOUSE_RES_X);
6304f6fdf08SChase Douglas input_abs_set_res(input, ABS_MT_POSITION_Y,
6314f6fdf08SChase Douglas MOUSE_RES_Y);
632*6e8348ecSCallahan Kovacs } else if (input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
633*6e8348ecSCallahan Kovacs input->id.product ==
634*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
6359d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_PRESSURE, 0, 253, 0, 0);
6369d7b1866SSean O'Brien input_set_abs_params(input, ABS_PRESSURE, 0, 253, 0, 0);
6379d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_ORIENTATION, -3, 4, 0, 0);
6389d7b1866SSean O'Brien input_set_abs_params(input, ABS_X, TRACKPAD2_MIN_X,
6399d7b1866SSean O'Brien TRACKPAD2_MAX_X, 0, 0);
6409d7b1866SSean O'Brien input_set_abs_params(input, ABS_Y, TRACKPAD2_MIN_Y,
6419d7b1866SSean O'Brien TRACKPAD2_MAX_Y, 0, 0);
6429d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_POSITION_X,
6439d7b1866SSean O'Brien TRACKPAD2_MIN_X, TRACKPAD2_MAX_X, 0, 0);
6449d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_POSITION_Y,
6459d7b1866SSean O'Brien TRACKPAD2_MIN_Y, TRACKPAD2_MAX_Y, 0, 0);
6469d7b1866SSean O'Brien
6479d7b1866SSean O'Brien input_abs_set_res(input, ABS_X, TRACKPAD2_RES_X);
6489d7b1866SSean O'Brien input_abs_set_res(input, ABS_Y, TRACKPAD2_RES_Y);
6499d7b1866SSean O'Brien input_abs_set_res(input, ABS_MT_POSITION_X, TRACKPAD2_RES_X);
6509d7b1866SSean O'Brien input_abs_set_res(input, ABS_MT_POSITION_Y, TRACKPAD2_RES_Y);
651a462230eSChase Douglas } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
6529d7b1866SSean O'Brien input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
6534f6fdf08SChase Douglas input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
6544f6fdf08SChase Douglas TRACKPAD_MAX_X, 4, 0);
6554f6fdf08SChase Douglas input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
6564f6fdf08SChase Douglas TRACKPAD_MAX_Y, 4, 0);
6574f6fdf08SChase Douglas input_set_abs_params(input, ABS_MT_POSITION_X,
6584f6fdf08SChase Douglas TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
6594f6fdf08SChase Douglas input_set_abs_params(input, ABS_MT_POSITION_Y,
6604f6fdf08SChase Douglas TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
6614f6fdf08SChase Douglas
6624f6fdf08SChase Douglas input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
6634f6fdf08SChase Douglas input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
6644f6fdf08SChase Douglas input_abs_set_res(input, ABS_MT_POSITION_X,
6654f6fdf08SChase Douglas TRACKPAD_RES_X);
6664f6fdf08SChase Douglas input_abs_set_res(input, ABS_MT_POSITION_Y,
6674f6fdf08SChase Douglas TRACKPAD_RES_Y);
668a462230eSChase Douglas }
669cc5e0f08SChase Douglas
670cc5e0f08SChase Douglas input_set_events_per_packet(input, 60);
671128537ceSMichael Poole
6729d7b1866SSean O'Brien if (report_undeciphered &&
673*6e8348ecSCallahan Kovacs input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
674*6e8348ecSCallahan Kovacs input->id.product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
67571b38bd4SMichael Poole __set_bit(EV_MSC, input->evbit);
67671b38bd4SMichael Poole __set_bit(MSC_RAW, input->mscbit);
677128537ceSMichael Poole }
678a6d1bc1dSYufeng Shen
6796363d206SDmitry Torokhov /*
6806363d206SDmitry Torokhov * hid-input may mark device as using autorepeat, but neither
6816363d206SDmitry Torokhov * the trackpad, nor the mouse actually want it.
6826363d206SDmitry Torokhov */
6836363d206SDmitry Torokhov __clear_bit(EV_REP, input->evbit);
6846363d206SDmitry Torokhov
685a6d1bc1dSYufeng Shen return 0;
686128537ceSMichael Poole }
687128537ceSMichael Poole
magicmouse_input_mapping(struct hid_device * hdev,struct hid_input * hi,struct hid_field * field,struct hid_usage * usage,unsigned long ** bit,int * max)68864eb105dSMichael Poole static int magicmouse_input_mapping(struct hid_device *hdev,
68964eb105dSMichael Poole struct hid_input *hi, struct hid_field *field,
69064eb105dSMichael Poole struct hid_usage *usage, unsigned long **bit, int *max)
69164eb105dSMichael Poole {
69264eb105dSMichael Poole struct magicmouse_sc *msc = hid_get_drvdata(hdev);
69364eb105dSMichael Poole
69464eb105dSMichael Poole if (!msc->input)
69564eb105dSMichael Poole msc->input = hi->input;
69664eb105dSMichael Poole
6976a66bbd6SChase Douglas /* Magic Trackpad does not give relative data after switching to MT */
6989d7b1866SSean O'Brien if ((hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD ||
699*6e8348ecSCallahan Kovacs hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
700*6e8348ecSCallahan Kovacs hi->input->id.product ==
701*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) &&
7026a66bbd6SChase Douglas field->flags & HID_MAIN_ITEM_RELATIVE)
7036a66bbd6SChase Douglas return -1;
7046a66bbd6SChase Douglas
70564eb105dSMichael Poole return 0;
70664eb105dSMichael Poole }
70764eb105dSMichael Poole
magicmouse_input_configured(struct hid_device * hdev,struct hid_input * hi)7089154301aSDmitry Torokhov static int magicmouse_input_configured(struct hid_device *hdev,
709f1a9a149SBenjamin Tissoires struct hid_input *hi)
710f1a9a149SBenjamin Tissoires
711f1a9a149SBenjamin Tissoires {
712f1a9a149SBenjamin Tissoires struct magicmouse_sc *msc = hid_get_drvdata(hdev);
7139154301aSDmitry Torokhov int ret;
714f1a9a149SBenjamin Tissoires
7159154301aSDmitry Torokhov ret = magicmouse_setup_input(msc->input, hdev);
716f1a9a149SBenjamin Tissoires if (ret) {
717f1a9a149SBenjamin Tissoires hid_err(hdev, "magicmouse setup input failed (%d)\n", ret);
718f1a9a149SBenjamin Tissoires /* clean msc->input to notify probe() of the failure */
719f1a9a149SBenjamin Tissoires msc->input = NULL;
7209154301aSDmitry Torokhov return ret;
721f1a9a149SBenjamin Tissoires }
7229154301aSDmitry Torokhov
7239154301aSDmitry Torokhov return 0;
724f1a9a149SBenjamin Tissoires }
725f1a9a149SBenjamin Tissoires
magicmouse_enable_multitouch(struct hid_device * hdev)726c0dc5582SJohn Chen static int magicmouse_enable_multitouch(struct hid_device *hdev)
727128537ceSMichael Poole {
7289d7b1866SSean O'Brien const u8 *feature;
7299d7b1866SSean O'Brien const u8 feature_mt[] = { 0xD7, 0x01 };
7302b0c086cSJohn Chen const u8 feature_mt_mouse2[] = { 0xF1, 0x02, 0x01 };
7319d7b1866SSean O'Brien const u8 feature_mt_trackpad2_usb[] = { 0x02, 0x01 };
7329d7b1866SSean O'Brien const u8 feature_mt_trackpad2_bt[] = { 0xF1, 0x02, 0x01 };
733b7a87ad6SBenjamin Tissoires u8 *buf;
734c0dc5582SJohn Chen int ret;
735c0dc5582SJohn Chen int feature_size;
736c0dc5582SJohn Chen
737*6e8348ecSCallahan Kovacs if (hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
738*6e8348ecSCallahan Kovacs hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
739c0dc5582SJohn Chen if (hdev->vendor == BT_VENDOR_ID_APPLE) {
740c0dc5582SJohn Chen feature_size = sizeof(feature_mt_trackpad2_bt);
741c0dc5582SJohn Chen feature = feature_mt_trackpad2_bt;
742c0dc5582SJohn Chen } else { /* USB_VENDOR_ID_APPLE */
743c0dc5582SJohn Chen feature_size = sizeof(feature_mt_trackpad2_usb);
744c0dc5582SJohn Chen feature = feature_mt_trackpad2_usb;
745c0dc5582SJohn Chen }
746c0dc5582SJohn Chen } else if (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
747c0dc5582SJohn Chen feature_size = sizeof(feature_mt_mouse2);
748c0dc5582SJohn Chen feature = feature_mt_mouse2;
749c0dc5582SJohn Chen } else {
750c0dc5582SJohn Chen feature_size = sizeof(feature_mt);
751c0dc5582SJohn Chen feature = feature_mt;
752c0dc5582SJohn Chen }
753c0dc5582SJohn Chen
754c0dc5582SJohn Chen buf = kmemdup(feature, feature_size, GFP_KERNEL);
755c0dc5582SJohn Chen if (!buf)
756c0dc5582SJohn Chen return -ENOMEM;
757c0dc5582SJohn Chen
758c0dc5582SJohn Chen ret = hid_hw_raw_request(hdev, buf[0], buf, feature_size,
759c0dc5582SJohn Chen HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
760c0dc5582SJohn Chen kfree(buf);
761c0dc5582SJohn Chen return ret;
762c0dc5582SJohn Chen }
763c0dc5582SJohn Chen
magicmouse_enable_mt_work(struct work_struct * work)764c0dc5582SJohn Chen static void magicmouse_enable_mt_work(struct work_struct *work)
765c0dc5582SJohn Chen {
766c0dc5582SJohn Chen struct magicmouse_sc *msc =
767c0dc5582SJohn Chen container_of(work, struct magicmouse_sc, work.work);
768c0dc5582SJohn Chen int ret;
769c0dc5582SJohn Chen
770c0dc5582SJohn Chen ret = magicmouse_enable_multitouch(msc->hdev);
771c0dc5582SJohn Chen if (ret < 0)
772c0dc5582SJohn Chen hid_err(msc->hdev, "unable to request touch data (%d)\n", ret);
773c0dc5582SJohn Chen }
774c0dc5582SJohn Chen
magicmouse_fetch_battery(struct hid_device * hdev)7750b91b4e4SJosé Expósito static int magicmouse_fetch_battery(struct hid_device *hdev)
7760b91b4e4SJosé Expósito {
7770b91b4e4SJosé Expósito #ifdef CONFIG_HID_BATTERY_STRENGTH
7780b91b4e4SJosé Expósito struct hid_report_enum *report_enum;
7790b91b4e4SJosé Expósito struct hid_report *report;
7800b91b4e4SJosé Expósito
7810b91b4e4SJosé Expósito if (!hdev->battery || hdev->vendor != USB_VENDOR_ID_APPLE ||
7820b91b4e4SJosé Expósito (hdev->product != USB_DEVICE_ID_APPLE_MAGICMOUSE2 &&
783*6e8348ecSCallahan Kovacs hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 &&
784*6e8348ecSCallahan Kovacs hdev->product != USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC))
7850b91b4e4SJosé Expósito return -1;
7860b91b4e4SJosé Expósito
7870b91b4e4SJosé Expósito report_enum = &hdev->report_enum[hdev->battery_report_type];
7880b91b4e4SJosé Expósito report = report_enum->report_id_hash[hdev->battery_report_id];
7890b91b4e4SJosé Expósito
7900b91b4e4SJosé Expósito if (!report || report->maxfield < 1)
7910b91b4e4SJosé Expósito return -1;
7920b91b4e4SJosé Expósito
7930b91b4e4SJosé Expósito if (hdev->battery_capacity == hdev->battery_max)
7940b91b4e4SJosé Expósito return -1;
7950b91b4e4SJosé Expósito
7960b91b4e4SJosé Expósito hid_hw_request(hdev, report, HID_REQ_GET_REPORT);
7970b91b4e4SJosé Expósito return 0;
7980b91b4e4SJosé Expósito #else
7990b91b4e4SJosé Expósito return -1;
8000b91b4e4SJosé Expósito #endif
8010b91b4e4SJosé Expósito }
8020b91b4e4SJosé Expósito
magicmouse_battery_timer_tick(struct timer_list * t)8030b91b4e4SJosé Expósito static void magicmouse_battery_timer_tick(struct timer_list *t)
8040b91b4e4SJosé Expósito {
8050b91b4e4SJosé Expósito struct magicmouse_sc *msc = from_timer(msc, t, battery_timer);
8060b91b4e4SJosé Expósito struct hid_device *hdev = msc->hdev;
8070b91b4e4SJosé Expósito
8080b91b4e4SJosé Expósito if (magicmouse_fetch_battery(hdev) == 0) {
8090b91b4e4SJosé Expósito mod_timer(&msc->battery_timer,
8100b91b4e4SJosé Expósito jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
8110b91b4e4SJosé Expósito }
8120b91b4e4SJosé Expósito }
8130b91b4e4SJosé Expósito
magicmouse_probe(struct hid_device * hdev,const struct hid_device_id * id)814c0dc5582SJohn Chen static int magicmouse_probe(struct hid_device *hdev,
815c0dc5582SJohn Chen const struct hid_device_id *id)
816c0dc5582SJohn Chen {
817128537ceSMichael Poole struct magicmouse_sc *msc;
818128537ceSMichael Poole struct hid_report *report;
819128537ceSMichael Poole int ret;
8209d7b1866SSean O'Brien
821abf832bfSBenjamin Tissoires msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
822128537ceSMichael Poole if (msc == NULL) {
8234291ee30SJoe Perches hid_err(hdev, "can't alloc magicmouse descriptor\n");
824128537ceSMichael Poole return -ENOMEM;
825128537ceSMichael Poole }
826128537ceSMichael Poole
8270b778e76SChase Douglas msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
828c0dc5582SJohn Chen msc->hdev = hdev;
829c0dc5582SJohn Chen INIT_DEFERRABLE_WORK(&msc->work, magicmouse_enable_mt_work);
8300b778e76SChase Douglas
831128537ceSMichael Poole msc->quirks = id->driver_data;
832128537ceSMichael Poole hid_set_drvdata(hdev, msc);
833128537ceSMichael Poole
834128537ceSMichael Poole ret = hid_parse(hdev);
835128537ceSMichael Poole if (ret) {
8364291ee30SJoe Perches hid_err(hdev, "magicmouse hid parse failed\n");
837abf832bfSBenjamin Tissoires return ret;
838128537ceSMichael Poole }
839128537ceSMichael Poole
84023d02116SJiri Kosina ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
841128537ceSMichael Poole if (ret) {
8424291ee30SJoe Perches hid_err(hdev, "magicmouse hw start failed\n");
843abf832bfSBenjamin Tissoires return ret;
844128537ceSMichael Poole }
845128537ceSMichael Poole
8460b91b4e4SJosé Expósito timer_setup(&msc->battery_timer, magicmouse_battery_timer_tick, 0);
8470b91b4e4SJosé Expósito mod_timer(&msc->battery_timer,
8480b91b4e4SJosé Expósito jiffies + msecs_to_jiffies(USB_BATTERY_TIMEOUT_MS));
8490b91b4e4SJosé Expósito magicmouse_fetch_battery(hdev);
8500b91b4e4SJosé Expósito
8510b91b4e4SJosé Expósito if (id->vendor == USB_VENDOR_ID_APPLE &&
8520b91b4e4SJosé Expósito (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
853*6e8348ecSCallahan Kovacs ((id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
854*6e8348ecSCallahan Kovacs id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) &&
855*6e8348ecSCallahan Kovacs hdev->type != HID_TYPE_USBMOUSE)))
8560b91b4e4SJosé Expósito return 0;
8570b91b4e4SJosé Expósito
858f1a9a149SBenjamin Tissoires if (!msc->input) {
859f1a9a149SBenjamin Tissoires hid_err(hdev, "magicmouse input not registered\n");
860f1a9a149SBenjamin Tissoires ret = -ENOMEM;
861a6d1bc1dSYufeng Shen goto err_stop_hw;
862a6d1bc1dSYufeng Shen }
86323d02116SJiri Kosina
864a462230eSChase Douglas if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
865a462230eSChase Douglas report = hid_register_report(hdev, HID_INPUT_REPORT,
866f07b3c1dSBenjamin Tissoires MOUSE_REPORT_ID, 0);
8672b0c086cSJohn Chen else if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2)
8682b0c086cSJohn Chen report = hid_register_report(hdev, HID_INPUT_REPORT,
8692b0c086cSJohn Chen MOUSE2_REPORT_ID, 0);
870*6e8348ecSCallahan Kovacs else if (id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
871*6e8348ecSCallahan Kovacs id->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) {
8729d7b1866SSean O'Brien if (id->vendor == BT_VENDOR_ID_APPLE)
8739d7b1866SSean O'Brien report = hid_register_report(hdev, HID_INPUT_REPORT,
8749d7b1866SSean O'Brien TRACKPAD2_BT_REPORT_ID, 0);
8759d7b1866SSean O'Brien else /* USB_VENDOR_ID_APPLE */
8769d7b1866SSean O'Brien report = hid_register_report(hdev, HID_INPUT_REPORT,
8779d7b1866SSean O'Brien TRACKPAD2_USB_REPORT_ID, 0);
8789d7b1866SSean O'Brien } else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
879a462230eSChase Douglas report = hid_register_report(hdev, HID_INPUT_REPORT,
880f07b3c1dSBenjamin Tissoires TRACKPAD_REPORT_ID, 0);
881a462230eSChase Douglas report = hid_register_report(hdev, HID_INPUT_REPORT,
882f07b3c1dSBenjamin Tissoires DOUBLE_REPORT_ID, 0);
883a462230eSChase Douglas }
884a462230eSChase Douglas
885128537ceSMichael Poole if (!report) {
8864291ee30SJoe Perches hid_err(hdev, "unable to register touch report\n");
887128537ceSMichael Poole ret = -ENOMEM;
88871b38bd4SMichael Poole goto err_stop_hw;
889128537ceSMichael Poole }
890128537ceSMichael Poole report->size = 6;
891128537ceSMichael Poole
89235d851dfSJiri Kosina /*
89335d851dfSJiri Kosina * Some devices repond with 'invalid report id' when feature
89435d851dfSJiri Kosina * report switching it into multitouch mode is sent to it.
89535d851dfSJiri Kosina *
89635d851dfSJiri Kosina * This results in -EIO from the _raw low-level transport callback,
89735d851dfSJiri Kosina * but there seems to be no other way of switching the mode.
89835d851dfSJiri Kosina * Thus the super-ugly hacky success check below.
89935d851dfSJiri Kosina */
900c0dc5582SJohn Chen ret = magicmouse_enable_multitouch(hdev);
901c0dc5582SJohn Chen if (ret != -EIO && ret < 0) {
9024291ee30SJoe Perches hid_err(hdev, "unable to request touch data (%d)\n", ret);
90371b38bd4SMichael Poole goto err_stop_hw;
904128537ceSMichael Poole }
905c0dc5582SJohn Chen if (ret == -EIO && id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2) {
906c0dc5582SJohn Chen schedule_delayed_work(&msc->work, msecs_to_jiffies(500));
907c0dc5582SJohn Chen }
908128537ceSMichael Poole
909128537ceSMichael Poole return 0;
91071b38bd4SMichael Poole err_stop_hw:
91133812fc7SChristophe JAILLET del_timer_sync(&msc->battery_timer);
91271b38bd4SMichael Poole hid_hw_stop(hdev);
913128537ceSMichael Poole return ret;
914128537ceSMichael Poole }
915128537ceSMichael Poole
magicmouse_remove(struct hid_device * hdev)916c0dc5582SJohn Chen static void magicmouse_remove(struct hid_device *hdev)
917c0dc5582SJohn Chen {
918c0dc5582SJohn Chen struct magicmouse_sc *msc = hid_get_drvdata(hdev);
9194fb12519SJosé Expósito
9200b91b4e4SJosé Expósito if (msc) {
921c0dc5582SJohn Chen cancel_delayed_work_sync(&msc->work);
9220b91b4e4SJosé Expósito del_timer_sync(&msc->battery_timer);
9230b91b4e4SJosé Expósito }
9244fb12519SJosé Expósito
925c0dc5582SJohn Chen hid_hw_stop(hdev);
926c0dc5582SJohn Chen }
927c0dc5582SJohn Chen
magicmouse_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)9280b91b4e4SJosé Expósito static __u8 *magicmouse_report_fixup(struct hid_device *hdev, __u8 *rdesc,
9290b91b4e4SJosé Expósito unsigned int *rsize)
9300b91b4e4SJosé Expósito {
9310b91b4e4SJosé Expósito /*
9320b91b4e4SJosé Expósito * Change the usage from:
9330b91b4e4SJosé Expósito * 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 0
9340b91b4e4SJosé Expósito * 0x09, 0x0b, // Usage (Vendor Usage 0x0b) 3
9350b91b4e4SJosé Expósito * To:
9360b91b4e4SJosé Expósito * 0x05, 0x01, // Usage Page (Generic Desktop) 0
9370b91b4e4SJosé Expósito * 0x09, 0x02, // Usage (Mouse) 2
9380b91b4e4SJosé Expósito */
9390b91b4e4SJosé Expósito if (hdev->vendor == USB_VENDOR_ID_APPLE &&
9400b91b4e4SJosé Expósito (hdev->product == USB_DEVICE_ID_APPLE_MAGICMOUSE2 ||
941*6e8348ecSCallahan Kovacs hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2 ||
942*6e8348ecSCallahan Kovacs hdev->product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC) &&
9430b91b4e4SJosé Expósito *rsize == 83 && rdesc[46] == 0x84 && rdesc[58] == 0x85) {
9440b91b4e4SJosé Expósito hid_info(hdev,
9450b91b4e4SJosé Expósito "fixing up magicmouse battery report descriptor\n");
9460b91b4e4SJosé Expósito *rsize = *rsize - 1;
9470b91b4e4SJosé Expósito rdesc = kmemdup(rdesc + 1, *rsize, GFP_KERNEL);
9480b91b4e4SJosé Expósito if (!rdesc)
9490b91b4e4SJosé Expósito return NULL;
9500b91b4e4SJosé Expósito
9510b91b4e4SJosé Expósito rdesc[0] = 0x05;
9520b91b4e4SJosé Expósito rdesc[1] = 0x01;
9530b91b4e4SJosé Expósito rdesc[2] = 0x09;
9540b91b4e4SJosé Expósito rdesc[3] = 0x02;
9550b91b4e4SJosé Expósito }
9560b91b4e4SJosé Expósito
9570b91b4e4SJosé Expósito return rdesc;
9580b91b4e4SJosé Expósito }
9590b91b4e4SJosé Expósito
960128537ceSMichael Poole static const struct hid_device_id magic_mice[] = {
961a462230eSChase Douglas { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
962a462230eSChase Douglas USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
9632b0c086cSJohn Chen { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
9642b0c086cSJohn Chen USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
9650b91b4e4SJosé Expósito { HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
9660b91b4e4SJosé Expósito USB_DEVICE_ID_APPLE_MAGICMOUSE2), .driver_data = 0 },
967a462230eSChase Douglas { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
968a462230eSChase Douglas USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
9699d7b1866SSean O'Brien { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
9709d7b1866SSean O'Brien USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
9719d7b1866SSean O'Brien { HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
9729d7b1866SSean O'Brien USB_DEVICE_ID_APPLE_MAGICTRACKPAD2), .driver_data = 0 },
973*6e8348ecSCallahan Kovacs { HID_BLUETOOTH_DEVICE(BT_VENDOR_ID_APPLE,
974*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 },
975*6e8348ecSCallahan Kovacs { HID_USB_DEVICE(USB_VENDOR_ID_APPLE,
976*6e8348ecSCallahan Kovacs USB_DEVICE_ID_APPLE_MAGICTRACKPAD2_USBC), .driver_data = 0 },
977128537ceSMichael Poole { }
978128537ceSMichael Poole };
979128537ceSMichael Poole MODULE_DEVICE_TABLE(hid, magic_mice);
980128537ceSMichael Poole
981128537ceSMichael Poole static struct hid_driver magicmouse_driver = {
982128537ceSMichael Poole .name = "magicmouse",
983128537ceSMichael Poole .id_table = magic_mice,
984128537ceSMichael Poole .probe = magicmouse_probe,
985c0dc5582SJohn Chen .remove = magicmouse_remove,
9860b91b4e4SJosé Expósito .report_fixup = magicmouse_report_fixup,
987128537ceSMichael Poole .raw_event = magicmouse_raw_event,
9883dcc5f7bSJohn Chen .event = magicmouse_event,
98964eb105dSMichael Poole .input_mapping = magicmouse_input_mapping,
990f1a9a149SBenjamin Tissoires .input_configured = magicmouse_input_configured,
991128537ceSMichael Poole };
992f425458eSH Hartley Sweeten module_hid_driver(magicmouse_driver);
993128537ceSMichael Poole
994128537ceSMichael Poole MODULE_LICENSE("GPL");
995