xref: /linux/drivers/input/mouse/bcm5974.c (revision 148c1c8ad3c4170186ebe6ea5900adde27d2a0e7)
1f89bd95cSHenrik Rydberg /*
2f89bd95cSHenrik Rydberg  * Apple USB BCM5974 (Macbook Air and Penryn Macbook Pro) multitouch driver
3f89bd95cSHenrik Rydberg  *
4f89bd95cSHenrik Rydberg  * Copyright (C) 2008	   Henrik Rydberg (rydberg@euromail.se)
5f89bd95cSHenrik Rydberg  *
6f89bd95cSHenrik Rydberg  * The USB initialization and package decoding was made by
7f89bd95cSHenrik Rydberg  * Scott Shawcroft as part of the touchd user-space driver project:
8f89bd95cSHenrik Rydberg  * Copyright (C) 2008	   Scott Shawcroft (scott.shawcroft@gmail.com)
9f89bd95cSHenrik Rydberg  *
10f89bd95cSHenrik Rydberg  * The BCM5974 driver is based on the appletouch driver:
11f89bd95cSHenrik Rydberg  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
12f89bd95cSHenrik Rydberg  * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
13f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Stelian Pop (stelian@popies.net)
14f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Frank Arnold (frank@scirocco-5v-turbo.de)
15f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Peter Osterlund (petero2@telia.com)
16f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Michael Hanselmann (linux-kernel@hansmi.ch)
17f89bd95cSHenrik Rydberg  * Copyright (C) 2006	   Nicolas Boichat (nicolas@boichat.ch)
18f89bd95cSHenrik Rydberg  *
19f89bd95cSHenrik Rydberg  * This program is free software; you can redistribute it and/or modify
20f89bd95cSHenrik Rydberg  * it under the terms of the GNU General Public License as published by
21f89bd95cSHenrik Rydberg  * the Free Software Foundation; either version 2 of the License, or
22f89bd95cSHenrik Rydberg  * (at your option) any later version.
23f89bd95cSHenrik Rydberg  *
24f89bd95cSHenrik Rydberg  * This program is distributed in the hope that it will be useful,
25f89bd95cSHenrik Rydberg  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26f89bd95cSHenrik Rydberg  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
27f89bd95cSHenrik Rydberg  * GNU General Public License for more details.
28f89bd95cSHenrik Rydberg  *
29f89bd95cSHenrik Rydberg  * You should have received a copy of the GNU General Public License
30f89bd95cSHenrik Rydberg  * along with this program; if not, write to the Free Software
31f89bd95cSHenrik Rydberg  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
32f89bd95cSHenrik Rydberg  *
33f89bd95cSHenrik Rydberg  */
34f89bd95cSHenrik Rydberg 
35f89bd95cSHenrik Rydberg #include <linux/kernel.h>
36f89bd95cSHenrik Rydberg #include <linux/errno.h>
37f89bd95cSHenrik Rydberg #include <linux/init.h>
38f89bd95cSHenrik Rydberg #include <linux/slab.h>
39f89bd95cSHenrik Rydberg #include <linux/module.h>
40f89bd95cSHenrik Rydberg #include <linux/usb/input.h>
41f89bd95cSHenrik Rydberg #include <linux/hid.h>
42f89bd95cSHenrik Rydberg #include <linux/mutex.h>
4351c80b74SHenrik Rydberg #include <linux/input/mt.h>
44f89bd95cSHenrik Rydberg 
45f89bd95cSHenrik Rydberg #define USB_VENDOR_ID_APPLE		0x05ac
46f89bd95cSHenrik Rydberg 
47f89bd95cSHenrik Rydberg /* MacbookAir, aka wellspring */
48f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223
49f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224
50f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225
51f89bd95cSHenrik Rydberg /* MacbookProPenryn, aka wellspring2 */
52f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230
53f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231
54f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232
55158e9287SHenrik Rydberg /* Macbook5,1 (unibody), aka wellspring3 */
56158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI	0x0236
57158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO	0x0237
58158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS	0x0238
596021afcfSEdgar (gimli) Hucek /* MacbookAir3,2 (unibody), aka wellspring5 */
606021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI	0x023f
616021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO	0x0240
626021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS	0x0241
636021afcfSEdgar (gimli) Hucek /* MacbookAir3,1 (unibody), aka wellspring4 */
646021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI	0x0242
656021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO	0x0243
666021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS	0x0244
6747340bd9SAndy Botting /* Macbook8 (unibody, March 2011) */
6847340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI	0x0245
6947340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO	0x0246
7047340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS	0x0247
711c601beaSPieter-Augustijn Van Malleghem /* MacbookAir4,1 (unibody, July 2011) */
721c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI	0x0249
731c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO	0x024a
741c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS	0x024b
75db0b34b0SJoshua V. Dillon /* MacbookAir4,2 (unibody, July 2011) */
76db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI	0x024c
77db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO	0x024d
78db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS	0x024e
79c331eb58SAndrew Drake /* Macbook8,2 (unibody) */
80c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI	0x0252
81c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO	0x0253
82c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS	0x0254
833dde22a9SHenrik Rydberg /* MacbookPro10,1 (unibody, June 2012) */
843dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI	0x0262
853dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_ISO	0x0263
863dde22a9SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING7_JIS	0x0264
878d80da90SDirk Hohndel /* MacbookPro10,2 (unibody, October 2012) */
888d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI	0x0259
898d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO	0x025a
908d80da90SDirk Hohndel #define USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS	0x025b
91*148c1c8aSDmitry Torokhov /* MacbookAir6,2 (unibody, June 2013) */
92*148c1c8aSDmitry Torokhov #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI	0x0291
93*148c1c8aSDmitry Torokhov #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO	0x0292
94*148c1c8aSDmitry Torokhov #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS	0x0293
95f89bd95cSHenrik Rydberg 
96f89bd95cSHenrik Rydberg #define BCM5974_DEVICE(prod) {					\
97f89bd95cSHenrik Rydberg 	.match_flags = (USB_DEVICE_ID_MATCH_DEVICE |		\
98f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_CLASS |		\
99f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_PROTOCOL),	\
100f89bd95cSHenrik Rydberg 	.idVendor = USB_VENDOR_ID_APPLE,			\
101f89bd95cSHenrik Rydberg 	.idProduct = (prod),					\
102f89bd95cSHenrik Rydberg 	.bInterfaceClass = USB_INTERFACE_CLASS_HID,		\
103f89bd95cSHenrik Rydberg 	.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE	\
104f89bd95cSHenrik Rydberg }
105f89bd95cSHenrik Rydberg 
106f89bd95cSHenrik Rydberg /* table of devices that work with this driver */
107f89bd95cSHenrik Rydberg static const struct usb_device_id bcm5974_table[] = {
108f89bd95cSHenrik Rydberg 	/* MacbookAir1.1 */
109f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
110f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
111f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
112f89bd95cSHenrik Rydberg 	/* MacbookProPenryn */
113f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
114f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
115f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
116158e9287SHenrik Rydberg 	/* Macbook5,1 */
117158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
118158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
119158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
1206021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,2 */
1216021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
1226021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
1236021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
1246021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,1 */
1256021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
1266021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
1276021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
12847340bd9SAndy Botting 	/* MacbookPro8 */
12947340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
13047340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
13147340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
1321c601beaSPieter-Augustijn Van Malleghem 	/* MacbookAir4,1 */
1331c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
1341c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
1351c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
136db0b34b0SJoshua V. Dillon 	/* MacbookAir4,2 */
137db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
138db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
139db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
140c331eb58SAndrew Drake 	/* MacbookPro8,2 */
141c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
142c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
143c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
1443dde22a9SHenrik Rydberg 	/* MacbookPro10,1 */
1453dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI),
1463dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO),
1473dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS),
1488d80da90SDirk Hohndel 	/* MacbookPro10,2 */
1498d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI),
1508d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO),
1518d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS),
152*148c1c8aSDmitry Torokhov 	/* MacbookAir6,2 */
153*148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI),
154*148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO),
155*148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
156f89bd95cSHenrik Rydberg 	/* Terminating entry */
157f89bd95cSHenrik Rydberg 	{}
158f89bd95cSHenrik Rydberg };
159f89bd95cSHenrik Rydberg MODULE_DEVICE_TABLE(usb, bcm5974_table);
160f89bd95cSHenrik Rydberg 
161f89bd95cSHenrik Rydberg MODULE_AUTHOR("Henrik Rydberg");
162f89bd95cSHenrik Rydberg MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
163f89bd95cSHenrik Rydberg MODULE_LICENSE("GPL");
164f89bd95cSHenrik Rydberg 
165f89bd95cSHenrik Rydberg #define dprintk(level, format, a...)\
166f89bd95cSHenrik Rydberg 	{ if (debug >= level) printk(KERN_DEBUG format, ##a); }
167f89bd95cSHenrik Rydberg 
168f89bd95cSHenrik Rydberg static int debug = 1;
169f89bd95cSHenrik Rydberg module_param(debug, int, 0644);
170f89bd95cSHenrik Rydberg MODULE_PARM_DESC(debug, "Activate debugging output");
171f89bd95cSHenrik Rydberg 
172f89bd95cSHenrik Rydberg /* button data structure */
173f89bd95cSHenrik Rydberg struct bt_data {
174f89bd95cSHenrik Rydberg 	u8 unknown1;		/* constant */
175f89bd95cSHenrik Rydberg 	u8 button;		/* left button */
176f89bd95cSHenrik Rydberg 	u8 rel_x;		/* relative x coordinate */
177f89bd95cSHenrik Rydberg 	u8 rel_y;		/* relative y coordinate */
178f89bd95cSHenrik Rydberg };
179f89bd95cSHenrik Rydberg 
1809894cf0fSHenrik Rydberg /* trackpad header types */
1819894cf0fSHenrik Rydberg enum tp_type {
182158e9287SHenrik Rydberg 	TYPE1,			/* plain trackpad */
183*148c1c8aSDmitry Torokhov 	TYPE2,			/* button integrated in trackpad */
184*148c1c8aSDmitry Torokhov 	TYPE3			/* additional header fields since June 2013 */
185f89bd95cSHenrik Rydberg };
186f89bd95cSHenrik Rydberg 
1879894cf0fSHenrik Rydberg /* trackpad finger data offsets, le16-aligned */
1889894cf0fSHenrik Rydberg #define FINGER_TYPE1		(13 * sizeof(__le16))
189158e9287SHenrik Rydberg #define FINGER_TYPE2		(15 * sizeof(__le16))
190*148c1c8aSDmitry Torokhov #define FINGER_TYPE3		(19 * sizeof(__le16))
191158e9287SHenrik Rydberg 
192158e9287SHenrik Rydberg /* trackpad button data offsets */
193158e9287SHenrik Rydberg #define BUTTON_TYPE2		15
194*148c1c8aSDmitry Torokhov #define BUTTON_TYPE3		23
195158e9287SHenrik Rydberg 
196158e9287SHenrik Rydberg /* list of device capability bits */
197158e9287SHenrik Rydberg #define HAS_INTEGRATED_BUTTON	1
1989894cf0fSHenrik Rydberg 
1999894cf0fSHenrik Rydberg /* trackpad finger structure, le16-aligned */
200f89bd95cSHenrik Rydberg struct tp_finger {
20175e21e3fSHenrik Rydberg 	__le16 origin;		/* zero when switching track finger */
202f89bd95cSHenrik Rydberg 	__le16 abs_x;		/* absolute x coodinate */
203f89bd95cSHenrik Rydberg 	__le16 abs_y;		/* absolute y coodinate */
204f89bd95cSHenrik Rydberg 	__le16 rel_x;		/* relative x coodinate */
205f89bd95cSHenrik Rydberg 	__le16 rel_y;		/* relative y coodinate */
206f17953abSHenrik Rydberg 	__le16 tool_major;	/* tool area, major axis */
207f17953abSHenrik Rydberg 	__le16 tool_minor;	/* tool area, minor axis */
208f89bd95cSHenrik Rydberg 	__le16 orientation;	/* 16384 when point, else 15 bit angle */
209f17953abSHenrik Rydberg 	__le16 touch_major;	/* touch area, major axis */
210f17953abSHenrik Rydberg 	__le16 touch_minor;	/* touch area, minor axis */
211f89bd95cSHenrik Rydberg 	__le16 unused[3];	/* zeros */
212f89bd95cSHenrik Rydberg 	__le16 multi;		/* one finger: varies, more fingers: constant */
2139894cf0fSHenrik Rydberg } __attribute__((packed,aligned(2)));
214f89bd95cSHenrik Rydberg 
2159894cf0fSHenrik Rydberg /* trackpad finger data size, empirically at least ten fingers */
216f17953abSHenrik Rydberg #define MAX_FINGERS		16
2179894cf0fSHenrik Rydberg #define SIZEOF_FINGER		sizeof(struct tp_finger)
218f17953abSHenrik Rydberg #define SIZEOF_ALL_FINGERS	(MAX_FINGERS * SIZEOF_FINGER)
2196f2701b7SHenrik Rydberg #define MAX_FINGER_ORIENTATION	16384
220f89bd95cSHenrik Rydberg 
221f89bd95cSHenrik Rydberg /* device-specific parameters */
222f89bd95cSHenrik Rydberg struct bcm5974_param {
2230e726966SHenrik Rydberg 	int snratio;		/* signal-to-noise ratio */
2240e726966SHenrik Rydberg 	int min;		/* device minimum reading */
2250e726966SHenrik Rydberg 	int max;		/* device maximum reading */
226f89bd95cSHenrik Rydberg };
227f89bd95cSHenrik Rydberg 
228f89bd95cSHenrik Rydberg /* device-specific configuration */
229f89bd95cSHenrik Rydberg struct bcm5974_config {
230f89bd95cSHenrik Rydberg 	int ansi, iso, jis;	/* the product id of this device */
231158e9287SHenrik Rydberg 	int caps;		/* device capability bitmask */
232f89bd95cSHenrik Rydberg 	int bt_ep;		/* the endpoint of the button interface */
233f89bd95cSHenrik Rydberg 	int bt_datalen;		/* data length of the button interface */
234f89bd95cSHenrik Rydberg 	int tp_ep;		/* the endpoint of the trackpad interface */
2359894cf0fSHenrik Rydberg 	enum tp_type tp_type;	/* type of trackpad interface */
2369894cf0fSHenrik Rydberg 	int tp_offset;		/* offset to trackpad finger data */
237f89bd95cSHenrik Rydberg 	int tp_datalen;		/* data length of the trackpad interface */
238f89bd95cSHenrik Rydberg 	struct bcm5974_param p;	/* finger pressure limits */
239f89bd95cSHenrik Rydberg 	struct bcm5974_param w;	/* finger width limits */
240f89bd95cSHenrik Rydberg 	struct bcm5974_param x;	/* horizontal limits */
241f89bd95cSHenrik Rydberg 	struct bcm5974_param y;	/* vertical limits */
2420e726966SHenrik Rydberg 	struct bcm5974_param o;	/* orientation limits */
243f89bd95cSHenrik Rydberg };
244f89bd95cSHenrik Rydberg 
245f89bd95cSHenrik Rydberg /* logical device structure */
246f89bd95cSHenrik Rydberg struct bcm5974 {
247f89bd95cSHenrik Rydberg 	char phys[64];
248f89bd95cSHenrik Rydberg 	struct usb_device *udev;	/* usb device */
24988da765fSDmitry Torokhov 	struct usb_interface *intf;	/* our interface */
250f89bd95cSHenrik Rydberg 	struct input_dev *input;	/* input dev */
251f89bd95cSHenrik Rydberg 	struct bcm5974_config cfg;	/* device configuration */
252f89bd95cSHenrik Rydberg 	struct mutex pm_mutex;		/* serialize access to open/suspend */
253f89bd95cSHenrik Rydberg 	int opened;			/* 1: opened, 0: closed */
254f89bd95cSHenrik Rydberg 	struct urb *bt_urb;		/* button usb request block */
255f89bd95cSHenrik Rydberg 	struct bt_data *bt_data;	/* button transferred data */
256f89bd95cSHenrik Rydberg 	struct urb *tp_urb;		/* trackpad usb request block */
2579894cf0fSHenrik Rydberg 	u8 *tp_data;			/* trackpad transferred data */
25851c80b74SHenrik Rydberg 	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
25951c80b74SHenrik Rydberg 	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
26051c80b74SHenrik Rydberg 	int slots[MAX_FINGERS];				/* slot assignments */
261f89bd95cSHenrik Rydberg };
262f89bd95cSHenrik Rydberg 
263f89bd95cSHenrik Rydberg /* logical signal quality */
264f89bd95cSHenrik Rydberg #define SN_PRESSURE	45		/* pressure signal-to-noise ratio */
26551c80b74SHenrik Rydberg #define SN_WIDTH	25		/* width signal-to-noise ratio */
266f89bd95cSHenrik Rydberg #define SN_COORD	250		/* coordinate signal-to-noise ratio */
2670e726966SHenrik Rydberg #define SN_ORIENT	10		/* orientation signal-to-noise ratio */
26875e21e3fSHenrik Rydberg 
269f89bd95cSHenrik Rydberg /* device constants */
270f89bd95cSHenrik Rydberg static const struct bcm5974_config bcm5974_config_table[] = {
271f89bd95cSHenrik Rydberg 	{
272f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
273f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
274f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
275158e9287SHenrik Rydberg 		0,
276f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
2779894cf0fSHenrik Rydberg 		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
2780e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 256 },
2790e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
2800e726966SHenrik Rydberg 		{ SN_COORD, -4824, 5342 },
2810e726966SHenrik Rydberg 		{ SN_COORD, -172, 5820 },
2820e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
283f89bd95cSHenrik Rydberg 	},
284f89bd95cSHenrik Rydberg 	{
285f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
286f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
287f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
288158e9287SHenrik Rydberg 		0,
289f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
2909894cf0fSHenrik Rydberg 		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
2910e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 256 },
2920e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
2930e726966SHenrik Rydberg 		{ SN_COORD, -4824, 4824 },
2940e726966SHenrik Rydberg 		{ SN_COORD, -172, 4290 },
2950e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
296f89bd95cSHenrik Rydberg 	},
297158e9287SHenrik Rydberg 	{
298158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
299158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
300158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
301158e9287SHenrik Rydberg 		HAS_INTEGRATED_BUTTON,
302158e9287SHenrik Rydberg 		0x84, sizeof(struct bt_data),
303158e9287SHenrik Rydberg 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3040e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3050e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3060e726966SHenrik Rydberg 		{ SN_COORD, -4460, 5166 },
3070e726966SHenrik Rydberg 		{ SN_COORD, -75, 6700 },
3080e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
309158e9287SHenrik Rydberg 	},
3106021afcfSEdgar (gimli) Hucek 	{
3116021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
3126021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
3136021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_JIS,
3146021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
3156021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
3166021afcfSEdgar (gimli) Hucek 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3170e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3180e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3190e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
3200e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
3210e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
3226021afcfSEdgar (gimli) Hucek 	},
3236021afcfSEdgar (gimli) Hucek 	{
3246021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
3256021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
3266021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS,
3276021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
3286021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
3296021afcfSEdgar (gimli) Hucek 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3300e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3310e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3320e726966SHenrik Rydberg 		{ SN_COORD, -4616, 5112 },
3330e726966SHenrik Rydberg 		{ SN_COORD, -142, 5234 },
3340e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
3356021afcfSEdgar (gimli) Hucek 	},
33647340bd9SAndy Botting 	{
33747340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
33847340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
33947340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
34047340bd9SAndy Botting 		HAS_INTEGRATED_BUTTON,
34147340bd9SAndy Botting 		0x84, sizeof(struct bt_data),
34247340bd9SAndy Botting 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3430e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3440e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3450e726966SHenrik Rydberg 		{ SN_COORD, -4415, 5050 },
3460e726966SHenrik Rydberg 		{ SN_COORD, -55, 6680 },
3470e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
34847340bd9SAndy Botting 	},
349db0b34b0SJoshua V. Dillon 	{
350db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
351db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
352db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
353db0b34b0SJoshua V. Dillon 		HAS_INTEGRATED_BUTTON,
354db0b34b0SJoshua V. Dillon 		0x84, sizeof(struct bt_data),
355db0b34b0SJoshua V. Dillon 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3560e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3570e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3580e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
3590e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
3600e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
361db0b34b0SJoshua V. Dillon 	},
362c331eb58SAndrew Drake 	{
363c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
364c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
365c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
366c331eb58SAndrew Drake 		HAS_INTEGRATED_BUTTON,
367c331eb58SAndrew Drake 		0x84, sizeof(struct bt_data),
368c331eb58SAndrew Drake 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3690e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3700e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3710e726966SHenrik Rydberg 		{ SN_COORD, -4750, 5280 },
3720e726966SHenrik Rydberg 		{ SN_COORD, -150, 6730 },
3730e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
374c331eb58SAndrew Drake 	},
3751c601beaSPieter-Augustijn Van Malleghem 	{
3761c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
3771c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
3781c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
3791c601beaSPieter-Augustijn Van Malleghem 		HAS_INTEGRATED_BUTTON,
3801c601beaSPieter-Augustijn Van Malleghem 		0x84, sizeof(struct bt_data),
3811c601beaSPieter-Augustijn Van Malleghem 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3820e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3830e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3840e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
3850e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
3860e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
3871c601beaSPieter-Augustijn Van Malleghem 	},
3883dde22a9SHenrik Rydberg 	{
3893dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
3903dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_ISO,
3913dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_JIS,
3923dde22a9SHenrik Rydberg 		HAS_INTEGRATED_BUTTON,
3933dde22a9SHenrik Rydberg 		0x84, sizeof(struct bt_data),
3943dde22a9SHenrik Rydberg 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3950e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3960e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3970e726966SHenrik Rydberg 		{ SN_COORD, -4750, 5280 },
3980e726966SHenrik Rydberg 		{ SN_COORD, -150, 6730 },
3990e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
4003dde22a9SHenrik Rydberg 	},
4018d80da90SDirk Hohndel 	{
4028d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI,
4038d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO,
4048d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS,
4058d80da90SDirk Hohndel 		HAS_INTEGRATED_BUTTON,
4068d80da90SDirk Hohndel 		0x84, sizeof(struct bt_data),
4078d80da90SDirk Hohndel 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
4088d80da90SDirk Hohndel 		{ SN_PRESSURE, 0, 300 },
4098d80da90SDirk Hohndel 		{ SN_WIDTH, 0, 2048 },
4108d80da90SDirk Hohndel 		{ SN_COORD, -4750, 5280 },
4118d80da90SDirk Hohndel 		{ SN_COORD, -150, 6730 },
4128d80da90SDirk Hohndel 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
4138d80da90SDirk Hohndel 	},
414*148c1c8aSDmitry Torokhov 	{
415*148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI,
416*148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_ISO,
417*148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_JIS,
418*148c1c8aSDmitry Torokhov 		HAS_INTEGRATED_BUTTON,
419*148c1c8aSDmitry Torokhov 		0, sizeof(struct bt_data),
420*148c1c8aSDmitry Torokhov 		0x83, TYPE3, FINGER_TYPE3, FINGER_TYPE3 + SIZEOF_ALL_FINGERS,
421*148c1c8aSDmitry Torokhov 		{ SN_PRESSURE, 0, 300 },
422*148c1c8aSDmitry Torokhov 		{ SN_WIDTH, 0, 2048 },
423*148c1c8aSDmitry Torokhov 		{ SN_COORD, -4620, 5140 },
424*148c1c8aSDmitry Torokhov 		{ SN_COORD, -150, 6600 },
425*148c1c8aSDmitry Torokhov 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
426*148c1c8aSDmitry Torokhov 	},
427f89bd95cSHenrik Rydberg 	{}
428f89bd95cSHenrik Rydberg };
429f89bd95cSHenrik Rydberg 
430f89bd95cSHenrik Rydberg /* return the device-specific configuration by device */
431f89bd95cSHenrik Rydberg static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
432f89bd95cSHenrik Rydberg {
433f89bd95cSHenrik Rydberg 	u16 id = le16_to_cpu(udev->descriptor.idProduct);
434f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
435f89bd95cSHenrik Rydberg 
436f89bd95cSHenrik Rydberg 	for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
437f89bd95cSHenrik Rydberg 		if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
438f89bd95cSHenrik Rydberg 			return cfg;
439f89bd95cSHenrik Rydberg 
440f89bd95cSHenrik Rydberg 	return bcm5974_config_table;
441f89bd95cSHenrik Rydberg }
442f89bd95cSHenrik Rydberg 
443f89bd95cSHenrik Rydberg /* convert 16-bit little endian to signed integer */
444f89bd95cSHenrik Rydberg static inline int raw2int(__le16 x)
445f89bd95cSHenrik Rydberg {
446f89bd95cSHenrik Rydberg 	return (signed short)le16_to_cpu(x);
447f89bd95cSHenrik Rydberg }
448f89bd95cSHenrik Rydberg 
4490e726966SHenrik Rydberg static void set_abs(struct input_dev *input, unsigned int code,
4500e726966SHenrik Rydberg 		    const struct bcm5974_param *p)
451f89bd95cSHenrik Rydberg {
4520e726966SHenrik Rydberg 	int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
4530e726966SHenrik Rydberg 	input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
454f89bd95cSHenrik Rydberg }
455f89bd95cSHenrik Rydberg 
456f89bd95cSHenrik Rydberg /* setup which logical events to report */
457f89bd95cSHenrik Rydberg static void setup_events_to_report(struct input_dev *input_dev,
458f89bd95cSHenrik Rydberg 				   const struct bcm5974_config *cfg)
459f89bd95cSHenrik Rydberg {
460f89bd95cSHenrik Rydberg 	__set_bit(EV_ABS, input_dev->evbit);
461f89bd95cSHenrik Rydberg 
4620e726966SHenrik Rydberg 	/* for synaptics only */
4630e726966SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
4640e726966SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
4650e726966SHenrik Rydberg 
4666f2701b7SHenrik Rydberg 	/* finger touch area */
4670e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
4680e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
4696f2701b7SHenrik Rydberg 	/* finger approach area */
4700e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
4710e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
4726f2701b7SHenrik Rydberg 	/* finger orientation */
4730e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
4746f2701b7SHenrik Rydberg 	/* finger position */
4750e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
4760e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
4776f2701b7SHenrik Rydberg 
478f89bd95cSHenrik Rydberg 	__set_bit(EV_KEY, input_dev->evbit);
479f89bd95cSHenrik Rydberg 	__set_bit(BTN_LEFT, input_dev->keybit);
480c13aea03SHenrik Rydberg 
48152965cc0SJussi Pakkanen 	if (cfg->caps & HAS_INTEGRATED_BUTTON)
48252965cc0SJussi Pakkanen 		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
48352965cc0SJussi Pakkanen 
48451c80b74SHenrik Rydberg 	input_mt_init_slots(input_dev, MAX_FINGERS,
48551c80b74SHenrik Rydberg 		INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
486f89bd95cSHenrik Rydberg }
487f89bd95cSHenrik Rydberg 
488f89bd95cSHenrik Rydberg /* report button data as logical button state */
489f89bd95cSHenrik Rydberg static int report_bt_state(struct bcm5974 *dev, int size)
490f89bd95cSHenrik Rydberg {
491f89bd95cSHenrik Rydberg 	if (size != sizeof(struct bt_data))
492f89bd95cSHenrik Rydberg 		return -EIO;
493f89bd95cSHenrik Rydberg 
49453402193SHenrik Rydberg 	dprintk(7,
49553402193SHenrik Rydberg 		"bcm5974: button data: %x %x %x %x\n",
49653402193SHenrik Rydberg 		dev->bt_data->unknown1, dev->bt_data->button,
49753402193SHenrik Rydberg 		dev->bt_data->rel_x, dev->bt_data->rel_y);
49853402193SHenrik Rydberg 
499f89bd95cSHenrik Rydberg 	input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
500f89bd95cSHenrik Rydberg 	input_sync(dev->input);
501f89bd95cSHenrik Rydberg 
502f89bd95cSHenrik Rydberg 	return 0;
503f89bd95cSHenrik Rydberg }
504f89bd95cSHenrik Rydberg 
50551c80b74SHenrik Rydberg static void report_finger_data(struct input_dev *input, int slot,
50651c80b74SHenrik Rydberg 			       const struct input_mt_pos *pos,
5076f2701b7SHenrik Rydberg 			       const struct tp_finger *f)
5086f2701b7SHenrik Rydberg {
50951c80b74SHenrik Rydberg 	input_mt_slot(input, slot);
51051c80b74SHenrik Rydberg 	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
51151c80b74SHenrik Rydberg 
51257157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
513f17953abSHenrik Rydberg 			 raw2int(f->touch_major) << 1);
51457157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MINOR,
515f17953abSHenrik Rydberg 			 raw2int(f->touch_minor) << 1);
51657157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
517f17953abSHenrik Rydberg 			 raw2int(f->tool_major) << 1);
51857157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MINOR,
519f17953abSHenrik Rydberg 			 raw2int(f->tool_minor) << 1);
5206f2701b7SHenrik Rydberg 	input_report_abs(input, ABS_MT_ORIENTATION,
5216f2701b7SHenrik Rydberg 			 MAX_FINGER_ORIENTATION - raw2int(f->orientation));
52251c80b74SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_X, pos->x);
52351c80b74SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
5246f2701b7SHenrik Rydberg }
5256f2701b7SHenrik Rydberg 
5260e726966SHenrik Rydberg static void report_synaptics_data(struct input_dev *input,
5270e726966SHenrik Rydberg 				  const struct bcm5974_config *cfg,
5280e726966SHenrik Rydberg 				  const struct tp_finger *f, int raw_n)
5290e726966SHenrik Rydberg {
5300e726966SHenrik Rydberg 	int abs_p = 0, abs_w = 0;
5310e726966SHenrik Rydberg 
5320e726966SHenrik Rydberg 	if (raw_n) {
5330e726966SHenrik Rydberg 		int p = raw2int(f->touch_major);
5340e726966SHenrik Rydberg 		int w = raw2int(f->tool_major);
5350e726966SHenrik Rydberg 		if (p > 0 && raw2int(f->origin)) {
5360e726966SHenrik Rydberg 			abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
5370e726966SHenrik Rydberg 			abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
5380e726966SHenrik Rydberg 		}
5390e726966SHenrik Rydberg 	}
5400e726966SHenrik Rydberg 
5410e726966SHenrik Rydberg 	input_report_abs(input, ABS_PRESSURE, abs_p);
5420e726966SHenrik Rydberg 	input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
5430e726966SHenrik Rydberg }
5440e726966SHenrik Rydberg 
545f89bd95cSHenrik Rydberg /* report trackpad data as logical trackpad state */
546f89bd95cSHenrik Rydberg static int report_tp_state(struct bcm5974 *dev, int size)
547f89bd95cSHenrik Rydberg {
548f89bd95cSHenrik Rydberg 	const struct bcm5974_config *c = &dev->cfg;
5499894cf0fSHenrik Rydberg 	const struct tp_finger *f;
550f89bd95cSHenrik Rydberg 	struct input_dev *input = dev->input;
55151c80b74SHenrik Rydberg 	int raw_n, i, n = 0;
552f89bd95cSHenrik Rydberg 
5539894cf0fSHenrik Rydberg 	if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
554f89bd95cSHenrik Rydberg 		return -EIO;
555f89bd95cSHenrik Rydberg 
5569894cf0fSHenrik Rydberg 	/* finger data, le16-aligned */
5579894cf0fSHenrik Rydberg 	f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
5589894cf0fSHenrik Rydberg 	raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
5599894cf0fSHenrik Rydberg 
56051c80b74SHenrik Rydberg 	for (i = 0; i < raw_n; i++) {
56151c80b74SHenrik Rydberg 		if (raw2int(f[i].touch_major) == 0)
56251c80b74SHenrik Rydberg 			continue;
56351c80b74SHenrik Rydberg 		dev->pos[n].x = raw2int(f[i].abs_x);
56451c80b74SHenrik Rydberg 		dev->pos[n].y = c->y.min + c->y.max - raw2int(f[i].abs_y);
56551c80b74SHenrik Rydberg 		dev->index[n++] = &f[i];
56675e21e3fSHenrik Rydberg 	}
567f89bd95cSHenrik Rydberg 
56851c80b74SHenrik Rydberg 	input_mt_assign_slots(input, dev->slots, dev->pos, n);
56951c80b74SHenrik Rydberg 
57051c80b74SHenrik Rydberg 	for (i = 0; i < n; i++)
57151c80b74SHenrik Rydberg 		report_finger_data(input, dev->slots[i],
57251c80b74SHenrik Rydberg 				   &dev->pos[i], dev->index[i]);
57351c80b74SHenrik Rydberg 
57451c80b74SHenrik Rydberg 	input_mt_sync_frame(input);
57550635115SHenrik Rydberg 
5760e726966SHenrik Rydberg 	report_synaptics_data(input, c, f, raw_n);
57775e21e3fSHenrik Rydberg 
578158e9287SHenrik Rydberg 	/* type 2 reports button events via ibt only */
5790e726966SHenrik Rydberg 	if (c->tp_type == TYPE2) {
5800e726966SHenrik Rydberg 		int ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
581158e9287SHenrik Rydberg 		input_report_key(input, BTN_LEFT, ibt);
5820e726966SHenrik Rydberg 	}
583158e9287SHenrik Rydberg 
584*148c1c8aSDmitry Torokhov 	if (c->tp_type == TYPE3)
585*148c1c8aSDmitry Torokhov 		input_report_key(input, BTN_LEFT, dev->tp_data[BUTTON_TYPE3]);
586*148c1c8aSDmitry Torokhov 
587f89bd95cSHenrik Rydberg 	input_sync(input);
588f89bd95cSHenrik Rydberg 
589f89bd95cSHenrik Rydberg 	return 0;
590f89bd95cSHenrik Rydberg }
591f89bd95cSHenrik Rydberg 
592f89bd95cSHenrik Rydberg /* Wellspring initialization constants */
593f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID		1
594f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID	9
595f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_REQUEST_VALUE		0x300
596f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_REQUEST_INDEX		0
597f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_VENDOR_VALUE		0x01
598cd72ad3fSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_NORMAL_VALUE		0x08
599f89bd95cSHenrik Rydberg 
600cd72ad3fSHenrik Rydberg static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
601f89bd95cSHenrik Rydberg {
602f89bd95cSHenrik Rydberg 	int retval = 0, size;
603*148c1c8aSDmitry Torokhov 	char *data;
604f89bd95cSHenrik Rydberg 
605*148c1c8aSDmitry Torokhov 	/* Type 3 does not require a mode switch */
606*148c1c8aSDmitry Torokhov 	if (dev->cfg.tp_type == TYPE3)
607*148c1c8aSDmitry Torokhov 		return 0;
608*148c1c8aSDmitry Torokhov 
609*148c1c8aSDmitry Torokhov 	data = kmalloc(8, GFP_KERNEL);
610f89bd95cSHenrik Rydberg 	if (!data) {
611ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "out of memory\n");
612f89bd95cSHenrik Rydberg 		retval = -ENOMEM;
613f89bd95cSHenrik Rydberg 		goto out;
614f89bd95cSHenrik Rydberg 	}
615f89bd95cSHenrik Rydberg 
616f89bd95cSHenrik Rydberg 	/* read configuration */
617f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
618f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
619f89bd95cSHenrik Rydberg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
620f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
621f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
622f89bd95cSHenrik Rydberg 
623f89bd95cSHenrik Rydberg 	if (size != 8) {
624ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "could not read from device\n");
625f89bd95cSHenrik Rydberg 		retval = -EIO;
626f89bd95cSHenrik Rydberg 		goto out;
627f89bd95cSHenrik Rydberg 	}
628f89bd95cSHenrik Rydberg 
629f89bd95cSHenrik Rydberg 	/* apply the mode switch */
630cd72ad3fSHenrik Rydberg 	data[0] = on ?
631cd72ad3fSHenrik Rydberg 		BCM5974_WELLSPRING_MODE_VENDOR_VALUE :
632cd72ad3fSHenrik Rydberg 		BCM5974_WELLSPRING_MODE_NORMAL_VALUE;
633f89bd95cSHenrik Rydberg 
634f89bd95cSHenrik Rydberg 	/* write configuration */
635f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
636f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
637f89bd95cSHenrik Rydberg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
638f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
639f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
640f89bd95cSHenrik Rydberg 
641f89bd95cSHenrik Rydberg 	if (size != 8) {
642ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "could not write to device\n");
643f89bd95cSHenrik Rydberg 		retval = -EIO;
644f89bd95cSHenrik Rydberg 		goto out;
645f89bd95cSHenrik Rydberg 	}
646f89bd95cSHenrik Rydberg 
647cd72ad3fSHenrik Rydberg 	dprintk(2, "bcm5974: switched to %s mode.\n",
648cd72ad3fSHenrik Rydberg 		on ? "wellspring" : "normal");
649f89bd95cSHenrik Rydberg 
650f89bd95cSHenrik Rydberg  out:
651f89bd95cSHenrik Rydberg 	kfree(data);
652f89bd95cSHenrik Rydberg 	return retval;
653f89bd95cSHenrik Rydberg }
654f89bd95cSHenrik Rydberg 
655f89bd95cSHenrik Rydberg static void bcm5974_irq_button(struct urb *urb)
656f89bd95cSHenrik Rydberg {
657f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
658ab943ca8SGreg Kroah-Hartman 	struct usb_interface *intf = dev->intf;
659f89bd95cSHenrik Rydberg 	int error;
660f89bd95cSHenrik Rydberg 
661f89bd95cSHenrik Rydberg 	switch (urb->status) {
662f89bd95cSHenrik Rydberg 	case 0:
663f89bd95cSHenrik Rydberg 		break;
664f89bd95cSHenrik Rydberg 	case -EOVERFLOW:
665f89bd95cSHenrik Rydberg 	case -ECONNRESET:
666f89bd95cSHenrik Rydberg 	case -ENOENT:
667f89bd95cSHenrik Rydberg 	case -ESHUTDOWN:
668ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "button urb shutting down: %d\n",
669bd028769SGreg Kroah-Hartman 			urb->status);
670f89bd95cSHenrik Rydberg 		return;
671f89bd95cSHenrik Rydberg 	default:
672ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "button urb status: %d\n", urb->status);
673f89bd95cSHenrik Rydberg 		goto exit;
674f89bd95cSHenrik Rydberg 	}
675f89bd95cSHenrik Rydberg 
676f89bd95cSHenrik Rydberg 	if (report_bt_state(dev, dev->bt_urb->actual_length))
677f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad button package, length: %d\n",
678f89bd95cSHenrik Rydberg 			dev->bt_urb->actual_length);
679f89bd95cSHenrik Rydberg 
680f89bd95cSHenrik Rydberg exit:
681f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
682f89bd95cSHenrik Rydberg 	if (error)
683ab943ca8SGreg Kroah-Hartman 		dev_err(&intf->dev, "button urb failed: %d\n", error);
684f89bd95cSHenrik Rydberg }
685f89bd95cSHenrik Rydberg 
686f89bd95cSHenrik Rydberg static void bcm5974_irq_trackpad(struct urb *urb)
687f89bd95cSHenrik Rydberg {
688f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
689ab943ca8SGreg Kroah-Hartman 	struct usb_interface *intf = dev->intf;
690f89bd95cSHenrik Rydberg 	int error;
691f89bd95cSHenrik Rydberg 
692f89bd95cSHenrik Rydberg 	switch (urb->status) {
693f89bd95cSHenrik Rydberg 	case 0:
694f89bd95cSHenrik Rydberg 		break;
695f89bd95cSHenrik Rydberg 	case -EOVERFLOW:
696f89bd95cSHenrik Rydberg 	case -ECONNRESET:
697f89bd95cSHenrik Rydberg 	case -ENOENT:
698f89bd95cSHenrik Rydberg 	case -ESHUTDOWN:
699ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n",
700bd028769SGreg Kroah-Hartman 			urb->status);
701f89bd95cSHenrik Rydberg 		return;
702f89bd95cSHenrik Rydberg 	default:
703ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status);
704f89bd95cSHenrik Rydberg 		goto exit;
705f89bd95cSHenrik Rydberg 	}
706f89bd95cSHenrik Rydberg 
707f89bd95cSHenrik Rydberg 	/* control response ignored */
708f89bd95cSHenrik Rydberg 	if (dev->tp_urb->actual_length == 2)
709f89bd95cSHenrik Rydberg 		goto exit;
710f89bd95cSHenrik Rydberg 
711f89bd95cSHenrik Rydberg 	if (report_tp_state(dev, dev->tp_urb->actual_length))
712f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
713f89bd95cSHenrik Rydberg 			dev->tp_urb->actual_length);
714f89bd95cSHenrik Rydberg 
715f89bd95cSHenrik Rydberg exit:
716f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
717f89bd95cSHenrik Rydberg 	if (error)
718ab943ca8SGreg Kroah-Hartman 		dev_err(&intf->dev, "trackpad urb failed: %d\n", error);
719f89bd95cSHenrik Rydberg }
720f89bd95cSHenrik Rydberg 
721f89bd95cSHenrik Rydberg /*
722f89bd95cSHenrik Rydberg  * The Wellspring trackpad, like many recent Apple trackpads, share
723f89bd95cSHenrik Rydberg  * the usb device with the keyboard. Since keyboards are usually
724f89bd95cSHenrik Rydberg  * handled by the HID system, the device ends up being handled by two
725f89bd95cSHenrik Rydberg  * modules. Setting up the device therefore becomes slightly
726f89bd95cSHenrik Rydberg  * complicated. To enable multitouch features, a mode switch is
727f89bd95cSHenrik Rydberg  * required, which is usually applied via the control interface of the
728f89bd95cSHenrik Rydberg  * device.  It can be argued where this switch should take place. In
729f89bd95cSHenrik Rydberg  * some drivers, like appletouch, the switch is made during
730f89bd95cSHenrik Rydberg  * probe. However, the hid module may also alter the state of the
731f89bd95cSHenrik Rydberg  * device, resulting in trackpad malfunction under certain
732f89bd95cSHenrik Rydberg  * circumstances. To get around this problem, there is at least one
733f89bd95cSHenrik Rydberg  * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
73425985edcSLucas De Marchi  * receive a reset_resume request rather than the normal resume.
735f89bd95cSHenrik Rydberg  * Since the implementation of reset_resume is equal to mode switch
736f89bd95cSHenrik Rydberg  * plus start_traffic, it seems easier to always do the switch when
737f89bd95cSHenrik Rydberg  * starting traffic on the device.
738f89bd95cSHenrik Rydberg  */
739f89bd95cSHenrik Rydberg static int bcm5974_start_traffic(struct bcm5974 *dev)
740f89bd95cSHenrik Rydberg {
7411719ec41SLuo Jinghua 	int error;
7421719ec41SLuo Jinghua 
7431719ec41SLuo Jinghua 	error = bcm5974_wellspring_mode(dev, true);
7441719ec41SLuo Jinghua 	if (error) {
745f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: mode switch failed\n");
7461719ec41SLuo Jinghua 		goto err_out;
747f89bd95cSHenrik Rydberg 	}
748f89bd95cSHenrik Rydberg 
74943f482b4SHenrik Rydberg 	if (dev->bt_urb) {
7501719ec41SLuo Jinghua 		error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
7511719ec41SLuo Jinghua 		if (error)
7521719ec41SLuo Jinghua 			goto err_reset_mode;
75343f482b4SHenrik Rydberg 	}
754f89bd95cSHenrik Rydberg 
7551719ec41SLuo Jinghua 	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
7561719ec41SLuo Jinghua 	if (error)
757f89bd95cSHenrik Rydberg 		goto err_kill_bt;
758f89bd95cSHenrik Rydberg 
759f89bd95cSHenrik Rydberg 	return 0;
760f89bd95cSHenrik Rydberg 
761f89bd95cSHenrik Rydberg err_kill_bt:
762f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
7631719ec41SLuo Jinghua err_reset_mode:
7641719ec41SLuo Jinghua 	bcm5974_wellspring_mode(dev, false);
7651719ec41SLuo Jinghua err_out:
7661719ec41SLuo Jinghua 	return error;
767f89bd95cSHenrik Rydberg }
768f89bd95cSHenrik Rydberg 
769f89bd95cSHenrik Rydberg static void bcm5974_pause_traffic(struct bcm5974 *dev)
770f89bd95cSHenrik Rydberg {
771f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->tp_urb);
772f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
773cd72ad3fSHenrik Rydberg 	bcm5974_wellspring_mode(dev, false);
774f89bd95cSHenrik Rydberg }
775f89bd95cSHenrik Rydberg 
776f89bd95cSHenrik Rydberg /*
777f89bd95cSHenrik Rydberg  * The code below implements open/close and manual suspend/resume.
778f89bd95cSHenrik Rydberg  * All functions may be called in random order.
779f89bd95cSHenrik Rydberg  *
780f89bd95cSHenrik Rydberg  * Opening a suspended device fails with EACCES - permission denied.
781f89bd95cSHenrik Rydberg  *
782f89bd95cSHenrik Rydberg  * Failing a resume leaves the device resumed but closed.
783f89bd95cSHenrik Rydberg  */
784f89bd95cSHenrik Rydberg static int bcm5974_open(struct input_dev *input)
785f89bd95cSHenrik Rydberg {
786f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
787f89bd95cSHenrik Rydberg 	int error;
788f89bd95cSHenrik Rydberg 
78988da765fSDmitry Torokhov 	error = usb_autopm_get_interface(dev->intf);
79088da765fSDmitry Torokhov 	if (error)
79188da765fSDmitry Torokhov 		return error;
79288da765fSDmitry Torokhov 
793f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
794f89bd95cSHenrik Rydberg 
795f89bd95cSHenrik Rydberg 	error = bcm5974_start_traffic(dev);
796f89bd95cSHenrik Rydberg 	if (!error)
797f89bd95cSHenrik Rydberg 		dev->opened = 1;
798f89bd95cSHenrik Rydberg 
799f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
800f89bd95cSHenrik Rydberg 
80188da765fSDmitry Torokhov 	if (error)
80288da765fSDmitry Torokhov 		usb_autopm_put_interface(dev->intf);
80388da765fSDmitry Torokhov 
804f89bd95cSHenrik Rydberg 	return error;
805f89bd95cSHenrik Rydberg }
806f89bd95cSHenrik Rydberg 
807f89bd95cSHenrik Rydberg static void bcm5974_close(struct input_dev *input)
808f89bd95cSHenrik Rydberg {
809f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
810f89bd95cSHenrik Rydberg 
811f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
812f89bd95cSHenrik Rydberg 
813f89bd95cSHenrik Rydberg 	bcm5974_pause_traffic(dev);
814f89bd95cSHenrik Rydberg 	dev->opened = 0;
815f89bd95cSHenrik Rydberg 
816f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
81788da765fSDmitry Torokhov 
81888da765fSDmitry Torokhov 	usb_autopm_put_interface(dev->intf);
819f89bd95cSHenrik Rydberg }
820f89bd95cSHenrik Rydberg 
821f89bd95cSHenrik Rydberg static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
822f89bd95cSHenrik Rydberg {
823f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
824f89bd95cSHenrik Rydberg 
825f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
826f89bd95cSHenrik Rydberg 
827f89bd95cSHenrik Rydberg 	if (dev->opened)
828f89bd95cSHenrik Rydberg 		bcm5974_pause_traffic(dev);
829f89bd95cSHenrik Rydberg 
830f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
831f89bd95cSHenrik Rydberg 
832f89bd95cSHenrik Rydberg 	return 0;
833f89bd95cSHenrik Rydberg }
834f89bd95cSHenrik Rydberg 
835f89bd95cSHenrik Rydberg static int bcm5974_resume(struct usb_interface *iface)
836f89bd95cSHenrik Rydberg {
837f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
838f89bd95cSHenrik Rydberg 	int error = 0;
839f89bd95cSHenrik Rydberg 
840f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
841f89bd95cSHenrik Rydberg 
842f89bd95cSHenrik Rydberg 	if (dev->opened)
843f89bd95cSHenrik Rydberg 		error = bcm5974_start_traffic(dev);
844f89bd95cSHenrik Rydberg 
845f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
846f89bd95cSHenrik Rydberg 
847f89bd95cSHenrik Rydberg 	return error;
848f89bd95cSHenrik Rydberg }
849f89bd95cSHenrik Rydberg 
850f89bd95cSHenrik Rydberg static int bcm5974_probe(struct usb_interface *iface,
851f89bd95cSHenrik Rydberg 			 const struct usb_device_id *id)
852f89bd95cSHenrik Rydberg {
853f89bd95cSHenrik Rydberg 	struct usb_device *udev = interface_to_usbdev(iface);
854f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
855f89bd95cSHenrik Rydberg 	struct bcm5974 *dev;
856f89bd95cSHenrik Rydberg 	struct input_dev *input_dev;
857f89bd95cSHenrik Rydberg 	int error = -ENOMEM;
858f89bd95cSHenrik Rydberg 
859f89bd95cSHenrik Rydberg 	/* find the product index */
860f89bd95cSHenrik Rydberg 	cfg = bcm5974_get_config(udev);
861f89bd95cSHenrik Rydberg 
862f89bd95cSHenrik Rydberg 	/* allocate memory for our device state and initialize it */
863f89bd95cSHenrik Rydberg 	dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
864f89bd95cSHenrik Rydberg 	input_dev = input_allocate_device();
865f89bd95cSHenrik Rydberg 	if (!dev || !input_dev) {
8666c1d1b24SGreg Kroah-Hartman 		dev_err(&iface->dev, "out of memory\n");
867f89bd95cSHenrik Rydberg 		goto err_free_devs;
868f89bd95cSHenrik Rydberg 	}
869f89bd95cSHenrik Rydberg 
870f89bd95cSHenrik Rydberg 	dev->udev = udev;
87188da765fSDmitry Torokhov 	dev->intf = iface;
872f89bd95cSHenrik Rydberg 	dev->input = input_dev;
873f89bd95cSHenrik Rydberg 	dev->cfg = *cfg;
874f89bd95cSHenrik Rydberg 	mutex_init(&dev->pm_mutex);
875f89bd95cSHenrik Rydberg 
876f89bd95cSHenrik Rydberg 	/* setup urbs */
87743f482b4SHenrik Rydberg 	if (cfg->tp_type == TYPE1) {
878f89bd95cSHenrik Rydberg 		dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
879f89bd95cSHenrik Rydberg 		if (!dev->bt_urb)
880f89bd95cSHenrik Rydberg 			goto err_free_devs;
88143f482b4SHenrik Rydberg 	}
882f89bd95cSHenrik Rydberg 
883f89bd95cSHenrik Rydberg 	dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
884f89bd95cSHenrik Rydberg 	if (!dev->tp_urb)
885f89bd95cSHenrik Rydberg 		goto err_free_bt_urb;
886f89bd95cSHenrik Rydberg 
88743f482b4SHenrik Rydberg 	if (dev->bt_urb) {
888997ea58eSDaniel Mack 		dev->bt_data = usb_alloc_coherent(dev->udev,
889f89bd95cSHenrik Rydberg 					  dev->cfg.bt_datalen, GFP_KERNEL,
890f89bd95cSHenrik Rydberg 					  &dev->bt_urb->transfer_dma);
891f89bd95cSHenrik Rydberg 		if (!dev->bt_data)
892f89bd95cSHenrik Rydberg 			goto err_free_urb;
89343f482b4SHenrik Rydberg 	}
894f89bd95cSHenrik Rydberg 
895997ea58eSDaniel Mack 	dev->tp_data = usb_alloc_coherent(dev->udev,
896f89bd95cSHenrik Rydberg 					  dev->cfg.tp_datalen, GFP_KERNEL,
897f89bd95cSHenrik Rydberg 					  &dev->tp_urb->transfer_dma);
898f89bd95cSHenrik Rydberg 	if (!dev->tp_data)
899f89bd95cSHenrik Rydberg 		goto err_free_bt_buffer;
900f89bd95cSHenrik Rydberg 
90143f482b4SHenrik Rydberg 	if (dev->bt_urb)
902f89bd95cSHenrik Rydberg 		usb_fill_int_urb(dev->bt_urb, udev,
903f89bd95cSHenrik Rydberg 				 usb_rcvintpipe(udev, cfg->bt_ep),
904f89bd95cSHenrik Rydberg 				 dev->bt_data, dev->cfg.bt_datalen,
905f89bd95cSHenrik Rydberg 				 bcm5974_irq_button, dev, 1);
906f89bd95cSHenrik Rydberg 
907f89bd95cSHenrik Rydberg 	usb_fill_int_urb(dev->tp_urb, udev,
908f89bd95cSHenrik Rydberg 			 usb_rcvintpipe(udev, cfg->tp_ep),
909f89bd95cSHenrik Rydberg 			 dev->tp_data, dev->cfg.tp_datalen,
910f89bd95cSHenrik Rydberg 			 bcm5974_irq_trackpad, dev, 1);
911f89bd95cSHenrik Rydberg 
912f89bd95cSHenrik Rydberg 	/* create bcm5974 device */
913f89bd95cSHenrik Rydberg 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
914f89bd95cSHenrik Rydberg 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
915f89bd95cSHenrik Rydberg 
916f89bd95cSHenrik Rydberg 	input_dev->name = "bcm5974";
917f89bd95cSHenrik Rydberg 	input_dev->phys = dev->phys;
918f89bd95cSHenrik Rydberg 	usb_to_input_id(dev->udev, &input_dev->id);
919158e9287SHenrik Rydberg 	/* report driver capabilities via the version field */
920158e9287SHenrik Rydberg 	input_dev->id.version = cfg->caps;
921f89bd95cSHenrik Rydberg 	input_dev->dev.parent = &iface->dev;
922f89bd95cSHenrik Rydberg 
923f89bd95cSHenrik Rydberg 	input_set_drvdata(input_dev, dev);
924f89bd95cSHenrik Rydberg 
925f89bd95cSHenrik Rydberg 	input_dev->open = bcm5974_open;
926f89bd95cSHenrik Rydberg 	input_dev->close = bcm5974_close;
927f89bd95cSHenrik Rydberg 
928f89bd95cSHenrik Rydberg 	setup_events_to_report(input_dev, cfg);
929f89bd95cSHenrik Rydberg 
930f89bd95cSHenrik Rydberg 	error = input_register_device(dev->input);
931f89bd95cSHenrik Rydberg 	if (error)
932f89bd95cSHenrik Rydberg 		goto err_free_buffer;
933f89bd95cSHenrik Rydberg 
934f89bd95cSHenrik Rydberg 	/* save our data pointer in this interface device */
935f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, dev);
936f89bd95cSHenrik Rydberg 
937f89bd95cSHenrik Rydberg 	return 0;
938f89bd95cSHenrik Rydberg 
939f89bd95cSHenrik Rydberg err_free_buffer:
940997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
941f89bd95cSHenrik Rydberg 		dev->tp_data, dev->tp_urb->transfer_dma);
942f89bd95cSHenrik Rydberg err_free_bt_buffer:
94343f482b4SHenrik Rydberg 	if (dev->bt_urb)
944997ea58eSDaniel Mack 		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
945f89bd95cSHenrik Rydberg 				  dev->bt_data, dev->bt_urb->transfer_dma);
946f89bd95cSHenrik Rydberg err_free_urb:
947f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
948f89bd95cSHenrik Rydberg err_free_bt_urb:
949f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
950f89bd95cSHenrik Rydberg err_free_devs:
951f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
952f89bd95cSHenrik Rydberg 	input_free_device(input_dev);
953f89bd95cSHenrik Rydberg 	kfree(dev);
954f89bd95cSHenrik Rydberg 	return error;
955f89bd95cSHenrik Rydberg }
956f89bd95cSHenrik Rydberg 
957f89bd95cSHenrik Rydberg static void bcm5974_disconnect(struct usb_interface *iface)
958f89bd95cSHenrik Rydberg {
959f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
960f89bd95cSHenrik Rydberg 
961f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
962f89bd95cSHenrik Rydberg 
963f89bd95cSHenrik Rydberg 	input_unregister_device(dev->input);
964997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
965f89bd95cSHenrik Rydberg 			  dev->tp_data, dev->tp_urb->transfer_dma);
96643f482b4SHenrik Rydberg 	if (dev->bt_urb)
967997ea58eSDaniel Mack 		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
968f89bd95cSHenrik Rydberg 				  dev->bt_data, dev->bt_urb->transfer_dma);
969f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
970f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
971f89bd95cSHenrik Rydberg 	kfree(dev);
972f89bd95cSHenrik Rydberg }
973f89bd95cSHenrik Rydberg 
974f89bd95cSHenrik Rydberg static struct usb_driver bcm5974_driver = {
975f89bd95cSHenrik Rydberg 	.name			= "bcm5974",
976f89bd95cSHenrik Rydberg 	.probe			= bcm5974_probe,
977f89bd95cSHenrik Rydberg 	.disconnect		= bcm5974_disconnect,
978f89bd95cSHenrik Rydberg 	.suspend		= bcm5974_suspend,
979f89bd95cSHenrik Rydberg 	.resume			= bcm5974_resume,
980f89bd95cSHenrik Rydberg 	.id_table		= bcm5974_table,
98188da765fSDmitry Torokhov 	.supports_autosuspend	= 1,
982f89bd95cSHenrik Rydberg };
983f89bd95cSHenrik Rydberg 
98408642e7cSGreg Kroah-Hartman module_usb_driver(bcm5974_driver);
985