aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHenrik Rydberg <rydberg@euromail.se>2008-08-08 14:59:30 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2008-08-08 16:23:01 -0400
commitf89bd95c5c946776f116ffeb997653d4193d6a35 (patch)
treeb35757f654f3cd947e446244e1b95cbd6921b9e6
parente4ddcb0a6bf04d53ce77b4eb87bbbb32c4261d11 (diff)
Input: bcm5974 - add driver for Macbook Air and Pro Penryn touchpads
This driver adds support for the multitouch trackpad on the new Apple Macbook Air and Macbook Pro Penryn laptops. It replaces the appletouch driver on those computers, and integrates well with the synaptics driver of the Xorg system. [dtor@mail.ru: various cleanups] Signed-off-by: Henrik Rydberg <rydberg@euromail.se> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
-rw-r--r--drivers/hid/usbhid/hid-quirks.c12
-rw-r--r--drivers/input/mouse/Kconfig23
-rw-r--r--drivers/input/mouse/Makefile1
-rw-r--r--drivers/input/mouse/bcm5974.c669
4 files changed, 699 insertions, 6 deletions
diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
index 61e78a4369b9..b15f88249639 100644
--- a/drivers/hid/usbhid/hid-quirks.c
+++ b/drivers/hid/usbhid/hid-quirks.c
@@ -654,12 +654,12 @@ static const struct hid_blacklist {
654 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, 654 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ANSI, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
655 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, 655 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_ISO, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD },
656 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN }, 656 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_WIRELESS_JIS, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN },
657 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN }, 657 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
658 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, 658 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE},
659 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN }, 659 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
660 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN }, 660 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE},
661 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD }, 661 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_ISO, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_APPLE_ISO_KEYBOARD | HID_QUIRK_IGNORE_MOUSE },
662 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN }, 662 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_WELLSPRING2_JIS, HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
663 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, 663 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
664 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE }, 664 { USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY, HID_QUIRK_APPLE_NUMLOCK_EMULATION | HID_QUIRK_APPLE_HAS_FN | HID_QUIRK_IGNORE_MOUSE },
665 665
diff --git a/drivers/input/mouse/Kconfig b/drivers/input/mouse/Kconfig
index 7bbea097cda2..f996546fc443 100644
--- a/drivers/input/mouse/Kconfig
+++ b/drivers/input/mouse/Kconfig
@@ -130,6 +130,29 @@ config MOUSE_APPLETOUCH
130 To compile this driver as a module, choose M here: the 130 To compile this driver as a module, choose M here: the
131 module will be called appletouch. 131 module will be called appletouch.
132 132
133config MOUSE_BCM5974
134 tristate "Apple USB BCM5974 Multitouch trackpad support"
135 depends on USB_ARCH_HAS_HCD
136 select USB
137 help
138 Say Y here if you have an Apple USB BCM5974 Multitouch
139 trackpad.
140
141 The BCM5974 is the multitouch trackpad found in the Macbook
142 Air (JAN2008) and Macbook Pro Penryn (FEB2008) laptops.
143
144 It is also found in the IPhone (2007) and Ipod Touch (2008).
145
146 This driver provides multitouch functionality together with
147 the synaptics X11 driver.
148
149 The interface is currently identical to the appletouch interface,
150 for further information, see
151 <file:Documentation/input/appletouch.txt>.
152
153 To compile this driver as a module, choose M here: the
154 module will be called bcm5974.
155
133config MOUSE_INPORT 156config MOUSE_INPORT
134 tristate "InPort/MS/ATIXL busmouse" 157 tristate "InPort/MS/ATIXL busmouse"
135 depends on ISA 158 depends on ISA
diff --git a/drivers/input/mouse/Makefile b/drivers/input/mouse/Makefile
index 9e6e36330820..d4d202516090 100644
--- a/drivers/input/mouse/Makefile
+++ b/drivers/input/mouse/Makefile
@@ -6,6 +6,7 @@
6 6
7obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o 7obj-$(CONFIG_MOUSE_AMIGA) += amimouse.o
8obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o 8obj-$(CONFIG_MOUSE_APPLETOUCH) += appletouch.o
9obj-$(CONFIG_MOUSE_BCM5974) += bcm5974.o
9obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o 10obj-$(CONFIG_MOUSE_ATARI) += atarimouse.o
10obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o 11obj-$(CONFIG_MOUSE_RISCPC) += rpcmouse.o
11obj-$(CONFIG_MOUSE_INPORT) += inport.o 12obj-$(CONFIG_MOUSE_INPORT) += inport.o
diff --git a/drivers/input/mouse/bcm5974.c b/drivers/input/mouse/bcm5974.c
new file mode 100644
index 000000000000..6f852786c528
--- /dev/null
+++ b/drivers/input/mouse/bcm5974.c
@@ -0,0 +1,669 @@
1/*
2 * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
3 *
4 * Copyright (C) 2008 Henrik Rydberg (rydberg@euromail.se)
5 *
6 * The USB initialization and package decoding was made by
7 * Scott Shawcroft as part of the touchd user-space driver project:
8 * Copyright (C) 2008 Scott Shawcroft (scott.shawcroft@gmail.com)
9 *
10 * The BCM5974 driver is based on the appletouch driver:
11 * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
12 * Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
13 * Copyright (C) 2005 Stelian Pop (stelian@popies.net)
14 * Copyright (C) 2005 Frank Arnold (frank@scirocco-5v-turbo.de)
15 * Copyright (C) 2005 Peter Osterlund (petero2@telia.com)
16 * Copyright (C) 2005 Michael Hanselmann (linux-kernel@hansmi.ch)
17 * Copyright (C) 2006 Nicolas Boichat (nicolas@boichat.ch)
18 *
19 * This program is free software; you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation; either version 2 of the License, or
22 * (at your option) any later version.
23 *
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
28 *
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32 *
33 */
34
35#include <linux/kernel.h>
36#include <linux/errno.h>
37#include <linux/init.h>
38#include <linux/slab.h>
39#include <linux/module.h>
40#include <linux/usb/input.h>
41#include <linux/hid.h>
42#include <linux/mutex.h>
43
44#define USB_VENDOR_ID_APPLE 0x05ac
45
46/* MacbookAir, aka wellspring */
47#define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI 0x0223
48#define USB_DEVICE_ID_APPLE_WELLSPRING_ISO 0x0224
49#define USB_DEVICE_ID_APPLE_WELLSPRING_JIS 0x0225
50/* MacbookProPenryn, aka wellspring2 */
51#define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI 0x0230
52#define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO 0x0231
53#define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS 0x0232
54
55#define BCM5974_DEVICE(prod) { \
56 .match_flags = (USB_DEVICE_ID_MATCH_DEVICE | \
57 USB_DEVICE_ID_MATCH_INT_CLASS | \
58 USB_DEVICE_ID_MATCH_INT_PROTOCOL), \
59 .idVendor = USB_VENDOR_ID_APPLE, \
60 .idProduct = (prod), \
61 .bInterfaceClass = USB_INTERFACE_CLASS_HID, \
62 .bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE \
63}
64
65/* table of devices that work with this driver */
66static const struct usb_device_id bcm5974_table [] = {
67 /* MacbookAir1.1 */
68 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
69 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
70 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
71 /* MacbookProPenryn */
72 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
73 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
74 BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
75 /* Terminating entry */
76 {}
77};
78MODULE_DEVICE_TABLE(usb, bcm5974_table);
79
80MODULE_AUTHOR("Henrik Rydberg");
81MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
82MODULE_LICENSE("GPL");
83
84#define dprintk(level, format, a...)\
85 { if (debug >= level) printk(KERN_DEBUG format, ##a); }
86
87static int debug = 1;
88module_param(debug, int, 0644);
89MODULE_PARM_DESC(debug, "Activate debugging output");
90
91/* button data structure */
92struct bt_data {
93 u8 unknown1; /* constant */
94 u8 button; /* left button */
95 u8 rel_x; /* relative x coordinate */
96 u8 rel_y; /* relative y coordinate */
97};
98
99/* trackpad header structure */
100struct tp_header {
101 u8 unknown1[16]; /* constants, timers, etc */
102 u8 fingers; /* number of fingers on trackpad */
103 u8 unknown2[9]; /* constants, timers, etc */
104};
105
106/* trackpad finger structure */
107struct tp_finger {
108 __le16 origin; /* left/right origin? */
109 __le16 abs_x; /* absolute x coodinate */
110 __le16 abs_y; /* absolute y coodinate */
111 __le16 rel_x; /* relative x coodinate */
112 __le16 rel_y; /* relative y coodinate */
113 __le16 size_major; /* finger size, major axis? */
114 __le16 size_minor; /* finger size, minor axis? */
115 __le16 orientation; /* 16384 when point, else 15 bit angle */
116 __le16 force_major; /* trackpad force, major axis? */
117 __le16 force_minor; /* trackpad force, minor axis? */
118 __le16 unused[3]; /* zeros */
119 __le16 multi; /* one finger: varies, more fingers: constant */
120};
121
122/* trackpad data structure, empirically at least ten fingers */
123struct tp_data {
124 struct tp_header header;
125 struct tp_finger finger[16];
126};
127
128/* device-specific parameters */
129struct bcm5974_param {
130 int dim; /* logical dimension */
131 int fuzz; /* logical noise value */
132 int devmin; /* device minimum reading */
133 int devmax; /* device maximum reading */
134};
135
136/* device-specific configuration */
137struct bcm5974_config {
138 int ansi, iso, jis; /* the product id of this device */
139 int bt_ep; /* the endpoint of the button interface */
140 int bt_datalen; /* data length of the button interface */
141 int tp_ep; /* the endpoint of the trackpad interface */
142 int tp_datalen; /* data length of the trackpad interface */
143 struct bcm5974_param p; /* finger pressure limits */
144 struct bcm5974_param w; /* finger width limits */
145 struct bcm5974_param x; /* horizontal limits */
146 struct bcm5974_param y; /* vertical limits */
147};
148
149/* logical device structure */
150struct bcm5974 {
151 char phys[64];
152 struct usb_device *udev; /* usb device */
153 struct input_dev *input; /* input dev */
154 struct bcm5974_config cfg; /* device configuration */
155 struct mutex pm_mutex; /* serialize access to open/suspend */
156 int opened; /* 1: opened, 0: closed */
157 struct urb *bt_urb; /* button usb request block */
158 struct bt_data *bt_data; /* button transferred data */
159 struct urb *tp_urb; /* trackpad usb request block */
160 struct tp_data *tp_data; /* trackpad transferred data */
161};
162
163/* logical dimensions */
164#define DIM_PRESSURE 256 /* maximum finger pressure */
165#define DIM_WIDTH 16 /* maximum finger width */
166#define DIM_X 1280 /* maximum trackpad x value */
167#define DIM_Y 800 /* maximum trackpad y value */
168
169/* logical signal quality */
170#define SN_PRESSURE 45 /* pressure signal-to-noise ratio */
171#define SN_WIDTH 100 /* width signal-to-noise ratio */
172#define SN_COORD 250 /* coordinate signal-to-noise ratio */
173
174/* device constants */
175static const struct bcm5974_config bcm5974_config_table[] = {
176 {
177 USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
178 USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
179 USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
180 0x84, sizeof(struct bt_data),
181 0x81, sizeof(struct tp_data),
182 { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
183 { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
184 { DIM_X, DIM_X / SN_COORD, -4824, 5342 },
185 { DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
186 },
187 {
188 USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
189 USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
190 USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
191 0x84, sizeof(struct bt_data),
192 0x81, sizeof(struct tp_data),
193 { DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
194 { DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
195 { DIM_X, DIM_X / SN_COORD, -4824, 4824 },
196 { DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
197 },
198 {}
199};
200
201/* return the device-specific configuration by device */
202static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
203{
204 u16 id = le16_to_cpu(udev->descriptor.idProduct);
205 const struct bcm5974_config *cfg;
206
207 for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
208 if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
209 return cfg;
210
211 return bcm5974_config_table;
212}
213
214/* convert 16-bit little endian to signed integer */
215static inline int raw2int(__le16 x)
216{
217 return (signed short)le16_to_cpu(x);
218}
219
220/* scale device data to logical dimensions (asserts devmin < devmax) */
221static inline int int2scale(const struct bcm5974_param *p, int x)
222{
223 return x * p->dim / (p->devmax - p->devmin);
224}
225
226/* all logical value ranges are [0,dim). */
227static inline int int2bound(const struct bcm5974_param *p, int x)
228{
229 int s = int2scale(p, x);
230
231 return clamp_val(s, 0, p->dim - 1);
232}
233
234/* setup which logical events to report */
235static void setup_events_to_report(struct input_dev *input_dev,
236 const struct bcm5974_config *cfg)
237{
238 __set_bit(EV_ABS, input_dev->evbit);
239
240 input_set_abs_params(input_dev, ABS_PRESSURE,
241 0, cfg->p.dim, cfg->p.fuzz, 0);
242 input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
243 0, cfg->w.dim, cfg->w.fuzz, 0);
244 input_set_abs_params(input_dev, ABS_X,
245 0, cfg->x.dim, cfg->x.fuzz, 0);
246 input_set_abs_params(input_dev, ABS_Y,
247 0, cfg->y.dim, cfg->y.fuzz, 0);
248
249 __set_bit(EV_KEY, input_dev->evbit);
250 __set_bit(BTN_TOOL_FINGER, input_dev->keybit);
251 __set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
252 __set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
253 __set_bit(BTN_LEFT, input_dev->keybit);
254}
255
256/* report button data as logical button state */
257static int report_bt_state(struct bcm5974 *dev, int size)
258{
259 if (size != sizeof(struct bt_data))
260 return -EIO;
261
262 input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
263 input_sync(dev->input);
264
265 return 0;
266}
267
268/* report trackpad data as logical trackpad state */
269static int report_tp_state(struct bcm5974 *dev, int size)
270{
271 const struct bcm5974_config *c = &dev->cfg;
272 const struct tp_finger *f = dev->tp_data->finger;
273 struct input_dev *input = dev->input;
274 const int fingers = (size - 26) / 28;
275 int p = 0, w, x, y, n = 0;
276
277 if (size < 26 || (size - 26) % 28 != 0)
278 return -EIO;
279
280 if (fingers) {
281 p = raw2int(f->force_major);
282 w = raw2int(f->size_major);
283 x = raw2int(f->abs_x);
284 y = raw2int(f->abs_y);
285 n = p > 0 ? fingers : 0;
286
287 dprintk(9,
288 "bcm5974: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
289 p, w, x, y, n);
290
291 input_report_abs(input, ABS_TOOL_WIDTH, int2bound(&c->w, w));
292 input_report_abs(input, ABS_X, int2bound(&c->x, x - c->x.devmin));
293 input_report_abs(input, ABS_Y, int2bound(&c->y, c->y.devmax - y));
294 }
295
296 input_report_abs(input, ABS_PRESSURE, int2bound(&c->p, p));
297
298 input_report_key(input, BTN_TOOL_FINGER, n == 1);
299 input_report_key(input, BTN_TOOL_DOUBLETAP, n == 2);
300 input_report_key(input, BTN_TOOL_TRIPLETAP, n > 2);
301
302 input_sync(input);
303
304 return 0;
305}
306
307/* Wellspring initialization constants */
308#define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID 1
309#define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID 9
310#define BCM5974_WELLSPRING_MODE_REQUEST_VALUE 0x300
311#define BCM5974_WELLSPRING_MODE_REQUEST_INDEX 0
312#define BCM5974_WELLSPRING_MODE_VENDOR_VALUE 0x01
313
314static int bcm5974_wellspring_mode(struct bcm5974 *dev)
315{
316 char *data = kmalloc(8, GFP_KERNEL);
317 int retval = 0, size;
318
319 if (!data) {
320 err("bcm5974: out of memory");
321 retval = -ENOMEM;
322 goto out;
323 }
324
325 /* read configuration */
326 size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
327 BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
328 USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
329 BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
330 BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
331
332 if (size != 8) {
333 err("bcm5974: could not read from device");
334 retval = -EIO;
335 goto out;
336 }
337
338 /* apply the mode switch */
339 data[0] = BCM5974_WELLSPRING_MODE_VENDOR_VALUE;
340
341 /* write configuration */
342 size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
343 BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
344 USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
345 BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
346 BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
347
348 if (size != 8) {
349 err("bcm5974: could not write to device");
350 retval = -EIO;
351 goto out;
352 }
353
354 dprintk(2, "bcm5974: switched to wellspring mode.\n");
355
356 out:
357 kfree(data);
358 return retval;
359}
360
361static void bcm5974_irq_button(struct urb *urb)
362{
363 struct bcm5974 *dev = urb->context;
364 int error;
365
366 switch (urb->status) {
367 case 0:
368 break;
369 case -EOVERFLOW:
370 case -ECONNRESET:
371 case -ENOENT:
372 case -ESHUTDOWN:
373 dbg("bcm5974: button urb shutting down: %d", urb->status);
374 return;
375 default:
376 dbg("bcm5974: button urb status: %d", urb->status);
377 goto exit;
378 }
379
380 if (report_bt_state(dev, dev->bt_urb->actual_length))
381 dprintk(1, "bcm5974: bad button package, length: %d\n",
382 dev->bt_urb->actual_length);
383
384exit:
385 error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
386 if (error)
387 err("bcm5974: button urb failed: %d", error);
388}
389
390static void bcm5974_irq_trackpad(struct urb *urb)
391{
392 struct bcm5974 *dev = urb->context;
393 int error;
394
395 switch (urb->status) {
396 case 0:
397 break;
398 case -EOVERFLOW:
399 case -ECONNRESET:
400 case -ENOENT:
401 case -ESHUTDOWN:
402 dbg("bcm5974: trackpad urb shutting down: %d", urb->status);
403 return;
404 default:
405 dbg("bcm5974: trackpad urb status: %d", urb->status);
406 goto exit;
407 }
408
409 /* control response ignored */
410 if (dev->tp_urb->actual_length == 2)
411 goto exit;
412
413 if (report_tp_state(dev, dev->tp_urb->actual_length))
414 dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
415 dev->tp_urb->actual_length);
416
417exit:
418 error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
419 if (error)
420 err("bcm5974: trackpad urb failed: %d", error);
421}
422
423/*
424 * The Wellspring trackpad, like many recent Apple trackpads, share
425 * the usb device with the keyboard. Since keyboards are usually
426 * handled by the HID system, the device ends up being handled by two
427 * modules. Setting up the device therefore becomes slightly
428 * complicated. To enable multitouch features, a mode switch is
429 * required, which is usually applied via the control interface of the
430 * device. It can be argued where this switch should take place. In
431 * some drivers, like appletouch, the switch is made during
432 * probe. However, the hid module may also alter the state of the
433 * device, resulting in trackpad malfunction under certain
434 * circumstances. To get around this problem, there is at least one
435 * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
436 * recieve a reset_resume request rather than the normal resume.
437 * Since the implementation of reset_resume is equal to mode switch
438 * plus start_traffic, it seems easier to always do the switch when
439 * starting traffic on the device.
440 */
441static int bcm5974_start_traffic(struct bcm5974 *dev)
442{
443 if (bcm5974_wellspring_mode(dev)) {
444 dprintk(1, "bcm5974: mode switch failed\n");
445 goto error;
446 }
447
448 if (usb_submit_urb(dev->bt_urb, GFP_KERNEL))
449 goto error;
450
451 if (usb_submit_urb(dev->tp_urb, GFP_KERNEL))
452 goto err_kill_bt;
453
454 return 0;
455
456err_kill_bt:
457 usb_kill_urb(dev->bt_urb);
458error:
459 return -EIO;
460}
461
462static void bcm5974_pause_traffic(struct bcm5974 *dev)
463{
464 usb_kill_urb(dev->tp_urb);
465 usb_kill_urb(dev->bt_urb);
466}
467
468/*
469 * The code below implements open/close and manual suspend/resume.
470 * All functions may be called in random order.
471 *
472 * Opening a suspended device fails with EACCES - permission denied.
473 *
474 * Failing a resume leaves the device resumed but closed.
475 */
476static int bcm5974_open(struct input_dev *input)
477{
478 struct bcm5974 *dev = input_get_drvdata(input);
479 int error;
480
481 mutex_lock(&dev->pm_mutex);
482
483 error = bcm5974_start_traffic(dev);
484 if (!error)
485 dev->opened = 1;
486
487 mutex_unlock(&dev->pm_mutex);
488
489 return error;
490}
491
492static void bcm5974_close(struct input_dev *input)
493{
494 struct bcm5974 *dev = input_get_drvdata(input);
495
496 mutex_lock(&dev->pm_mutex);
497
498 bcm5974_pause_traffic(dev);
499 dev->opened = 0;
500
501 mutex_unlock(&dev->pm_mutex);
502}
503
504static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
505{
506 struct bcm5974 *dev = usb_get_intfdata(iface);
507
508 mutex_lock(&dev->pm_mutex);
509
510 if (dev->opened)
511 bcm5974_pause_traffic(dev);
512
513 mutex_unlock(&dev->pm_mutex);
514
515 return 0;
516}
517
518static int bcm5974_resume(struct usb_interface *iface)
519{
520 struct bcm5974 *dev = usb_get_intfdata(iface);
521 int error = 0;
522
523 mutex_lock(&dev->pm_mutex);
524
525 if (dev->opened)
526 error = bcm5974_start_traffic(dev);
527
528 mutex_unlock(&dev->pm_mutex);
529
530 return error;
531}
532
533static int bcm5974_probe(struct usb_interface *iface,
534 const struct usb_device_id *id)
535{
536 struct usb_device *udev = interface_to_usbdev(iface);
537 const struct bcm5974_config *cfg;
538 struct bcm5974 *dev;
539 struct input_dev *input_dev;
540 int error = -ENOMEM;
541
542 /* find the product index */
543 cfg = bcm5974_get_config(udev);
544
545 /* allocate memory for our device state and initialize it */
546 dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
547 input_dev = input_allocate_device();
548 if (!dev || !input_dev) {
549 err("bcm5974: out of memory");
550 goto err_free_devs;
551 }
552
553 dev->udev = udev;
554 dev->input = input_dev;
555 dev->cfg = *cfg;
556 mutex_init(&dev->pm_mutex);
557
558 /* setup urbs */
559 dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
560 if (!dev->bt_urb)
561 goto err_free_devs;
562
563 dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
564 if (!dev->tp_urb)
565 goto err_free_bt_urb;
566
567 dev->bt_data = usb_buffer_alloc(dev->udev,
568 dev->cfg.bt_datalen, GFP_KERNEL,
569 &dev->bt_urb->transfer_dma);
570 if (!dev->bt_data)
571 goto err_free_urb;
572
573 dev->tp_data = usb_buffer_alloc(dev->udev,
574 dev->cfg.tp_datalen, GFP_KERNEL,
575 &dev->tp_urb->transfer_dma);
576 if (!dev->tp_data)
577 goto err_free_bt_buffer;
578
579 usb_fill_int_urb(dev->bt_urb, udev,
580 usb_rcvintpipe(udev, cfg->bt_ep),
581 dev->bt_data, dev->cfg.bt_datalen,
582 bcm5974_irq_button, dev, 1);
583
584 usb_fill_int_urb(dev->tp_urb, udev,
585 usb_rcvintpipe(udev, cfg->tp_ep),
586 dev->tp_data, dev->cfg.tp_datalen,
587 bcm5974_irq_trackpad, dev, 1);
588
589 /* create bcm5974 device */
590 usb_make_path(udev, dev->phys, sizeof(dev->phys));
591 strlcat(dev->phys, "/input0", sizeof(dev->phys));
592
593 input_dev->name = "bcm5974";
594 input_dev->phys = dev->phys;
595 usb_to_input_id(dev->udev, &input_dev->id);
596 input_dev->dev.parent = &iface->dev;
597
598 input_set_drvdata(input_dev, dev);
599
600 input_dev->open = bcm5974_open;
601 input_dev->close = bcm5974_close;
602
603 setup_events_to_report(input_dev, cfg);
604
605 error = input_register_device(dev->input);
606 if (error)
607 goto err_free_buffer;
608
609 /* save our data pointer in this interface device */
610 usb_set_intfdata(iface, dev);
611
612 return 0;
613
614err_free_buffer:
615 usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
616 dev->tp_data, dev->tp_urb->transfer_dma);
617err_free_bt_buffer:
618 usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
619 dev->bt_data, dev->bt_urb->transfer_dma);
620err_free_urb:
621 usb_free_urb(dev->tp_urb);
622err_free_bt_urb:
623 usb_free_urb(dev->bt_urb);
624err_free_devs:
625 usb_set_intfdata(iface, NULL);
626 input_free_device(input_dev);
627 kfree(dev);
628 return error;
629}
630
631static void bcm5974_disconnect(struct usb_interface *iface)
632{
633 struct bcm5974 *dev = usb_get_intfdata(iface);
634
635 usb_set_intfdata(iface, NULL);
636
637 input_unregister_device(dev->input);
638 usb_buffer_free(dev->udev, dev->cfg.tp_datalen,
639 dev->tp_data, dev->tp_urb->transfer_dma);
640 usb_buffer_free(dev->udev, dev->cfg.bt_datalen,
641 dev->bt_data, dev->bt_urb->transfer_dma);
642 usb_free_urb(dev->tp_urb);
643 usb_free_urb(dev->bt_urb);
644 kfree(dev);
645}
646
647static struct usb_driver bcm5974_driver = {
648 .name = "bcm5974",
649 .probe = bcm5974_probe,
650 .disconnect = bcm5974_disconnect,
651 .suspend = bcm5974_suspend,
652 .resume = bcm5974_resume,
653 .reset_resume = bcm5974_resume,
654 .id_table = bcm5974_table,
655};
656
657static int __init bcm5974_init(void)
658{
659 return usb_register(&bcm5974_driver);
660}
661
662static void __exit bcm5974_exit(void)
663{
664 usb_deregister(&bcm5974_driver);
665}
666
667module_init(bcm5974_init);
668module_exit(bcm5974_exit);
669