xref: /linux/drivers/input/mouse/bcm5974.c (revision 08642e7c52cf43616821520828e504bc717e54a6)
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>
43f89bd95cSHenrik Rydberg 
44f89bd95cSHenrik Rydberg #define USB_VENDOR_ID_APPLE		0x05ac
45f89bd95cSHenrik Rydberg 
46f89bd95cSHenrik Rydberg /* MacbookAir, aka wellspring */
47f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ANSI	0x0223
48f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_ISO	0x0224
49f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING_JIS	0x0225
50f89bd95cSHenrik Rydberg /* MacbookProPenryn, aka wellspring2 */
51f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI	0x0230
52f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_ISO	0x0231
53f89bd95cSHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING2_JIS	0x0232
54158e9287SHenrik Rydberg /* Macbook5,1 (unibody), aka wellspring3 */
55158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI	0x0236
56158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_ISO	0x0237
57158e9287SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS	0x0238
586021afcfSEdgar (gimli) Hucek /* MacbookAir3,2 (unibody), aka wellspring5 */
596021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI	0x023f
606021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_ISO	0x0240
616021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4_JIS	0x0241
626021afcfSEdgar (gimli) Hucek /* MacbookAir3,1 (unibody), aka wellspring4 */
636021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI	0x0242
646021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO	0x0243
656021afcfSEdgar (gimli) Hucek #define USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS	0x0244
6647340bd9SAndy Botting /* Macbook8 (unibody, March 2011) */
6747340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI	0x0245
6847340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_ISO	0x0246
6947340bd9SAndy Botting #define USB_DEVICE_ID_APPLE_WELLSPRING5_JIS	0x0247
701c601beaSPieter-Augustijn Van Malleghem /* MacbookAir4,1 (unibody, July 2011) */
711c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI	0x0249
721c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO	0x024a
731c601beaSPieter-Augustijn Van Malleghem #define USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS	0x024b
74db0b34b0SJoshua V. Dillon /* MacbookAir4,2 (unibody, July 2011) */
75db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI	0x024c
76db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_ISO	0x024d
77db0b34b0SJoshua V. Dillon #define USB_DEVICE_ID_APPLE_WELLSPRING6_JIS	0x024e
78c331eb58SAndrew Drake /* Macbook8,2 (unibody) */
79c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI	0x0252
80c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO	0x0253
81c331eb58SAndrew Drake #define USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS	0x0254
82f89bd95cSHenrik Rydberg 
83f89bd95cSHenrik Rydberg #define BCM5974_DEVICE(prod) {					\
84f89bd95cSHenrik Rydberg 	.match_flags = (USB_DEVICE_ID_MATCH_DEVICE |		\
85f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_CLASS |		\
86f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_PROTOCOL),	\
87f89bd95cSHenrik Rydberg 	.idVendor = USB_VENDOR_ID_APPLE,			\
88f89bd95cSHenrik Rydberg 	.idProduct = (prod),					\
89f89bd95cSHenrik Rydberg 	.bInterfaceClass = USB_INTERFACE_CLASS_HID,		\
90f89bd95cSHenrik Rydberg 	.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE	\
91f89bd95cSHenrik Rydberg }
92f89bd95cSHenrik Rydberg 
93f89bd95cSHenrik Rydberg /* table of devices that work with this driver */
94f89bd95cSHenrik Rydberg static const struct usb_device_id bcm5974_table[] = {
95f89bd95cSHenrik Rydberg 	/* MacbookAir1.1 */
96f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
97f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
98f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
99f89bd95cSHenrik Rydberg 	/* MacbookProPenryn */
100f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
101f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
102f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
103158e9287SHenrik Rydberg 	/* Macbook5,1 */
104158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
105158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
106158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
1076021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,2 */
1086021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
1096021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
1106021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
1116021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,1 */
1126021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
1136021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
1146021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
11547340bd9SAndy Botting 	/* MacbookPro8 */
11647340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
11747340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
11847340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
1191c601beaSPieter-Augustijn Van Malleghem 	/* MacbookAir4,1 */
1201c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
1211c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
1221c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
123db0b34b0SJoshua V. Dillon 	/* MacbookAir4,2 */
124db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
125db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
126db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
127c331eb58SAndrew Drake 	/* MacbookPro8,2 */
128c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
129c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
130c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
131f89bd95cSHenrik Rydberg 	/* Terminating entry */
132f89bd95cSHenrik Rydberg 	{}
133f89bd95cSHenrik Rydberg };
134f89bd95cSHenrik Rydberg MODULE_DEVICE_TABLE(usb, bcm5974_table);
135f89bd95cSHenrik Rydberg 
136f89bd95cSHenrik Rydberg MODULE_AUTHOR("Henrik Rydberg");
137f89bd95cSHenrik Rydberg MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
138f89bd95cSHenrik Rydberg MODULE_LICENSE("GPL");
139f89bd95cSHenrik Rydberg 
140f89bd95cSHenrik Rydberg #define dprintk(level, format, a...)\
141f89bd95cSHenrik Rydberg 	{ if (debug >= level) printk(KERN_DEBUG format, ##a); }
142f89bd95cSHenrik Rydberg 
143f89bd95cSHenrik Rydberg static int debug = 1;
144f89bd95cSHenrik Rydberg module_param(debug, int, 0644);
145f89bd95cSHenrik Rydberg MODULE_PARM_DESC(debug, "Activate debugging output");
146f89bd95cSHenrik Rydberg 
147f89bd95cSHenrik Rydberg /* button data structure */
148f89bd95cSHenrik Rydberg struct bt_data {
149f89bd95cSHenrik Rydberg 	u8 unknown1;		/* constant */
150f89bd95cSHenrik Rydberg 	u8 button;		/* left button */
151f89bd95cSHenrik Rydberg 	u8 rel_x;		/* relative x coordinate */
152f89bd95cSHenrik Rydberg 	u8 rel_y;		/* relative y coordinate */
153f89bd95cSHenrik Rydberg };
154f89bd95cSHenrik Rydberg 
1559894cf0fSHenrik Rydberg /* trackpad header types */
1569894cf0fSHenrik Rydberg enum tp_type {
157158e9287SHenrik Rydberg 	TYPE1,			/* plain trackpad */
158158e9287SHenrik Rydberg 	TYPE2			/* button integrated in trackpad */
159f89bd95cSHenrik Rydberg };
160f89bd95cSHenrik Rydberg 
1619894cf0fSHenrik Rydberg /* trackpad finger data offsets, le16-aligned */
1629894cf0fSHenrik Rydberg #define FINGER_TYPE1		(13 * sizeof(__le16))
163158e9287SHenrik Rydberg #define FINGER_TYPE2		(15 * sizeof(__le16))
164158e9287SHenrik Rydberg 
165158e9287SHenrik Rydberg /* trackpad button data offsets */
166158e9287SHenrik Rydberg #define BUTTON_TYPE2		15
167158e9287SHenrik Rydberg 
168158e9287SHenrik Rydberg /* list of device capability bits */
169158e9287SHenrik Rydberg #define HAS_INTEGRATED_BUTTON	1
1709894cf0fSHenrik Rydberg 
1719894cf0fSHenrik Rydberg /* trackpad finger structure, le16-aligned */
172f89bd95cSHenrik Rydberg struct tp_finger {
17375e21e3fSHenrik Rydberg 	__le16 origin;		/* zero when switching track finger */
174f89bd95cSHenrik Rydberg 	__le16 abs_x;		/* absolute x coodinate */
175f89bd95cSHenrik Rydberg 	__le16 abs_y;		/* absolute y coodinate */
176f89bd95cSHenrik Rydberg 	__le16 rel_x;		/* relative x coodinate */
177f89bd95cSHenrik Rydberg 	__le16 rel_y;		/* relative y coodinate */
178f89bd95cSHenrik Rydberg 	__le16 size_major;	/* finger size, major axis? */
179f89bd95cSHenrik Rydberg 	__le16 size_minor;	/* finger size, minor axis? */
180f89bd95cSHenrik Rydberg 	__le16 orientation;	/* 16384 when point, else 15 bit angle */
181f89bd95cSHenrik Rydberg 	__le16 force_major;	/* trackpad force, major axis? */
182f89bd95cSHenrik Rydberg 	__le16 force_minor;	/* trackpad force, minor axis? */
183f89bd95cSHenrik Rydberg 	__le16 unused[3];	/* zeros */
184f89bd95cSHenrik Rydberg 	__le16 multi;		/* one finger: varies, more fingers: constant */
1859894cf0fSHenrik Rydberg } __attribute__((packed,aligned(2)));
186f89bd95cSHenrik Rydberg 
1879894cf0fSHenrik Rydberg /* trackpad finger data size, empirically at least ten fingers */
1889894cf0fSHenrik Rydberg #define SIZEOF_FINGER		sizeof(struct tp_finger)
1899894cf0fSHenrik Rydberg #define SIZEOF_ALL_FINGERS	(16 * SIZEOF_FINGER)
1906f2701b7SHenrik Rydberg #define MAX_FINGER_ORIENTATION	16384
191f89bd95cSHenrik Rydberg 
192f89bd95cSHenrik Rydberg /* device-specific parameters */
193f89bd95cSHenrik Rydberg struct bcm5974_param {
194f89bd95cSHenrik Rydberg 	int dim;		/* logical dimension */
195f89bd95cSHenrik Rydberg 	int fuzz;		/* logical noise value */
196f89bd95cSHenrik Rydberg 	int devmin;		/* device minimum reading */
197f89bd95cSHenrik Rydberg 	int devmax;		/* device maximum reading */
198f89bd95cSHenrik Rydberg };
199f89bd95cSHenrik Rydberg 
200f89bd95cSHenrik Rydberg /* device-specific configuration */
201f89bd95cSHenrik Rydberg struct bcm5974_config {
202f89bd95cSHenrik Rydberg 	int ansi, iso, jis;	/* the product id of this device */
203158e9287SHenrik Rydberg 	int caps;		/* device capability bitmask */
204f89bd95cSHenrik Rydberg 	int bt_ep;		/* the endpoint of the button interface */
205f89bd95cSHenrik Rydberg 	int bt_datalen;		/* data length of the button interface */
206f89bd95cSHenrik Rydberg 	int tp_ep;		/* the endpoint of the trackpad interface */
2079894cf0fSHenrik Rydberg 	enum tp_type tp_type;	/* type of trackpad interface */
2089894cf0fSHenrik Rydberg 	int tp_offset;		/* offset to trackpad finger data */
209f89bd95cSHenrik Rydberg 	int tp_datalen;		/* data length of the trackpad interface */
210f89bd95cSHenrik Rydberg 	struct bcm5974_param p;	/* finger pressure limits */
211f89bd95cSHenrik Rydberg 	struct bcm5974_param w;	/* finger width limits */
212f89bd95cSHenrik Rydberg 	struct bcm5974_param x;	/* horizontal limits */
213f89bd95cSHenrik Rydberg 	struct bcm5974_param y;	/* vertical limits */
214f89bd95cSHenrik Rydberg };
215f89bd95cSHenrik Rydberg 
216f89bd95cSHenrik Rydberg /* logical device structure */
217f89bd95cSHenrik Rydberg struct bcm5974 {
218f89bd95cSHenrik Rydberg 	char phys[64];
219f89bd95cSHenrik Rydberg 	struct usb_device *udev;	/* usb device */
22088da765fSDmitry Torokhov 	struct usb_interface *intf;	/* our interface */
221f89bd95cSHenrik Rydberg 	struct input_dev *input;	/* input dev */
222f89bd95cSHenrik Rydberg 	struct bcm5974_config cfg;	/* device configuration */
223f89bd95cSHenrik Rydberg 	struct mutex pm_mutex;		/* serialize access to open/suspend */
224f89bd95cSHenrik Rydberg 	int opened;			/* 1: opened, 0: closed */
225f89bd95cSHenrik Rydberg 	struct urb *bt_urb;		/* button usb request block */
226f89bd95cSHenrik Rydberg 	struct bt_data *bt_data;	/* button transferred data */
227f89bd95cSHenrik Rydberg 	struct urb *tp_urb;		/* trackpad usb request block */
2289894cf0fSHenrik Rydberg 	u8 *tp_data;			/* trackpad transferred data */
22975e21e3fSHenrik Rydberg 	int fingers;			/* number of fingers on trackpad */
230f89bd95cSHenrik Rydberg };
231f89bd95cSHenrik Rydberg 
232f89bd95cSHenrik Rydberg /* logical dimensions */
233f89bd95cSHenrik Rydberg #define DIM_PRESSURE	256		/* maximum finger pressure */
234f89bd95cSHenrik Rydberg #define DIM_WIDTH	16		/* maximum finger width */
235f89bd95cSHenrik Rydberg #define DIM_X		1280		/* maximum trackpad x value */
236f89bd95cSHenrik Rydberg #define DIM_Y		800		/* maximum trackpad y value */
237f89bd95cSHenrik Rydberg 
238f89bd95cSHenrik Rydberg /* logical signal quality */
239f89bd95cSHenrik Rydberg #define SN_PRESSURE	45		/* pressure signal-to-noise ratio */
240f89bd95cSHenrik Rydberg #define SN_WIDTH	100		/* width signal-to-noise ratio */
241f89bd95cSHenrik Rydberg #define SN_COORD	250		/* coordinate signal-to-noise ratio */
242f89bd95cSHenrik Rydberg 
24375e21e3fSHenrik Rydberg /* pressure thresholds */
24475e21e3fSHenrik Rydberg #define PRESSURE_LOW	(2 * DIM_PRESSURE / SN_PRESSURE)
24575e21e3fSHenrik Rydberg #define PRESSURE_HIGH	(3 * PRESSURE_LOW)
24675e21e3fSHenrik Rydberg 
247f89bd95cSHenrik Rydberg /* device constants */
248f89bd95cSHenrik Rydberg static const struct bcm5974_config bcm5974_config_table[] = {
249f89bd95cSHenrik Rydberg 	{
250f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
251f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
252f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
253158e9287SHenrik Rydberg 		0,
254f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
2559894cf0fSHenrik Rydberg 		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
256f89bd95cSHenrik Rydberg 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
257f89bd95cSHenrik Rydberg 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
258f89bd95cSHenrik Rydberg 		{ DIM_X, DIM_X / SN_COORD, -4824, 5342 },
259f89bd95cSHenrik Rydberg 		{ DIM_Y, DIM_Y / SN_COORD, -172, 5820 }
260f89bd95cSHenrik Rydberg 	},
261f89bd95cSHenrik Rydberg 	{
262f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
263f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
264f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
265158e9287SHenrik Rydberg 		0,
266f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
2679894cf0fSHenrik Rydberg 		0x81, TYPE1, FINGER_TYPE1, FINGER_TYPE1 + SIZEOF_ALL_FINGERS,
268f89bd95cSHenrik Rydberg 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 256 },
269f89bd95cSHenrik Rydberg 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
270f89bd95cSHenrik Rydberg 		{ DIM_X, DIM_X / SN_COORD, -4824, 4824 },
271f89bd95cSHenrik Rydberg 		{ DIM_Y, DIM_Y / SN_COORD, -172, 4290 }
272f89bd95cSHenrik Rydberg 	},
273158e9287SHenrik Rydberg 	{
274158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
275158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
276158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
277158e9287SHenrik Rydberg 		HAS_INTEGRATED_BUTTON,
278158e9287SHenrik Rydberg 		0x84, sizeof(struct bt_data),
279158e9287SHenrik Rydberg 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
280158e9287SHenrik Rydberg 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
281158e9287SHenrik Rydberg 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
282158e9287SHenrik Rydberg 		{ DIM_X, DIM_X / SN_COORD, -4460, 5166 },
283158e9287SHenrik Rydberg 		{ DIM_Y, DIM_Y / SN_COORD, -75, 6700 }
284158e9287SHenrik Rydberg 	},
2856021afcfSEdgar (gimli) Hucek 	{
2866021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
2876021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
2886021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_JIS,
2896021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
2906021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
2916021afcfSEdgar (gimli) Hucek 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
2926021afcfSEdgar (gimli) Hucek 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
2936021afcfSEdgar (gimli) Hucek 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
2946021afcfSEdgar (gimli) Hucek 		{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
2956021afcfSEdgar (gimli) Hucek 		{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
2966021afcfSEdgar (gimli) Hucek 	},
2976021afcfSEdgar (gimli) Hucek 	{
2986021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
2996021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
3006021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS,
3016021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
3026021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
3036021afcfSEdgar (gimli) Hucek 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3046021afcfSEdgar (gimli) Hucek 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
3056021afcfSEdgar (gimli) Hucek 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
3066021afcfSEdgar (gimli) Hucek 		{ DIM_X, DIM_X / SN_COORD, -4616, 5112 },
3076021afcfSEdgar (gimli) Hucek 		{ DIM_Y, DIM_Y / SN_COORD, -142, 5234 }
3086021afcfSEdgar (gimli) Hucek 	},
30947340bd9SAndy Botting 	{
31047340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
31147340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
31247340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
31347340bd9SAndy Botting 		HAS_INTEGRATED_BUTTON,
31447340bd9SAndy Botting 		0x84, sizeof(struct bt_data),
31547340bd9SAndy Botting 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
31647340bd9SAndy Botting 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
31747340bd9SAndy Botting 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
31847340bd9SAndy Botting 		{ DIM_X, DIM_X / SN_COORD, -4415, 5050 },
31947340bd9SAndy Botting 		{ DIM_Y, DIM_Y / SN_COORD, -55, 6680 }
32047340bd9SAndy Botting 	},
321db0b34b0SJoshua V. Dillon 	{
322db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
323db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
324db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
325db0b34b0SJoshua V. Dillon 		HAS_INTEGRATED_BUTTON,
326db0b34b0SJoshua V. Dillon 		0x84, sizeof(struct bt_data),
327db0b34b0SJoshua V. Dillon 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
328db0b34b0SJoshua V. Dillon 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
329db0b34b0SJoshua V. Dillon 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
330db0b34b0SJoshua V. Dillon 		{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
331db0b34b0SJoshua V. Dillon 		{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
332db0b34b0SJoshua V. Dillon 	},
333c331eb58SAndrew Drake 	{
334c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
335c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
336c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
337c331eb58SAndrew Drake 		HAS_INTEGRATED_BUTTON,
338c331eb58SAndrew Drake 		0x84, sizeof(struct bt_data),
339c331eb58SAndrew Drake 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
340c331eb58SAndrew Drake 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
341c331eb58SAndrew Drake 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
342c331eb58SAndrew Drake 		{ DIM_X, DIM_X / SN_COORD, -4750, 5280 },
343c331eb58SAndrew Drake 		{ DIM_Y, DIM_Y / SN_COORD, -150, 6730 }
344c331eb58SAndrew Drake 	},
3451c601beaSPieter-Augustijn Van Malleghem 	{
3461c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
3471c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
3481c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
3491c601beaSPieter-Augustijn Van Malleghem 		HAS_INTEGRATED_BUTTON,
3501c601beaSPieter-Augustijn Van Malleghem 		0x84, sizeof(struct bt_data),
3511c601beaSPieter-Augustijn Van Malleghem 		0x81, TYPE2, FINGER_TYPE2, FINGER_TYPE2 + SIZEOF_ALL_FINGERS,
3521c601beaSPieter-Augustijn Van Malleghem 		{ DIM_PRESSURE, DIM_PRESSURE / SN_PRESSURE, 0, 300 },
3531c601beaSPieter-Augustijn Van Malleghem 		{ DIM_WIDTH, DIM_WIDTH / SN_WIDTH, 0, 2048 },
3541c601beaSPieter-Augustijn Van Malleghem 		{ DIM_X, DIM_X / SN_COORD, -4620, 5140 },
3551c601beaSPieter-Augustijn Van Malleghem 		{ DIM_Y, DIM_Y / SN_COORD, -150, 6600 }
3561c601beaSPieter-Augustijn Van Malleghem 	},
357f89bd95cSHenrik Rydberg 	{}
358f89bd95cSHenrik Rydberg };
359f89bd95cSHenrik Rydberg 
360f89bd95cSHenrik Rydberg /* return the device-specific configuration by device */
361f89bd95cSHenrik Rydberg static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
362f89bd95cSHenrik Rydberg {
363f89bd95cSHenrik Rydberg 	u16 id = le16_to_cpu(udev->descriptor.idProduct);
364f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
365f89bd95cSHenrik Rydberg 
366f89bd95cSHenrik Rydberg 	for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
367f89bd95cSHenrik Rydberg 		if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
368f89bd95cSHenrik Rydberg 			return cfg;
369f89bd95cSHenrik Rydberg 
370f89bd95cSHenrik Rydberg 	return bcm5974_config_table;
371f89bd95cSHenrik Rydberg }
372f89bd95cSHenrik Rydberg 
373f89bd95cSHenrik Rydberg /* convert 16-bit little endian to signed integer */
374f89bd95cSHenrik Rydberg static inline int raw2int(__le16 x)
375f89bd95cSHenrik Rydberg {
376f89bd95cSHenrik Rydberg 	return (signed short)le16_to_cpu(x);
377f89bd95cSHenrik Rydberg }
378f89bd95cSHenrik Rydberg 
379f89bd95cSHenrik Rydberg /* scale device data to logical dimensions (asserts devmin < devmax) */
380f89bd95cSHenrik Rydberg static inline int int2scale(const struct bcm5974_param *p, int x)
381f89bd95cSHenrik Rydberg {
382f89bd95cSHenrik Rydberg 	return x * p->dim / (p->devmax - p->devmin);
383f89bd95cSHenrik Rydberg }
384f89bd95cSHenrik Rydberg 
385f89bd95cSHenrik Rydberg /* all logical value ranges are [0,dim). */
386f89bd95cSHenrik Rydberg static inline int int2bound(const struct bcm5974_param *p, int x)
387f89bd95cSHenrik Rydberg {
388f89bd95cSHenrik Rydberg 	int s = int2scale(p, x);
389f89bd95cSHenrik Rydberg 
390f89bd95cSHenrik Rydberg 	return clamp_val(s, 0, p->dim - 1);
391f89bd95cSHenrik Rydberg }
392f89bd95cSHenrik Rydberg 
393f89bd95cSHenrik Rydberg /* setup which logical events to report */
394f89bd95cSHenrik Rydberg static void setup_events_to_report(struct input_dev *input_dev,
395f89bd95cSHenrik Rydberg 				   const struct bcm5974_config *cfg)
396f89bd95cSHenrik Rydberg {
397f89bd95cSHenrik Rydberg 	__set_bit(EV_ABS, input_dev->evbit);
398f89bd95cSHenrik Rydberg 
399f89bd95cSHenrik Rydberg 	input_set_abs_params(input_dev, ABS_PRESSURE,
400f89bd95cSHenrik Rydberg 				0, cfg->p.dim, cfg->p.fuzz, 0);
401f89bd95cSHenrik Rydberg 	input_set_abs_params(input_dev, ABS_TOOL_WIDTH,
402f89bd95cSHenrik Rydberg 				0, cfg->w.dim, cfg->w.fuzz, 0);
403f89bd95cSHenrik Rydberg 	input_set_abs_params(input_dev, ABS_X,
404f89bd95cSHenrik Rydberg 				0, cfg->x.dim, cfg->x.fuzz, 0);
405f89bd95cSHenrik Rydberg 	input_set_abs_params(input_dev, ABS_Y,
406f89bd95cSHenrik Rydberg 				0, cfg->y.dim, cfg->y.fuzz, 0);
407f89bd95cSHenrik Rydberg 
4086f2701b7SHenrik Rydberg 	/* finger touch area */
4096f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
4106f2701b7SHenrik Rydberg 			     cfg->w.devmin, cfg->w.devmax, 0, 0);
4116f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_TOUCH_MINOR,
4126f2701b7SHenrik Rydberg 			     cfg->w.devmin, cfg->w.devmax, 0, 0);
4136f2701b7SHenrik Rydberg 	/* finger approach area */
4146f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR,
4156f2701b7SHenrik Rydberg 			     cfg->w.devmin, cfg->w.devmax, 0, 0);
4166f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_WIDTH_MINOR,
4176f2701b7SHenrik Rydberg 			     cfg->w.devmin, cfg->w.devmax, 0, 0);
4186f2701b7SHenrik Rydberg 	/* finger orientation */
4196f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_ORIENTATION,
4206f2701b7SHenrik Rydberg 			     -MAX_FINGER_ORIENTATION,
4216f2701b7SHenrik Rydberg 			     MAX_FINGER_ORIENTATION, 0, 0);
4226f2701b7SHenrik Rydberg 	/* finger position */
4236f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_POSITION_X,
4246f2701b7SHenrik Rydberg 			     cfg->x.devmin, cfg->x.devmax, 0, 0);
4256f2701b7SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
4266f2701b7SHenrik Rydberg 			     cfg->y.devmin, cfg->y.devmax, 0, 0);
4276f2701b7SHenrik Rydberg 
428f89bd95cSHenrik Rydberg 	__set_bit(EV_KEY, input_dev->evbit);
429a6821f34SHenrik Rydberg 	__set_bit(BTN_TOUCH, input_dev->keybit);
430f89bd95cSHenrik Rydberg 	__set_bit(BTN_TOOL_FINGER, input_dev->keybit);
431f89bd95cSHenrik Rydberg 	__set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
432f89bd95cSHenrik Rydberg 	__set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
4336916d97fSHenrik Rydberg 	__set_bit(BTN_TOOL_QUADTAP, input_dev->keybit);
434f89bd95cSHenrik Rydberg 	__set_bit(BTN_LEFT, input_dev->keybit);
435c13aea03SHenrik Rydberg 
436c13aea03SHenrik Rydberg 	input_set_events_per_packet(input_dev, 60);
437f89bd95cSHenrik Rydberg }
438f89bd95cSHenrik Rydberg 
439f89bd95cSHenrik Rydberg /* report button data as logical button state */
440f89bd95cSHenrik Rydberg static int report_bt_state(struct bcm5974 *dev, int size)
441f89bd95cSHenrik Rydberg {
442f89bd95cSHenrik Rydberg 	if (size != sizeof(struct bt_data))
443f89bd95cSHenrik Rydberg 		return -EIO;
444f89bd95cSHenrik Rydberg 
44553402193SHenrik Rydberg 	dprintk(7,
44653402193SHenrik Rydberg 		"bcm5974: button data: %x %x %x %x\n",
44753402193SHenrik Rydberg 		dev->bt_data->unknown1, dev->bt_data->button,
44853402193SHenrik Rydberg 		dev->bt_data->rel_x, dev->bt_data->rel_y);
44953402193SHenrik Rydberg 
450f89bd95cSHenrik Rydberg 	input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
451f89bd95cSHenrik Rydberg 	input_sync(dev->input);
452f89bd95cSHenrik Rydberg 
453f89bd95cSHenrik Rydberg 	return 0;
454f89bd95cSHenrik Rydberg }
455f89bd95cSHenrik Rydberg 
4566f2701b7SHenrik Rydberg static void report_finger_data(struct input_dev *input,
4576f2701b7SHenrik Rydberg 			       const struct bcm5974_config *cfg,
4586f2701b7SHenrik Rydberg 			       const struct tp_finger *f)
4596f2701b7SHenrik Rydberg {
46057157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
46157157becSHenrik Rydberg 			 raw2int(f->force_major) << 1);
46257157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MINOR,
46357157becSHenrik Rydberg 			 raw2int(f->force_minor) << 1);
46457157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
46557157becSHenrik Rydberg 			 raw2int(f->size_major) << 1);
46657157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MINOR,
46757157becSHenrik Rydberg 			 raw2int(f->size_minor) << 1);
4686f2701b7SHenrik Rydberg 	input_report_abs(input, ABS_MT_ORIENTATION,
4696f2701b7SHenrik Rydberg 			 MAX_FINGER_ORIENTATION - raw2int(f->orientation));
4706f2701b7SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_X, raw2int(f->abs_x));
4716f2701b7SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_Y,
4726f2701b7SHenrik Rydberg 			 cfg->y.devmin + cfg->y.devmax - raw2int(f->abs_y));
4736f2701b7SHenrik Rydberg 	input_mt_sync(input);
4746f2701b7SHenrik Rydberg }
4756f2701b7SHenrik Rydberg 
476f89bd95cSHenrik Rydberg /* report trackpad data as logical trackpad state */
477f89bd95cSHenrik Rydberg static int report_tp_state(struct bcm5974 *dev, int size)
478f89bd95cSHenrik Rydberg {
479f89bd95cSHenrik Rydberg 	const struct bcm5974_config *c = &dev->cfg;
4809894cf0fSHenrik Rydberg 	const struct tp_finger *f;
481f89bd95cSHenrik Rydberg 	struct input_dev *input = dev->input;
4826f2701b7SHenrik Rydberg 	int raw_p, raw_w, raw_x, raw_y, raw_n, i;
4839de48cc3SHenrik Rydberg 	int ptest, origin, ibt = 0, nmin = 0, nmax = 0;
48475e21e3fSHenrik Rydberg 	int abs_p = 0, abs_w = 0, abs_x = 0, abs_y = 0;
485f89bd95cSHenrik Rydberg 
4869894cf0fSHenrik Rydberg 	if (size < c->tp_offset || (size - c->tp_offset) % SIZEOF_FINGER != 0)
487f89bd95cSHenrik Rydberg 		return -EIO;
488f89bd95cSHenrik Rydberg 
4899894cf0fSHenrik Rydberg 	/* finger data, le16-aligned */
4909894cf0fSHenrik Rydberg 	f = (const struct tp_finger *)(dev->tp_data + c->tp_offset);
4919894cf0fSHenrik Rydberg 	raw_n = (size - c->tp_offset) / SIZEOF_FINGER;
4929894cf0fSHenrik Rydberg 
49375e21e3fSHenrik Rydberg 	/* always track the first finger; when detached, start over */
4949894cf0fSHenrik Rydberg 	if (raw_n) {
4956f2701b7SHenrik Rydberg 
4966f2701b7SHenrik Rydberg 		/* report raw trackpad data */
4976f2701b7SHenrik Rydberg 		for (i = 0; i < raw_n; i++)
4986f2701b7SHenrik Rydberg 			report_finger_data(input, c, &f[i]);
4996f2701b7SHenrik Rydberg 
50075e21e3fSHenrik Rydberg 		raw_p = raw2int(f->force_major);
50175e21e3fSHenrik Rydberg 		raw_w = raw2int(f->size_major);
50275e21e3fSHenrik Rydberg 		raw_x = raw2int(f->abs_x);
50375e21e3fSHenrik Rydberg 		raw_y = raw2int(f->abs_y);
504f89bd95cSHenrik Rydberg 
505f89bd95cSHenrik Rydberg 		dprintk(9,
50653402193SHenrik Rydberg 			"bcm5974: "
50753402193SHenrik Rydberg 			"raw: p: %+05d w: %+05d x: %+05d y: %+05d n: %d\n",
50853402193SHenrik Rydberg 			raw_p, raw_w, raw_x, raw_y, raw_n);
509f89bd95cSHenrik Rydberg 
51075e21e3fSHenrik Rydberg 		ptest = int2bound(&c->p, raw_p);
51175e21e3fSHenrik Rydberg 		origin = raw2int(f->origin);
512158e9287SHenrik Rydberg 
51375e21e3fSHenrik Rydberg 		/* while tracking finger still valid, count all fingers */
51475e21e3fSHenrik Rydberg 		if (ptest > PRESSURE_LOW && origin) {
51575e21e3fSHenrik Rydberg 			abs_p = ptest;
51675e21e3fSHenrik Rydberg 			abs_w = int2bound(&c->w, raw_w);
51775e21e3fSHenrik Rydberg 			abs_x = int2bound(&c->x, raw_x - c->x.devmin);
51875e21e3fSHenrik Rydberg 			abs_y = int2bound(&c->y, c->y.devmax - raw_y);
5199894cf0fSHenrik Rydberg 			while (raw_n--) {
5209de48cc3SHenrik Rydberg 				ptest = int2bound(&c->p,
5219de48cc3SHenrik Rydberg 						  raw2int(f->force_major));
52275e21e3fSHenrik Rydberg 				if (ptest > PRESSURE_LOW)
52375e21e3fSHenrik Rydberg 					nmax++;
52475e21e3fSHenrik Rydberg 				if (ptest > PRESSURE_HIGH)
52575e21e3fSHenrik Rydberg 					nmin++;
5269894cf0fSHenrik Rydberg 				f++;
52775e21e3fSHenrik Rydberg 			}
52875e21e3fSHenrik Rydberg 		}
5299de48cc3SHenrik Rydberg 	}
530f89bd95cSHenrik Rydberg 
53150635115SHenrik Rydberg 	/* set the integrated button if applicable */
53250635115SHenrik Rydberg 	if (c->tp_type == TYPE2)
53350635115SHenrik Rydberg 		ibt = raw2int(dev->tp_data[BUTTON_TYPE2]);
53450635115SHenrik Rydberg 
53575e21e3fSHenrik Rydberg 	if (dev->fingers < nmin)
53675e21e3fSHenrik Rydberg 		dev->fingers = nmin;
53775e21e3fSHenrik Rydberg 	if (dev->fingers > nmax)
53875e21e3fSHenrik Rydberg 		dev->fingers = nmax;
53975e21e3fSHenrik Rydberg 
540a6821f34SHenrik Rydberg 	input_report_key(input, BTN_TOUCH, dev->fingers > 0);
54175e21e3fSHenrik Rydberg 	input_report_key(input, BTN_TOOL_FINGER, dev->fingers == 1);
54275e21e3fSHenrik Rydberg 	input_report_key(input, BTN_TOOL_DOUBLETAP, dev->fingers == 2);
5436916d97fSHenrik Rydberg 	input_report_key(input, BTN_TOOL_TRIPLETAP, dev->fingers == 3);
5446916d97fSHenrik Rydberg 	input_report_key(input, BTN_TOOL_QUADTAP, dev->fingers > 3);
54575e21e3fSHenrik Rydberg 
54675e21e3fSHenrik Rydberg 	input_report_abs(input, ABS_PRESSURE, abs_p);
54775e21e3fSHenrik Rydberg 	input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
54875e21e3fSHenrik Rydberg 
54975e21e3fSHenrik Rydberg 	if (abs_p) {
55075e21e3fSHenrik Rydberg 		input_report_abs(input, ABS_X, abs_x);
55175e21e3fSHenrik Rydberg 		input_report_abs(input, ABS_Y, abs_y);
55275e21e3fSHenrik Rydberg 
55375e21e3fSHenrik Rydberg 		dprintk(8,
55475e21e3fSHenrik Rydberg 			"bcm5974: abs: p: %+05d w: %+05d x: %+05d y: %+05d "
55553402193SHenrik Rydberg 			"nmin: %d nmax: %d n: %d ibt: %d\n", abs_p, abs_w,
55653402193SHenrik Rydberg 			abs_x, abs_y, nmin, nmax, dev->fingers, ibt);
55775e21e3fSHenrik Rydberg 
55875e21e3fSHenrik Rydberg 	}
559f89bd95cSHenrik Rydberg 
560158e9287SHenrik Rydberg 	/* type 2 reports button events via ibt only */
561158e9287SHenrik Rydberg 	if (c->tp_type == TYPE2)
562158e9287SHenrik Rydberg 		input_report_key(input, BTN_LEFT, ibt);
563158e9287SHenrik Rydberg 
564f89bd95cSHenrik Rydberg 	input_sync(input);
565f89bd95cSHenrik Rydberg 
566f89bd95cSHenrik Rydberg 	return 0;
567f89bd95cSHenrik Rydberg }
568f89bd95cSHenrik Rydberg 
569f89bd95cSHenrik Rydberg /* Wellspring initialization constants */
570f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID		1
571f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID	9
572f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_REQUEST_VALUE		0x300
573f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_REQUEST_INDEX		0
574f89bd95cSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_VENDOR_VALUE		0x01
575cd72ad3fSHenrik Rydberg #define BCM5974_WELLSPRING_MODE_NORMAL_VALUE		0x08
576f89bd95cSHenrik Rydberg 
577cd72ad3fSHenrik Rydberg static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
578f89bd95cSHenrik Rydberg {
579f89bd95cSHenrik Rydberg 	char *data = kmalloc(8, GFP_KERNEL);
580f89bd95cSHenrik Rydberg 	int retval = 0, size;
581f89bd95cSHenrik Rydberg 
582f89bd95cSHenrik Rydberg 	if (!data) {
583f89bd95cSHenrik Rydberg 		err("bcm5974: out of memory");
584f89bd95cSHenrik Rydberg 		retval = -ENOMEM;
585f89bd95cSHenrik Rydberg 		goto out;
586f89bd95cSHenrik Rydberg 	}
587f89bd95cSHenrik Rydberg 
588f89bd95cSHenrik Rydberg 	/* read configuration */
589f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
590f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
591f89bd95cSHenrik Rydberg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
592f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
593f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
594f89bd95cSHenrik Rydberg 
595f89bd95cSHenrik Rydberg 	if (size != 8) {
596f89bd95cSHenrik Rydberg 		err("bcm5974: could not read from device");
597f89bd95cSHenrik Rydberg 		retval = -EIO;
598f89bd95cSHenrik Rydberg 		goto out;
599f89bd95cSHenrik Rydberg 	}
600f89bd95cSHenrik Rydberg 
601f89bd95cSHenrik Rydberg 	/* apply the mode switch */
602cd72ad3fSHenrik Rydberg 	data[0] = on ?
603cd72ad3fSHenrik Rydberg 		BCM5974_WELLSPRING_MODE_VENDOR_VALUE :
604cd72ad3fSHenrik Rydberg 		BCM5974_WELLSPRING_MODE_NORMAL_VALUE;
605f89bd95cSHenrik Rydberg 
606f89bd95cSHenrik Rydberg 	/* write configuration */
607f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
608f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
609f89bd95cSHenrik Rydberg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
610f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_VALUE,
611f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_REQUEST_INDEX, data, 8, 5000);
612f89bd95cSHenrik Rydberg 
613f89bd95cSHenrik Rydberg 	if (size != 8) {
614f89bd95cSHenrik Rydberg 		err("bcm5974: could not write to device");
615f89bd95cSHenrik Rydberg 		retval = -EIO;
616f89bd95cSHenrik Rydberg 		goto out;
617f89bd95cSHenrik Rydberg 	}
618f89bd95cSHenrik Rydberg 
619cd72ad3fSHenrik Rydberg 	dprintk(2, "bcm5974: switched to %s mode.\n",
620cd72ad3fSHenrik Rydberg 		on ? "wellspring" : "normal");
621f89bd95cSHenrik Rydberg 
622f89bd95cSHenrik Rydberg  out:
623f89bd95cSHenrik Rydberg 	kfree(data);
624f89bd95cSHenrik Rydberg 	return retval;
625f89bd95cSHenrik Rydberg }
626f89bd95cSHenrik Rydberg 
627f89bd95cSHenrik Rydberg static void bcm5974_irq_button(struct urb *urb)
628f89bd95cSHenrik Rydberg {
629f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
630f89bd95cSHenrik Rydberg 	int error;
631f89bd95cSHenrik Rydberg 
632f89bd95cSHenrik Rydberg 	switch (urb->status) {
633f89bd95cSHenrik Rydberg 	case 0:
634f89bd95cSHenrik Rydberg 		break;
635f89bd95cSHenrik Rydberg 	case -EOVERFLOW:
636f89bd95cSHenrik Rydberg 	case -ECONNRESET:
637f89bd95cSHenrik Rydberg 	case -ENOENT:
638f89bd95cSHenrik Rydberg 	case -ESHUTDOWN:
639f89bd95cSHenrik Rydberg 		dbg("bcm5974: button urb shutting down: %d", urb->status);
640f89bd95cSHenrik Rydberg 		return;
641f89bd95cSHenrik Rydberg 	default:
642f89bd95cSHenrik Rydberg 		dbg("bcm5974: button urb status: %d", urb->status);
643f89bd95cSHenrik Rydberg 		goto exit;
644f89bd95cSHenrik Rydberg 	}
645f89bd95cSHenrik Rydberg 
646f89bd95cSHenrik Rydberg 	if (report_bt_state(dev, dev->bt_urb->actual_length))
647f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad button package, length: %d\n",
648f89bd95cSHenrik Rydberg 			dev->bt_urb->actual_length);
649f89bd95cSHenrik Rydberg 
650f89bd95cSHenrik Rydberg exit:
651f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
652f89bd95cSHenrik Rydberg 	if (error)
653f89bd95cSHenrik Rydberg 		err("bcm5974: button urb failed: %d", error);
654f89bd95cSHenrik Rydberg }
655f89bd95cSHenrik Rydberg 
656f89bd95cSHenrik Rydberg static void bcm5974_irq_trackpad(struct urb *urb)
657f89bd95cSHenrik Rydberg {
658f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
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:
668f89bd95cSHenrik Rydberg 		dbg("bcm5974: trackpad urb shutting down: %d", urb->status);
669f89bd95cSHenrik Rydberg 		return;
670f89bd95cSHenrik Rydberg 	default:
671f89bd95cSHenrik Rydberg 		dbg("bcm5974: trackpad urb status: %d", urb->status);
672f89bd95cSHenrik Rydberg 		goto exit;
673f89bd95cSHenrik Rydberg 	}
674f89bd95cSHenrik Rydberg 
675f89bd95cSHenrik Rydberg 	/* control response ignored */
676f89bd95cSHenrik Rydberg 	if (dev->tp_urb->actual_length == 2)
677f89bd95cSHenrik Rydberg 		goto exit;
678f89bd95cSHenrik Rydberg 
679f89bd95cSHenrik Rydberg 	if (report_tp_state(dev, dev->tp_urb->actual_length))
680f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
681f89bd95cSHenrik Rydberg 			dev->tp_urb->actual_length);
682f89bd95cSHenrik Rydberg 
683f89bd95cSHenrik Rydberg exit:
684f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
685f89bd95cSHenrik Rydberg 	if (error)
686f89bd95cSHenrik Rydberg 		err("bcm5974: trackpad urb failed: %d", error);
687f89bd95cSHenrik Rydberg }
688f89bd95cSHenrik Rydberg 
689f89bd95cSHenrik Rydberg /*
690f89bd95cSHenrik Rydberg  * The Wellspring trackpad, like many recent Apple trackpads, share
691f89bd95cSHenrik Rydberg  * the usb device with the keyboard. Since keyboards are usually
692f89bd95cSHenrik Rydberg  * handled by the HID system, the device ends up being handled by two
693f89bd95cSHenrik Rydberg  * modules. Setting up the device therefore becomes slightly
694f89bd95cSHenrik Rydberg  * complicated. To enable multitouch features, a mode switch is
695f89bd95cSHenrik Rydberg  * required, which is usually applied via the control interface of the
696f89bd95cSHenrik Rydberg  * device.  It can be argued where this switch should take place. In
697f89bd95cSHenrik Rydberg  * some drivers, like appletouch, the switch is made during
698f89bd95cSHenrik Rydberg  * probe. However, the hid module may also alter the state of the
699f89bd95cSHenrik Rydberg  * device, resulting in trackpad malfunction under certain
700f89bd95cSHenrik Rydberg  * circumstances. To get around this problem, there is at least one
701f89bd95cSHenrik Rydberg  * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
70225985edcSLucas De Marchi  * receive a reset_resume request rather than the normal resume.
703f89bd95cSHenrik Rydberg  * Since the implementation of reset_resume is equal to mode switch
704f89bd95cSHenrik Rydberg  * plus start_traffic, it seems easier to always do the switch when
705f89bd95cSHenrik Rydberg  * starting traffic on the device.
706f89bd95cSHenrik Rydberg  */
707f89bd95cSHenrik Rydberg static int bcm5974_start_traffic(struct bcm5974 *dev)
708f89bd95cSHenrik Rydberg {
7091719ec41SLuo Jinghua 	int error;
7101719ec41SLuo Jinghua 
7111719ec41SLuo Jinghua 	error = bcm5974_wellspring_mode(dev, true);
7121719ec41SLuo Jinghua 	if (error) {
713f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: mode switch failed\n");
7141719ec41SLuo Jinghua 		goto err_out;
715f89bd95cSHenrik Rydberg 	}
716f89bd95cSHenrik Rydberg 
7171719ec41SLuo Jinghua 	error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
7181719ec41SLuo Jinghua 	if (error)
7191719ec41SLuo Jinghua 		goto err_reset_mode;
720f89bd95cSHenrik Rydberg 
7211719ec41SLuo Jinghua 	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
7221719ec41SLuo Jinghua 	if (error)
723f89bd95cSHenrik Rydberg 		goto err_kill_bt;
724f89bd95cSHenrik Rydberg 
725f89bd95cSHenrik Rydberg 	return 0;
726f89bd95cSHenrik Rydberg 
727f89bd95cSHenrik Rydberg err_kill_bt:
728f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
7291719ec41SLuo Jinghua err_reset_mode:
7301719ec41SLuo Jinghua 	bcm5974_wellspring_mode(dev, false);
7311719ec41SLuo Jinghua err_out:
7321719ec41SLuo Jinghua 	return error;
733f89bd95cSHenrik Rydberg }
734f89bd95cSHenrik Rydberg 
735f89bd95cSHenrik Rydberg static void bcm5974_pause_traffic(struct bcm5974 *dev)
736f89bd95cSHenrik Rydberg {
737f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->tp_urb);
738f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
739cd72ad3fSHenrik Rydberg 	bcm5974_wellspring_mode(dev, false);
740f89bd95cSHenrik Rydberg }
741f89bd95cSHenrik Rydberg 
742f89bd95cSHenrik Rydberg /*
743f89bd95cSHenrik Rydberg  * The code below implements open/close and manual suspend/resume.
744f89bd95cSHenrik Rydberg  * All functions may be called in random order.
745f89bd95cSHenrik Rydberg  *
746f89bd95cSHenrik Rydberg  * Opening a suspended device fails with EACCES - permission denied.
747f89bd95cSHenrik Rydberg  *
748f89bd95cSHenrik Rydberg  * Failing a resume leaves the device resumed but closed.
749f89bd95cSHenrik Rydberg  */
750f89bd95cSHenrik Rydberg static int bcm5974_open(struct input_dev *input)
751f89bd95cSHenrik Rydberg {
752f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
753f89bd95cSHenrik Rydberg 	int error;
754f89bd95cSHenrik Rydberg 
75588da765fSDmitry Torokhov 	error = usb_autopm_get_interface(dev->intf);
75688da765fSDmitry Torokhov 	if (error)
75788da765fSDmitry Torokhov 		return error;
75888da765fSDmitry Torokhov 
759f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
760f89bd95cSHenrik Rydberg 
761f89bd95cSHenrik Rydberg 	error = bcm5974_start_traffic(dev);
762f89bd95cSHenrik Rydberg 	if (!error)
763f89bd95cSHenrik Rydberg 		dev->opened = 1;
764f89bd95cSHenrik Rydberg 
765f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
766f89bd95cSHenrik Rydberg 
76788da765fSDmitry Torokhov 	if (error)
76888da765fSDmitry Torokhov 		usb_autopm_put_interface(dev->intf);
76988da765fSDmitry Torokhov 
770f89bd95cSHenrik Rydberg 	return error;
771f89bd95cSHenrik Rydberg }
772f89bd95cSHenrik Rydberg 
773f89bd95cSHenrik Rydberg static void bcm5974_close(struct input_dev *input)
774f89bd95cSHenrik Rydberg {
775f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
776f89bd95cSHenrik Rydberg 
777f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
778f89bd95cSHenrik Rydberg 
779f89bd95cSHenrik Rydberg 	bcm5974_pause_traffic(dev);
780f89bd95cSHenrik Rydberg 	dev->opened = 0;
781f89bd95cSHenrik Rydberg 
782f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
78388da765fSDmitry Torokhov 
78488da765fSDmitry Torokhov 	usb_autopm_put_interface(dev->intf);
785f89bd95cSHenrik Rydberg }
786f89bd95cSHenrik Rydberg 
787f89bd95cSHenrik Rydberg static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
788f89bd95cSHenrik Rydberg {
789f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
790f89bd95cSHenrik Rydberg 
791f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
792f89bd95cSHenrik Rydberg 
793f89bd95cSHenrik Rydberg 	if (dev->opened)
794f89bd95cSHenrik Rydberg 		bcm5974_pause_traffic(dev);
795f89bd95cSHenrik Rydberg 
796f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
797f89bd95cSHenrik Rydberg 
798f89bd95cSHenrik Rydberg 	return 0;
799f89bd95cSHenrik Rydberg }
800f89bd95cSHenrik Rydberg 
801f89bd95cSHenrik Rydberg static int bcm5974_resume(struct usb_interface *iface)
802f89bd95cSHenrik Rydberg {
803f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
804f89bd95cSHenrik Rydberg 	int error = 0;
805f89bd95cSHenrik Rydberg 
806f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
807f89bd95cSHenrik Rydberg 
808f89bd95cSHenrik Rydberg 	if (dev->opened)
809f89bd95cSHenrik Rydberg 		error = bcm5974_start_traffic(dev);
810f89bd95cSHenrik Rydberg 
811f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
812f89bd95cSHenrik Rydberg 
813f89bd95cSHenrik Rydberg 	return error;
814f89bd95cSHenrik Rydberg }
815f89bd95cSHenrik Rydberg 
816f89bd95cSHenrik Rydberg static int bcm5974_probe(struct usb_interface *iface,
817f89bd95cSHenrik Rydberg 			 const struct usb_device_id *id)
818f89bd95cSHenrik Rydberg {
819f89bd95cSHenrik Rydberg 	struct usb_device *udev = interface_to_usbdev(iface);
820f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
821f89bd95cSHenrik Rydberg 	struct bcm5974 *dev;
822f89bd95cSHenrik Rydberg 	struct input_dev *input_dev;
823f89bd95cSHenrik Rydberg 	int error = -ENOMEM;
824f89bd95cSHenrik Rydberg 
825f89bd95cSHenrik Rydberg 	/* find the product index */
826f89bd95cSHenrik Rydberg 	cfg = bcm5974_get_config(udev);
827f89bd95cSHenrik Rydberg 
828f89bd95cSHenrik Rydberg 	/* allocate memory for our device state and initialize it */
829f89bd95cSHenrik Rydberg 	dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
830f89bd95cSHenrik Rydberg 	input_dev = input_allocate_device();
831f89bd95cSHenrik Rydberg 	if (!dev || !input_dev) {
832f89bd95cSHenrik Rydberg 		err("bcm5974: out of memory");
833f89bd95cSHenrik Rydberg 		goto err_free_devs;
834f89bd95cSHenrik Rydberg 	}
835f89bd95cSHenrik Rydberg 
836f89bd95cSHenrik Rydberg 	dev->udev = udev;
83788da765fSDmitry Torokhov 	dev->intf = iface;
838f89bd95cSHenrik Rydberg 	dev->input = input_dev;
839f89bd95cSHenrik Rydberg 	dev->cfg = *cfg;
840f89bd95cSHenrik Rydberg 	mutex_init(&dev->pm_mutex);
841f89bd95cSHenrik Rydberg 
842f89bd95cSHenrik Rydberg 	/* setup urbs */
843f89bd95cSHenrik Rydberg 	dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
844f89bd95cSHenrik Rydberg 	if (!dev->bt_urb)
845f89bd95cSHenrik Rydberg 		goto err_free_devs;
846f89bd95cSHenrik Rydberg 
847f89bd95cSHenrik Rydberg 	dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
848f89bd95cSHenrik Rydberg 	if (!dev->tp_urb)
849f89bd95cSHenrik Rydberg 		goto err_free_bt_urb;
850f89bd95cSHenrik Rydberg 
851997ea58eSDaniel Mack 	dev->bt_data = usb_alloc_coherent(dev->udev,
852f89bd95cSHenrik Rydberg 					  dev->cfg.bt_datalen, GFP_KERNEL,
853f89bd95cSHenrik Rydberg 					  &dev->bt_urb->transfer_dma);
854f89bd95cSHenrik Rydberg 	if (!dev->bt_data)
855f89bd95cSHenrik Rydberg 		goto err_free_urb;
856f89bd95cSHenrik Rydberg 
857997ea58eSDaniel Mack 	dev->tp_data = usb_alloc_coherent(dev->udev,
858f89bd95cSHenrik Rydberg 					  dev->cfg.tp_datalen, GFP_KERNEL,
859f89bd95cSHenrik Rydberg 					  &dev->tp_urb->transfer_dma);
860f89bd95cSHenrik Rydberg 	if (!dev->tp_data)
861f89bd95cSHenrik Rydberg 		goto err_free_bt_buffer;
862f89bd95cSHenrik Rydberg 
863f89bd95cSHenrik Rydberg 	usb_fill_int_urb(dev->bt_urb, udev,
864f89bd95cSHenrik Rydberg 			 usb_rcvintpipe(udev, cfg->bt_ep),
865f89bd95cSHenrik Rydberg 			 dev->bt_data, dev->cfg.bt_datalen,
866f89bd95cSHenrik Rydberg 			 bcm5974_irq_button, dev, 1);
867f89bd95cSHenrik Rydberg 
868f89bd95cSHenrik Rydberg 	usb_fill_int_urb(dev->tp_urb, udev,
869f89bd95cSHenrik Rydberg 			 usb_rcvintpipe(udev, cfg->tp_ep),
870f89bd95cSHenrik Rydberg 			 dev->tp_data, dev->cfg.tp_datalen,
871f89bd95cSHenrik Rydberg 			 bcm5974_irq_trackpad, dev, 1);
872f89bd95cSHenrik Rydberg 
873f89bd95cSHenrik Rydberg 	/* create bcm5974 device */
874f89bd95cSHenrik Rydberg 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
875f89bd95cSHenrik Rydberg 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
876f89bd95cSHenrik Rydberg 
877f89bd95cSHenrik Rydberg 	input_dev->name = "bcm5974";
878f89bd95cSHenrik Rydberg 	input_dev->phys = dev->phys;
879f89bd95cSHenrik Rydberg 	usb_to_input_id(dev->udev, &input_dev->id);
880158e9287SHenrik Rydberg 	/* report driver capabilities via the version field */
881158e9287SHenrik Rydberg 	input_dev->id.version = cfg->caps;
882f89bd95cSHenrik Rydberg 	input_dev->dev.parent = &iface->dev;
883f89bd95cSHenrik Rydberg 
884f89bd95cSHenrik Rydberg 	input_set_drvdata(input_dev, dev);
885f89bd95cSHenrik Rydberg 
886f89bd95cSHenrik Rydberg 	input_dev->open = bcm5974_open;
887f89bd95cSHenrik Rydberg 	input_dev->close = bcm5974_close;
888f89bd95cSHenrik Rydberg 
889f89bd95cSHenrik Rydberg 	setup_events_to_report(input_dev, cfg);
890f89bd95cSHenrik Rydberg 
891f89bd95cSHenrik Rydberg 	error = input_register_device(dev->input);
892f89bd95cSHenrik Rydberg 	if (error)
893f89bd95cSHenrik Rydberg 		goto err_free_buffer;
894f89bd95cSHenrik Rydberg 
895f89bd95cSHenrik Rydberg 	/* save our data pointer in this interface device */
896f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, dev);
897f89bd95cSHenrik Rydberg 
898f89bd95cSHenrik Rydberg 	return 0;
899f89bd95cSHenrik Rydberg 
900f89bd95cSHenrik Rydberg err_free_buffer:
901997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
902f89bd95cSHenrik Rydberg 		dev->tp_data, dev->tp_urb->transfer_dma);
903f89bd95cSHenrik Rydberg err_free_bt_buffer:
904997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
905f89bd95cSHenrik Rydberg 		dev->bt_data, dev->bt_urb->transfer_dma);
906f89bd95cSHenrik Rydberg err_free_urb:
907f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
908f89bd95cSHenrik Rydberg err_free_bt_urb:
909f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
910f89bd95cSHenrik Rydberg err_free_devs:
911f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
912f89bd95cSHenrik Rydberg 	input_free_device(input_dev);
913f89bd95cSHenrik Rydberg 	kfree(dev);
914f89bd95cSHenrik Rydberg 	return error;
915f89bd95cSHenrik Rydberg }
916f89bd95cSHenrik Rydberg 
917f89bd95cSHenrik Rydberg static void bcm5974_disconnect(struct usb_interface *iface)
918f89bd95cSHenrik Rydberg {
919f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
920f89bd95cSHenrik Rydberg 
921f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
922f89bd95cSHenrik Rydberg 
923f89bd95cSHenrik Rydberg 	input_unregister_device(dev->input);
924997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
925f89bd95cSHenrik Rydberg 			  dev->tp_data, dev->tp_urb->transfer_dma);
926997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
927f89bd95cSHenrik Rydberg 			  dev->bt_data, dev->bt_urb->transfer_dma);
928f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
929f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
930f89bd95cSHenrik Rydberg 	kfree(dev);
931f89bd95cSHenrik Rydberg }
932f89bd95cSHenrik Rydberg 
933f89bd95cSHenrik Rydberg static struct usb_driver bcm5974_driver = {
934f89bd95cSHenrik Rydberg 	.name			= "bcm5974",
935f89bd95cSHenrik Rydberg 	.probe			= bcm5974_probe,
936f89bd95cSHenrik Rydberg 	.disconnect		= bcm5974_disconnect,
937f89bd95cSHenrik Rydberg 	.suspend		= bcm5974_suspend,
938f89bd95cSHenrik Rydberg 	.resume			= bcm5974_resume,
939f89bd95cSHenrik Rydberg 	.id_table		= bcm5974_table,
94088da765fSDmitry Torokhov 	.supports_autosuspend	= 1,
941f89bd95cSHenrik Rydberg };
942f89bd95cSHenrik Rydberg 
943*08642e7cSGreg Kroah-Hartman module_usb_driver(bcm5974_driver);
944