Blame view

drivers/hid/hid-magicmouse.c 17 KB
128537cea   Michael Poole   HID: add a device...
1
2
3
4
  /*
   *   Apple "Magic" Wireless Mouse driver
   *
   *   Copyright (c) 2010 Michael Poole <mdpoole@troilus.org>
a462230e1   Chase Douglas   HID: magicmouse: ...
5
   *   Copyright (c) 2010 Chase Douglas <chase.douglas@canonical.com>
128537cea   Michael Poole   HID: add a device...
6
7
8
9
10
11
12
13
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2 of the License, or (at your option)
   * any later version.
   */
4291ee305   Joe Perches   HID: Add and use ...
14
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
128537cea   Michael Poole   HID: add a device...
15
16
  #include <linux/device.h>
  #include <linux/hid.h>
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
17
  #include <linux/input/mt.h>
128537cea   Michael Poole   HID: add a device...
18
  #include <linux/module.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
128537cea   Michael Poole   HID: add a device...
20
21
  
  #include "hid-ids.h"
71b38bd4c   Michael Poole   HID: magicmouse: ...
22
  static bool emulate_3button = true;
128537cea   Michael Poole   HID: add a device...
23
24
25
26
27
  module_param(emulate_3button, bool, 0644);
  MODULE_PARM_DESC(emulate_3button, "Emulate a middle button");
  
  static int middle_button_start = -350;
  static int middle_button_stop = +350;
71b38bd4c   Michael Poole   HID: magicmouse: ...
28
  static bool emulate_scroll_wheel = true;
128537cea   Michael Poole   HID: add a device...
29
30
  module_param(emulate_scroll_wheel, bool, 0644);
  MODULE_PARM_DESC(emulate_scroll_wheel, "Emulate a scroll wheel");
0b778e76c   Chase Douglas   HID: magicmouse: ...
31
32
33
  static unsigned int scroll_speed = 32;
  static int param_set_scroll_speed(const char *val, struct kernel_param *kp) {
  	unsigned long speed;
dfc450b55   Jingoo Han   HID: replace stri...
34
  	if (!val || kstrtoul(val, 0, &speed) || speed > 63)
0b778e76c   Chase Douglas   HID: magicmouse: ...
35
36
37
38
39
40
  		return -EINVAL;
  	scroll_speed = speed;
  	return 0;
  }
  module_param_call(scroll_speed, param_set_scroll_speed, param_get_uint, &scroll_speed, 0644);
  MODULE_PARM_DESC(scroll_speed, "Scroll speed, value from 0 (slow) to 63 (fast)");
9846f350e   Chase Douglas   HID: magicmouse: ...
41
42
43
  static bool scroll_acceleration = false;
  module_param(scroll_acceleration, bool, 0644);
  MODULE_PARM_DESC(scroll_acceleration, "Accelerate sequential scroll events");
71b38bd4c   Michael Poole   HID: magicmouse: ...
44
  static bool report_undeciphered;
128537cea   Michael Poole   HID: add a device...
45
46
  module_param(report_undeciphered, bool, 0644);
  MODULE_PARM_DESC(report_undeciphered, "Report undeciphered multi-touch state field using a MSC_RAW event");
a462230e1   Chase Douglas   HID: magicmouse: ...
47
48
49
  #define TRACKPAD_REPORT_ID 0x28
  #define MOUSE_REPORT_ID    0x29
  #define DOUBLE_REPORT_ID   0xf7
128537cea   Michael Poole   HID: add a device...
50
51
52
53
54
55
56
57
58
59
  /* 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,
   * and 0x40 is actual contact, and 0x10 may be a start/stop or change
   * indication.)
   */
  #define TOUCH_STATE_MASK  0xf0
  #define TOUCH_STATE_NONE  0x00
  #define TOUCH_STATE_START 0x30
  #define TOUCH_STATE_DRAG  0x40
0b778e76c   Chase Douglas   HID: magicmouse: ...
60
  #define SCROLL_ACCEL_DEFAULT 7
4f6fdf086   Chase Douglas   HID: magicmouse: ...
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
  /* Touch surface information. Dimension is in hundredths of a mm, min and max
   * are in units. */
  #define MOUSE_DIMENSION_X (float)9056
  #define MOUSE_MIN_X -1100
  #define MOUSE_MAX_X 1258
  #define MOUSE_RES_X ((MOUSE_MAX_X - MOUSE_MIN_X) / (MOUSE_DIMENSION_X / 100))
  #define MOUSE_DIMENSION_Y (float)5152
  #define MOUSE_MIN_Y -1589
  #define MOUSE_MAX_Y 2047
  #define MOUSE_RES_Y ((MOUSE_MAX_Y - MOUSE_MIN_Y) / (MOUSE_DIMENSION_Y / 100))
  
  #define TRACKPAD_DIMENSION_X (float)13000
  #define TRACKPAD_MIN_X -2909
  #define TRACKPAD_MAX_X 3167
  #define TRACKPAD_RES_X \
  	((TRACKPAD_MAX_X - TRACKPAD_MIN_X) / (TRACKPAD_DIMENSION_X / 100))
  #define TRACKPAD_DIMENSION_Y (float)11000
  #define TRACKPAD_MIN_Y -2456
  #define TRACKPAD_MAX_Y 2565
  #define TRACKPAD_RES_Y \
  	((TRACKPAD_MAX_Y - TRACKPAD_MIN_Y) / (TRACKPAD_DIMENSION_Y / 100))
128537cea   Michael Poole   HID: add a device...
82
83
84
85
  /**
   * struct magicmouse_sc - Tracks Magic Mouse-specific data.
   * @input: Input device through which we report events.
   * @quirks: Currently unused.
128537cea   Michael Poole   HID: add a device...
86
87
88
89
90
91
92
93
94
   * @ntouches: Number of touches in most recent touch report.
   * @scroll_accel: Number of consecutive scroll motions.
   * @scroll_jiffies: Time of last scroll motion.
   * @touches: Most recent data for a touch, indexed by tracking ID.
   * @tracking_ids: Mapping of current touch input data to @touches.
   */
  struct magicmouse_sc {
  	struct input_dev *input;
  	unsigned long quirks;
128537cea   Michael Poole   HID: add a device...
95
96
97
98
99
100
101
  	int ntouches;
  	int scroll_accel;
  	unsigned long scroll_jiffies;
  
  	struct {
  		short x;
  		short y;
c04266889   Chase Douglas   HID: magicmouse: ...
102
  		short scroll_x;
128537cea   Michael Poole   HID: add a device...
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  		short scroll_y;
  		u8 size;
  	} touches[16];
  	int tracking_ids[16];
  };
  
  static int magicmouse_firm_touch(struct magicmouse_sc *msc)
  {
  	int touch = -1;
  	int ii;
  
  	/* If there is only one "firm" touch, set touch to its
  	 * tracking ID.
  	 */
  	for (ii = 0; ii < msc->ntouches; ii++) {
  		int idx = msc->tracking_ids[ii];
  		if (msc->touches[idx].size < 8) {
  			/* Ignore this touch. */
  		} else if (touch >= 0) {
  			touch = -1;
  			break;
  		} else {
  			touch = idx;
  		}
  	}
  
  	return touch;
  }
  
  static void magicmouse_emit_buttons(struct magicmouse_sc *msc, int state)
  {
71b38bd4c   Michael Poole   HID: magicmouse: ...
134
135
136
  	int last_state = test_bit(BTN_LEFT, msc->input->key) << 0 |
  		test_bit(BTN_RIGHT, msc->input->key) << 1 |
  		test_bit(BTN_MIDDLE, msc->input->key) << 2;
128537cea   Michael Poole   HID: add a device...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  
  	if (emulate_3button) {
  		int id;
  
  		/* If some button was pressed before, keep it held
  		 * down.  Otherwise, if there's exactly one firm
  		 * touch, use that to override the mouse's guess.
  		 */
  		if (state == 0) {
  			/* The button was released. */
  		} else if (last_state != 0) {
  			state = last_state;
  		} else if ((id = magicmouse_firm_touch(msc)) >= 0) {
  			int x = msc->touches[id].x;
  			if (x < middle_button_start)
  				state = 1;
  			else if (x > middle_button_stop)
  				state = 2;
  			else
  				state = 4;
  		} /* else: we keep the mouse's guess */
  
  		input_report_key(msc->input, BTN_MIDDLE, state & 4);
  	}
  
  	input_report_key(msc->input, BTN_LEFT, state & 1);
  	input_report_key(msc->input, BTN_RIGHT, state & 2);
  
  	if (state != last_state)
0b778e76c   Chase Douglas   HID: magicmouse: ...
166
  		msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
128537cea   Michael Poole   HID: add a device...
167
168
169
170
171
  }
  
  static void magicmouse_emit_touch(struct magicmouse_sc *msc, int raw_id, u8 *tdata)
  {
  	struct input_dev *input = msc->input;
a462230e1   Chase Douglas   HID: magicmouse: ...
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
  	int id, x, y, size, orientation, touch_major, touch_minor, state, down;
  
  	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
  		id = (tdata[6] << 2 | tdata[5] >> 6) & 0xf;
  		x = (tdata[1] << 28 | tdata[0] << 20) >> 20;
  		y = -((tdata[2] << 24 | tdata[1] << 16) >> 20);
  		size = tdata[5] & 0x3f;
  		orientation = (tdata[6] >> 2) - 32;
  		touch_major = tdata[3];
  		touch_minor = tdata[4];
  		state = tdata[7] & TOUCH_STATE_MASK;
  		down = state != TOUCH_STATE_NONE;
  	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
  		id = (tdata[7] << 2 | tdata[6] >> 6) & 0xf;
  		x = (tdata[1] << 27 | tdata[0] << 19) >> 19;
  		y = -((tdata[3] << 30 | tdata[2] << 22 | tdata[1] << 14) >> 19);
  		size = tdata[6] & 0x3f;
  		orientation = (tdata[7] >> 2) - 32;
  		touch_major = tdata[4];
  		touch_minor = tdata[5];
  		state = tdata[8] & TOUCH_STATE_MASK;
  		down = state != TOUCH_STATE_NONE;
  	}
128537cea   Michael Poole   HID: add a device...
195
196
197
198
199
  
  	/* Store tracking ID and other fields. */
  	msc->tracking_ids[raw_id] = id;
  	msc->touches[id].x = x;
  	msc->touches[id].y = y;
6de048bf1   Chase Douglas   HID: magicmouse: ...
200
  	msc->touches[id].size = size;
128537cea   Michael Poole   HID: add a device...
201
202
  
  	/* If requested, emulate a scroll wheel by detecting small
ef566d30a   Chase Douglas   HID: magicmouse: ...
203
  	 * vertical touch motions.
128537cea   Michael Poole   HID: add a device...
204
  	 */
ef566d30a   Chase Douglas   HID: magicmouse: ...
205
  	if (emulate_scroll_wheel) {
128537cea   Michael Poole   HID: add a device...
206
  		unsigned long now = jiffies;
c04266889   Chase Douglas   HID: magicmouse: ...
207
208
  		int step_x = msc->touches[id].scroll_x - x;
  		int step_y = msc->touches[id].scroll_y - y;
128537cea   Michael Poole   HID: add a device...
209

128537cea   Michael Poole   HID: add a device...
210
  		/* Calculate and apply the scroll motion. */
6de048bf1   Chase Douglas   HID: magicmouse: ...
211
  		switch (state) {
128537cea   Michael Poole   HID: add a device...
212
  		case TOUCH_STATE_START:
c04266889   Chase Douglas   HID: magicmouse: ...
213
  			msc->touches[id].scroll_x = x;
128537cea   Michael Poole   HID: add a device...
214
  			msc->touches[id].scroll_y = y;
0b778e76c   Chase Douglas   HID: magicmouse: ...
215
216
217
218
219
220
221
222
  
  			/* Reset acceleration after half a second. */
  			if (scroll_acceleration && time_before(now,
  						msc->scroll_jiffies + HZ / 2))
  				msc->scroll_accel = max_t(int,
  						msc->scroll_accel - 1, 1);
  			else
  				msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
128537cea   Michael Poole   HID: add a device...
223
224
  			break;
  		case TOUCH_STATE_DRAG:
c04266889   Chase Douglas   HID: magicmouse: ...
225
226
227
  			step_x /= (64 - (int)scroll_speed) * msc->scroll_accel;
  			if (step_x != 0) {
  				msc->touches[id].scroll_x -= step_x *
0b778e76c   Chase Douglas   HID: magicmouse: ...
228
  					(64 - scroll_speed) * msc->scroll_accel;
128537cea   Michael Poole   HID: add a device...
229
  				msc->scroll_jiffies = now;
c04266889   Chase Douglas   HID: magicmouse: ...
230
231
232
233
234
235
236
237
238
  				input_report_rel(input, REL_HWHEEL, -step_x);
  			}
  
  			step_y /= (64 - (int)scroll_speed) * msc->scroll_accel;
  			if (step_y != 0) {
  				msc->touches[id].scroll_y -= step_y *
  					(64 - scroll_speed) * msc->scroll_accel;
  				msc->scroll_jiffies = now;
  				input_report_rel(input, REL_WHEEL, step_y);
128537cea   Michael Poole   HID: add a device...
239
240
241
242
  			}
  			break;
  		}
  	}
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
243
  	if (down)
a462230e1   Chase Douglas   HID: magicmouse: ...
244
  		msc->ntouches++;
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
245
246
247
  
  	input_mt_slot(input, id);
  	input_mt_report_slot_state(input, MT_TOOL_FINGER, down);
a462230e1   Chase Douglas   HID: magicmouse: ...
248

128537cea   Michael Poole   HID: add a device...
249
  	/* Generate the input events for this touch. */
6264307ed   Yufeng Shen   HID: magicmouse: ...
250
  	if (down) {
921990b70   Henrik Rydberg   HID: magicmouse: ...
251
252
  		input_report_abs(input, ABS_MT_TOUCH_MAJOR, touch_major << 2);
  		input_report_abs(input, ABS_MT_TOUCH_MINOR, touch_minor << 2);
2d9ca4e9f   Henrik Rydberg   HID: hid-magicmou...
253
  		input_report_abs(input, ABS_MT_ORIENTATION, -orientation);
128537cea   Michael Poole   HID: add a device...
254
255
  		input_report_abs(input, ABS_MT_POSITION_X, x);
  		input_report_abs(input, ABS_MT_POSITION_Y, y);
a462230e1   Chase Douglas   HID: magicmouse: ...
256
257
258
259
260
261
  		if (report_undeciphered) {
  			if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
  				input_event(input, EV_MSC, MSC_RAW, tdata[7]);
  			else /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
  				input_event(input, EV_MSC, MSC_RAW, tdata[8]);
  		}
128537cea   Michael Poole   HID: add a device...
262
263
264
265
266
267
268
269
  	}
  }
  
  static int magicmouse_raw_event(struct hid_device *hdev,
  		struct hid_report *report, u8 *data, int size)
  {
  	struct magicmouse_sc *msc = hid_get_drvdata(hdev);
  	struct input_dev *input = msc->input;
a462230e1   Chase Douglas   HID: magicmouse: ...
270
  	int x = 0, y = 0, ii, clicks = 0, npoints;
128537cea   Michael Poole   HID: add a device...
271
272
  
  	switch (data[0]) {
a462230e1   Chase Douglas   HID: magicmouse: ...
273
274
275
276
277
  	case TRACKPAD_REPORT_ID:
  		/* Expect four bytes of prefix, and N*9 bytes of touch data. */
  		if (size < 4 || ((size - 4) % 9) != 0)
  			return 0;
  		npoints = (size - 4) / 9;
c54def7bd   Jiri Kosina   HID: magicmouse: ...
278
279
280
281
282
283
  		if (npoints > 15) {
  			hid_warn(hdev, "invalid size value (%d) for TRACKPAD_REPORT_ID
  ",
  					size);
  			return 0;
  		}
a462230e1   Chase Douglas   HID: magicmouse: ...
284
285
286
  		msc->ntouches = 0;
  		for (ii = 0; ii < npoints; ii++)
  			magicmouse_emit_touch(msc, ii, data + ii * 9 + 4);
a462230e1   Chase Douglas   HID: magicmouse: ...
287
288
289
290
291
292
293
294
295
  		clicks = data[1];
  
  		/* The following bits provide a device specific timestamp. They
  		 * are unused here.
  		 *
  		 * ts = data[1] >> 6 | data[2] << 2 | data[3] << 10;
  		 */
  		break;
  	case MOUSE_REPORT_ID:
128537cea   Michael Poole   HID: add a device...
296
297
298
  		/* Expect six bytes of prefix, and N*8 bytes of touch data. */
  		if (size < 6 || ((size - 6) % 8) != 0)
  			return 0;
0228db70c   Chase Douglas   HID: magicmouse: ...
299
  		npoints = (size - 6) / 8;
c54def7bd   Jiri Kosina   HID: magicmouse: ...
300
301
302
303
304
305
  		if (npoints > 15) {
  			hid_warn(hdev, "invalid size value (%d) for MOUSE_REPORT_ID
  ",
  					size);
  			return 0;
  		}
0228db70c   Chase Douglas   HID: magicmouse: ...
306
307
  		msc->ntouches = 0;
  		for (ii = 0; ii < npoints; ii++)
128537cea   Michael Poole   HID: add a device...
308
  			magicmouse_emit_touch(msc, ii, data + ii * 8 + 6);
e3612e866   Chase Douglas   HID: magicmouse: ...
309

128537cea   Michael Poole   HID: add a device...
310
311
312
313
  		/* When emulating three-button mode, it is important
  		 * to have the current touch information before
  		 * generating a click event.
  		 */
7d876c05f   Michael Poole   HID: magicmouse: ...
314
315
  		x = (int)(((data[3] & 0x0c) << 28) | (data[1] << 22)) >> 22;
  		y = (int)(((data[3] & 0x30) << 26) | (data[2] << 22)) >> 22;
128537cea   Michael Poole   HID: add a device...
316
  		clicks = data[3];
c61b7cee6   Chase Douglas   HID: magicmouse: ...
317
318
319
320
321
322
  
  		/* The following bits provide a device specific timestamp. They
  		 * are unused here.
  		 *
  		 * ts = data[3] >> 6 | data[4] << 2 | data[5] << 10;
  		 */
128537cea   Michael Poole   HID: add a device...
323
  		break;
a462230e1   Chase Douglas   HID: magicmouse: ...
324
325
326
327
328
329
330
331
  	case DOUBLE_REPORT_ID:
  		/* Sometimes the trackpad sends two touch reports in one
  		 * packet.
  		 */
  		magicmouse_raw_event(hdev, report, data + 2, data[1]);
  		magicmouse_raw_event(hdev, report, data + 2 + data[1],
  			size - 2 - data[1]);
  		break;
128537cea   Michael Poole   HID: add a device...
332
333
334
  	default:
  		return 0;
  	}
a462230e1   Chase Douglas   HID: magicmouse: ...
335
336
337
338
339
340
  	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
  		magicmouse_emit_buttons(msc, clicks & 3);
  		input_report_rel(input, REL_X, x);
  		input_report_rel(input, REL_Y, y);
  	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
  		input_report_key(input, BTN_MOUSE, clicks & 1);
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
341
  		input_mt_report_pointer_emulation(input, true);
a462230e1   Chase Douglas   HID: magicmouse: ...
342
  	}
128537cea   Michael Poole   HID: add a device...
343
344
345
  	input_sync(input);
  	return 1;
  }
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
346
  static int magicmouse_setup_input(struct input_dev *input, struct hid_device *hdev)
128537cea   Michael Poole   HID: add a device...
347
  {
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
348
  	int error;
71b38bd4c   Michael Poole   HID: magicmouse: ...
349
  	__set_bit(EV_KEY, input->evbit);
a462230e1   Chase Douglas   HID: magicmouse: ...
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
  
  	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
  		__set_bit(BTN_LEFT, input->keybit);
  		__set_bit(BTN_RIGHT, input->keybit);
  		if (emulate_3button)
  			__set_bit(BTN_MIDDLE, input->keybit);
  
  		__set_bit(EV_REL, input->evbit);
  		__set_bit(REL_X, input->relbit);
  		__set_bit(REL_Y, input->relbit);
  		if (emulate_scroll_wheel) {
  			__set_bit(REL_WHEEL, input->relbit);
  			__set_bit(REL_HWHEEL, input->relbit);
  		}
  	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
bca621421   Daniel van Vugt   HID: hid-magicmou...
365
366
367
368
369
370
371
  		/* input->keybit is initialized with incorrect button info
  		 * for Magic Trackpad. There really is only one physical
  		 * button (BTN_LEFT == BTN_MOUSE). Make sure we don't
  		 * advertise buttons that don't exist...
  		 */
  		__clear_bit(BTN_RIGHT, input->keybit);
  		__clear_bit(BTN_MIDDLE, input->keybit);
a462230e1   Chase Douglas   HID: magicmouse: ...
372
  		__set_bit(BTN_MOUSE, input->keybit);
53145c2e3   Daniel Stone   Revert "HID: magi...
373
374
375
376
377
378
379
  		__set_bit(BTN_TOOL_FINGER, input->keybit);
  		__set_bit(BTN_TOOL_DOUBLETAP, input->keybit);
  		__set_bit(BTN_TOOL_TRIPLETAP, input->keybit);
  		__set_bit(BTN_TOOL_QUADTAP, input->keybit);
  		__set_bit(BTN_TOOL_QUINTTAP, input->keybit);
  		__set_bit(BTN_TOUCH, input->keybit);
  		__set_bit(INPUT_PROP_POINTER, input->propbit);
503f7d53b   Chase Douglas   HID: hid-magicmou...
380
  		__set_bit(INPUT_PROP_BUTTONPAD, input->propbit);
c04266889   Chase Douglas   HID: magicmouse: ...
381
  	}
128537cea   Michael Poole   HID: add a device...
382

cc5e0f08c   Chase Douglas   HID: hid-magicmou...
383

6264307ed   Yufeng Shen   HID: magicmouse: ...
384
  	__set_bit(EV_ABS, input->evbit);
b4adbbefc   Henrik Rydberg   Input: MT - Add f...
385
  	error = input_mt_init_slots(input, 16, 0);
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
386
387
  	if (error)
  		return error;
6264307ed   Yufeng Shen   HID: magicmouse: ...
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  	input_set_abs_params(input, ABS_MT_TOUCH_MAJOR, 0, 255 << 2,
  			     4, 0);
  	input_set_abs_params(input, ABS_MT_TOUCH_MINOR, 0, 255 << 2,
  			     4, 0);
  	input_set_abs_params(input, ABS_MT_ORIENTATION, -31, 32, 1, 0);
  
  	/* Note: Touch Y position from the device is inverted relative
  	 * to how pointer motion is reported (and relative to how USB
  	 * HID recommends the coordinates work).  This driver keeps
  	 * the origin at the same position, and just uses the additive
  	 * inverse of the reported Y.
  	 */
  	if (input->id.product == USB_DEVICE_ID_APPLE_MAGICMOUSE) {
  		input_set_abs_params(input, ABS_MT_POSITION_X,
  				     MOUSE_MIN_X, MOUSE_MAX_X, 4, 0);
  		input_set_abs_params(input, ABS_MT_POSITION_Y,
  				     MOUSE_MIN_Y, MOUSE_MAX_Y, 4, 0);
  
  		input_abs_set_res(input, ABS_MT_POSITION_X,
  				  MOUSE_RES_X);
  		input_abs_set_res(input, ABS_MT_POSITION_Y,
  				  MOUSE_RES_Y);
  	} else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
  		input_set_abs_params(input, ABS_X, TRACKPAD_MIN_X,
  				     TRACKPAD_MAX_X, 4, 0);
  		input_set_abs_params(input, ABS_Y, TRACKPAD_MIN_Y,
  				     TRACKPAD_MAX_Y, 4, 0);
  		input_set_abs_params(input, ABS_MT_POSITION_X,
  				     TRACKPAD_MIN_X, TRACKPAD_MAX_X, 4, 0);
  		input_set_abs_params(input, ABS_MT_POSITION_Y,
  				     TRACKPAD_MIN_Y, TRACKPAD_MAX_Y, 4, 0);
  
  		input_abs_set_res(input, ABS_X, TRACKPAD_RES_X);
  		input_abs_set_res(input, ABS_Y, TRACKPAD_RES_Y);
  		input_abs_set_res(input, ABS_MT_POSITION_X,
  				  TRACKPAD_RES_X);
  		input_abs_set_res(input, ABS_MT_POSITION_Y,
  				  TRACKPAD_RES_Y);
128537cea   Michael Poole   HID: add a device...
426
  	}
6264307ed   Yufeng Shen   HID: magicmouse: ...
427
  	input_set_events_per_packet(input, 60);
128537cea   Michael Poole   HID: add a device...
428
  	if (report_undeciphered) {
71b38bd4c   Michael Poole   HID: magicmouse: ...
429
430
  		__set_bit(EV_MSC, input->evbit);
  		__set_bit(MSC_RAW, input->mscbit);
128537cea   Michael Poole   HID: add a device...
431
  	}
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
432
433
  
  	return 0;
128537cea   Michael Poole   HID: add a device...
434
  }
64eb105d7   Michael Poole   HID: magicmouse: ...
435
436
437
438
439
440
441
442
  static int magicmouse_input_mapping(struct hid_device *hdev,
  		struct hid_input *hi, struct hid_field *field,
  		struct hid_usage *usage, unsigned long **bit, int *max)
  {
  	struct magicmouse_sc *msc = hid_get_drvdata(hdev);
  
  	if (!msc->input)
  		msc->input = hi->input;
6a66bbd69   Chase Douglas   HID: magicmouse: ...
443
444
445
446
  	/* Magic Trackpad does not give relative data after switching to MT */
  	if (hi->input->id.product == USB_DEVICE_ID_APPLE_MAGICTRACKPAD &&
  	    field->flags & HID_MAIN_ITEM_RELATIVE)
  		return -1;
64eb105d7   Michael Poole   HID: magicmouse: ...
447
448
  	return 0;
  }
9154301a4   Dmitry Torokhov   HID: hid-input: a...
449
  static int magicmouse_input_configured(struct hid_device *hdev,
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
450
451
452
453
  		struct hid_input *hi)
  
  {
  	struct magicmouse_sc *msc = hid_get_drvdata(hdev);
9154301a4   Dmitry Torokhov   HID: hid-input: a...
454
  	int ret;
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
455

9154301a4   Dmitry Torokhov   HID: hid-input: a...
456
  	ret = magicmouse_setup_input(msc->input, hdev);
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
457
458
459
460
461
  	if (ret) {
  		hid_err(hdev, "magicmouse setup input failed (%d)
  ", ret);
  		/* clean msc->input to notify probe() of the failure */
  		msc->input = NULL;
9154301a4   Dmitry Torokhov   HID: hid-input: a...
462
  		return ret;
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
463
  	}
9154301a4   Dmitry Torokhov   HID: hid-input: a...
464
465
  
  	return 0;
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
466
  }
128537cea   Michael Poole   HID: add a device...
467
468
469
  static int magicmouse_probe(struct hid_device *hdev,
  	const struct hid_device_id *id)
  {
b7a87ad67   Benjamin Tissoires   HID: magicmouse: ...
470
471
  	const u8 feature[] = { 0xd7, 0x01 };
  	u8 *buf;
128537cea   Michael Poole   HID: add a device...
472
473
474
  	struct magicmouse_sc *msc;
  	struct hid_report *report;
  	int ret;
abf832bfc   Benjamin Tissoires   HID: trivial devm...
475
  	msc = devm_kzalloc(&hdev->dev, sizeof(*msc), GFP_KERNEL);
128537cea   Michael Poole   HID: add a device...
476
  	if (msc == NULL) {
4291ee305   Joe Perches   HID: Add and use ...
477
478
  		hid_err(hdev, "can't alloc magicmouse descriptor
  ");
128537cea   Michael Poole   HID: add a device...
479
480
  		return -ENOMEM;
  	}
0b778e76c   Chase Douglas   HID: magicmouse: ...
481
  	msc->scroll_accel = SCROLL_ACCEL_DEFAULT;
128537cea   Michael Poole   HID: add a device...
482
483
484
485
486
  	msc->quirks = id->driver_data;
  	hid_set_drvdata(hdev, msc);
  
  	ret = hid_parse(hdev);
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
487
488
  		hid_err(hdev, "magicmouse hid parse failed
  ");
abf832bfc   Benjamin Tissoires   HID: trivial devm...
489
  		return ret;
128537cea   Michael Poole   HID: add a device...
490
  	}
23d021167   Jiri Kosina   HID: magicmouse: ...
491
  	ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
128537cea   Michael Poole   HID: add a device...
492
  	if (ret) {
4291ee305   Joe Perches   HID: Add and use ...
493
494
  		hid_err(hdev, "magicmouse hw start failed
  ");
abf832bfc   Benjamin Tissoires   HID: trivial devm...
495
  		return ret;
128537cea   Michael Poole   HID: add a device...
496
  	}
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
497
498
499
500
501
  	if (!msc->input) {
  		hid_err(hdev, "magicmouse input not registered
  ");
  		ret = -ENOMEM;
  		goto err_stop_hw;
a6d1bc1d5   Yufeng Shen   HID: magicmouse: ...
502
  	}
23d021167   Jiri Kosina   HID: magicmouse: ...
503

a462230e1   Chase Douglas   HID: magicmouse: ...
504
505
506
507
508
509
510
511
512
  	if (id->product == USB_DEVICE_ID_APPLE_MAGICMOUSE)
  		report = hid_register_report(hdev, HID_INPUT_REPORT,
  			MOUSE_REPORT_ID);
  	else { /* USB_DEVICE_ID_APPLE_MAGICTRACKPAD */
  		report = hid_register_report(hdev, HID_INPUT_REPORT,
  			TRACKPAD_REPORT_ID);
  		report = hid_register_report(hdev, HID_INPUT_REPORT,
  			DOUBLE_REPORT_ID);
  	}
128537cea   Michael Poole   HID: add a device...
513
  	if (!report) {
4291ee305   Joe Perches   HID: Add and use ...
514
515
  		hid_err(hdev, "unable to register touch report
  ");
128537cea   Michael Poole   HID: add a device...
516
  		ret = -ENOMEM;
71b38bd4c   Michael Poole   HID: magicmouse: ...
517
  		goto err_stop_hw;
128537cea   Michael Poole   HID: add a device...
518
519
  	}
  	report->size = 6;
b7a87ad67   Benjamin Tissoires   HID: magicmouse: ...
520
521
522
523
524
  	buf = kmemdup(feature, sizeof(feature), GFP_KERNEL);
  	if (!buf) {
  		ret = -ENOMEM;
  		goto err_stop_hw;
  	}
35d851df2   Jiri Kosina   HID: magicmouse: ...
525
526
527
528
529
530
531
532
  	/*
  	 * Some devices repond with 'invalid report id' when feature
  	 * report switching it into multitouch mode is sent to it.
  	 *
  	 * This results in -EIO from the _raw low-level transport callback,
  	 * but there seems to be no other way of switching the mode.
  	 * Thus the super-ugly hacky success check below.
  	 */
b7a87ad67   Benjamin Tissoires   HID: magicmouse: ...
533
  	ret = hid_hw_raw_request(hdev, buf[0], buf, sizeof(feature),
b0dd72aaf   Benjamin Tissoires   HID: replace hid_...
534
  				HID_FEATURE_REPORT, HID_REQ_SET_REPORT);
b7a87ad67   Benjamin Tissoires   HID: magicmouse: ...
535
  	kfree(buf);
35d851df2   Jiri Kosina   HID: magicmouse: ...
536
  	if (ret != -EIO && ret != sizeof(feature)) {
4291ee305   Joe Perches   HID: Add and use ...
537
538
  		hid_err(hdev, "unable to request touch data (%d)
  ", ret);
71b38bd4c   Michael Poole   HID: magicmouse: ...
539
  		goto err_stop_hw;
128537cea   Michael Poole   HID: add a device...
540
  	}
128537cea   Michael Poole   HID: add a device...
541
  	return 0;
71b38bd4c   Michael Poole   HID: magicmouse: ...
542
543
  err_stop_hw:
  	hid_hw_stop(hdev);
128537cea   Michael Poole   HID: add a device...
544
545
  	return ret;
  }
128537cea   Michael Poole   HID: add a device...
546
  static const struct hid_device_id magic_mice[] = {
a462230e1   Chase Douglas   HID: magicmouse: ...
547
548
549
550
  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
  		USB_DEVICE_ID_APPLE_MAGICMOUSE), .driver_data = 0 },
  	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
  		USB_DEVICE_ID_APPLE_MAGICTRACKPAD), .driver_data = 0 },
128537cea   Michael Poole   HID: add a device...
551
552
553
554
555
556
557
558
  	{ }
  };
  MODULE_DEVICE_TABLE(hid, magic_mice);
  
  static struct hid_driver magicmouse_driver = {
  	.name = "magicmouse",
  	.id_table = magic_mice,
  	.probe = magicmouse_probe,
128537cea   Michael Poole   HID: add a device...
559
  	.raw_event = magicmouse_raw_event,
64eb105d7   Michael Poole   HID: magicmouse: ...
560
  	.input_mapping = magicmouse_input_mapping,
f1a9a149a   Benjamin Tissoires   HID: magicmouse: ...
561
  	.input_configured = magicmouse_input_configured,
128537cea   Michael Poole   HID: add a device...
562
  };
f425458ea   H Hartley Sweeten   HID: Use module_h...
563
  module_hid_driver(magicmouse_driver);
128537cea   Michael Poole   HID: add a device...
564

128537cea   Michael Poole   HID: add a device...
565
  MODULE_LICENSE("GPL");