xref: /linux/drivers/input/mouse/bcm5974.c (revision d58069265c9d15c04c9e3832cd1d9dffe9d4d5f6)
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)
5*d5806926SJohn Horan  * Copyright (C) 2015      John Horan (knasher@gmail.com)
6f89bd95cSHenrik Rydberg  *
7f89bd95cSHenrik Rydberg  * The USB initialization and package decoding was made by
8f89bd95cSHenrik Rydberg  * Scott Shawcroft as part of the touchd user-space driver project:
9f89bd95cSHenrik Rydberg  * Copyright (C) 2008	   Scott Shawcroft (scott.shawcroft@gmail.com)
10f89bd95cSHenrik Rydberg  *
11f89bd95cSHenrik Rydberg  * The BCM5974 driver is based on the appletouch driver:
12f89bd95cSHenrik Rydberg  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
13f89bd95cSHenrik Rydberg  * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
14f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Stelian Pop (stelian@popies.net)
15f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Frank Arnold (frank@scirocco-5v-turbo.de)
16f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Peter Osterlund (petero2@telia.com)
17f89bd95cSHenrik Rydberg  * Copyright (C) 2005	   Michael Hanselmann (linux-kernel@hansmi.ch)
18f89bd95cSHenrik Rydberg  * Copyright (C) 2006	   Nicolas Boichat (nicolas@boichat.ch)
19f89bd95cSHenrik Rydberg  *
20f89bd95cSHenrik Rydberg  * This program is free software; you can redistribute it and/or modify
21f89bd95cSHenrik Rydberg  * it under the terms of the GNU General Public License as published by
22f89bd95cSHenrik Rydberg  * the Free Software Foundation; either version 2 of the License, or
23f89bd95cSHenrik Rydberg  * (at your option) any later version.
24f89bd95cSHenrik Rydberg  *
25f89bd95cSHenrik Rydberg  * This program is distributed in the hope that it will be useful,
26f89bd95cSHenrik Rydberg  * but WITHOUT ANY WARRANTY; without even the implied warranty of
27f89bd95cSHenrik Rydberg  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
28f89bd95cSHenrik Rydberg  * GNU General Public License for more details.
29f89bd95cSHenrik Rydberg  *
30f89bd95cSHenrik Rydberg  * You should have received a copy of the GNU General Public License
31f89bd95cSHenrik Rydberg  * along with this program; if not, write to the Free Software
32f89bd95cSHenrik Rydberg  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
33f89bd95cSHenrik Rydberg  *
34f89bd95cSHenrik Rydberg  */
35f89bd95cSHenrik Rydberg 
36f89bd95cSHenrik Rydberg #include <linux/kernel.h>
37f89bd95cSHenrik Rydberg #include <linux/errno.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
91148c1c8aSDmitry Torokhov /* MacbookAir6,2 (unibody, June 2013) */
928c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI	0x0290
938c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_ISO	0x0291
948c89cc17SHenrik Rydberg #define USB_DEVICE_ID_APPLE_WELLSPRING8_JIS	0x0292
95*d5806926SJohn Horan /* MacbookPro12,1 (2015) */
96*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI	0x0272
97*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_ISO	0x0273
98*d5806926SJohn Horan #define USB_DEVICE_ID_APPLE_WELLSPRING9_JIS	0x0274
99f89bd95cSHenrik Rydberg 
100f89bd95cSHenrik Rydberg #define BCM5974_DEVICE(prod) {					\
101f89bd95cSHenrik Rydberg 	.match_flags = (USB_DEVICE_ID_MATCH_DEVICE |		\
102f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_CLASS |		\
103f89bd95cSHenrik Rydberg 			USB_DEVICE_ID_MATCH_INT_PROTOCOL),	\
104f89bd95cSHenrik Rydberg 	.idVendor = USB_VENDOR_ID_APPLE,			\
105f89bd95cSHenrik Rydberg 	.idProduct = (prod),					\
106f89bd95cSHenrik Rydberg 	.bInterfaceClass = USB_INTERFACE_CLASS_HID,		\
107f89bd95cSHenrik Rydberg 	.bInterfaceProtocol = USB_INTERFACE_PROTOCOL_MOUSE	\
108f89bd95cSHenrik Rydberg }
109f89bd95cSHenrik Rydberg 
110f89bd95cSHenrik Rydberg /* table of devices that work with this driver */
111f89bd95cSHenrik Rydberg static const struct usb_device_id bcm5974_table[] = {
112f89bd95cSHenrik Rydberg 	/* MacbookAir1.1 */
113f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ANSI),
114f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_ISO),
115f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING_JIS),
116f89bd95cSHenrik Rydberg 	/* MacbookProPenryn */
117f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI),
118f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_ISO),
119f89bd95cSHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING2_JIS),
120158e9287SHenrik Rydberg 	/* Macbook5,1 */
121158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI),
122158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_ISO),
123158e9287SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING3_JIS),
1246021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,2 */
1256021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI),
1266021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_ISO),
1276021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4_JIS),
1286021afcfSEdgar (gimli) Hucek 	/* MacbookAir3,1 */
1296021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI),
1306021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO),
1316021afcfSEdgar (gimli) Hucek 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS),
13247340bd9SAndy Botting 	/* MacbookPro8 */
13347340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI),
13447340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_ISO),
13547340bd9SAndy Botting 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5_JIS),
1361c601beaSPieter-Augustijn Van Malleghem 	/* MacbookAir4,1 */
1371c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI),
1381c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO),
1391c601beaSPieter-Augustijn Van Malleghem 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS),
140db0b34b0SJoshua V. Dillon 	/* MacbookAir4,2 */
141db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI),
142db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_ISO),
143db0b34b0SJoshua V. Dillon 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING6_JIS),
144c331eb58SAndrew Drake 	/* MacbookPro8,2 */
145c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI),
146c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO),
147c331eb58SAndrew Drake 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS),
1483dde22a9SHenrik Rydberg 	/* MacbookPro10,1 */
1493dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI),
1503dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_ISO),
1513dde22a9SHenrik Rydberg 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7_JIS),
1528d80da90SDirk Hohndel 	/* MacbookPro10,2 */
1538d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI),
1548d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO),
1558d80da90SDirk Hohndel 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS),
156148c1c8aSDmitry Torokhov 	/* MacbookAir6,2 */
157148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI),
158148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_ISO),
159148c1c8aSDmitry Torokhov 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING8_JIS),
160*d5806926SJohn Horan 	/* MacbookPro12,1 */
161*d5806926SJohn Horan 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI),
162*d5806926SJohn Horan 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_ISO),
163*d5806926SJohn Horan 	BCM5974_DEVICE(USB_DEVICE_ID_APPLE_WELLSPRING9_JIS),
164f89bd95cSHenrik Rydberg 	/* Terminating entry */
165f89bd95cSHenrik Rydberg 	{}
166f89bd95cSHenrik Rydberg };
167f89bd95cSHenrik Rydberg MODULE_DEVICE_TABLE(usb, bcm5974_table);
168f89bd95cSHenrik Rydberg 
169f89bd95cSHenrik Rydberg MODULE_AUTHOR("Henrik Rydberg");
170f89bd95cSHenrik Rydberg MODULE_DESCRIPTION("Apple USB BCM5974 multitouch driver");
171f89bd95cSHenrik Rydberg MODULE_LICENSE("GPL");
172f89bd95cSHenrik Rydberg 
173f89bd95cSHenrik Rydberg #define dprintk(level, format, a...)\
174f89bd95cSHenrik Rydberg 	{ if (debug >= level) printk(KERN_DEBUG format, ##a); }
175f89bd95cSHenrik Rydberg 
176f89bd95cSHenrik Rydberg static int debug = 1;
177f89bd95cSHenrik Rydberg module_param(debug, int, 0644);
178f89bd95cSHenrik Rydberg MODULE_PARM_DESC(debug, "Activate debugging output");
179f89bd95cSHenrik Rydberg 
180f89bd95cSHenrik Rydberg /* button data structure */
181f89bd95cSHenrik Rydberg struct bt_data {
182f89bd95cSHenrik Rydberg 	u8 unknown1;		/* constant */
183f89bd95cSHenrik Rydberg 	u8 button;		/* left button */
184f89bd95cSHenrik Rydberg 	u8 rel_x;		/* relative x coordinate */
185f89bd95cSHenrik Rydberg 	u8 rel_y;		/* relative y coordinate */
186f89bd95cSHenrik Rydberg };
187f89bd95cSHenrik Rydberg 
1889894cf0fSHenrik Rydberg /* trackpad header types */
1899894cf0fSHenrik Rydberg enum tp_type {
190158e9287SHenrik Rydberg 	TYPE1,			/* plain trackpad */
191148c1c8aSDmitry Torokhov 	TYPE2,			/* button integrated in trackpad */
192*d5806926SJohn Horan 	TYPE3,			/* additional header fields since June 2013 */
193*d5806926SJohn Horan 	TYPE4			/* additional header field for pressure data */
194f89bd95cSHenrik Rydberg };
195f89bd95cSHenrik Rydberg 
1969894cf0fSHenrik Rydberg /* trackpad finger data offsets, le16-aligned */
197efbd3470SHenrik Rydberg #define HEADER_TYPE1		(13 * sizeof(__le16))
198efbd3470SHenrik Rydberg #define HEADER_TYPE2		(15 * sizeof(__le16))
199efbd3470SHenrik Rydberg #define HEADER_TYPE3		(19 * sizeof(__le16))
200*d5806926SJohn Horan #define HEADER_TYPE4		(23 * sizeof(__le16))
201158e9287SHenrik Rydberg 
202158e9287SHenrik Rydberg /* trackpad button data offsets */
203efbd3470SHenrik Rydberg #define BUTTON_TYPE1		0
204158e9287SHenrik Rydberg #define BUTTON_TYPE2		15
205148c1c8aSDmitry Torokhov #define BUTTON_TYPE3		23
206*d5806926SJohn Horan #define BUTTON_TYPE4		31
207158e9287SHenrik Rydberg 
208158e9287SHenrik Rydberg /* list of device capability bits */
209158e9287SHenrik Rydberg #define HAS_INTEGRATED_BUTTON	1
2109894cf0fSHenrik Rydberg 
211efbd3470SHenrik Rydberg /* trackpad finger data block size */
212efbd3470SHenrik Rydberg #define FSIZE_TYPE1		(14 * sizeof(__le16))
213efbd3470SHenrik Rydberg #define FSIZE_TYPE2		(14 * sizeof(__le16))
214efbd3470SHenrik Rydberg #define FSIZE_TYPE3		(14 * sizeof(__le16))
215*d5806926SJohn Horan #define FSIZE_TYPE4		(15 * sizeof(__le16))
216efbd3470SHenrik Rydberg 
217efbd3470SHenrik Rydberg /* offset from header to finger struct */
218efbd3470SHenrik Rydberg #define DELTA_TYPE1		(0 * sizeof(__le16))
219efbd3470SHenrik Rydberg #define DELTA_TYPE2		(0 * sizeof(__le16))
220efbd3470SHenrik Rydberg #define DELTA_TYPE3		(0 * sizeof(__le16))
221*d5806926SJohn Horan #define DELTA_TYPE4		(1 * sizeof(__le16))
222efbd3470SHenrik Rydberg 
223efbd3470SHenrik Rydberg /* usb control message mode switch data */
224efbd3470SHenrik Rydberg #define USBMSG_TYPE1		8, 0x300, 0, 0, 0x1, 0x8
225efbd3470SHenrik Rydberg #define USBMSG_TYPE2		8, 0x300, 0, 0, 0x1, 0x8
226efbd3470SHenrik Rydberg #define USBMSG_TYPE3		8, 0x300, 0, 0, 0x1, 0x8
227*d5806926SJohn Horan #define USBMSG_TYPE4		2, 0x302, 2, 1, 0x1, 0x0
228efbd3470SHenrik Rydberg 
229efbd3470SHenrik Rydberg /* Wellspring initialization constants */
230efbd3470SHenrik Rydberg #define BCM5974_WELLSPRING_MODE_READ_REQUEST_ID		1
231efbd3470SHenrik Rydberg #define BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID	9
232efbd3470SHenrik Rydberg 
2339894cf0fSHenrik Rydberg /* trackpad finger structure, le16-aligned */
234f89bd95cSHenrik Rydberg struct tp_finger {
23575e21e3fSHenrik Rydberg 	__le16 origin;		/* zero when switching track finger */
236f89bd95cSHenrik Rydberg 	__le16 abs_x;		/* absolute x coodinate */
237f89bd95cSHenrik Rydberg 	__le16 abs_y;		/* absolute y coodinate */
238f89bd95cSHenrik Rydberg 	__le16 rel_x;		/* relative x coodinate */
239f89bd95cSHenrik Rydberg 	__le16 rel_y;		/* relative y coodinate */
240f17953abSHenrik Rydberg 	__le16 tool_major;	/* tool area, major axis */
241f17953abSHenrik Rydberg 	__le16 tool_minor;	/* tool area, minor axis */
242f89bd95cSHenrik Rydberg 	__le16 orientation;	/* 16384 when point, else 15 bit angle */
243f17953abSHenrik Rydberg 	__le16 touch_major;	/* touch area, major axis */
244f17953abSHenrik Rydberg 	__le16 touch_minor;	/* touch area, minor axis */
245*d5806926SJohn Horan 	__le16 unused[2];	/* zeros */
246*d5806926SJohn Horan 	__le16 pressure;	/* pressure on forcetouch touchpad */
247f89bd95cSHenrik Rydberg 	__le16 multi;		/* one finger: varies, more fingers: constant */
2489894cf0fSHenrik Rydberg } __attribute__((packed,aligned(2)));
249f89bd95cSHenrik Rydberg 
2509894cf0fSHenrik Rydberg /* trackpad finger data size, empirically at least ten fingers */
251f17953abSHenrik Rydberg #define MAX_FINGERS		16
2526f2701b7SHenrik Rydberg #define MAX_FINGER_ORIENTATION	16384
253f89bd95cSHenrik Rydberg 
254f89bd95cSHenrik Rydberg /* device-specific parameters */
255f89bd95cSHenrik Rydberg struct bcm5974_param {
2560e726966SHenrik Rydberg 	int snratio;		/* signal-to-noise ratio */
2570e726966SHenrik Rydberg 	int min;		/* device minimum reading */
2580e726966SHenrik Rydberg 	int max;		/* device maximum reading */
259f89bd95cSHenrik Rydberg };
260f89bd95cSHenrik Rydberg 
261f89bd95cSHenrik Rydberg /* device-specific configuration */
262f89bd95cSHenrik Rydberg struct bcm5974_config {
263f89bd95cSHenrik Rydberg 	int ansi, iso, jis;	/* the product id of this device */
264158e9287SHenrik Rydberg 	int caps;		/* device capability bitmask */
265f89bd95cSHenrik Rydberg 	int bt_ep;		/* the endpoint of the button interface */
266f89bd95cSHenrik Rydberg 	int bt_datalen;		/* data length of the button interface */
267f89bd95cSHenrik Rydberg 	int tp_ep;		/* the endpoint of the trackpad interface */
2689894cf0fSHenrik Rydberg 	enum tp_type tp_type;	/* type of trackpad interface */
269efbd3470SHenrik Rydberg 	int tp_header;		/* bytes in header block */
270f89bd95cSHenrik Rydberg 	int tp_datalen;		/* data length of the trackpad interface */
271efbd3470SHenrik Rydberg 	int tp_button;		/* offset to button data */
272efbd3470SHenrik Rydberg 	int tp_fsize;		/* bytes in single finger block */
273efbd3470SHenrik Rydberg 	int tp_delta;		/* offset from header to finger struct */
274efbd3470SHenrik Rydberg 	int um_size;		/* usb control message length */
275efbd3470SHenrik Rydberg 	int um_req_val;		/* usb control message value */
276efbd3470SHenrik Rydberg 	int um_req_idx;		/* usb control message index */
277efbd3470SHenrik Rydberg 	int um_switch_idx;	/* usb control message mode switch index */
278efbd3470SHenrik Rydberg 	int um_switch_on;	/* usb control message mode switch on */
279efbd3470SHenrik Rydberg 	int um_switch_off;	/* usb control message mode switch off */
280f89bd95cSHenrik Rydberg 	struct bcm5974_param p;	/* finger pressure limits */
281f89bd95cSHenrik Rydberg 	struct bcm5974_param w;	/* finger width limits */
282f89bd95cSHenrik Rydberg 	struct bcm5974_param x;	/* horizontal limits */
283f89bd95cSHenrik Rydberg 	struct bcm5974_param y;	/* vertical limits */
2840e726966SHenrik Rydberg 	struct bcm5974_param o;	/* orientation limits */
285f89bd95cSHenrik Rydberg };
286f89bd95cSHenrik Rydberg 
287f89bd95cSHenrik Rydberg /* logical device structure */
288f89bd95cSHenrik Rydberg struct bcm5974 {
289f89bd95cSHenrik Rydberg 	char phys[64];
290f89bd95cSHenrik Rydberg 	struct usb_device *udev;	/* usb device */
29188da765fSDmitry Torokhov 	struct usb_interface *intf;	/* our interface */
292f89bd95cSHenrik Rydberg 	struct input_dev *input;	/* input dev */
293f89bd95cSHenrik Rydberg 	struct bcm5974_config cfg;	/* device configuration */
294f89bd95cSHenrik Rydberg 	struct mutex pm_mutex;		/* serialize access to open/suspend */
295f89bd95cSHenrik Rydberg 	int opened;			/* 1: opened, 0: closed */
296f89bd95cSHenrik Rydberg 	struct urb *bt_urb;		/* button usb request block */
297f89bd95cSHenrik Rydberg 	struct bt_data *bt_data;	/* button transferred data */
298f89bd95cSHenrik Rydberg 	struct urb *tp_urb;		/* trackpad usb request block */
2999894cf0fSHenrik Rydberg 	u8 *tp_data;			/* trackpad transferred data */
30051c80b74SHenrik Rydberg 	const struct tp_finger *index[MAX_FINGERS];	/* finger index data */
30151c80b74SHenrik Rydberg 	struct input_mt_pos pos[MAX_FINGERS];		/* position array */
30251c80b74SHenrik Rydberg 	int slots[MAX_FINGERS];				/* slot assignments */
303f89bd95cSHenrik Rydberg };
304f89bd95cSHenrik Rydberg 
305efbd3470SHenrik Rydberg /* trackpad finger block data, le16-aligned */
306efbd3470SHenrik Rydberg static const struct tp_finger *get_tp_finger(const struct bcm5974 *dev, int i)
307efbd3470SHenrik Rydberg {
308efbd3470SHenrik Rydberg 	const struct bcm5974_config *c = &dev->cfg;
309efbd3470SHenrik Rydberg 	u8 *f_base = dev->tp_data + c->tp_header + c->tp_delta;
310efbd3470SHenrik Rydberg 
311efbd3470SHenrik Rydberg 	return (const struct tp_finger *)(f_base + i * c->tp_fsize);
312efbd3470SHenrik Rydberg }
313efbd3470SHenrik Rydberg 
314efbd3470SHenrik Rydberg #define DATAFORMAT(type)				\
315efbd3470SHenrik Rydberg 	type,						\
316efbd3470SHenrik Rydberg 	HEADER_##type,					\
317efbd3470SHenrik Rydberg 	HEADER_##type + (MAX_FINGERS) * (FSIZE_##type),	\
318efbd3470SHenrik Rydberg 	BUTTON_##type,					\
319efbd3470SHenrik Rydberg 	FSIZE_##type,					\
320efbd3470SHenrik Rydberg 	DELTA_##type,					\
321efbd3470SHenrik Rydberg 	USBMSG_##type
322efbd3470SHenrik Rydberg 
323f89bd95cSHenrik Rydberg /* logical signal quality */
324f89bd95cSHenrik Rydberg #define SN_PRESSURE	45		/* pressure signal-to-noise ratio */
32551c80b74SHenrik Rydberg #define SN_WIDTH	25		/* width signal-to-noise ratio */
326f89bd95cSHenrik Rydberg #define SN_COORD	250		/* coordinate signal-to-noise ratio */
3270e726966SHenrik Rydberg #define SN_ORIENT	10		/* orientation signal-to-noise ratio */
32875e21e3fSHenrik Rydberg 
329f89bd95cSHenrik Rydberg /* device constants */
330f89bd95cSHenrik Rydberg static const struct bcm5974_config bcm5974_config_table[] = {
331f89bd95cSHenrik Rydberg 	{
332f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ANSI,
333f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_ISO,
334f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING_JIS,
335158e9287SHenrik Rydberg 		0,
336f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
337efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE1),
3380e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 256 },
3390e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3400e726966SHenrik Rydberg 		{ SN_COORD, -4824, 5342 },
3410e726966SHenrik Rydberg 		{ SN_COORD, -172, 5820 },
3420e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
343f89bd95cSHenrik Rydberg 	},
344f89bd95cSHenrik Rydberg 	{
345f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ANSI,
346f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_ISO,
347f89bd95cSHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING2_JIS,
348158e9287SHenrik Rydberg 		0,
349f89bd95cSHenrik Rydberg 		0x84, sizeof(struct bt_data),
350efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE1),
3510e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 256 },
3520e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3530e726966SHenrik Rydberg 		{ SN_COORD, -4824, 4824 },
3540e726966SHenrik Rydberg 		{ SN_COORD, -172, 4290 },
3550e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
356f89bd95cSHenrik Rydberg 	},
357158e9287SHenrik Rydberg 	{
358158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ANSI,
359158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_ISO,
360158e9287SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING3_JIS,
361158e9287SHenrik Rydberg 		HAS_INTEGRATED_BUTTON,
362158e9287SHenrik Rydberg 		0x84, sizeof(struct bt_data),
363efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
3640e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3650e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3660e726966SHenrik Rydberg 		{ SN_COORD, -4460, 5166 },
3670e726966SHenrik Rydberg 		{ SN_COORD, -75, 6700 },
3680e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
369158e9287SHenrik Rydberg 	},
3706021afcfSEdgar (gimli) Hucek 	{
3716021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ANSI,
3726021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_ISO,
3736021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4_JIS,
3746021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
3756021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
376efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
3770e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3780e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3790e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
3800e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
3810e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
3826021afcfSEdgar (gimli) Hucek 	},
3836021afcfSEdgar (gimli) Hucek 	{
3846021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ANSI,
3856021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_ISO,
3866021afcfSEdgar (gimli) Hucek 		USB_DEVICE_ID_APPLE_WELLSPRING4A_JIS,
3876021afcfSEdgar (gimli) Hucek 		HAS_INTEGRATED_BUTTON,
3886021afcfSEdgar (gimli) Hucek 		0x84, sizeof(struct bt_data),
389efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
3900e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
3910e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
3920e726966SHenrik Rydberg 		{ SN_COORD, -4616, 5112 },
3930e726966SHenrik Rydberg 		{ SN_COORD, -142, 5234 },
3940e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
3956021afcfSEdgar (gimli) Hucek 	},
39647340bd9SAndy Botting 	{
39747340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ANSI,
39847340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_ISO,
39947340bd9SAndy Botting 		USB_DEVICE_ID_APPLE_WELLSPRING5_JIS,
40047340bd9SAndy Botting 		HAS_INTEGRATED_BUTTON,
40147340bd9SAndy Botting 		0x84, sizeof(struct bt_data),
402efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4030e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
4040e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
4050e726966SHenrik Rydberg 		{ SN_COORD, -4415, 5050 },
4060e726966SHenrik Rydberg 		{ SN_COORD, -55, 6680 },
4070e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
40847340bd9SAndy Botting 	},
409db0b34b0SJoshua V. Dillon 	{
410db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ANSI,
411db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_ISO,
412db0b34b0SJoshua V. Dillon 		USB_DEVICE_ID_APPLE_WELLSPRING6_JIS,
413db0b34b0SJoshua V. Dillon 		HAS_INTEGRATED_BUTTON,
414db0b34b0SJoshua V. Dillon 		0x84, sizeof(struct bt_data),
415efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4160e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
4170e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
4180e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
4190e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
4200e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
421db0b34b0SJoshua V. Dillon 	},
422c331eb58SAndrew Drake 	{
423c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ANSI,
424c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_ISO,
425c331eb58SAndrew Drake 		USB_DEVICE_ID_APPLE_WELLSPRING5A_JIS,
426c331eb58SAndrew Drake 		HAS_INTEGRATED_BUTTON,
427c331eb58SAndrew Drake 		0x84, sizeof(struct bt_data),
428efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4290e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
4300e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
4310e726966SHenrik Rydberg 		{ SN_COORD, -4750, 5280 },
4320e726966SHenrik Rydberg 		{ SN_COORD, -150, 6730 },
4330e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
434c331eb58SAndrew Drake 	},
4351c601beaSPieter-Augustijn Van Malleghem 	{
4361c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ANSI,
4371c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_ISO,
4381c601beaSPieter-Augustijn Van Malleghem 		USB_DEVICE_ID_APPLE_WELLSPRING6A_JIS,
4391c601beaSPieter-Augustijn Van Malleghem 		HAS_INTEGRATED_BUTTON,
4401c601beaSPieter-Augustijn Van Malleghem 		0x84, sizeof(struct bt_data),
441efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4420e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
4430e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
4440e726966SHenrik Rydberg 		{ SN_COORD, -4620, 5140 },
4450e726966SHenrik Rydberg 		{ SN_COORD, -150, 6600 },
4460e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
4471c601beaSPieter-Augustijn Van Malleghem 	},
4483dde22a9SHenrik Rydberg 	{
4493dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_ANSI,
4503dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_ISO,
4513dde22a9SHenrik Rydberg 		USB_DEVICE_ID_APPLE_WELLSPRING7_JIS,
4523dde22a9SHenrik Rydberg 		HAS_INTEGRATED_BUTTON,
4533dde22a9SHenrik Rydberg 		0x84, sizeof(struct bt_data),
454efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4550e726966SHenrik Rydberg 		{ SN_PRESSURE, 0, 300 },
4560e726966SHenrik Rydberg 		{ SN_WIDTH, 0, 2048 },
4570e726966SHenrik Rydberg 		{ SN_COORD, -4750, 5280 },
4580e726966SHenrik Rydberg 		{ SN_COORD, -150, 6730 },
4590e726966SHenrik Rydberg 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
4603dde22a9SHenrik Rydberg 	},
4618d80da90SDirk Hohndel 	{
4628d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_ANSI,
4638d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_ISO,
4648d80da90SDirk Hohndel 		USB_DEVICE_ID_APPLE_WELLSPRING7A_JIS,
4658d80da90SDirk Hohndel 		HAS_INTEGRATED_BUTTON,
4668d80da90SDirk Hohndel 		0x84, sizeof(struct bt_data),
467efbd3470SHenrik Rydberg 		0x81, DATAFORMAT(TYPE2),
4688d80da90SDirk Hohndel 		{ SN_PRESSURE, 0, 300 },
4698d80da90SDirk Hohndel 		{ SN_WIDTH, 0, 2048 },
4708d80da90SDirk Hohndel 		{ SN_COORD, -4750, 5280 },
4718d80da90SDirk Hohndel 		{ SN_COORD, -150, 6730 },
4728d80da90SDirk Hohndel 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
4738d80da90SDirk Hohndel 	},
474148c1c8aSDmitry Torokhov 	{
475148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_ANSI,
476148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_ISO,
477148c1c8aSDmitry Torokhov 		USB_DEVICE_ID_APPLE_WELLSPRING8_JIS,
478148c1c8aSDmitry Torokhov 		HAS_INTEGRATED_BUTTON,
479148c1c8aSDmitry Torokhov 		0, sizeof(struct bt_data),
480efbd3470SHenrik Rydberg 		0x83, DATAFORMAT(TYPE3),
481148c1c8aSDmitry Torokhov 		{ SN_PRESSURE, 0, 300 },
482148c1c8aSDmitry Torokhov 		{ SN_WIDTH, 0, 2048 },
483148c1c8aSDmitry Torokhov 		{ SN_COORD, -4620, 5140 },
484148c1c8aSDmitry Torokhov 		{ SN_COORD, -150, 6600 },
485148c1c8aSDmitry Torokhov 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
486148c1c8aSDmitry Torokhov 	},
487*d5806926SJohn Horan 	{
488*d5806926SJohn Horan 		USB_DEVICE_ID_APPLE_WELLSPRING9_ANSI,
489*d5806926SJohn Horan 		USB_DEVICE_ID_APPLE_WELLSPRING9_ISO,
490*d5806926SJohn Horan 		USB_DEVICE_ID_APPLE_WELLSPRING9_JIS,
491*d5806926SJohn Horan 		HAS_INTEGRATED_BUTTON,
492*d5806926SJohn Horan 		0, sizeof(struct bt_data),
493*d5806926SJohn Horan 		0x83, DATAFORMAT(TYPE4),
494*d5806926SJohn Horan 		{ SN_PRESSURE, 0, 300 },
495*d5806926SJohn Horan 		{ SN_WIDTH, 0, 2048 },
496*d5806926SJohn Horan 		{ SN_COORD, -4828, 5345 },
497*d5806926SJohn Horan 		{ SN_COORD, -203, 6803 },
498*d5806926SJohn Horan 		{ SN_ORIENT, -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION }
499*d5806926SJohn Horan 	},
500f89bd95cSHenrik Rydberg 	{}
501f89bd95cSHenrik Rydberg };
502f89bd95cSHenrik Rydberg 
503f89bd95cSHenrik Rydberg /* return the device-specific configuration by device */
504f89bd95cSHenrik Rydberg static const struct bcm5974_config *bcm5974_get_config(struct usb_device *udev)
505f89bd95cSHenrik Rydberg {
506f89bd95cSHenrik Rydberg 	u16 id = le16_to_cpu(udev->descriptor.idProduct);
507f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
508f89bd95cSHenrik Rydberg 
509f89bd95cSHenrik Rydberg 	for (cfg = bcm5974_config_table; cfg->ansi; ++cfg)
510f89bd95cSHenrik Rydberg 		if (cfg->ansi == id || cfg->iso == id || cfg->jis == id)
511f89bd95cSHenrik Rydberg 			return cfg;
512f89bd95cSHenrik Rydberg 
513f89bd95cSHenrik Rydberg 	return bcm5974_config_table;
514f89bd95cSHenrik Rydberg }
515f89bd95cSHenrik Rydberg 
516f89bd95cSHenrik Rydberg /* convert 16-bit little endian to signed integer */
517f89bd95cSHenrik Rydberg static inline int raw2int(__le16 x)
518f89bd95cSHenrik Rydberg {
519f89bd95cSHenrik Rydberg 	return (signed short)le16_to_cpu(x);
520f89bd95cSHenrik Rydberg }
521f89bd95cSHenrik Rydberg 
5220e726966SHenrik Rydberg static void set_abs(struct input_dev *input, unsigned int code,
5230e726966SHenrik Rydberg 		    const struct bcm5974_param *p)
524f89bd95cSHenrik Rydberg {
5250e726966SHenrik Rydberg 	int fuzz = p->snratio ? (p->max - p->min) / p->snratio : 0;
5260e726966SHenrik Rydberg 	input_set_abs_params(input, code, p->min, p->max, fuzz, 0);
527f89bd95cSHenrik Rydberg }
528f89bd95cSHenrik Rydberg 
529f89bd95cSHenrik Rydberg /* setup which logical events to report */
530f89bd95cSHenrik Rydberg static void setup_events_to_report(struct input_dev *input_dev,
531f89bd95cSHenrik Rydberg 				   const struct bcm5974_config *cfg)
532f89bd95cSHenrik Rydberg {
533f89bd95cSHenrik Rydberg 	__set_bit(EV_ABS, input_dev->evbit);
534f89bd95cSHenrik Rydberg 
5350e726966SHenrik Rydberg 	/* for synaptics only */
5360e726966SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, 256, 5, 0);
5370e726966SHenrik Rydberg 	input_set_abs_params(input_dev, ABS_TOOL_WIDTH, 0, 16, 0, 0);
5380e726966SHenrik Rydberg 
5396f2701b7SHenrik Rydberg 	/* finger touch area */
5400e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_TOUCH_MAJOR, &cfg->w);
5410e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_TOUCH_MINOR, &cfg->w);
5426f2701b7SHenrik Rydberg 	/* finger approach area */
5430e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_WIDTH_MAJOR, &cfg->w);
5440e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_WIDTH_MINOR, &cfg->w);
5456f2701b7SHenrik Rydberg 	/* finger orientation */
5460e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_ORIENTATION, &cfg->o);
5476f2701b7SHenrik Rydberg 	/* finger position */
5480e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_POSITION_X, &cfg->x);
5490e726966SHenrik Rydberg 	set_abs(input_dev, ABS_MT_POSITION_Y, &cfg->y);
5506f2701b7SHenrik Rydberg 
551f89bd95cSHenrik Rydberg 	__set_bit(EV_KEY, input_dev->evbit);
552f89bd95cSHenrik Rydberg 	__set_bit(BTN_LEFT, input_dev->keybit);
553c13aea03SHenrik Rydberg 
55452965cc0SJussi Pakkanen 	if (cfg->caps & HAS_INTEGRATED_BUTTON)
55552965cc0SJussi Pakkanen 		__set_bit(INPUT_PROP_BUTTONPAD, input_dev->propbit);
55652965cc0SJussi Pakkanen 
55751c80b74SHenrik Rydberg 	input_mt_init_slots(input_dev, MAX_FINGERS,
55851c80b74SHenrik Rydberg 		INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED | INPUT_MT_TRACK);
559f89bd95cSHenrik Rydberg }
560f89bd95cSHenrik Rydberg 
561f89bd95cSHenrik Rydberg /* report button data as logical button state */
562f89bd95cSHenrik Rydberg static int report_bt_state(struct bcm5974 *dev, int size)
563f89bd95cSHenrik Rydberg {
564f89bd95cSHenrik Rydberg 	if (size != sizeof(struct bt_data))
565f89bd95cSHenrik Rydberg 		return -EIO;
566f89bd95cSHenrik Rydberg 
56753402193SHenrik Rydberg 	dprintk(7,
56853402193SHenrik Rydberg 		"bcm5974: button data: %x %x %x %x\n",
56953402193SHenrik Rydberg 		dev->bt_data->unknown1, dev->bt_data->button,
57053402193SHenrik Rydberg 		dev->bt_data->rel_x, dev->bt_data->rel_y);
57153402193SHenrik Rydberg 
572f89bd95cSHenrik Rydberg 	input_report_key(dev->input, BTN_LEFT, dev->bt_data->button);
573f89bd95cSHenrik Rydberg 	input_sync(dev->input);
574f89bd95cSHenrik Rydberg 
575f89bd95cSHenrik Rydberg 	return 0;
576f89bd95cSHenrik Rydberg }
577f89bd95cSHenrik Rydberg 
57851c80b74SHenrik Rydberg static void report_finger_data(struct input_dev *input, int slot,
57951c80b74SHenrik Rydberg 			       const struct input_mt_pos *pos,
5806f2701b7SHenrik Rydberg 			       const struct tp_finger *f)
5816f2701b7SHenrik Rydberg {
58251c80b74SHenrik Rydberg 	input_mt_slot(input, slot);
58351c80b74SHenrik Rydberg 	input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
58451c80b74SHenrik Rydberg 
58557157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MAJOR,
586f17953abSHenrik Rydberg 			 raw2int(f->touch_major) << 1);
58757157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_TOUCH_MINOR,
588f17953abSHenrik Rydberg 			 raw2int(f->touch_minor) << 1);
58957157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MAJOR,
590f17953abSHenrik Rydberg 			 raw2int(f->tool_major) << 1);
59157157becSHenrik Rydberg 	input_report_abs(input, ABS_MT_WIDTH_MINOR,
592f17953abSHenrik Rydberg 			 raw2int(f->tool_minor) << 1);
5936f2701b7SHenrik Rydberg 	input_report_abs(input, ABS_MT_ORIENTATION,
5946f2701b7SHenrik Rydberg 			 MAX_FINGER_ORIENTATION - raw2int(f->orientation));
59551c80b74SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_X, pos->x);
59651c80b74SHenrik Rydberg 	input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
5976f2701b7SHenrik Rydberg }
5986f2701b7SHenrik Rydberg 
5990e726966SHenrik Rydberg static void report_synaptics_data(struct input_dev *input,
6000e726966SHenrik Rydberg 				  const struct bcm5974_config *cfg,
6010e726966SHenrik Rydberg 				  const struct tp_finger *f, int raw_n)
6020e726966SHenrik Rydberg {
6030e726966SHenrik Rydberg 	int abs_p = 0, abs_w = 0;
6040e726966SHenrik Rydberg 
6050e726966SHenrik Rydberg 	if (raw_n) {
6060e726966SHenrik Rydberg 		int p = raw2int(f->touch_major);
6070e726966SHenrik Rydberg 		int w = raw2int(f->tool_major);
6080e726966SHenrik Rydberg 		if (p > 0 && raw2int(f->origin)) {
6090e726966SHenrik Rydberg 			abs_p = clamp_val(256 * p / cfg->p.max, 0, 255);
6100e726966SHenrik Rydberg 			abs_w = clamp_val(16 * w / cfg->w.max, 0, 15);
6110e726966SHenrik Rydberg 		}
6120e726966SHenrik Rydberg 	}
6130e726966SHenrik Rydberg 
6140e726966SHenrik Rydberg 	input_report_abs(input, ABS_PRESSURE, abs_p);
6150e726966SHenrik Rydberg 	input_report_abs(input, ABS_TOOL_WIDTH, abs_w);
6160e726966SHenrik Rydberg }
6170e726966SHenrik Rydberg 
618f89bd95cSHenrik Rydberg /* report trackpad data as logical trackpad state */
619f89bd95cSHenrik Rydberg static int report_tp_state(struct bcm5974 *dev, int size)
620f89bd95cSHenrik Rydberg {
621f89bd95cSHenrik Rydberg 	const struct bcm5974_config *c = &dev->cfg;
6229894cf0fSHenrik Rydberg 	const struct tp_finger *f;
623f89bd95cSHenrik Rydberg 	struct input_dev *input = dev->input;
62451c80b74SHenrik Rydberg 	int raw_n, i, n = 0;
625f89bd95cSHenrik Rydberg 
626efbd3470SHenrik Rydberg 	if (size < c->tp_header || (size - c->tp_header) % c->tp_fsize != 0)
627f89bd95cSHenrik Rydberg 		return -EIO;
628f89bd95cSHenrik Rydberg 
629efbd3470SHenrik Rydberg 	raw_n = (size - c->tp_header) / c->tp_fsize;
6309894cf0fSHenrik Rydberg 
63151c80b74SHenrik Rydberg 	for (i = 0; i < raw_n; i++) {
632efbd3470SHenrik Rydberg 		f = get_tp_finger(dev, i);
633efbd3470SHenrik Rydberg 		if (raw2int(f->touch_major) == 0)
63451c80b74SHenrik Rydberg 			continue;
635efbd3470SHenrik Rydberg 		dev->pos[n].x = raw2int(f->abs_x);
636efbd3470SHenrik Rydberg 		dev->pos[n].y = c->y.min + c->y.max - raw2int(f->abs_y);
637efbd3470SHenrik Rydberg 		dev->index[n++] = f;
63875e21e3fSHenrik Rydberg 	}
639f89bd95cSHenrik Rydberg 
640448c7f38SHenrik Rydberg 	input_mt_assign_slots(input, dev->slots, dev->pos, n, 0);
64151c80b74SHenrik Rydberg 
64251c80b74SHenrik Rydberg 	for (i = 0; i < n; i++)
64351c80b74SHenrik Rydberg 		report_finger_data(input, dev->slots[i],
64451c80b74SHenrik Rydberg 				   &dev->pos[i], dev->index[i]);
64551c80b74SHenrik Rydberg 
64651c80b74SHenrik Rydberg 	input_mt_sync_frame(input);
64750635115SHenrik Rydberg 
648efbd3470SHenrik Rydberg 	report_synaptics_data(input, c, get_tp_finger(dev, 0), raw_n);
64975e21e3fSHenrik Rydberg 
650efbd3470SHenrik Rydberg 	/* later types report button events via integrated button only */
651efbd3470SHenrik Rydberg 	if (c->caps & HAS_INTEGRATED_BUTTON) {
652efbd3470SHenrik Rydberg 		int ibt = raw2int(dev->tp_data[c->tp_button]);
653158e9287SHenrik Rydberg 		input_report_key(input, BTN_LEFT, ibt);
6540e726966SHenrik Rydberg 	}
655158e9287SHenrik Rydberg 
656f89bd95cSHenrik Rydberg 	input_sync(input);
657f89bd95cSHenrik Rydberg 
658f89bd95cSHenrik Rydberg 	return 0;
659f89bd95cSHenrik Rydberg }
660f89bd95cSHenrik Rydberg 
661cd72ad3fSHenrik Rydberg static int bcm5974_wellspring_mode(struct bcm5974 *dev, bool on)
662f89bd95cSHenrik Rydberg {
663efbd3470SHenrik Rydberg 	const struct bcm5974_config *c = &dev->cfg;
664f89bd95cSHenrik Rydberg 	int retval = 0, size;
665148c1c8aSDmitry Torokhov 	char *data;
666f89bd95cSHenrik Rydberg 
667148c1c8aSDmitry Torokhov 	/* Type 3 does not require a mode switch */
668148c1c8aSDmitry Torokhov 	if (dev->cfg.tp_type == TYPE3)
669148c1c8aSDmitry Torokhov 		return 0;
670148c1c8aSDmitry Torokhov 
671efbd3470SHenrik Rydberg 	data = kmalloc(c->um_size, GFP_KERNEL);
672f89bd95cSHenrik Rydberg 	if (!data) {
673ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "out of memory\n");
674f89bd95cSHenrik Rydberg 		retval = -ENOMEM;
675f89bd95cSHenrik Rydberg 		goto out;
676f89bd95cSHenrik Rydberg 	}
677f89bd95cSHenrik Rydberg 
678f89bd95cSHenrik Rydberg 	/* read configuration */
679f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
680f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_READ_REQUEST_ID,
681f89bd95cSHenrik Rydberg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
682efbd3470SHenrik Rydberg 			c->um_req_val, c->um_req_idx, data, c->um_size, 5000);
683f89bd95cSHenrik Rydberg 
684efbd3470SHenrik Rydberg 	if (size != c->um_size) {
685ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "could not read from device\n");
686f89bd95cSHenrik Rydberg 		retval = -EIO;
687f89bd95cSHenrik Rydberg 		goto out;
688f89bd95cSHenrik Rydberg 	}
689f89bd95cSHenrik Rydberg 
690f89bd95cSHenrik Rydberg 	/* apply the mode switch */
691efbd3470SHenrik Rydberg 	data[c->um_switch_idx] = on ? c->um_switch_on : c->um_switch_off;
692f89bd95cSHenrik Rydberg 
693f89bd95cSHenrik Rydberg 	/* write configuration */
694f89bd95cSHenrik Rydberg 	size = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
695f89bd95cSHenrik Rydberg 			BCM5974_WELLSPRING_MODE_WRITE_REQUEST_ID,
696f89bd95cSHenrik Rydberg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
697efbd3470SHenrik Rydberg 			c->um_req_val, c->um_req_idx, data, c->um_size, 5000);
698f89bd95cSHenrik Rydberg 
699efbd3470SHenrik Rydberg 	if (size != c->um_size) {
700ab943ca8SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "could not write to device\n");
701f89bd95cSHenrik Rydberg 		retval = -EIO;
702f89bd95cSHenrik Rydberg 		goto out;
703f89bd95cSHenrik Rydberg 	}
704f89bd95cSHenrik Rydberg 
705cd72ad3fSHenrik Rydberg 	dprintk(2, "bcm5974: switched to %s mode.\n",
706cd72ad3fSHenrik Rydberg 		on ? "wellspring" : "normal");
707f89bd95cSHenrik Rydberg 
708f89bd95cSHenrik Rydberg  out:
709f89bd95cSHenrik Rydberg 	kfree(data);
710f89bd95cSHenrik Rydberg 	return retval;
711f89bd95cSHenrik Rydberg }
712f89bd95cSHenrik Rydberg 
713f89bd95cSHenrik Rydberg static void bcm5974_irq_button(struct urb *urb)
714f89bd95cSHenrik Rydberg {
715f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
716ab943ca8SGreg Kroah-Hartman 	struct usb_interface *intf = dev->intf;
717f89bd95cSHenrik Rydberg 	int error;
718f89bd95cSHenrik Rydberg 
719f89bd95cSHenrik Rydberg 	switch (urb->status) {
720f89bd95cSHenrik Rydberg 	case 0:
721f89bd95cSHenrik Rydberg 		break;
722f89bd95cSHenrik Rydberg 	case -EOVERFLOW:
723f89bd95cSHenrik Rydberg 	case -ECONNRESET:
724f89bd95cSHenrik Rydberg 	case -ENOENT:
725f89bd95cSHenrik Rydberg 	case -ESHUTDOWN:
726ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "button urb shutting down: %d\n",
727bd028769SGreg Kroah-Hartman 			urb->status);
728f89bd95cSHenrik Rydberg 		return;
729f89bd95cSHenrik Rydberg 	default:
730ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "button urb status: %d\n", urb->status);
731f89bd95cSHenrik Rydberg 		goto exit;
732f89bd95cSHenrik Rydberg 	}
733f89bd95cSHenrik Rydberg 
734f89bd95cSHenrik Rydberg 	if (report_bt_state(dev, dev->bt_urb->actual_length))
735f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad button package, length: %d\n",
736f89bd95cSHenrik Rydberg 			dev->bt_urb->actual_length);
737f89bd95cSHenrik Rydberg 
738f89bd95cSHenrik Rydberg exit:
739f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->bt_urb, GFP_ATOMIC);
740f89bd95cSHenrik Rydberg 	if (error)
741ab943ca8SGreg Kroah-Hartman 		dev_err(&intf->dev, "button urb failed: %d\n", error);
742f89bd95cSHenrik Rydberg }
743f89bd95cSHenrik Rydberg 
744f89bd95cSHenrik Rydberg static void bcm5974_irq_trackpad(struct urb *urb)
745f89bd95cSHenrik Rydberg {
746f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = urb->context;
747ab943ca8SGreg Kroah-Hartman 	struct usb_interface *intf = dev->intf;
748f89bd95cSHenrik Rydberg 	int error;
749f89bd95cSHenrik Rydberg 
750f89bd95cSHenrik Rydberg 	switch (urb->status) {
751f89bd95cSHenrik Rydberg 	case 0:
752f89bd95cSHenrik Rydberg 		break;
753f89bd95cSHenrik Rydberg 	case -EOVERFLOW:
754f89bd95cSHenrik Rydberg 	case -ECONNRESET:
755f89bd95cSHenrik Rydberg 	case -ENOENT:
756f89bd95cSHenrik Rydberg 	case -ESHUTDOWN:
757ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "trackpad urb shutting down: %d\n",
758bd028769SGreg Kroah-Hartman 			urb->status);
759f89bd95cSHenrik Rydberg 		return;
760f89bd95cSHenrik Rydberg 	default:
761ab943ca8SGreg Kroah-Hartman 		dev_dbg(&intf->dev, "trackpad urb status: %d\n", urb->status);
762f89bd95cSHenrik Rydberg 		goto exit;
763f89bd95cSHenrik Rydberg 	}
764f89bd95cSHenrik Rydberg 
765f89bd95cSHenrik Rydberg 	/* control response ignored */
766f89bd95cSHenrik Rydberg 	if (dev->tp_urb->actual_length == 2)
767f89bd95cSHenrik Rydberg 		goto exit;
768f89bd95cSHenrik Rydberg 
769f89bd95cSHenrik Rydberg 	if (report_tp_state(dev, dev->tp_urb->actual_length))
770f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: bad trackpad package, length: %d\n",
771f89bd95cSHenrik Rydberg 			dev->tp_urb->actual_length);
772f89bd95cSHenrik Rydberg 
773f89bd95cSHenrik Rydberg exit:
774f89bd95cSHenrik Rydberg 	error = usb_submit_urb(dev->tp_urb, GFP_ATOMIC);
775f89bd95cSHenrik Rydberg 	if (error)
776ab943ca8SGreg Kroah-Hartman 		dev_err(&intf->dev, "trackpad urb failed: %d\n", error);
777f89bd95cSHenrik Rydberg }
778f89bd95cSHenrik Rydberg 
779f89bd95cSHenrik Rydberg /*
780f89bd95cSHenrik Rydberg  * The Wellspring trackpad, like many recent Apple trackpads, share
781f89bd95cSHenrik Rydberg  * the usb device with the keyboard. Since keyboards are usually
782f89bd95cSHenrik Rydberg  * handled by the HID system, the device ends up being handled by two
783f89bd95cSHenrik Rydberg  * modules. Setting up the device therefore becomes slightly
784f89bd95cSHenrik Rydberg  * complicated. To enable multitouch features, a mode switch is
785f89bd95cSHenrik Rydberg  * required, which is usually applied via the control interface of the
786f89bd95cSHenrik Rydberg  * device.  It can be argued where this switch should take place. In
787f89bd95cSHenrik Rydberg  * some drivers, like appletouch, the switch is made during
788f89bd95cSHenrik Rydberg  * probe. However, the hid module may also alter the state of the
789f89bd95cSHenrik Rydberg  * device, resulting in trackpad malfunction under certain
790f89bd95cSHenrik Rydberg  * circumstances. To get around this problem, there is at least one
791f89bd95cSHenrik Rydberg  * example that utilizes the USB_QUIRK_RESET_RESUME quirk in order to
79225985edcSLucas De Marchi  * receive a reset_resume request rather than the normal resume.
793f89bd95cSHenrik Rydberg  * Since the implementation of reset_resume is equal to mode switch
794f89bd95cSHenrik Rydberg  * plus start_traffic, it seems easier to always do the switch when
795f89bd95cSHenrik Rydberg  * starting traffic on the device.
796f89bd95cSHenrik Rydberg  */
797f89bd95cSHenrik Rydberg static int bcm5974_start_traffic(struct bcm5974 *dev)
798f89bd95cSHenrik Rydberg {
7991719ec41SLuo Jinghua 	int error;
8001719ec41SLuo Jinghua 
8011719ec41SLuo Jinghua 	error = bcm5974_wellspring_mode(dev, true);
8021719ec41SLuo Jinghua 	if (error) {
803f89bd95cSHenrik Rydberg 		dprintk(1, "bcm5974: mode switch failed\n");
8041719ec41SLuo Jinghua 		goto err_out;
805f89bd95cSHenrik Rydberg 	}
806f89bd95cSHenrik Rydberg 
80743f482b4SHenrik Rydberg 	if (dev->bt_urb) {
8081719ec41SLuo Jinghua 		error = usb_submit_urb(dev->bt_urb, GFP_KERNEL);
8091719ec41SLuo Jinghua 		if (error)
8101719ec41SLuo Jinghua 			goto err_reset_mode;
81143f482b4SHenrik Rydberg 	}
812f89bd95cSHenrik Rydberg 
8131719ec41SLuo Jinghua 	error = usb_submit_urb(dev->tp_urb, GFP_KERNEL);
8141719ec41SLuo Jinghua 	if (error)
815f89bd95cSHenrik Rydberg 		goto err_kill_bt;
816f89bd95cSHenrik Rydberg 
817f89bd95cSHenrik Rydberg 	return 0;
818f89bd95cSHenrik Rydberg 
819f89bd95cSHenrik Rydberg err_kill_bt:
820f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
8211719ec41SLuo Jinghua err_reset_mode:
8221719ec41SLuo Jinghua 	bcm5974_wellspring_mode(dev, false);
8231719ec41SLuo Jinghua err_out:
8241719ec41SLuo Jinghua 	return error;
825f89bd95cSHenrik Rydberg }
826f89bd95cSHenrik Rydberg 
827f89bd95cSHenrik Rydberg static void bcm5974_pause_traffic(struct bcm5974 *dev)
828f89bd95cSHenrik Rydberg {
829f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->tp_urb);
830f89bd95cSHenrik Rydberg 	usb_kill_urb(dev->bt_urb);
831cd72ad3fSHenrik Rydberg 	bcm5974_wellspring_mode(dev, false);
832f89bd95cSHenrik Rydberg }
833f89bd95cSHenrik Rydberg 
834f89bd95cSHenrik Rydberg /*
835f89bd95cSHenrik Rydberg  * The code below implements open/close and manual suspend/resume.
836f89bd95cSHenrik Rydberg  * All functions may be called in random order.
837f89bd95cSHenrik Rydberg  *
838f89bd95cSHenrik Rydberg  * Opening a suspended device fails with EACCES - permission denied.
839f89bd95cSHenrik Rydberg  *
840f89bd95cSHenrik Rydberg  * Failing a resume leaves the device resumed but closed.
841f89bd95cSHenrik Rydberg  */
842f89bd95cSHenrik Rydberg static int bcm5974_open(struct input_dev *input)
843f89bd95cSHenrik Rydberg {
844f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
845f89bd95cSHenrik Rydberg 	int error;
846f89bd95cSHenrik Rydberg 
84788da765fSDmitry Torokhov 	error = usb_autopm_get_interface(dev->intf);
84888da765fSDmitry Torokhov 	if (error)
84988da765fSDmitry Torokhov 		return error;
85088da765fSDmitry Torokhov 
851f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
852f89bd95cSHenrik Rydberg 
853f89bd95cSHenrik Rydberg 	error = bcm5974_start_traffic(dev);
854f89bd95cSHenrik Rydberg 	if (!error)
855f89bd95cSHenrik Rydberg 		dev->opened = 1;
856f89bd95cSHenrik Rydberg 
857f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
858f89bd95cSHenrik Rydberg 
85988da765fSDmitry Torokhov 	if (error)
86088da765fSDmitry Torokhov 		usb_autopm_put_interface(dev->intf);
86188da765fSDmitry Torokhov 
862f89bd95cSHenrik Rydberg 	return error;
863f89bd95cSHenrik Rydberg }
864f89bd95cSHenrik Rydberg 
865f89bd95cSHenrik Rydberg static void bcm5974_close(struct input_dev *input)
866f89bd95cSHenrik Rydberg {
867f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = input_get_drvdata(input);
868f89bd95cSHenrik Rydberg 
869f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
870f89bd95cSHenrik Rydberg 
871f89bd95cSHenrik Rydberg 	bcm5974_pause_traffic(dev);
872f89bd95cSHenrik Rydberg 	dev->opened = 0;
873f89bd95cSHenrik Rydberg 
874f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
87588da765fSDmitry Torokhov 
87688da765fSDmitry Torokhov 	usb_autopm_put_interface(dev->intf);
877f89bd95cSHenrik Rydberg }
878f89bd95cSHenrik Rydberg 
879f89bd95cSHenrik Rydberg static int bcm5974_suspend(struct usb_interface *iface, pm_message_t message)
880f89bd95cSHenrik Rydberg {
881f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
882f89bd95cSHenrik Rydberg 
883f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
884f89bd95cSHenrik Rydberg 
885f89bd95cSHenrik Rydberg 	if (dev->opened)
886f89bd95cSHenrik Rydberg 		bcm5974_pause_traffic(dev);
887f89bd95cSHenrik Rydberg 
888f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
889f89bd95cSHenrik Rydberg 
890f89bd95cSHenrik Rydberg 	return 0;
891f89bd95cSHenrik Rydberg }
892f89bd95cSHenrik Rydberg 
893f89bd95cSHenrik Rydberg static int bcm5974_resume(struct usb_interface *iface)
894f89bd95cSHenrik Rydberg {
895f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
896f89bd95cSHenrik Rydberg 	int error = 0;
897f89bd95cSHenrik Rydberg 
898f89bd95cSHenrik Rydberg 	mutex_lock(&dev->pm_mutex);
899f89bd95cSHenrik Rydberg 
900f89bd95cSHenrik Rydberg 	if (dev->opened)
901f89bd95cSHenrik Rydberg 		error = bcm5974_start_traffic(dev);
902f89bd95cSHenrik Rydberg 
903f89bd95cSHenrik Rydberg 	mutex_unlock(&dev->pm_mutex);
904f89bd95cSHenrik Rydberg 
905f89bd95cSHenrik Rydberg 	return error;
906f89bd95cSHenrik Rydberg }
907f89bd95cSHenrik Rydberg 
908f89bd95cSHenrik Rydberg static int bcm5974_probe(struct usb_interface *iface,
909f89bd95cSHenrik Rydberg 			 const struct usb_device_id *id)
910f89bd95cSHenrik Rydberg {
911f89bd95cSHenrik Rydberg 	struct usb_device *udev = interface_to_usbdev(iface);
912f89bd95cSHenrik Rydberg 	const struct bcm5974_config *cfg;
913f89bd95cSHenrik Rydberg 	struct bcm5974 *dev;
914f89bd95cSHenrik Rydberg 	struct input_dev *input_dev;
915f89bd95cSHenrik Rydberg 	int error = -ENOMEM;
916f89bd95cSHenrik Rydberg 
917f89bd95cSHenrik Rydberg 	/* find the product index */
918f89bd95cSHenrik Rydberg 	cfg = bcm5974_get_config(udev);
919f89bd95cSHenrik Rydberg 
920f89bd95cSHenrik Rydberg 	/* allocate memory for our device state and initialize it */
921f89bd95cSHenrik Rydberg 	dev = kzalloc(sizeof(struct bcm5974), GFP_KERNEL);
922f89bd95cSHenrik Rydberg 	input_dev = input_allocate_device();
923f89bd95cSHenrik Rydberg 	if (!dev || !input_dev) {
9246c1d1b24SGreg Kroah-Hartman 		dev_err(&iface->dev, "out of memory\n");
925f89bd95cSHenrik Rydberg 		goto err_free_devs;
926f89bd95cSHenrik Rydberg 	}
927f89bd95cSHenrik Rydberg 
928f89bd95cSHenrik Rydberg 	dev->udev = udev;
92988da765fSDmitry Torokhov 	dev->intf = iface;
930f89bd95cSHenrik Rydberg 	dev->input = input_dev;
931f89bd95cSHenrik Rydberg 	dev->cfg = *cfg;
932f89bd95cSHenrik Rydberg 	mutex_init(&dev->pm_mutex);
933f89bd95cSHenrik Rydberg 
934f89bd95cSHenrik Rydberg 	/* setup urbs */
93543f482b4SHenrik Rydberg 	if (cfg->tp_type == TYPE1) {
936f89bd95cSHenrik Rydberg 		dev->bt_urb = usb_alloc_urb(0, GFP_KERNEL);
937f89bd95cSHenrik Rydberg 		if (!dev->bt_urb)
938f89bd95cSHenrik Rydberg 			goto err_free_devs;
93943f482b4SHenrik Rydberg 	}
940f89bd95cSHenrik Rydberg 
941f89bd95cSHenrik Rydberg 	dev->tp_urb = usb_alloc_urb(0, GFP_KERNEL);
942f89bd95cSHenrik Rydberg 	if (!dev->tp_urb)
943f89bd95cSHenrik Rydberg 		goto err_free_bt_urb;
944f89bd95cSHenrik Rydberg 
94543f482b4SHenrik Rydberg 	if (dev->bt_urb) {
946997ea58eSDaniel Mack 		dev->bt_data = usb_alloc_coherent(dev->udev,
947f89bd95cSHenrik Rydberg 					  dev->cfg.bt_datalen, GFP_KERNEL,
948f89bd95cSHenrik Rydberg 					  &dev->bt_urb->transfer_dma);
949f89bd95cSHenrik Rydberg 		if (!dev->bt_data)
950f89bd95cSHenrik Rydberg 			goto err_free_urb;
95143f482b4SHenrik Rydberg 	}
952f89bd95cSHenrik Rydberg 
953997ea58eSDaniel Mack 	dev->tp_data = usb_alloc_coherent(dev->udev,
954f89bd95cSHenrik Rydberg 					  dev->cfg.tp_datalen, GFP_KERNEL,
955f89bd95cSHenrik Rydberg 					  &dev->tp_urb->transfer_dma);
956f89bd95cSHenrik Rydberg 	if (!dev->tp_data)
957f89bd95cSHenrik Rydberg 		goto err_free_bt_buffer;
958f89bd95cSHenrik Rydberg 
95943f482b4SHenrik Rydberg 	if (dev->bt_urb)
960f89bd95cSHenrik Rydberg 		usb_fill_int_urb(dev->bt_urb, udev,
961f89bd95cSHenrik Rydberg 				 usb_rcvintpipe(udev, cfg->bt_ep),
962f89bd95cSHenrik Rydberg 				 dev->bt_data, dev->cfg.bt_datalen,
963f89bd95cSHenrik Rydberg 				 bcm5974_irq_button, dev, 1);
964f89bd95cSHenrik Rydberg 
965f89bd95cSHenrik Rydberg 	usb_fill_int_urb(dev->tp_urb, udev,
966f89bd95cSHenrik Rydberg 			 usb_rcvintpipe(udev, cfg->tp_ep),
967f89bd95cSHenrik Rydberg 			 dev->tp_data, dev->cfg.tp_datalen,
968f89bd95cSHenrik Rydberg 			 bcm5974_irq_trackpad, dev, 1);
969f89bd95cSHenrik Rydberg 
970f89bd95cSHenrik Rydberg 	/* create bcm5974 device */
971f89bd95cSHenrik Rydberg 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
972f89bd95cSHenrik Rydberg 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
973f89bd95cSHenrik Rydberg 
974f89bd95cSHenrik Rydberg 	input_dev->name = "bcm5974";
975f89bd95cSHenrik Rydberg 	input_dev->phys = dev->phys;
976f89bd95cSHenrik Rydberg 	usb_to_input_id(dev->udev, &input_dev->id);
977158e9287SHenrik Rydberg 	/* report driver capabilities via the version field */
978158e9287SHenrik Rydberg 	input_dev->id.version = cfg->caps;
979f89bd95cSHenrik Rydberg 	input_dev->dev.parent = &iface->dev;
980f89bd95cSHenrik Rydberg 
981f89bd95cSHenrik Rydberg 	input_set_drvdata(input_dev, dev);
982f89bd95cSHenrik Rydberg 
983f89bd95cSHenrik Rydberg 	input_dev->open = bcm5974_open;
984f89bd95cSHenrik Rydberg 	input_dev->close = bcm5974_close;
985f89bd95cSHenrik Rydberg 
986f89bd95cSHenrik Rydberg 	setup_events_to_report(input_dev, cfg);
987f89bd95cSHenrik Rydberg 
988f89bd95cSHenrik Rydberg 	error = input_register_device(dev->input);
989f89bd95cSHenrik Rydberg 	if (error)
990f89bd95cSHenrik Rydberg 		goto err_free_buffer;
991f89bd95cSHenrik Rydberg 
992f89bd95cSHenrik Rydberg 	/* save our data pointer in this interface device */
993f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, dev);
994f89bd95cSHenrik Rydberg 
995f89bd95cSHenrik Rydberg 	return 0;
996f89bd95cSHenrik Rydberg 
997f89bd95cSHenrik Rydberg err_free_buffer:
998997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
999f89bd95cSHenrik Rydberg 		dev->tp_data, dev->tp_urb->transfer_dma);
1000f89bd95cSHenrik Rydberg err_free_bt_buffer:
100143f482b4SHenrik Rydberg 	if (dev->bt_urb)
1002997ea58eSDaniel Mack 		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
1003f89bd95cSHenrik Rydberg 				  dev->bt_data, dev->bt_urb->transfer_dma);
1004f89bd95cSHenrik Rydberg err_free_urb:
1005f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
1006f89bd95cSHenrik Rydberg err_free_bt_urb:
1007f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
1008f89bd95cSHenrik Rydberg err_free_devs:
1009f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
1010f89bd95cSHenrik Rydberg 	input_free_device(input_dev);
1011f89bd95cSHenrik Rydberg 	kfree(dev);
1012f89bd95cSHenrik Rydberg 	return error;
1013f89bd95cSHenrik Rydberg }
1014f89bd95cSHenrik Rydberg 
1015f89bd95cSHenrik Rydberg static void bcm5974_disconnect(struct usb_interface *iface)
1016f89bd95cSHenrik Rydberg {
1017f89bd95cSHenrik Rydberg 	struct bcm5974 *dev = usb_get_intfdata(iface);
1018f89bd95cSHenrik Rydberg 
1019f89bd95cSHenrik Rydberg 	usb_set_intfdata(iface, NULL);
1020f89bd95cSHenrik Rydberg 
1021f89bd95cSHenrik Rydberg 	input_unregister_device(dev->input);
1022997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->cfg.tp_datalen,
1023f89bd95cSHenrik Rydberg 			  dev->tp_data, dev->tp_urb->transfer_dma);
102443f482b4SHenrik Rydberg 	if (dev->bt_urb)
1025997ea58eSDaniel Mack 		usb_free_coherent(dev->udev, dev->cfg.bt_datalen,
1026f89bd95cSHenrik Rydberg 				  dev->bt_data, dev->bt_urb->transfer_dma);
1027f89bd95cSHenrik Rydberg 	usb_free_urb(dev->tp_urb);
1028f89bd95cSHenrik Rydberg 	usb_free_urb(dev->bt_urb);
1029f89bd95cSHenrik Rydberg 	kfree(dev);
1030f89bd95cSHenrik Rydberg }
1031f89bd95cSHenrik Rydberg 
1032f89bd95cSHenrik Rydberg static struct usb_driver bcm5974_driver = {
1033f89bd95cSHenrik Rydberg 	.name			= "bcm5974",
1034f89bd95cSHenrik Rydberg 	.probe			= bcm5974_probe,
1035f89bd95cSHenrik Rydberg 	.disconnect		= bcm5974_disconnect,
1036f89bd95cSHenrik Rydberg 	.suspend		= bcm5974_suspend,
1037f89bd95cSHenrik Rydberg 	.resume			= bcm5974_resume,
1038f89bd95cSHenrik Rydberg 	.id_table		= bcm5974_table,
103988da765fSDmitry Torokhov 	.supports_autosuspend	= 1,
1040f89bd95cSHenrik Rydberg };
1041f89bd95cSHenrik Rydberg 
104208642e7cSGreg Kroah-Hartman module_usb_driver(bcm5974_driver);
1043