xref: /openbmc/linux/drivers/input/mouse/appletouch.c (revision 09779678d12482024e06380cacc4c3ff2f129f23)
1b5da20f8SDmitry Torokhov /*
2b5da20f8SDmitry Torokhov  * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
3b5da20f8SDmitry Torokhov  *
4b5da20f8SDmitry Torokhov  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
57dce869fSJohannes Berg  * Copyright (C) 2005-2008 Johannes Berg (johannes@sipsolutions.net)
6*09779678SStelian Pop  * Copyright (C) 2005-2008 Stelian Pop (stelian@popies.net)
7b5da20f8SDmitry Torokhov  * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
8b5da20f8SDmitry Torokhov  * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
9b5da20f8SDmitry Torokhov  * Copyright (C) 2005      Michael Hanselmann (linux-kernel@hansmi.ch)
10b5da20f8SDmitry Torokhov  * Copyright (C) 2006      Nicolas Boichat (nicolas@boichat.ch)
117dce869fSJohannes Berg  * Copyright (C) 2007-2008 Sven Anders (anders@anduras.de)
12b5da20f8SDmitry Torokhov  *
13b5da20f8SDmitry Torokhov  * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
14b5da20f8SDmitry Torokhov  *
15b5da20f8SDmitry Torokhov  * This program is free software; you can redistribute it and/or modify
16b5da20f8SDmitry Torokhov  * it under the terms of the GNU General Public License as published by
17b5da20f8SDmitry Torokhov  * the Free Software Foundation; either version 2 of the License, or
18b5da20f8SDmitry Torokhov  * (at your option) any later version.
19b5da20f8SDmitry Torokhov  *
20b5da20f8SDmitry Torokhov  * This program is distributed in the hope that it will be useful,
21b5da20f8SDmitry Torokhov  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22b5da20f8SDmitry Torokhov  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23b5da20f8SDmitry Torokhov  * GNU General Public License for more details.
24b5da20f8SDmitry Torokhov  *
25b5da20f8SDmitry Torokhov  * You should have received a copy of the GNU General Public License
26b5da20f8SDmitry Torokhov  * along with this program; if not, write to the Free Software
27b5da20f8SDmitry Torokhov  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
28b5da20f8SDmitry Torokhov  *
29b5da20f8SDmitry Torokhov  */
30b5da20f8SDmitry Torokhov 
31b5da20f8SDmitry Torokhov #include <linux/kernel.h>
32b5da20f8SDmitry Torokhov #include <linux/errno.h>
33b5da20f8SDmitry Torokhov #include <linux/init.h>
34b5da20f8SDmitry Torokhov #include <linux/slab.h>
35b5da20f8SDmitry Torokhov #include <linux/module.h>
36b5da20f8SDmitry Torokhov #include <linux/usb/input.h>
37b5da20f8SDmitry Torokhov 
38*09779678SStelian Pop /*
39*09779678SStelian Pop  * Note: We try to keep the touchpad aspect ratio while still doing only
40*09779678SStelian Pop  * simple arithmetics:
41*09779678SStelian Pop  *	0 <= x <= (xsensors - 1) * xfact
42*09779678SStelian Pop  *	0 <= y <= (ysensors - 1) * yfact
43*09779678SStelian Pop  */
44*09779678SStelian Pop struct atp_info {
45*09779678SStelian Pop 	int xsensors;				/* number of X sensors */
46*09779678SStelian Pop 	int xsensors_17;			/* 17" models have more sensors */
47*09779678SStelian Pop 	int ysensors;				/* number of Y sensors */
48*09779678SStelian Pop 	int xfact;				/* X multiplication factor */
49*09779678SStelian Pop 	int yfact;				/* Y multiplication factor */
50*09779678SStelian Pop 	int datalen;				/* size of USB transfers */
51*09779678SStelian Pop 	void (*callback)(struct urb *);		/* callback function */
52e9542dffSSven Anders };
53b5da20f8SDmitry Torokhov 
54*09779678SStelian Pop static void atp_complete_geyser_1_2(struct urb *urb);
55*09779678SStelian Pop static void atp_complete_geyser_3_4(struct urb *urb);
56*09779678SStelian Pop 
57*09779678SStelian Pop static const struct atp_info fountain_info = {
58*09779678SStelian Pop 	.xsensors	= 16,
59*09779678SStelian Pop 	.xsensors_17	= 26,
60*09779678SStelian Pop 	.ysensors	= 16,
61*09779678SStelian Pop 	.xfact		= 64,
62*09779678SStelian Pop 	.yfact		= 43,
63*09779678SStelian Pop 	.datalen	= 81,
64*09779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
65*09779678SStelian Pop };
66*09779678SStelian Pop 
67*09779678SStelian Pop static const struct atp_info geyser1_info = {
68*09779678SStelian Pop 	.xsensors	= 16,
69*09779678SStelian Pop 	.xsensors_17	= 26,
70*09779678SStelian Pop 	.ysensors	= 16,
71*09779678SStelian Pop 	.xfact		= 64,
72*09779678SStelian Pop 	.yfact		= 43,
73*09779678SStelian Pop 	.datalen	= 81,
74*09779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
75*09779678SStelian Pop };
76*09779678SStelian Pop 
77*09779678SStelian Pop static const struct atp_info geyser2_info = {
78*09779678SStelian Pop 	.xsensors	= 15,
79*09779678SStelian Pop 	.xsensors_17	= 20,
80*09779678SStelian Pop 	.ysensors	= 9,
81*09779678SStelian Pop 	.xfact		= 64,
82*09779678SStelian Pop 	.yfact		= 43,
83*09779678SStelian Pop 	.datalen	= 64,
84*09779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
85*09779678SStelian Pop };
86*09779678SStelian Pop 
87*09779678SStelian Pop static const struct atp_info geyser3_info = {
88*09779678SStelian Pop 	.xsensors	= 20,
89*09779678SStelian Pop 	.ysensors	= 10,
90*09779678SStelian Pop 	.xfact		= 64,
91*09779678SStelian Pop 	.yfact		= 64,
92*09779678SStelian Pop 	.datalen	= 64,
93*09779678SStelian Pop 	.callback	= atp_complete_geyser_3_4,
94*09779678SStelian Pop };
95*09779678SStelian Pop 
96*09779678SStelian Pop static const struct atp_info geyser4_info = {
97*09779678SStelian Pop 	.xsensors	= 20,
98*09779678SStelian Pop 	.ysensors	= 10,
99*09779678SStelian Pop 	.xfact		= 64,
100*09779678SStelian Pop 	.yfact		= 64,
101*09779678SStelian Pop 	.datalen	= 64,
102*09779678SStelian Pop 	.callback	= atp_complete_geyser_3_4,
103*09779678SStelian Pop };
104*09779678SStelian Pop 
105*09779678SStelian Pop #define ATP_DEVICE(prod, info)					\
106e9542dffSSven Anders {								\
107b5da20f8SDmitry Torokhov 	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |		\
108b5da20f8SDmitry Torokhov 		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
109b5da20f8SDmitry Torokhov 		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
110e9542dffSSven Anders 	.idVendor = 0x05ac, /* Apple */				\
111b5da20f8SDmitry Torokhov 	.idProduct = (prod),					\
112b5da20f8SDmitry Torokhov 	.bInterfaceClass = 0x03,				\
113e9542dffSSven Anders 	.bInterfaceProtocol = 0x02,				\
114*09779678SStelian Pop 	.driver_info = (unsigned long) &info,			\
115e9542dffSSven Anders }
116b5da20f8SDmitry Torokhov 
117e9542dffSSven Anders /*
118e9542dffSSven Anders  * Table of devices (Product IDs) that work with this driver.
119e9542dffSSven Anders  * (The names come from Info.plist in AppleUSBTrackpad.kext,
120e9542dffSSven Anders  *  According to Info.plist Geyser IV is the same as Geyser III.)
121e9542dffSSven Anders  */
122e9542dffSSven Anders 
123b5da20f8SDmitry Torokhov static struct usb_device_id atp_table[] = {
1247dce869fSJohannes Berg 	/* PowerBooks Feb 2005, iBooks G4 */
125*09779678SStelian Pop 	ATP_DEVICE(0x020e, fountain_info),	/* FOUNTAIN ANSI */
126*09779678SStelian Pop 	ATP_DEVICE(0x020f, fountain_info),	/* FOUNTAIN ISO */
127*09779678SStelian Pop 	ATP_DEVICE(0x030a, fountain_info),	/* FOUNTAIN TP ONLY */
128*09779678SStelian Pop 	ATP_DEVICE(0x030b, geyser1_info),	/* GEYSER 1 TP ONLY */
129b5da20f8SDmitry Torokhov 
130b5da20f8SDmitry Torokhov 	/* PowerBooks Oct 2005 */
131*09779678SStelian Pop 	ATP_DEVICE(0x0214, geyser2_info),	/* GEYSER 2 ANSI */
132*09779678SStelian Pop 	ATP_DEVICE(0x0215, geyser2_info),	/* GEYSER 2 ISO */
133*09779678SStelian Pop 	ATP_DEVICE(0x0216, geyser2_info),	/* GEYSER 2 JIS */
134b5da20f8SDmitry Torokhov 
135b5da20f8SDmitry Torokhov 	/* Core Duo MacBook & MacBook Pro */
136*09779678SStelian Pop 	ATP_DEVICE(0x0217, geyser3_info),	/* GEYSER 3 ANSI */
137*09779678SStelian Pop 	ATP_DEVICE(0x0218, geyser3_info),	/* GEYSER 3 ISO */
138*09779678SStelian Pop 	ATP_DEVICE(0x0219, geyser3_info),	/* GEYSER 3 JIS */
139b5da20f8SDmitry Torokhov 
140b5da20f8SDmitry Torokhov 	/* Core2 Duo MacBook & MacBook Pro */
141*09779678SStelian Pop 	ATP_DEVICE(0x021a, geyser4_info),	/* GEYSER 4 ANSI */
142*09779678SStelian Pop 	ATP_DEVICE(0x021b, geyser4_info),	/* GEYSER 4 ISO */
143*09779678SStelian Pop 	ATP_DEVICE(0x021c, geyser4_info),	/* GEYSER 4 JIS */
144b5da20f8SDmitry Torokhov 
1457dce869fSJohannes Berg 	/* Core2 Duo MacBook3,1 */
146*09779678SStelian Pop 	ATP_DEVICE(0x0229, geyser4_info),	/* GEYSER 4 HF ANSI */
147*09779678SStelian Pop 	ATP_DEVICE(0x022a, geyser4_info),	/* GEYSER 4 HF ISO */
148*09779678SStelian Pop 	ATP_DEVICE(0x022b, geyser4_info),	/* GEYSER 4 HF JIS */
1490035a1dcSTobias Mueller 
150b5da20f8SDmitry Torokhov 	/* Terminating entry */
151b5da20f8SDmitry Torokhov 	{ }
152b5da20f8SDmitry Torokhov };
153b5da20f8SDmitry Torokhov MODULE_DEVICE_TABLE(usb, atp_table);
154b5da20f8SDmitry Torokhov 
155*09779678SStelian Pop /* maximum number of sensors */
156b5da20f8SDmitry Torokhov #define ATP_XSENSORS	26
157b5da20f8SDmitry Torokhov #define ATP_YSENSORS	16
158b5da20f8SDmitry Torokhov 
159b5da20f8SDmitry Torokhov /* amount of fuzz this touchpad generates */
160b5da20f8SDmitry Torokhov #define ATP_FUZZ	16
161b5da20f8SDmitry Torokhov 
162b5da20f8SDmitry Torokhov /* maximum pressure this driver will report */
163b5da20f8SDmitry Torokhov #define ATP_PRESSURE	300
164b5da20f8SDmitry Torokhov 
165b5da20f8SDmitry Torokhov /*
166b5da20f8SDmitry Torokhov  * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
167b5da20f8SDmitry Torokhov  * ignored.
168b5da20f8SDmitry Torokhov  */
169b5da20f8SDmitry Torokhov #define ATP_THRESHOLD	 5
170b5da20f8SDmitry Torokhov 
1712a3e480dSDmitry Torokhov /* Geyser initialization constants */
1722a3e480dSDmitry Torokhov #define ATP_GEYSER_MODE_READ_REQUEST_ID		1
1732a3e480dSDmitry Torokhov #define ATP_GEYSER_MODE_WRITE_REQUEST_ID	9
1742a3e480dSDmitry Torokhov #define ATP_GEYSER_MODE_REQUEST_VALUE		0x300
1752a3e480dSDmitry Torokhov #define ATP_GEYSER_MODE_REQUEST_INDEX		0
1762a3e480dSDmitry Torokhov #define ATP_GEYSER_MODE_VENDOR_VALUE		0x04
177b5da20f8SDmitry Torokhov 
17882a196f4SSven Anders /**
17982a196f4SSven Anders  * enum atp_status_bits - status bit meanings
18082a196f4SSven Anders  *
18182a196f4SSven Anders  * These constants represent the meaning of the status bits.
18282a196f4SSven Anders  * (only Geyser 3/4)
18382a196f4SSven Anders  *
18482a196f4SSven Anders  * @ATP_STATUS_BUTTON: The button was pressed
18582a196f4SSven Anders  * @ATP_STATUS_BASE_UPDATE: Update of the base values (untouched pad)
18682a196f4SSven Anders  * @ATP_STATUS_FROM_RESET: Reset previously performed
18782a196f4SSven Anders  */
18882a196f4SSven Anders enum atp_status_bits {
18982a196f4SSven Anders 	ATP_STATUS_BUTTON	= BIT(0),
19082a196f4SSven Anders 	ATP_STATUS_BASE_UPDATE	= BIT(2),
19182a196f4SSven Anders 	ATP_STATUS_FROM_RESET	= BIT(4),
19282a196f4SSven Anders };
19382a196f4SSven Anders 
194b5da20f8SDmitry Torokhov /* Structure to hold all of our device specific stuff */
195b5da20f8SDmitry Torokhov struct atp {
196b5da20f8SDmitry Torokhov 	char			phys[64];
197b5da20f8SDmitry Torokhov 	struct usb_device	*udev;		/* usb device */
198b5da20f8SDmitry Torokhov 	struct urb		*urb;		/* usb request block */
19982a196f4SSven Anders 	u8			*data;		/* transferred data */
200b5da20f8SDmitry Torokhov 	struct input_dev	*input;		/* input dev */
201*09779678SStelian Pop 	const struct atp_info	*info;		/* touchpad model */
2027dce869fSJohannes Berg 	bool			open;
2037dce869fSJohannes Berg 	bool			valid;		/* are the samples valid? */
2047dce869fSJohannes Berg 	bool			size_detect_done;
2057dce869fSJohannes Berg 	bool			overflow_warned;
206b5da20f8SDmitry Torokhov 	int			x_old;		/* last reported x/y, */
207b5da20f8SDmitry Torokhov 	int			y_old;		/* used for smoothing */
208b5da20f8SDmitry Torokhov 	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
209b5da20f8SDmitry Torokhov 	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];
210b5da20f8SDmitry Torokhov 	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS];
2115a6eb676SSoeren Sonnenburg 	int			idlecount;	/* number of empty packets */
2125a6eb676SSoeren Sonnenburg 	struct work_struct	work;
213b5da20f8SDmitry Torokhov };
214b5da20f8SDmitry Torokhov 
215b5da20f8SDmitry Torokhov #define dbg_dump(msg, tab) \
216b5da20f8SDmitry Torokhov 	if (debug > 1) {						\
2177dce869fSJohannes Berg 		int __i;						\
2187dce869fSJohannes Berg 		printk(KERN_DEBUG "appletouch: %s", msg);		\
2197dce869fSJohannes Berg 		for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++)	\
2207dce869fSJohannes Berg 			printk(" %02x", tab[__i]);			\
221b5da20f8SDmitry Torokhov 		printk("\n");						\
222b5da20f8SDmitry Torokhov 	}
223b5da20f8SDmitry Torokhov 
224b5da20f8SDmitry Torokhov #define dprintk(format, a...)						\
225b5da20f8SDmitry Torokhov 	do {								\
2267dce869fSJohannes Berg 		if (debug)						\
2277dce869fSJohannes Berg 			printk(KERN_DEBUG format, ##a);			\
228b5da20f8SDmitry Torokhov 	} while (0)
229b5da20f8SDmitry Torokhov 
2307dce869fSJohannes Berg MODULE_AUTHOR("Johannes Berg");
2317dce869fSJohannes Berg MODULE_AUTHOR("Stelian Pop");
2327dce869fSJohannes Berg MODULE_AUTHOR("Frank Arnold");
2337dce869fSJohannes Berg MODULE_AUTHOR("Michael Hanselmann");
2347dce869fSJohannes Berg MODULE_AUTHOR("Sven Anders");
2357dce869fSJohannes Berg MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver");
236b5da20f8SDmitry Torokhov MODULE_LICENSE("GPL");
237b5da20f8SDmitry Torokhov 
238b5da20f8SDmitry Torokhov /*
239b5da20f8SDmitry Torokhov  * Make the threshold a module parameter
240b5da20f8SDmitry Torokhov  */
241b5da20f8SDmitry Torokhov static int threshold = ATP_THRESHOLD;
242b5da20f8SDmitry Torokhov module_param(threshold, int, 0644);
2437dce869fSJohannes Berg MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor"
2447dce869fSJohannes Berg 			    " (the trackpad has many of these sensors)"
2457dce869fSJohannes Berg 			    " less than this value.");
246b5da20f8SDmitry Torokhov 
2477dce869fSJohannes Berg static int debug;
248b5da20f8SDmitry Torokhov module_param(debug, int, 0644);
249b5da20f8SDmitry Torokhov MODULE_PARM_DESC(debug, "Activate debugging output");
250b5da20f8SDmitry Torokhov 
2515a6eb676SSoeren Sonnenburg /*
2522a3e480dSDmitry Torokhov  * By default newer Geyser devices send standard USB HID mouse
2535a6eb676SSoeren Sonnenburg  * packets (Report ID 2). This code changes device mode, so it
2545a6eb676SSoeren Sonnenburg  * sends raw sensor reports (Report ID 5).
2555a6eb676SSoeren Sonnenburg  */
2562a3e480dSDmitry Torokhov static int atp_geyser_init(struct usb_device *udev)
2575a6eb676SSoeren Sonnenburg {
2585a6eb676SSoeren Sonnenburg 	char data[8];
2595a6eb676SSoeren Sonnenburg 	int size;
2607dce869fSJohannes Berg 	int i;
2615a6eb676SSoeren Sonnenburg 
2625a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
2632a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_READ_REQUEST_ID,
2645a6eb676SSoeren Sonnenburg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2652a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2662a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
2675a6eb676SSoeren Sonnenburg 
2685a6eb676SSoeren Sonnenburg 	if (size != 8) {
2697dce869fSJohannes Berg 		dprintk("atp_geyser_init: read error\n");
2707dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2717dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
2727dce869fSJohannes Berg 
2737dce869fSJohannes Berg 		err("Failed to read mode from device.");
2745a6eb676SSoeren Sonnenburg 		return -EIO;
2755a6eb676SSoeren Sonnenburg 	}
2765a6eb676SSoeren Sonnenburg 
2775a6eb676SSoeren Sonnenburg 	/* Apply the mode switch */
2782a3e480dSDmitry Torokhov 	data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
2795a6eb676SSoeren Sonnenburg 
2805a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
2812a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_WRITE_REQUEST_ID,
2825a6eb676SSoeren Sonnenburg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2832a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2842a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_INDEX, &data, 8, 5000);
2855a6eb676SSoeren Sonnenburg 
2865a6eb676SSoeren Sonnenburg 	if (size != 8) {
2877dce869fSJohannes Berg 		dprintk("atp_geyser_init: write error\n");
2887dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2897dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
2907dce869fSJohannes Berg 
2917dce869fSJohannes Berg 		err("Failed to request geyser raw mode");
2925a6eb676SSoeren Sonnenburg 		return -EIO;
2935a6eb676SSoeren Sonnenburg 	}
2945a6eb676SSoeren Sonnenburg 	return 0;
2955a6eb676SSoeren Sonnenburg }
2965a6eb676SSoeren Sonnenburg 
2972a3e480dSDmitry Torokhov /*
2982a3e480dSDmitry Torokhov  * Reinitialise the device. This usually stops stream of empty packets
2992a3e480dSDmitry Torokhov  * coming from it.
3002a3e480dSDmitry Torokhov  */
3015a6eb676SSoeren Sonnenburg static void atp_reinit(struct work_struct *work)
3025a6eb676SSoeren Sonnenburg {
3035a6eb676SSoeren Sonnenburg 	struct atp *dev = container_of(work, struct atp, work);
3045a6eb676SSoeren Sonnenburg 	struct usb_device *udev = dev->udev;
3052a3e480dSDmitry Torokhov 	int retval;
3065a6eb676SSoeren Sonnenburg 
3077dce869fSJohannes Berg 	dprintk("appletouch: putting appletouch to sleep (reinit)\n");
3082a3e480dSDmitry Torokhov 	atp_geyser_init(udev);
3092a3e480dSDmitry Torokhov 
3102a3e480dSDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
3117dce869fSJohannes Berg 	if (retval)
3127dce869fSJohannes Berg 		err("atp_reinit: usb_submit_urb failed with error %d",
3137dce869fSJohannes Berg 		    retval);
3145a6eb676SSoeren Sonnenburg }
3155a6eb676SSoeren Sonnenburg 
316b5da20f8SDmitry Torokhov static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
317b5da20f8SDmitry Torokhov 			     int *z, int *fingers)
318b5da20f8SDmitry Torokhov {
319b5da20f8SDmitry Torokhov 	int i;
320b5da20f8SDmitry Torokhov 	/* values to calculate mean */
321b5da20f8SDmitry Torokhov 	int pcum = 0, psum = 0;
322b5da20f8SDmitry Torokhov 	int is_increasing = 0;
323b5da20f8SDmitry Torokhov 
324b5da20f8SDmitry Torokhov 	*fingers = 0;
325b5da20f8SDmitry Torokhov 
326b5da20f8SDmitry Torokhov 	for (i = 0; i < nb_sensors; i++) {
327b5da20f8SDmitry Torokhov 		if (xy_sensors[i] < threshold) {
328b5da20f8SDmitry Torokhov 			if (is_increasing)
329b5da20f8SDmitry Torokhov 				is_increasing = 0;
330b5da20f8SDmitry Torokhov 
331b5da20f8SDmitry Torokhov 			continue;
332b5da20f8SDmitry Torokhov 		}
333b5da20f8SDmitry Torokhov 
334b5da20f8SDmitry Torokhov 		/*
335b5da20f8SDmitry Torokhov 		 * Makes the finger detection more versatile.  For example,
336b5da20f8SDmitry Torokhov 		 * two fingers with no gap will be detected.  Also, my
337b5da20f8SDmitry Torokhov 		 * tests show it less likely to have intermittent loss
338b5da20f8SDmitry Torokhov 		 * of multiple finger readings while moving around (scrolling).
339b5da20f8SDmitry Torokhov 		 *
340b5da20f8SDmitry Torokhov 		 * Changes the multiple finger detection to counting humps on
341b5da20f8SDmitry Torokhov 		 * sensors (transitions from nonincreasing to increasing)
342b5da20f8SDmitry Torokhov 		 * instead of counting transitions from low sensors (no
343b5da20f8SDmitry Torokhov 		 * finger reading) to high sensors (finger above
344b5da20f8SDmitry Torokhov 		 * sensor)
345b5da20f8SDmitry Torokhov 		 *
346b5da20f8SDmitry Torokhov 		 * - Jason Parekh <jasonparekh@gmail.com>
347b5da20f8SDmitry Torokhov 		 */
3487dce869fSJohannes Berg 		if (i < 1 ||
3497dce869fSJohannes Berg 		    (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
350b5da20f8SDmitry Torokhov 			(*fingers)++;
351b5da20f8SDmitry Torokhov 			is_increasing = 1;
352b5da20f8SDmitry Torokhov 		} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
353b5da20f8SDmitry Torokhov 			is_increasing = 0;
354b5da20f8SDmitry Torokhov 		}
355b5da20f8SDmitry Torokhov 
356b5da20f8SDmitry Torokhov 		/*
3577dce869fSJohannes Berg 		 * Subtracts threshold so a high sensor that just passes the
3587dce869fSJohannes Berg 		 * threshold won't skew the calculated absolute coordinate.
3597dce869fSJohannes Berg 		 * Fixes an issue where slowly moving the mouse would
3607dce869fSJohannes Berg 		 * occasionally jump a number of pixels (slowly moving the
3617dce869fSJohannes Berg 		 * finger makes this issue most apparent.)
362b5da20f8SDmitry Torokhov 		 */
363b5da20f8SDmitry Torokhov 		pcum += (xy_sensors[i] - threshold) * i;
364b5da20f8SDmitry Torokhov 		psum += (xy_sensors[i] - threshold);
365b5da20f8SDmitry Torokhov 	}
366b5da20f8SDmitry Torokhov 
367b5da20f8SDmitry Torokhov 	if (psum > 0) {
368b5da20f8SDmitry Torokhov 		*z = psum;
369b5da20f8SDmitry Torokhov 		return pcum * fact / psum;
370b5da20f8SDmitry Torokhov 	}
371b5da20f8SDmitry Torokhov 
372b5da20f8SDmitry Torokhov 	return 0;
373b5da20f8SDmitry Torokhov }
374b5da20f8SDmitry Torokhov 
375b5da20f8SDmitry Torokhov static inline void atp_report_fingers(struct input_dev *input, int fingers)
376b5da20f8SDmitry Torokhov {
377b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
378b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
379b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
380b5da20f8SDmitry Torokhov }
381b5da20f8SDmitry Torokhov 
382d83d213dSSven Anders /* Check URB status and for correct length of data package */
383d83d213dSSven Anders 
384d83d213dSSven Anders #define ATP_URB_STATUS_SUCCESS		0
385d83d213dSSven Anders #define ATP_URB_STATUS_ERROR		1
386d83d213dSSven Anders #define ATP_URB_STATUS_ERROR_FATAL	2
387d83d213dSSven Anders 
388d83d213dSSven Anders static int atp_status_check(struct urb *urb)
389b5da20f8SDmitry Torokhov {
390b5da20f8SDmitry Torokhov 	struct atp *dev = urb->context;
391b5da20f8SDmitry Torokhov 
392b5da20f8SDmitry Torokhov 	switch (urb->status) {
393b5da20f8SDmitry Torokhov 	case 0:
394b5da20f8SDmitry Torokhov 		/* success */
395b5da20f8SDmitry Torokhov 		break;
396b5da20f8SDmitry Torokhov 	case -EOVERFLOW:
3977dce869fSJohannes Berg 		if (!dev->overflow_warned) {
3982a3e480dSDmitry Torokhov 			printk(KERN_WARNING "appletouch: OVERFLOW with data "
399b5da20f8SDmitry Torokhov 				"length %d, actual length is %d\n",
400*09779678SStelian Pop 				dev->info->datalen, dev->urb->actual_length);
4017dce869fSJohannes Berg 			dev->overflow_warned = true;
402b5da20f8SDmitry Torokhov 		}
403b5da20f8SDmitry Torokhov 	case -ECONNRESET:
404b5da20f8SDmitry Torokhov 	case -ENOENT:
405b5da20f8SDmitry Torokhov 	case -ESHUTDOWN:
406b5da20f8SDmitry Torokhov 		/* This urb is terminated, clean up */
4077dce869fSJohannes Berg 		dbg("atp_complete: urb shutting down with status: %d",
4087dce869fSJohannes Berg 		    urb->status);
409d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR_FATAL;
410d83d213dSSven Anders 
411b5da20f8SDmitry Torokhov 	default:
4127dce869fSJohannes Berg 		dbg("atp_complete: nonzero urb status received: %d",
4137dce869fSJohannes Berg 		    urb->status);
414d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
415b5da20f8SDmitry Torokhov 	}
416b5da20f8SDmitry Torokhov 
417b5da20f8SDmitry Torokhov 	/* drop incomplete datasets */
418*09779678SStelian Pop 	if (dev->urb->actual_length != dev->info->datalen) {
419b5da20f8SDmitry Torokhov 		dprintk("appletouch: incomplete data package"
420b5da20f8SDmitry Torokhov 			" (first byte: %d, length: %d).\n",
421b5da20f8SDmitry Torokhov 			dev->data[0], dev->urb->actual_length);
422d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
423b5da20f8SDmitry Torokhov 	}
424b5da20f8SDmitry Torokhov 
425d83d213dSSven Anders 	return ATP_URB_STATUS_SUCCESS;
426d83d213dSSven Anders }
427b5da20f8SDmitry Torokhov 
428*09779678SStelian Pop static void atp_detect_size(struct atp *dev)
429*09779678SStelian Pop {
430*09779678SStelian Pop 	int i;
431*09779678SStelian Pop 
432*09779678SStelian Pop 	/* 17" Powerbooks have extra X sensors */
433*09779678SStelian Pop 	for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
434*09779678SStelian Pop 		if (dev->xy_cur[i]) {
435*09779678SStelian Pop 
436*09779678SStelian Pop 			printk(KERN_INFO "appletouch: 17\" model detected.\n");
437*09779678SStelian Pop 
438*09779678SStelian Pop 			input_set_abs_params(dev->input, ABS_X, 0,
439*09779678SStelian Pop 					     (dev->info->xsensors_17 - 1) *
440*09779678SStelian Pop 							dev->info->xfact - 1,
441*09779678SStelian Pop 					     ATP_FUZZ, 0);
442*09779678SStelian Pop 			break;
443*09779678SStelian Pop 		}
444*09779678SStelian Pop 	}
445*09779678SStelian Pop }
446*09779678SStelian Pop 
447b5da20f8SDmitry Torokhov /*
448d83d213dSSven Anders  * USB interrupt callback functions
449b5da20f8SDmitry Torokhov  */
450b5da20f8SDmitry Torokhov 
451d83d213dSSven Anders /* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
452d83d213dSSven Anders 
453d83d213dSSven Anders static void atp_complete_geyser_1_2(struct urb *urb)
454d83d213dSSven Anders {
455d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
456d83d213dSSven Anders 	int retval, i, j;
457d83d213dSSven Anders 	int key;
458d83d213dSSven Anders 	struct atp *dev = urb->context;
459d83d213dSSven Anders 	int status = atp_status_check(urb);
460d83d213dSSven Anders 
461d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
462d83d213dSSven Anders 		return;
463d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
464d83d213dSSven Anders 		goto exit;
465d83d213dSSven Anders 
466d83d213dSSven Anders 	/* reorder the sensors values */
467*09779678SStelian Pop 	if (dev->info == &geyser2_info) {
468b5da20f8SDmitry Torokhov 		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
469b5da20f8SDmitry Torokhov 
470b5da20f8SDmitry Torokhov 		/*
471b5da20f8SDmitry Torokhov 		 * The values are laid out like this:
472b5da20f8SDmitry Torokhov 		 * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
473b5da20f8SDmitry Torokhov 		 * '-' is an unused value.
474b5da20f8SDmitry Torokhov 		 */
475b5da20f8SDmitry Torokhov 
476b5da20f8SDmitry Torokhov 		/* read X values */
477b5da20f8SDmitry Torokhov 		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
478b5da20f8SDmitry Torokhov 			dev->xy_cur[i] = dev->data[j];
479b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 1] = dev->data[j + 1];
480b5da20f8SDmitry Torokhov 		}
481b5da20f8SDmitry Torokhov 
482b5da20f8SDmitry Torokhov 		/* read Y values */
483b5da20f8SDmitry Torokhov 		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
484b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
485b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
486b5da20f8SDmitry Torokhov 		}
487b5da20f8SDmitry Torokhov 	} else {
488b5da20f8SDmitry Torokhov 		for (i = 0; i < 8; i++) {
489b5da20f8SDmitry Torokhov 			/* X values */
4907dce869fSJohannes Berg 			dev->xy_cur[i +  0] = dev->data[5 * i +  2];
491b5da20f8SDmitry Torokhov 			dev->xy_cur[i +  8] = dev->data[5 * i +  4];
492b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 16] = dev->data[5 * i + 42];
493b5da20f8SDmitry Torokhov 			if (i < 2)
494b5da20f8SDmitry Torokhov 				dev->xy_cur[i + 24] = dev->data[5 * i + 44];
495b5da20f8SDmitry Torokhov 
496b5da20f8SDmitry Torokhov 			/* Y values */
497*09779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i +  1];
498*09779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
499b5da20f8SDmitry Torokhov 		}
500b5da20f8SDmitry Torokhov 	}
501b5da20f8SDmitry Torokhov 
502b5da20f8SDmitry Torokhov 	dbg_dump("sample", dev->xy_cur);
503b5da20f8SDmitry Torokhov 
504b5da20f8SDmitry Torokhov 	if (!dev->valid) {
505b5da20f8SDmitry Torokhov 		/* first sample */
5067dce869fSJohannes Berg 		dev->valid = true;
507b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
508d83d213dSSven Anders 
509d83d213dSSven Anders 		/* Store first sample */
510b5da20f8SDmitry Torokhov 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
511b5da20f8SDmitry Torokhov 
512d83d213dSSven Anders 		/* Perform size detection, if not done already */
513*09779678SStelian Pop 		if (unlikely(!dev->size_detect_done)) {
514*09779678SStelian Pop 			atp_detect_size(dev);
5152a3e480dSDmitry Torokhov 			dev->size_detect_done = 1;
516b5da20f8SDmitry Torokhov 			goto exit;
517b5da20f8SDmitry Torokhov 		}
518d83d213dSSven Anders 	}
519d83d213dSSven Anders 
520d83d213dSSven Anders 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
521d83d213dSSven Anders 		/* accumulate the change */
522d83d213dSSven Anders 		signed char change = dev->xy_old[i] - dev->xy_cur[i];
523d83d213dSSven Anders 		dev->xy_acc[i] -= change;
524d83d213dSSven Anders 
525d83d213dSSven Anders 		/* prevent down drifting */
526d83d213dSSven Anders 		if (dev->xy_acc[i] < 0)
527d83d213dSSven Anders 			dev->xy_acc[i] = 0;
528d83d213dSSven Anders 	}
529d83d213dSSven Anders 
530d83d213dSSven Anders 	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
531d83d213dSSven Anders 
532d83d213dSSven Anders 	dbg_dump("accumulator", dev->xy_acc);
533d83d213dSSven Anders 
534d83d213dSSven Anders 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
535*09779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
536d83d213dSSven Anders 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
537*09779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
538*09779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
539d83d213dSSven Anders 
540d83d213dSSven Anders 	if (x && y) {
541d83d213dSSven Anders 		if (dev->x_old != -1) {
542d83d213dSSven Anders 			x = (dev->x_old * 3 + x) >> 2;
543d83d213dSSven Anders 			y = (dev->y_old * 3 + y) >> 2;
544d83d213dSSven Anders 			dev->x_old = x;
545d83d213dSSven Anders 			dev->y_old = y;
546d83d213dSSven Anders 
547d83d213dSSven Anders 			if (debug > 1)
548d83d213dSSven Anders 				printk(KERN_DEBUG "appletouch: "
549d83d213dSSven Anders 					"X: %3d Y: %3d Xz: %3d Yz: %3d\n",
550d83d213dSSven Anders 					x, y, x_z, y_z);
551d83d213dSSven Anders 
552d83d213dSSven Anders 			input_report_key(dev->input, BTN_TOUCH, 1);
553d83d213dSSven Anders 			input_report_abs(dev->input, ABS_X, x);
554d83d213dSSven Anders 			input_report_abs(dev->input, ABS_Y, y);
555d83d213dSSven Anders 			input_report_abs(dev->input, ABS_PRESSURE,
556d83d213dSSven Anders 					 min(ATP_PRESSURE, x_z + y_z));
557d83d213dSSven Anders 			atp_report_fingers(dev->input, max(x_f, y_f));
558d83d213dSSven Anders 		}
559d83d213dSSven Anders 		dev->x_old = x;
560d83d213dSSven Anders 		dev->y_old = y;
561d83d213dSSven Anders 
562d83d213dSSven Anders 	} else if (!x && !y) {
563d83d213dSSven Anders 
564d83d213dSSven Anders 		dev->x_old = dev->y_old = -1;
565d83d213dSSven Anders 		input_report_key(dev->input, BTN_TOUCH, 0);
566d83d213dSSven Anders 		input_report_abs(dev->input, ABS_PRESSURE, 0);
567d83d213dSSven Anders 		atp_report_fingers(dev->input, 0);
568d83d213dSSven Anders 
569d83d213dSSven Anders 		/* reset the accumulator on release */
570d83d213dSSven Anders 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
571d83d213dSSven Anders 	}
572d83d213dSSven Anders 
573d83d213dSSven Anders 	input_report_key(dev->input, BTN_LEFT, key);
574d83d213dSSven Anders 	input_sync(dev->input);
575d83d213dSSven Anders 
576d83d213dSSven Anders  exit:
577d83d213dSSven Anders 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
578d83d213dSSven Anders 	if (retval)
579d83d213dSSven Anders 		err("atp_complete: usb_submit_urb failed with result %d",
580d83d213dSSven Anders 		    retval);
581d83d213dSSven Anders }
582d83d213dSSven Anders 
583d83d213dSSven Anders /* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
584d83d213dSSven Anders 
585d83d213dSSven Anders static void atp_complete_geyser_3_4(struct urb *urb)
586d83d213dSSven Anders {
587d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
588d83d213dSSven Anders 	int retval, i, j;
589d83d213dSSven Anders 	int key;
590d83d213dSSven Anders 	struct atp *dev = urb->context;
591d83d213dSSven Anders 	int status = atp_status_check(urb);
592d83d213dSSven Anders 
593d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
594d83d213dSSven Anders 		return;
595d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
596d83d213dSSven Anders 		goto exit;
597d83d213dSSven Anders 
598d83d213dSSven Anders 	/* Reorder the sensors values:
599d83d213dSSven Anders 	 *
600d83d213dSSven Anders 	 * The values are laid out like this:
601d83d213dSSven Anders 	 * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
602d83d213dSSven Anders 	 * '-' is an unused value.
603d83d213dSSven Anders 	 */
604d83d213dSSven Anders 
605d83d213dSSven Anders 	/* read X values */
606d83d213dSSven Anders 	for (i = 0, j = 19; i < 20; i += 2, j += 3) {
607d83d213dSSven Anders 		dev->xy_cur[i] = dev->data[j + 1];
608d83d213dSSven Anders 		dev->xy_cur[i + 1] = dev->data[j + 2];
609d83d213dSSven Anders 	}
610d83d213dSSven Anders 	/* read Y values */
611d83d213dSSven Anders 	for (i = 0, j = 1; i < 9; i += 2, j += 3) {
612d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
613d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
614d83d213dSSven Anders 	}
615d83d213dSSven Anders 
616d83d213dSSven Anders 	dbg_dump("sample", dev->xy_cur);
617d83d213dSSven Anders 
61882a196f4SSven Anders 	/* Just update the base values (i.e. touchpad in untouched state) */
619*09779678SStelian Pop 	if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
620d83d213dSSven Anders 
62182a196f4SSven Anders 		dprintk(KERN_DEBUG "appletouch: updated base values\n");
62282a196f4SSven Anders 
62382a196f4SSven Anders 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
624d83d213dSSven Anders 		goto exit;
625d83d213dSSven Anders 	}
626b5da20f8SDmitry Torokhov 
627b5da20f8SDmitry Torokhov 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
62882a196f4SSven Anders 		/* calculate the change */
62982a196f4SSven Anders 		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
63082a196f4SSven Anders 
63182a196f4SSven Anders 		/* this is a round-robin value, so couple with that */
63282a196f4SSven Anders 		if (dev->xy_acc[i] > 127)
63382a196f4SSven Anders 			dev->xy_acc[i] -= 256;
63482a196f4SSven Anders 
63582a196f4SSven Anders 		if (dev->xy_acc[i] < -127)
63682a196f4SSven Anders 			dev->xy_acc[i] += 256;
637b5da20f8SDmitry Torokhov 
638b5da20f8SDmitry Torokhov 		/* prevent down drifting */
639b5da20f8SDmitry Torokhov 		if (dev->xy_acc[i] < 0)
640b5da20f8SDmitry Torokhov 			dev->xy_acc[i] = 0;
641b5da20f8SDmitry Torokhov 	}
642b5da20f8SDmitry Torokhov 
643b5da20f8SDmitry Torokhov 	dbg_dump("accumulator", dev->xy_acc);
644b5da20f8SDmitry Torokhov 
645b5da20f8SDmitry Torokhov 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
646*09779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
647b5da20f8SDmitry Torokhov 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
648*09779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
649*09779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
650b5da20f8SDmitry Torokhov 
651b5da20f8SDmitry Torokhov 	if (x && y) {
652b5da20f8SDmitry Torokhov 		if (dev->x_old != -1) {
653b5da20f8SDmitry Torokhov 			x = (dev->x_old * 3 + x) >> 2;
654b5da20f8SDmitry Torokhov 			y = (dev->y_old * 3 + y) >> 2;
655b5da20f8SDmitry Torokhov 			dev->x_old = x;
656b5da20f8SDmitry Torokhov 			dev->y_old = y;
657b5da20f8SDmitry Torokhov 
658b5da20f8SDmitry Torokhov 			if (debug > 1)
6592a3e480dSDmitry Torokhov 				printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
660b5da20f8SDmitry Torokhov 				       "Xz: %3d Yz: %3d\n",
661b5da20f8SDmitry Torokhov 				       x, y, x_z, y_z);
662b5da20f8SDmitry Torokhov 
663b5da20f8SDmitry Torokhov 			input_report_key(dev->input, BTN_TOUCH, 1);
664b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_X, x);
665b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_Y, y);
666b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_PRESSURE,
667b5da20f8SDmitry Torokhov 					 min(ATP_PRESSURE, x_z + y_z));
668b5da20f8SDmitry Torokhov 			atp_report_fingers(dev->input, max(x_f, y_f));
669b5da20f8SDmitry Torokhov 		}
670b5da20f8SDmitry Torokhov 		dev->x_old = x;
671b5da20f8SDmitry Torokhov 		dev->y_old = y;
6725a6eb676SSoeren Sonnenburg 
6735a6eb676SSoeren Sonnenburg 	} else if (!x && !y) {
674b5da20f8SDmitry Torokhov 
675b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
676b5da20f8SDmitry Torokhov 		input_report_key(dev->input, BTN_TOUCH, 0);
677b5da20f8SDmitry Torokhov 		input_report_abs(dev->input, ABS_PRESSURE, 0);
678b5da20f8SDmitry Torokhov 		atp_report_fingers(dev->input, 0);
679b5da20f8SDmitry Torokhov 
680b5da20f8SDmitry Torokhov 		/* reset the accumulator on release */
681b5da20f8SDmitry Torokhov 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
682937ad5c1SSoeren Sonnenburg 	}
6835a6eb676SSoeren Sonnenburg 
68446249ea6SAnton Ekblad 	input_report_key(dev->input, BTN_LEFT, key);
68546249ea6SAnton Ekblad 	input_sync(dev->input);
68646249ea6SAnton Ekblad 
6872a3e480dSDmitry Torokhov 	/*
688d83d213dSSven Anders 	 * Geysers 3/4 will continue to send packets continually after
6892a3e480dSDmitry Torokhov 	 * the first touch unless reinitialised. Do so if it's been
6902a3e480dSDmitry Torokhov 	 * idle for a while in order to avoid waking the kernel up
691d83d213dSSven Anders 	 * several hundred times a second.
6922a3e480dSDmitry Torokhov 	 */
693d83d213dSSven Anders 
6947dce869fSJohannes Berg 	/*
6957dce869fSJohannes Berg 	 * Button must not be pressed when entering suspend,
6967dce869fSJohannes Berg 	 * otherwise we will never release the button.
6977dce869fSJohannes Berg 	 */
698937ad5c1SSoeren Sonnenburg 	if (!x && !y && !key) {
6995a6eb676SSoeren Sonnenburg 		dev->idlecount++;
7005a6eb676SSoeren Sonnenburg 		if (dev->idlecount == 10) {
70182a196f4SSven Anders 			dev->x_old = dev->y_old = -1;
70282a196f4SSven Anders 			dev->idlecount = 0;
7035a6eb676SSoeren Sonnenburg 			schedule_work(&dev->work);
7042a3e480dSDmitry Torokhov 			/* Don't resubmit urb here, wait for reinit */
7052a3e480dSDmitry Torokhov 			return;
7065a6eb676SSoeren Sonnenburg 		}
70746249ea6SAnton Ekblad 	} else
708937ad5c1SSoeren Sonnenburg 		dev->idlecount = 0;
709b5da20f8SDmitry Torokhov 
710b5da20f8SDmitry Torokhov  exit:
711b5da20f8SDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
7127dce869fSJohannes Berg 	if (retval)
7137dce869fSJohannes Berg 		err("atp_complete: usb_submit_urb failed with result %d",
7147dce869fSJohannes Berg 		    retval);
715b5da20f8SDmitry Torokhov }
716b5da20f8SDmitry Torokhov 
717b5da20f8SDmitry Torokhov static int atp_open(struct input_dev *input)
718b5da20f8SDmitry Torokhov {
719b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
720b5da20f8SDmitry Torokhov 
721b5da20f8SDmitry Torokhov 	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
722b5da20f8SDmitry Torokhov 		return -EIO;
723b5da20f8SDmitry Torokhov 
724b5da20f8SDmitry Torokhov 	dev->open = 1;
725b5da20f8SDmitry Torokhov 	return 0;
726b5da20f8SDmitry Torokhov }
727b5da20f8SDmitry Torokhov 
728b5da20f8SDmitry Torokhov static void atp_close(struct input_dev *input)
729b5da20f8SDmitry Torokhov {
730b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
731b5da20f8SDmitry Torokhov 
732b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
7335a6eb676SSoeren Sonnenburg 	cancel_work_sync(&dev->work);
734b5da20f8SDmitry Torokhov 	dev->open = 0;
735b5da20f8SDmitry Torokhov }
736b5da20f8SDmitry Torokhov 
73790d95ef6SOliver Neukum static int atp_handle_geyser(struct atp *dev)
73890d95ef6SOliver Neukum {
73990d95ef6SOliver Neukum 	struct usb_device *udev = dev->udev;
74090d95ef6SOliver Neukum 
741*09779678SStelian Pop 	if (dev->info != &fountain_info) {
74290d95ef6SOliver Neukum 		/* switch to raw sensor mode */
74390d95ef6SOliver Neukum 		if (atp_geyser_init(udev))
74490d95ef6SOliver Neukum 			return -EIO;
74590d95ef6SOliver Neukum 
74690d95ef6SOliver Neukum 		printk(KERN_INFO "appletouch: Geyser mode initialized.\n");
74790d95ef6SOliver Neukum 	}
74890d95ef6SOliver Neukum 
74990d95ef6SOliver Neukum 	return 0;
75090d95ef6SOliver Neukum }
75190d95ef6SOliver Neukum 
7527dce869fSJohannes Berg static int atp_probe(struct usb_interface *iface,
7537dce869fSJohannes Berg 		     const struct usb_device_id *id)
754b5da20f8SDmitry Torokhov {
755b5da20f8SDmitry Torokhov 	struct atp *dev;
756b5da20f8SDmitry Torokhov 	struct input_dev *input_dev;
757b5da20f8SDmitry Torokhov 	struct usb_device *udev = interface_to_usbdev(iface);
758b5da20f8SDmitry Torokhov 	struct usb_host_interface *iface_desc;
759b5da20f8SDmitry Torokhov 	struct usb_endpoint_descriptor *endpoint;
760b5da20f8SDmitry Torokhov 	int int_in_endpointAddr = 0;
761b5da20f8SDmitry Torokhov 	int i, error = -ENOMEM;
762*09779678SStelian Pop 	const struct atp_info *info = (const struct atp_info *)id->driver_info;
763b5da20f8SDmitry Torokhov 
764b5da20f8SDmitry Torokhov 	/* set up the endpoint information */
765b5da20f8SDmitry Torokhov 	/* use only the first interrupt-in endpoint */
766b5da20f8SDmitry Torokhov 	iface_desc = iface->cur_altsetting;
767b5da20f8SDmitry Torokhov 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
768b5da20f8SDmitry Torokhov 		endpoint = &iface_desc->endpoint[i].desc;
769b5da20f8SDmitry Torokhov 		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
770b5da20f8SDmitry Torokhov 			/* we found an interrupt in endpoint */
771b5da20f8SDmitry Torokhov 			int_in_endpointAddr = endpoint->bEndpointAddress;
772b5da20f8SDmitry Torokhov 			break;
773b5da20f8SDmitry Torokhov 		}
774b5da20f8SDmitry Torokhov 	}
775b5da20f8SDmitry Torokhov 	if (!int_in_endpointAddr) {
776b5da20f8SDmitry Torokhov 		err("Could not find int-in endpoint");
777b5da20f8SDmitry Torokhov 		return -EIO;
778b5da20f8SDmitry Torokhov 	}
779b5da20f8SDmitry Torokhov 
780b5da20f8SDmitry Torokhov 	/* allocate memory for our device state and initialize it */
781b5da20f8SDmitry Torokhov 	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
782b5da20f8SDmitry Torokhov 	input_dev = input_allocate_device();
783b5da20f8SDmitry Torokhov 	if (!dev || !input_dev) {
784b5da20f8SDmitry Torokhov 		err("Out of memory");
785b5da20f8SDmitry Torokhov 		goto err_free_devs;
786b5da20f8SDmitry Torokhov 	}
787b5da20f8SDmitry Torokhov 
788b5da20f8SDmitry Torokhov 	dev->udev = udev;
789b5da20f8SDmitry Torokhov 	dev->input = input_dev;
790*09779678SStelian Pop 	dev->info = info;
7917dce869fSJohannes Berg 	dev->overflow_warned = false;
792b5da20f8SDmitry Torokhov 
793b5da20f8SDmitry Torokhov 	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
794b5da20f8SDmitry Torokhov 	if (!dev->urb)
795b5da20f8SDmitry Torokhov 		goto err_free_devs;
796b5da20f8SDmitry Torokhov 
797*09779678SStelian Pop 	dev->data = usb_buffer_alloc(dev->udev, dev->info->datalen, GFP_KERNEL,
798b5da20f8SDmitry Torokhov 				     &dev->urb->transfer_dma);
799b5da20f8SDmitry Torokhov 	if (!dev->data)
800b5da20f8SDmitry Torokhov 		goto err_free_urb;
801b5da20f8SDmitry Torokhov 
802b5da20f8SDmitry Torokhov 	usb_fill_int_urb(dev->urb, udev,
803b5da20f8SDmitry Torokhov 			 usb_rcvintpipe(udev, int_in_endpointAddr),
804*09779678SStelian Pop 			 dev->data, dev->info->datalen,
805*09779678SStelian Pop 			 dev->info->callback, dev, 1);
806b5da20f8SDmitry Torokhov 
80790d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
80890d95ef6SOliver Neukum 	if (error)
80990d95ef6SOliver Neukum 		goto err_free_buffer;
81090d95ef6SOliver Neukum 
811b5da20f8SDmitry Torokhov 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
812b5da20f8SDmitry Torokhov 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
813b5da20f8SDmitry Torokhov 
814b5da20f8SDmitry Torokhov 	input_dev->name = "appletouch";
815b5da20f8SDmitry Torokhov 	input_dev->phys = dev->phys;
816b5da20f8SDmitry Torokhov 	usb_to_input_id(dev->udev, &input_dev->id);
817b5da20f8SDmitry Torokhov 	input_dev->dev.parent = &iface->dev;
818b5da20f8SDmitry Torokhov 
819b5da20f8SDmitry Torokhov 	input_set_drvdata(input_dev, dev);
820b5da20f8SDmitry Torokhov 
821b5da20f8SDmitry Torokhov 	input_dev->open = atp_open;
822b5da20f8SDmitry Torokhov 	input_dev->close = atp_close;
823b5da20f8SDmitry Torokhov 
824b5da20f8SDmitry Torokhov 	set_bit(EV_ABS, input_dev->evbit);
825b5da20f8SDmitry Torokhov 
826b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_X, 0,
827*09779678SStelian Pop 			     (dev->info->xsensors - 1) * dev->info->xfact - 1,
8287dce869fSJohannes Berg 			     ATP_FUZZ, 0);
829b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_Y, 0,
830*09779678SStelian Pop 			     (dev->info->ysensors - 1) * dev->info->yfact - 1,
8317dce869fSJohannes Berg 			     ATP_FUZZ, 0);
832b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
833b5da20f8SDmitry Torokhov 
834b5da20f8SDmitry Torokhov 	set_bit(EV_KEY, input_dev->evbit);
835b5da20f8SDmitry Torokhov 	set_bit(BTN_TOUCH, input_dev->keybit);
836b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
837b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
838b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
839b5da20f8SDmitry Torokhov 	set_bit(BTN_LEFT, input_dev->keybit);
840b5da20f8SDmitry Torokhov 
841b5da20f8SDmitry Torokhov 	error = input_register_device(dev->input);
842b5da20f8SDmitry Torokhov 	if (error)
843b5da20f8SDmitry Torokhov 		goto err_free_buffer;
844b5da20f8SDmitry Torokhov 
845b5da20f8SDmitry Torokhov 	/* save our data pointer in this interface device */
846b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, dev);
847b5da20f8SDmitry Torokhov 
8485a6eb676SSoeren Sonnenburg 	INIT_WORK(&dev->work, atp_reinit);
8495a6eb676SSoeren Sonnenburg 
850b5da20f8SDmitry Torokhov 	return 0;
851b5da20f8SDmitry Torokhov 
852b5da20f8SDmitry Torokhov  err_free_buffer:
853*09779678SStelian Pop 	usb_buffer_free(dev->udev, dev->info->datalen,
854b5da20f8SDmitry Torokhov 			dev->data, dev->urb->transfer_dma);
855b5da20f8SDmitry Torokhov  err_free_urb:
856b5da20f8SDmitry Torokhov 	usb_free_urb(dev->urb);
857b5da20f8SDmitry Torokhov  err_free_devs:
858b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
859b5da20f8SDmitry Torokhov 	kfree(dev);
860b5da20f8SDmitry Torokhov 	input_free_device(input_dev);
861b5da20f8SDmitry Torokhov 	return error;
862b5da20f8SDmitry Torokhov }
863b5da20f8SDmitry Torokhov 
864b5da20f8SDmitry Torokhov static void atp_disconnect(struct usb_interface *iface)
865b5da20f8SDmitry Torokhov {
866b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
867b5da20f8SDmitry Torokhov 
868b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
869b5da20f8SDmitry Torokhov 	if (dev) {
870b5da20f8SDmitry Torokhov 		usb_kill_urb(dev->urb);
871b5da20f8SDmitry Torokhov 		input_unregister_device(dev->input);
872*09779678SStelian Pop 		usb_buffer_free(dev->udev, dev->info->datalen,
873b5da20f8SDmitry Torokhov 				dev->data, dev->urb->transfer_dma);
874b5da20f8SDmitry Torokhov 		usb_free_urb(dev->urb);
875b5da20f8SDmitry Torokhov 		kfree(dev);
876b5da20f8SDmitry Torokhov 	}
877b5da20f8SDmitry Torokhov 	printk(KERN_INFO "input: appletouch disconnected\n");
878b5da20f8SDmitry Torokhov }
879b5da20f8SDmitry Torokhov 
88090d95ef6SOliver Neukum static int atp_recover(struct atp *dev)
88190d95ef6SOliver Neukum {
88290d95ef6SOliver Neukum 	int error;
88390d95ef6SOliver Neukum 
88490d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
88590d95ef6SOliver Neukum 	if (error)
88690d95ef6SOliver Neukum 		return error;
88790d95ef6SOliver Neukum 
88890d95ef6SOliver Neukum 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
88990d95ef6SOliver Neukum 		return -EIO;
89090d95ef6SOliver Neukum 
89190d95ef6SOliver Neukum 	return 0;
89290d95ef6SOliver Neukum }
89390d95ef6SOliver Neukum 
894b5da20f8SDmitry Torokhov static int atp_suspend(struct usb_interface *iface, pm_message_t message)
895b5da20f8SDmitry Torokhov {
896b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
8975a6eb676SSoeren Sonnenburg 
898b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
899b5da20f8SDmitry Torokhov 	return 0;
900b5da20f8SDmitry Torokhov }
901b5da20f8SDmitry Torokhov 
902b5da20f8SDmitry Torokhov static int atp_resume(struct usb_interface *iface)
903b5da20f8SDmitry Torokhov {
904b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
9055a6eb676SSoeren Sonnenburg 
906b5da20f8SDmitry Torokhov 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
907b5da20f8SDmitry Torokhov 		return -EIO;
908b5da20f8SDmitry Torokhov 
909b5da20f8SDmitry Torokhov 	return 0;
910b5da20f8SDmitry Torokhov }
911b5da20f8SDmitry Torokhov 
91290d95ef6SOliver Neukum static int atp_reset_resume(struct usb_interface *iface)
91390d95ef6SOliver Neukum {
91490d95ef6SOliver Neukum 	struct atp *dev = usb_get_intfdata(iface);
91590d95ef6SOliver Neukum 
91690d95ef6SOliver Neukum 	return atp_recover(dev);
91790d95ef6SOliver Neukum }
91890d95ef6SOliver Neukum 
919b5da20f8SDmitry Torokhov static struct usb_driver atp_driver = {
920b5da20f8SDmitry Torokhov 	.name		= "appletouch",
921b5da20f8SDmitry Torokhov 	.probe		= atp_probe,
922b5da20f8SDmitry Torokhov 	.disconnect	= atp_disconnect,
923b5da20f8SDmitry Torokhov 	.suspend	= atp_suspend,
924b5da20f8SDmitry Torokhov 	.resume		= atp_resume,
92590d95ef6SOliver Neukum 	.reset_resume	= atp_reset_resume,
926b5da20f8SDmitry Torokhov 	.id_table	= atp_table,
927b5da20f8SDmitry Torokhov };
928b5da20f8SDmitry Torokhov 
929b5da20f8SDmitry Torokhov static int __init atp_init(void)
930b5da20f8SDmitry Torokhov {
931b5da20f8SDmitry Torokhov 	return usb_register(&atp_driver);
932b5da20f8SDmitry Torokhov }
933b5da20f8SDmitry Torokhov 
934b5da20f8SDmitry Torokhov static void __exit atp_exit(void)
935b5da20f8SDmitry Torokhov {
936b5da20f8SDmitry Torokhov 	usb_deregister(&atp_driver);
937b5da20f8SDmitry Torokhov }
938b5da20f8SDmitry Torokhov 
939b5da20f8SDmitry Torokhov module_init(atp_init);
940b5da20f8SDmitry Torokhov module_exit(atp_exit);
941