xref: /openbmc/linux/drivers/input/mouse/appletouch.c (revision 05e882f890038c702a4f15d385135d03cf74ad48)
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)
609779678SStelian 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 
3809779678SStelian Pop /*
3909779678SStelian Pop  * Note: We try to keep the touchpad aspect ratio while still doing only
4009779678SStelian Pop  * simple arithmetics:
4109779678SStelian Pop  *	0 <= x <= (xsensors - 1) * xfact
4209779678SStelian Pop  *	0 <= y <= (ysensors - 1) * yfact
4309779678SStelian Pop  */
4409779678SStelian Pop struct atp_info {
4509779678SStelian Pop 	int xsensors;				/* number of X sensors */
4609779678SStelian Pop 	int xsensors_17;			/* 17" models have more sensors */
4709779678SStelian Pop 	int ysensors;				/* number of Y sensors */
4809779678SStelian Pop 	int xfact;				/* X multiplication factor */
4909779678SStelian Pop 	int yfact;				/* Y multiplication factor */
5009779678SStelian Pop 	int datalen;				/* size of USB transfers */
5109779678SStelian Pop 	void (*callback)(struct urb *);		/* callback function */
52e9542dffSSven Anders };
53b5da20f8SDmitry Torokhov 
5409779678SStelian Pop static void atp_complete_geyser_1_2(struct urb *urb);
5509779678SStelian Pop static void atp_complete_geyser_3_4(struct urb *urb);
5609779678SStelian Pop 
5709779678SStelian Pop static const struct atp_info fountain_info = {
5809779678SStelian Pop 	.xsensors	= 16,
5909779678SStelian Pop 	.xsensors_17	= 26,
6009779678SStelian Pop 	.ysensors	= 16,
6109779678SStelian Pop 	.xfact		= 64,
6209779678SStelian Pop 	.yfact		= 43,
6309779678SStelian Pop 	.datalen	= 81,
6409779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
6509779678SStelian Pop };
6609779678SStelian Pop 
6709779678SStelian Pop static const struct atp_info geyser1_info = {
6809779678SStelian Pop 	.xsensors	= 16,
6909779678SStelian Pop 	.xsensors_17	= 26,
7009779678SStelian Pop 	.ysensors	= 16,
7109779678SStelian Pop 	.xfact		= 64,
7209779678SStelian Pop 	.yfact		= 43,
7309779678SStelian Pop 	.datalen	= 81,
7409779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
7509779678SStelian Pop };
7609779678SStelian Pop 
7709779678SStelian Pop static const struct atp_info geyser2_info = {
7809779678SStelian Pop 	.xsensors	= 15,
7909779678SStelian Pop 	.xsensors_17	= 20,
8009779678SStelian Pop 	.ysensors	= 9,
8109779678SStelian Pop 	.xfact		= 64,
8209779678SStelian Pop 	.yfact		= 43,
8309779678SStelian Pop 	.datalen	= 64,
8409779678SStelian Pop 	.callback	= atp_complete_geyser_1_2,
8509779678SStelian Pop };
8609779678SStelian Pop 
8709779678SStelian Pop static const struct atp_info geyser3_info = {
8809779678SStelian Pop 	.xsensors	= 20,
8909779678SStelian Pop 	.ysensors	= 10,
9009779678SStelian Pop 	.xfact		= 64,
9109779678SStelian Pop 	.yfact		= 64,
9209779678SStelian Pop 	.datalen	= 64,
9309779678SStelian Pop 	.callback	= atp_complete_geyser_3_4,
9409779678SStelian Pop };
9509779678SStelian Pop 
9609779678SStelian Pop static const struct atp_info geyser4_info = {
9709779678SStelian Pop 	.xsensors	= 20,
9809779678SStelian Pop 	.ysensors	= 10,
9909779678SStelian Pop 	.xfact		= 64,
10009779678SStelian Pop 	.yfact		= 64,
10109779678SStelian Pop 	.datalen	= 64,
10209779678SStelian Pop 	.callback	= atp_complete_geyser_3_4,
10309779678SStelian Pop };
10409779678SStelian Pop 
10509779678SStelian 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,				\
11409779678SStelian 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 */
12509779678SStelian Pop 	ATP_DEVICE(0x020e, fountain_info),	/* FOUNTAIN ANSI */
12609779678SStelian Pop 	ATP_DEVICE(0x020f, fountain_info),	/* FOUNTAIN ISO */
12709779678SStelian Pop 	ATP_DEVICE(0x030a, fountain_info),	/* FOUNTAIN TP ONLY */
12809779678SStelian Pop 	ATP_DEVICE(0x030b, geyser1_info),	/* GEYSER 1 TP ONLY */
129b5da20f8SDmitry Torokhov 
130b5da20f8SDmitry Torokhov 	/* PowerBooks Oct 2005 */
13109779678SStelian Pop 	ATP_DEVICE(0x0214, geyser2_info),	/* GEYSER 2 ANSI */
13209779678SStelian Pop 	ATP_DEVICE(0x0215, geyser2_info),	/* GEYSER 2 ISO */
13309779678SStelian Pop 	ATP_DEVICE(0x0216, geyser2_info),	/* GEYSER 2 JIS */
134b5da20f8SDmitry Torokhov 
135b5da20f8SDmitry Torokhov 	/* Core Duo MacBook & MacBook Pro */
13609779678SStelian Pop 	ATP_DEVICE(0x0217, geyser3_info),	/* GEYSER 3 ANSI */
13709779678SStelian Pop 	ATP_DEVICE(0x0218, geyser3_info),	/* GEYSER 3 ISO */
13809779678SStelian Pop 	ATP_DEVICE(0x0219, geyser3_info),	/* GEYSER 3 JIS */
139b5da20f8SDmitry Torokhov 
140b5da20f8SDmitry Torokhov 	/* Core2 Duo MacBook & MacBook Pro */
14109779678SStelian Pop 	ATP_DEVICE(0x021a, geyser4_info),	/* GEYSER 4 ANSI */
14209779678SStelian Pop 	ATP_DEVICE(0x021b, geyser4_info),	/* GEYSER 4 ISO */
14309779678SStelian Pop 	ATP_DEVICE(0x021c, geyser4_info),	/* GEYSER 4 JIS */
144b5da20f8SDmitry Torokhov 
1457dce869fSJohannes Berg 	/* Core2 Duo MacBook3,1 */
14609779678SStelian Pop 	ATP_DEVICE(0x0229, geyser4_info),	/* GEYSER 4 HF ANSI */
14709779678SStelian Pop 	ATP_DEVICE(0x022a, geyser4_info),	/* GEYSER 4 HF ISO */
14809779678SStelian 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 
15509779678SStelian 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 */
20109779678SStelian 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 {
2580385c5eeSBob Copeland 	char *data;
2595a6eb676SSoeren Sonnenburg 	int size;
2607dce869fSJohannes Berg 	int i;
2610385c5eeSBob Copeland 	int ret;
2620385c5eeSBob Copeland 
2630385c5eeSBob Copeland 	data = kmalloc(8, GFP_KERNEL);
2640385c5eeSBob Copeland 	if (!data) {
2650385c5eeSBob Copeland 		err("Out of memory");
2660385c5eeSBob Copeland 		return -ENOMEM;
2670385c5eeSBob Copeland 	}
2685a6eb676SSoeren Sonnenburg 
2695a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
2702a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_READ_REQUEST_ID,
2715a6eb676SSoeren Sonnenburg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2722a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2730385c5eeSBob Copeland 			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
2745a6eb676SSoeren Sonnenburg 
2755a6eb676SSoeren Sonnenburg 	if (size != 8) {
2767dce869fSJohannes Berg 		dprintk("atp_geyser_init: read error\n");
2777dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2787dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
2797dce869fSJohannes Berg 
2807dce869fSJohannes Berg 		err("Failed to read mode from device.");
2810385c5eeSBob Copeland 		ret = -EIO;
2820385c5eeSBob Copeland 		goto out_free;
2835a6eb676SSoeren Sonnenburg 	}
2845a6eb676SSoeren Sonnenburg 
2855a6eb676SSoeren Sonnenburg 	/* Apply the mode switch */
2862a3e480dSDmitry Torokhov 	data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
2875a6eb676SSoeren Sonnenburg 
2885a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
2892a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_WRITE_REQUEST_ID,
2905a6eb676SSoeren Sonnenburg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2912a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2920385c5eeSBob Copeland 			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
2935a6eb676SSoeren Sonnenburg 
2945a6eb676SSoeren Sonnenburg 	if (size != 8) {
2957dce869fSJohannes Berg 		dprintk("atp_geyser_init: write error\n");
2967dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2977dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
2987dce869fSJohannes Berg 
2997dce869fSJohannes Berg 		err("Failed to request geyser raw mode");
3000385c5eeSBob Copeland 		ret = -EIO;
3010385c5eeSBob Copeland 		goto out_free;
3025a6eb676SSoeren Sonnenburg 	}
3030385c5eeSBob Copeland 	ret = 0;
3040385c5eeSBob Copeland out_free:
3050385c5eeSBob Copeland 	kfree(data);
3060385c5eeSBob Copeland 	return ret;
3075a6eb676SSoeren Sonnenburg }
3085a6eb676SSoeren Sonnenburg 
3092a3e480dSDmitry Torokhov /*
3102a3e480dSDmitry Torokhov  * Reinitialise the device. This usually stops stream of empty packets
3112a3e480dSDmitry Torokhov  * coming from it.
3122a3e480dSDmitry Torokhov  */
3135a6eb676SSoeren Sonnenburg static void atp_reinit(struct work_struct *work)
3145a6eb676SSoeren Sonnenburg {
3155a6eb676SSoeren Sonnenburg 	struct atp *dev = container_of(work, struct atp, work);
3165a6eb676SSoeren Sonnenburg 	struct usb_device *udev = dev->udev;
3172a3e480dSDmitry Torokhov 	int retval;
3185a6eb676SSoeren Sonnenburg 
3197dce869fSJohannes Berg 	dprintk("appletouch: putting appletouch to sleep (reinit)\n");
3202a3e480dSDmitry Torokhov 	atp_geyser_init(udev);
3212a3e480dSDmitry Torokhov 
3222a3e480dSDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
3237dce869fSJohannes Berg 	if (retval)
3247dce869fSJohannes Berg 		err("atp_reinit: usb_submit_urb failed with error %d",
3257dce869fSJohannes Berg 		    retval);
3265a6eb676SSoeren Sonnenburg }
3275a6eb676SSoeren Sonnenburg 
328b5da20f8SDmitry Torokhov static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
329b5da20f8SDmitry Torokhov 			     int *z, int *fingers)
330b5da20f8SDmitry Torokhov {
331b5da20f8SDmitry Torokhov 	int i;
332b5da20f8SDmitry Torokhov 	/* values to calculate mean */
333b5da20f8SDmitry Torokhov 	int pcum = 0, psum = 0;
334b5da20f8SDmitry Torokhov 	int is_increasing = 0;
335b5da20f8SDmitry Torokhov 
336b5da20f8SDmitry Torokhov 	*fingers = 0;
337b5da20f8SDmitry Torokhov 
338b5da20f8SDmitry Torokhov 	for (i = 0; i < nb_sensors; i++) {
339b5da20f8SDmitry Torokhov 		if (xy_sensors[i] < threshold) {
340b5da20f8SDmitry Torokhov 			if (is_increasing)
341b5da20f8SDmitry Torokhov 				is_increasing = 0;
342b5da20f8SDmitry Torokhov 
343b5da20f8SDmitry Torokhov 			continue;
344b5da20f8SDmitry Torokhov 		}
345b5da20f8SDmitry Torokhov 
346b5da20f8SDmitry Torokhov 		/*
347b5da20f8SDmitry Torokhov 		 * Makes the finger detection more versatile.  For example,
348b5da20f8SDmitry Torokhov 		 * two fingers with no gap will be detected.  Also, my
349b5da20f8SDmitry Torokhov 		 * tests show it less likely to have intermittent loss
350b5da20f8SDmitry Torokhov 		 * of multiple finger readings while moving around (scrolling).
351b5da20f8SDmitry Torokhov 		 *
352b5da20f8SDmitry Torokhov 		 * Changes the multiple finger detection to counting humps on
353b5da20f8SDmitry Torokhov 		 * sensors (transitions from nonincreasing to increasing)
354b5da20f8SDmitry Torokhov 		 * instead of counting transitions from low sensors (no
355b5da20f8SDmitry Torokhov 		 * finger reading) to high sensors (finger above
356b5da20f8SDmitry Torokhov 		 * sensor)
357b5da20f8SDmitry Torokhov 		 *
358b5da20f8SDmitry Torokhov 		 * - Jason Parekh <jasonparekh@gmail.com>
359b5da20f8SDmitry Torokhov 		 */
3607dce869fSJohannes Berg 		if (i < 1 ||
3617dce869fSJohannes Berg 		    (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
362b5da20f8SDmitry Torokhov 			(*fingers)++;
363b5da20f8SDmitry Torokhov 			is_increasing = 1;
364*05e882f8SJeremy Huddleston 		} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
365b5da20f8SDmitry Torokhov 			is_increasing = 0;
366b5da20f8SDmitry Torokhov 		}
367b5da20f8SDmitry Torokhov 
368b5da20f8SDmitry Torokhov 		/*
3697dce869fSJohannes Berg 		 * Subtracts threshold so a high sensor that just passes the
3707dce869fSJohannes Berg 		 * threshold won't skew the calculated absolute coordinate.
3717dce869fSJohannes Berg 		 * Fixes an issue where slowly moving the mouse would
3727dce869fSJohannes Berg 		 * occasionally jump a number of pixels (slowly moving the
3737dce869fSJohannes Berg 		 * finger makes this issue most apparent.)
374b5da20f8SDmitry Torokhov 		 */
375b5da20f8SDmitry Torokhov 		pcum += (xy_sensors[i] - threshold) * i;
376b5da20f8SDmitry Torokhov 		psum += (xy_sensors[i] - threshold);
377b5da20f8SDmitry Torokhov 	}
378b5da20f8SDmitry Torokhov 
379b5da20f8SDmitry Torokhov 	if (psum > 0) {
380b5da20f8SDmitry Torokhov 		*z = psum;
381b5da20f8SDmitry Torokhov 		return pcum * fact / psum;
382b5da20f8SDmitry Torokhov 	}
383b5da20f8SDmitry Torokhov 
384b5da20f8SDmitry Torokhov 	return 0;
385b5da20f8SDmitry Torokhov }
386b5da20f8SDmitry Torokhov 
387b5da20f8SDmitry Torokhov static inline void atp_report_fingers(struct input_dev *input, int fingers)
388b5da20f8SDmitry Torokhov {
389b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
390b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
391b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
392b5da20f8SDmitry Torokhov }
393b5da20f8SDmitry Torokhov 
394d83d213dSSven Anders /* Check URB status and for correct length of data package */
395d83d213dSSven Anders 
396d83d213dSSven Anders #define ATP_URB_STATUS_SUCCESS		0
397d83d213dSSven Anders #define ATP_URB_STATUS_ERROR		1
398d83d213dSSven Anders #define ATP_URB_STATUS_ERROR_FATAL	2
399d83d213dSSven Anders 
400d83d213dSSven Anders static int atp_status_check(struct urb *urb)
401b5da20f8SDmitry Torokhov {
402b5da20f8SDmitry Torokhov 	struct atp *dev = urb->context;
403b5da20f8SDmitry Torokhov 
404b5da20f8SDmitry Torokhov 	switch (urb->status) {
405b5da20f8SDmitry Torokhov 	case 0:
406b5da20f8SDmitry Torokhov 		/* success */
407b5da20f8SDmitry Torokhov 		break;
408b5da20f8SDmitry Torokhov 	case -EOVERFLOW:
4097dce869fSJohannes Berg 		if (!dev->overflow_warned) {
4102a3e480dSDmitry Torokhov 			printk(KERN_WARNING "appletouch: OVERFLOW with data "
411b5da20f8SDmitry Torokhov 				"length %d, actual length is %d\n",
41209779678SStelian Pop 				dev->info->datalen, dev->urb->actual_length);
4137dce869fSJohannes Berg 			dev->overflow_warned = true;
414b5da20f8SDmitry Torokhov 		}
415b5da20f8SDmitry Torokhov 	case -ECONNRESET:
416b5da20f8SDmitry Torokhov 	case -ENOENT:
417b5da20f8SDmitry Torokhov 	case -ESHUTDOWN:
418b5da20f8SDmitry Torokhov 		/* This urb is terminated, clean up */
4197dce869fSJohannes Berg 		dbg("atp_complete: urb shutting down with status: %d",
4207dce869fSJohannes Berg 		    urb->status);
421d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR_FATAL;
422d83d213dSSven Anders 
423b5da20f8SDmitry Torokhov 	default:
4247dce869fSJohannes Berg 		dbg("atp_complete: nonzero urb status received: %d",
4257dce869fSJohannes Berg 		    urb->status);
426d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
427b5da20f8SDmitry Torokhov 	}
428b5da20f8SDmitry Torokhov 
429b5da20f8SDmitry Torokhov 	/* drop incomplete datasets */
43009779678SStelian Pop 	if (dev->urb->actual_length != dev->info->datalen) {
431b5da20f8SDmitry Torokhov 		dprintk("appletouch: incomplete data package"
432b5da20f8SDmitry Torokhov 			" (first byte: %d, length: %d).\n",
433b5da20f8SDmitry Torokhov 			dev->data[0], dev->urb->actual_length);
434d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
435b5da20f8SDmitry Torokhov 	}
436b5da20f8SDmitry Torokhov 
437d83d213dSSven Anders 	return ATP_URB_STATUS_SUCCESS;
438d83d213dSSven Anders }
439b5da20f8SDmitry Torokhov 
44009779678SStelian Pop static void atp_detect_size(struct atp *dev)
44109779678SStelian Pop {
44209779678SStelian Pop 	int i;
44309779678SStelian Pop 
44409779678SStelian Pop 	/* 17" Powerbooks have extra X sensors */
44509779678SStelian Pop 	for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
44609779678SStelian Pop 		if (dev->xy_cur[i]) {
44709779678SStelian Pop 
44809779678SStelian Pop 			printk(KERN_INFO "appletouch: 17\" model detected.\n");
44909779678SStelian Pop 
45009779678SStelian Pop 			input_set_abs_params(dev->input, ABS_X, 0,
45109779678SStelian Pop 					     (dev->info->xsensors_17 - 1) *
45209779678SStelian Pop 							dev->info->xfact - 1,
45309779678SStelian Pop 					     ATP_FUZZ, 0);
45409779678SStelian Pop 			break;
45509779678SStelian Pop 		}
45609779678SStelian Pop 	}
45709779678SStelian Pop }
45809779678SStelian Pop 
459b5da20f8SDmitry Torokhov /*
460d83d213dSSven Anders  * USB interrupt callback functions
461b5da20f8SDmitry Torokhov  */
462b5da20f8SDmitry Torokhov 
463d83d213dSSven Anders /* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
464d83d213dSSven Anders 
465d83d213dSSven Anders static void atp_complete_geyser_1_2(struct urb *urb)
466d83d213dSSven Anders {
467d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
468d83d213dSSven Anders 	int retval, i, j;
469d83d213dSSven Anders 	int key;
470d83d213dSSven Anders 	struct atp *dev = urb->context;
471d83d213dSSven Anders 	int status = atp_status_check(urb);
472d83d213dSSven Anders 
473d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
474d83d213dSSven Anders 		return;
475d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
476d83d213dSSven Anders 		goto exit;
477d83d213dSSven Anders 
478d83d213dSSven Anders 	/* reorder the sensors values */
47909779678SStelian Pop 	if (dev->info == &geyser2_info) {
480b5da20f8SDmitry Torokhov 		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
481b5da20f8SDmitry Torokhov 
482b5da20f8SDmitry Torokhov 		/*
483b5da20f8SDmitry Torokhov 		 * The values are laid out like this:
484b5da20f8SDmitry Torokhov 		 * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
485b5da20f8SDmitry Torokhov 		 * '-' is an unused value.
486b5da20f8SDmitry Torokhov 		 */
487b5da20f8SDmitry Torokhov 
488b5da20f8SDmitry Torokhov 		/* read X values */
489b5da20f8SDmitry Torokhov 		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
490b5da20f8SDmitry Torokhov 			dev->xy_cur[i] = dev->data[j];
491b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 1] = dev->data[j + 1];
492b5da20f8SDmitry Torokhov 		}
493b5da20f8SDmitry Torokhov 
494b5da20f8SDmitry Torokhov 		/* read Y values */
495b5da20f8SDmitry Torokhov 		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
496b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
497b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
498b5da20f8SDmitry Torokhov 		}
499b5da20f8SDmitry Torokhov 	} else {
500b5da20f8SDmitry Torokhov 		for (i = 0; i < 8; i++) {
501b5da20f8SDmitry Torokhov 			/* X values */
5027dce869fSJohannes Berg 			dev->xy_cur[i +  0] = dev->data[5 * i +  2];
503b5da20f8SDmitry Torokhov 			dev->xy_cur[i +  8] = dev->data[5 * i +  4];
504b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 16] = dev->data[5 * i + 42];
505b5da20f8SDmitry Torokhov 			if (i < 2)
506b5da20f8SDmitry Torokhov 				dev->xy_cur[i + 24] = dev->data[5 * i + 44];
507b5da20f8SDmitry Torokhov 
508b5da20f8SDmitry Torokhov 			/* Y values */
50909779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i +  1];
51009779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
511b5da20f8SDmitry Torokhov 		}
512b5da20f8SDmitry Torokhov 	}
513b5da20f8SDmitry Torokhov 
514b5da20f8SDmitry Torokhov 	dbg_dump("sample", dev->xy_cur);
515b5da20f8SDmitry Torokhov 
516b5da20f8SDmitry Torokhov 	if (!dev->valid) {
517b5da20f8SDmitry Torokhov 		/* first sample */
5187dce869fSJohannes Berg 		dev->valid = true;
519b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
520d83d213dSSven Anders 
521d83d213dSSven Anders 		/* Store first sample */
522b5da20f8SDmitry Torokhov 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
523b5da20f8SDmitry Torokhov 
524d83d213dSSven Anders 		/* Perform size detection, if not done already */
52509779678SStelian Pop 		if (unlikely(!dev->size_detect_done)) {
52609779678SStelian Pop 			atp_detect_size(dev);
5272a3e480dSDmitry Torokhov 			dev->size_detect_done = 1;
528b5da20f8SDmitry Torokhov 			goto exit;
529b5da20f8SDmitry Torokhov 		}
530d83d213dSSven Anders 	}
531d83d213dSSven Anders 
532d83d213dSSven Anders 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
533d83d213dSSven Anders 		/* accumulate the change */
534d83d213dSSven Anders 		signed char change = dev->xy_old[i] - dev->xy_cur[i];
535d83d213dSSven Anders 		dev->xy_acc[i] -= change;
536d83d213dSSven Anders 
537d83d213dSSven Anders 		/* prevent down drifting */
538d83d213dSSven Anders 		if (dev->xy_acc[i] < 0)
539d83d213dSSven Anders 			dev->xy_acc[i] = 0;
540d83d213dSSven Anders 	}
541d83d213dSSven Anders 
542d83d213dSSven Anders 	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
543d83d213dSSven Anders 
544d83d213dSSven Anders 	dbg_dump("accumulator", dev->xy_acc);
545d83d213dSSven Anders 
546d83d213dSSven Anders 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
54709779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
548d83d213dSSven Anders 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
54909779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
55009779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
551d83d213dSSven Anders 
552d83d213dSSven Anders 	if (x && y) {
553d83d213dSSven Anders 		if (dev->x_old != -1) {
554d83d213dSSven Anders 			x = (dev->x_old * 3 + x) >> 2;
555d83d213dSSven Anders 			y = (dev->y_old * 3 + y) >> 2;
556d83d213dSSven Anders 			dev->x_old = x;
557d83d213dSSven Anders 			dev->y_old = y;
558d83d213dSSven Anders 
559d83d213dSSven Anders 			if (debug > 1)
560d83d213dSSven Anders 				printk(KERN_DEBUG "appletouch: "
561d83d213dSSven Anders 					"X: %3d Y: %3d Xz: %3d Yz: %3d\n",
562d83d213dSSven Anders 					x, y, x_z, y_z);
563d83d213dSSven Anders 
564d83d213dSSven Anders 			input_report_key(dev->input, BTN_TOUCH, 1);
565d83d213dSSven Anders 			input_report_abs(dev->input, ABS_X, x);
566d83d213dSSven Anders 			input_report_abs(dev->input, ABS_Y, y);
567d83d213dSSven Anders 			input_report_abs(dev->input, ABS_PRESSURE,
568d83d213dSSven Anders 					 min(ATP_PRESSURE, x_z + y_z));
569d83d213dSSven Anders 			atp_report_fingers(dev->input, max(x_f, y_f));
570d83d213dSSven Anders 		}
571d83d213dSSven Anders 		dev->x_old = x;
572d83d213dSSven Anders 		dev->y_old = y;
573d83d213dSSven Anders 
574d83d213dSSven Anders 	} else if (!x && !y) {
575d83d213dSSven Anders 
576d83d213dSSven Anders 		dev->x_old = dev->y_old = -1;
577d83d213dSSven Anders 		input_report_key(dev->input, BTN_TOUCH, 0);
578d83d213dSSven Anders 		input_report_abs(dev->input, ABS_PRESSURE, 0);
579d83d213dSSven Anders 		atp_report_fingers(dev->input, 0);
580d83d213dSSven Anders 
581d83d213dSSven Anders 		/* reset the accumulator on release */
582d83d213dSSven Anders 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
583d83d213dSSven Anders 	}
584d83d213dSSven Anders 
585d83d213dSSven Anders 	input_report_key(dev->input, BTN_LEFT, key);
586d83d213dSSven Anders 	input_sync(dev->input);
587d83d213dSSven Anders 
588d83d213dSSven Anders  exit:
589d83d213dSSven Anders 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
590d83d213dSSven Anders 	if (retval)
591d83d213dSSven Anders 		err("atp_complete: usb_submit_urb failed with result %d",
592d83d213dSSven Anders 		    retval);
593d83d213dSSven Anders }
594d83d213dSSven Anders 
595d83d213dSSven Anders /* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
596d83d213dSSven Anders 
597d83d213dSSven Anders static void atp_complete_geyser_3_4(struct urb *urb)
598d83d213dSSven Anders {
599d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
600d83d213dSSven Anders 	int retval, i, j;
601d83d213dSSven Anders 	int key;
602d83d213dSSven Anders 	struct atp *dev = urb->context;
603d83d213dSSven Anders 	int status = atp_status_check(urb);
604d83d213dSSven Anders 
605d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
606d83d213dSSven Anders 		return;
607d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
608d83d213dSSven Anders 		goto exit;
609d83d213dSSven Anders 
610d83d213dSSven Anders 	/* Reorder the sensors values:
611d83d213dSSven Anders 	 *
612d83d213dSSven Anders 	 * The values are laid out like this:
613d83d213dSSven Anders 	 * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
614d83d213dSSven Anders 	 * '-' is an unused value.
615d83d213dSSven Anders 	 */
616d83d213dSSven Anders 
617d83d213dSSven Anders 	/* read X values */
618d83d213dSSven Anders 	for (i = 0, j = 19; i < 20; i += 2, j += 3) {
619d83d213dSSven Anders 		dev->xy_cur[i] = dev->data[j + 1];
620d83d213dSSven Anders 		dev->xy_cur[i + 1] = dev->data[j + 2];
621d83d213dSSven Anders 	}
622d83d213dSSven Anders 	/* read Y values */
623d83d213dSSven Anders 	for (i = 0, j = 1; i < 9; i += 2, j += 3) {
624d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
625d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
626d83d213dSSven Anders 	}
627d83d213dSSven Anders 
628d83d213dSSven Anders 	dbg_dump("sample", dev->xy_cur);
629d83d213dSSven Anders 
63082a196f4SSven Anders 	/* Just update the base values (i.e. touchpad in untouched state) */
63109779678SStelian Pop 	if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
632d83d213dSSven Anders 
63382a196f4SSven Anders 		dprintk(KERN_DEBUG "appletouch: updated base values\n");
63482a196f4SSven Anders 
63582a196f4SSven Anders 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
636d83d213dSSven Anders 		goto exit;
637d83d213dSSven Anders 	}
638b5da20f8SDmitry Torokhov 
639b5da20f8SDmitry Torokhov 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
64082a196f4SSven Anders 		/* calculate the change */
64182a196f4SSven Anders 		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
64282a196f4SSven Anders 
64382a196f4SSven Anders 		/* this is a round-robin value, so couple with that */
64482a196f4SSven Anders 		if (dev->xy_acc[i] > 127)
64582a196f4SSven Anders 			dev->xy_acc[i] -= 256;
64682a196f4SSven Anders 
64782a196f4SSven Anders 		if (dev->xy_acc[i] < -127)
64882a196f4SSven Anders 			dev->xy_acc[i] += 256;
649b5da20f8SDmitry Torokhov 
650b5da20f8SDmitry Torokhov 		/* prevent down drifting */
651b5da20f8SDmitry Torokhov 		if (dev->xy_acc[i] < 0)
652b5da20f8SDmitry Torokhov 			dev->xy_acc[i] = 0;
653b5da20f8SDmitry Torokhov 	}
654b5da20f8SDmitry Torokhov 
655b5da20f8SDmitry Torokhov 	dbg_dump("accumulator", dev->xy_acc);
656b5da20f8SDmitry Torokhov 
657b5da20f8SDmitry Torokhov 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
65809779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
659b5da20f8SDmitry Torokhov 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
66009779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
66109779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
662b5da20f8SDmitry Torokhov 
663b5da20f8SDmitry Torokhov 	if (x && y) {
664b5da20f8SDmitry Torokhov 		if (dev->x_old != -1) {
665b5da20f8SDmitry Torokhov 			x = (dev->x_old * 3 + x) >> 2;
666b5da20f8SDmitry Torokhov 			y = (dev->y_old * 3 + y) >> 2;
667b5da20f8SDmitry Torokhov 			dev->x_old = x;
668b5da20f8SDmitry Torokhov 			dev->y_old = y;
669b5da20f8SDmitry Torokhov 
670b5da20f8SDmitry Torokhov 			if (debug > 1)
6712a3e480dSDmitry Torokhov 				printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
672b5da20f8SDmitry Torokhov 				       "Xz: %3d Yz: %3d\n",
673b5da20f8SDmitry Torokhov 				       x, y, x_z, y_z);
674b5da20f8SDmitry Torokhov 
675b5da20f8SDmitry Torokhov 			input_report_key(dev->input, BTN_TOUCH, 1);
676b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_X, x);
677b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_Y, y);
678b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_PRESSURE,
679b5da20f8SDmitry Torokhov 					 min(ATP_PRESSURE, x_z + y_z));
680b5da20f8SDmitry Torokhov 			atp_report_fingers(dev->input, max(x_f, y_f));
681b5da20f8SDmitry Torokhov 		}
682b5da20f8SDmitry Torokhov 		dev->x_old = x;
683b5da20f8SDmitry Torokhov 		dev->y_old = y;
6845a6eb676SSoeren Sonnenburg 
6855a6eb676SSoeren Sonnenburg 	} else if (!x && !y) {
686b5da20f8SDmitry Torokhov 
687b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
688b5da20f8SDmitry Torokhov 		input_report_key(dev->input, BTN_TOUCH, 0);
689b5da20f8SDmitry Torokhov 		input_report_abs(dev->input, ABS_PRESSURE, 0);
690b5da20f8SDmitry Torokhov 		atp_report_fingers(dev->input, 0);
691b5da20f8SDmitry Torokhov 
692b5da20f8SDmitry Torokhov 		/* reset the accumulator on release */
693b5da20f8SDmitry Torokhov 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
694937ad5c1SSoeren Sonnenburg 	}
6955a6eb676SSoeren Sonnenburg 
69646249ea6SAnton Ekblad 	input_report_key(dev->input, BTN_LEFT, key);
69746249ea6SAnton Ekblad 	input_sync(dev->input);
69846249ea6SAnton Ekblad 
6992a3e480dSDmitry Torokhov 	/*
700d83d213dSSven Anders 	 * Geysers 3/4 will continue to send packets continually after
7012a3e480dSDmitry Torokhov 	 * the first touch unless reinitialised. Do so if it's been
7022a3e480dSDmitry Torokhov 	 * idle for a while in order to avoid waking the kernel up
703d83d213dSSven Anders 	 * several hundred times a second.
7042a3e480dSDmitry Torokhov 	 */
705d83d213dSSven Anders 
7067dce869fSJohannes Berg 	/*
7077dce869fSJohannes Berg 	 * Button must not be pressed when entering suspend,
7087dce869fSJohannes Berg 	 * otherwise we will never release the button.
7097dce869fSJohannes Berg 	 */
710937ad5c1SSoeren Sonnenburg 	if (!x && !y && !key) {
7115a6eb676SSoeren Sonnenburg 		dev->idlecount++;
7125a6eb676SSoeren Sonnenburg 		if (dev->idlecount == 10) {
71382a196f4SSven Anders 			dev->x_old = dev->y_old = -1;
71482a196f4SSven Anders 			dev->idlecount = 0;
7155a6eb676SSoeren Sonnenburg 			schedule_work(&dev->work);
7162a3e480dSDmitry Torokhov 			/* Don't resubmit urb here, wait for reinit */
7172a3e480dSDmitry Torokhov 			return;
7185a6eb676SSoeren Sonnenburg 		}
71946249ea6SAnton Ekblad 	} else
720937ad5c1SSoeren Sonnenburg 		dev->idlecount = 0;
721b5da20f8SDmitry Torokhov 
722b5da20f8SDmitry Torokhov  exit:
723b5da20f8SDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
7247dce869fSJohannes Berg 	if (retval)
7257dce869fSJohannes Berg 		err("atp_complete: usb_submit_urb failed with result %d",
7267dce869fSJohannes Berg 		    retval);
727b5da20f8SDmitry Torokhov }
728b5da20f8SDmitry Torokhov 
729b5da20f8SDmitry Torokhov static int atp_open(struct input_dev *input)
730b5da20f8SDmitry Torokhov {
731b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
732b5da20f8SDmitry Torokhov 
733b5da20f8SDmitry Torokhov 	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
734b5da20f8SDmitry Torokhov 		return -EIO;
735b5da20f8SDmitry Torokhov 
736b5da20f8SDmitry Torokhov 	dev->open = 1;
737b5da20f8SDmitry Torokhov 	return 0;
738b5da20f8SDmitry Torokhov }
739b5da20f8SDmitry Torokhov 
740b5da20f8SDmitry Torokhov static void atp_close(struct input_dev *input)
741b5da20f8SDmitry Torokhov {
742b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
743b5da20f8SDmitry Torokhov 
744b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
7455a6eb676SSoeren Sonnenburg 	cancel_work_sync(&dev->work);
746b5da20f8SDmitry Torokhov 	dev->open = 0;
747b5da20f8SDmitry Torokhov }
748b5da20f8SDmitry Torokhov 
74990d95ef6SOliver Neukum static int atp_handle_geyser(struct atp *dev)
75090d95ef6SOliver Neukum {
75190d95ef6SOliver Neukum 	struct usb_device *udev = dev->udev;
75290d95ef6SOliver Neukum 
75309779678SStelian Pop 	if (dev->info != &fountain_info) {
75490d95ef6SOliver Neukum 		/* switch to raw sensor mode */
75590d95ef6SOliver Neukum 		if (atp_geyser_init(udev))
75690d95ef6SOliver Neukum 			return -EIO;
75790d95ef6SOliver Neukum 
75890d95ef6SOliver Neukum 		printk(KERN_INFO "appletouch: Geyser mode initialized.\n");
75990d95ef6SOliver Neukum 	}
76090d95ef6SOliver Neukum 
76190d95ef6SOliver Neukum 	return 0;
76290d95ef6SOliver Neukum }
76390d95ef6SOliver Neukum 
7647dce869fSJohannes Berg static int atp_probe(struct usb_interface *iface,
7657dce869fSJohannes Berg 		     const struct usb_device_id *id)
766b5da20f8SDmitry Torokhov {
767b5da20f8SDmitry Torokhov 	struct atp *dev;
768b5da20f8SDmitry Torokhov 	struct input_dev *input_dev;
769b5da20f8SDmitry Torokhov 	struct usb_device *udev = interface_to_usbdev(iface);
770b5da20f8SDmitry Torokhov 	struct usb_host_interface *iface_desc;
771b5da20f8SDmitry Torokhov 	struct usb_endpoint_descriptor *endpoint;
772b5da20f8SDmitry Torokhov 	int int_in_endpointAddr = 0;
773b5da20f8SDmitry Torokhov 	int i, error = -ENOMEM;
77409779678SStelian Pop 	const struct atp_info *info = (const struct atp_info *)id->driver_info;
775b5da20f8SDmitry Torokhov 
776b5da20f8SDmitry Torokhov 	/* set up the endpoint information */
777b5da20f8SDmitry Torokhov 	/* use only the first interrupt-in endpoint */
778b5da20f8SDmitry Torokhov 	iface_desc = iface->cur_altsetting;
779b5da20f8SDmitry Torokhov 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
780b5da20f8SDmitry Torokhov 		endpoint = &iface_desc->endpoint[i].desc;
781b5da20f8SDmitry Torokhov 		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
782b5da20f8SDmitry Torokhov 			/* we found an interrupt in endpoint */
783b5da20f8SDmitry Torokhov 			int_in_endpointAddr = endpoint->bEndpointAddress;
784b5da20f8SDmitry Torokhov 			break;
785b5da20f8SDmitry Torokhov 		}
786b5da20f8SDmitry Torokhov 	}
787b5da20f8SDmitry Torokhov 	if (!int_in_endpointAddr) {
788b5da20f8SDmitry Torokhov 		err("Could not find int-in endpoint");
789b5da20f8SDmitry Torokhov 		return -EIO;
790b5da20f8SDmitry Torokhov 	}
791b5da20f8SDmitry Torokhov 
792b5da20f8SDmitry Torokhov 	/* allocate memory for our device state and initialize it */
793b5da20f8SDmitry Torokhov 	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
794b5da20f8SDmitry Torokhov 	input_dev = input_allocate_device();
795b5da20f8SDmitry Torokhov 	if (!dev || !input_dev) {
796b5da20f8SDmitry Torokhov 		err("Out of memory");
797b5da20f8SDmitry Torokhov 		goto err_free_devs;
798b5da20f8SDmitry Torokhov 	}
799b5da20f8SDmitry Torokhov 
800b5da20f8SDmitry Torokhov 	dev->udev = udev;
801b5da20f8SDmitry Torokhov 	dev->input = input_dev;
80209779678SStelian Pop 	dev->info = info;
8037dce869fSJohannes Berg 	dev->overflow_warned = false;
804b5da20f8SDmitry Torokhov 
805b5da20f8SDmitry Torokhov 	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
806b5da20f8SDmitry Torokhov 	if (!dev->urb)
807b5da20f8SDmitry Torokhov 		goto err_free_devs;
808b5da20f8SDmitry Torokhov 
80909779678SStelian Pop 	dev->data = usb_buffer_alloc(dev->udev, dev->info->datalen, GFP_KERNEL,
810b5da20f8SDmitry Torokhov 				     &dev->urb->transfer_dma);
811b5da20f8SDmitry Torokhov 	if (!dev->data)
812b5da20f8SDmitry Torokhov 		goto err_free_urb;
813b5da20f8SDmitry Torokhov 
814b5da20f8SDmitry Torokhov 	usb_fill_int_urb(dev->urb, udev,
815b5da20f8SDmitry Torokhov 			 usb_rcvintpipe(udev, int_in_endpointAddr),
81609779678SStelian Pop 			 dev->data, dev->info->datalen,
81709779678SStelian Pop 			 dev->info->callback, dev, 1);
818b5da20f8SDmitry Torokhov 
81990d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
82090d95ef6SOliver Neukum 	if (error)
82190d95ef6SOliver Neukum 		goto err_free_buffer;
82290d95ef6SOliver Neukum 
823b5da20f8SDmitry Torokhov 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
824b5da20f8SDmitry Torokhov 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
825b5da20f8SDmitry Torokhov 
826b5da20f8SDmitry Torokhov 	input_dev->name = "appletouch";
827b5da20f8SDmitry Torokhov 	input_dev->phys = dev->phys;
828b5da20f8SDmitry Torokhov 	usb_to_input_id(dev->udev, &input_dev->id);
829b5da20f8SDmitry Torokhov 	input_dev->dev.parent = &iface->dev;
830b5da20f8SDmitry Torokhov 
831b5da20f8SDmitry Torokhov 	input_set_drvdata(input_dev, dev);
832b5da20f8SDmitry Torokhov 
833b5da20f8SDmitry Torokhov 	input_dev->open = atp_open;
834b5da20f8SDmitry Torokhov 	input_dev->close = atp_close;
835b5da20f8SDmitry Torokhov 
836b5da20f8SDmitry Torokhov 	set_bit(EV_ABS, input_dev->evbit);
837b5da20f8SDmitry Torokhov 
838b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_X, 0,
83909779678SStelian Pop 			     (dev->info->xsensors - 1) * dev->info->xfact - 1,
8407dce869fSJohannes Berg 			     ATP_FUZZ, 0);
841b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_Y, 0,
84209779678SStelian Pop 			     (dev->info->ysensors - 1) * dev->info->yfact - 1,
8437dce869fSJohannes Berg 			     ATP_FUZZ, 0);
844b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
845b5da20f8SDmitry Torokhov 
846b5da20f8SDmitry Torokhov 	set_bit(EV_KEY, input_dev->evbit);
847b5da20f8SDmitry Torokhov 	set_bit(BTN_TOUCH, input_dev->keybit);
848b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
849b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
850b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
851b5da20f8SDmitry Torokhov 	set_bit(BTN_LEFT, input_dev->keybit);
852b5da20f8SDmitry Torokhov 
853b5da20f8SDmitry Torokhov 	error = input_register_device(dev->input);
854b5da20f8SDmitry Torokhov 	if (error)
855b5da20f8SDmitry Torokhov 		goto err_free_buffer;
856b5da20f8SDmitry Torokhov 
857b5da20f8SDmitry Torokhov 	/* save our data pointer in this interface device */
858b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, dev);
859b5da20f8SDmitry Torokhov 
8605a6eb676SSoeren Sonnenburg 	INIT_WORK(&dev->work, atp_reinit);
8615a6eb676SSoeren Sonnenburg 
862b5da20f8SDmitry Torokhov 	return 0;
863b5da20f8SDmitry Torokhov 
864b5da20f8SDmitry Torokhov  err_free_buffer:
86509779678SStelian Pop 	usb_buffer_free(dev->udev, dev->info->datalen,
866b5da20f8SDmitry Torokhov 			dev->data, dev->urb->transfer_dma);
867b5da20f8SDmitry Torokhov  err_free_urb:
868b5da20f8SDmitry Torokhov 	usb_free_urb(dev->urb);
869b5da20f8SDmitry Torokhov  err_free_devs:
870b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
871b5da20f8SDmitry Torokhov 	kfree(dev);
872b5da20f8SDmitry Torokhov 	input_free_device(input_dev);
873b5da20f8SDmitry Torokhov 	return error;
874b5da20f8SDmitry Torokhov }
875b5da20f8SDmitry Torokhov 
876b5da20f8SDmitry Torokhov static void atp_disconnect(struct usb_interface *iface)
877b5da20f8SDmitry Torokhov {
878b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
879b5da20f8SDmitry Torokhov 
880b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
881b5da20f8SDmitry Torokhov 	if (dev) {
882b5da20f8SDmitry Torokhov 		usb_kill_urb(dev->urb);
883b5da20f8SDmitry Torokhov 		input_unregister_device(dev->input);
88409779678SStelian Pop 		usb_buffer_free(dev->udev, dev->info->datalen,
885b5da20f8SDmitry Torokhov 				dev->data, dev->urb->transfer_dma);
886b5da20f8SDmitry Torokhov 		usb_free_urb(dev->urb);
887b5da20f8SDmitry Torokhov 		kfree(dev);
888b5da20f8SDmitry Torokhov 	}
889b5da20f8SDmitry Torokhov 	printk(KERN_INFO "input: appletouch disconnected\n");
890b5da20f8SDmitry Torokhov }
891b5da20f8SDmitry Torokhov 
89290d95ef6SOliver Neukum static int atp_recover(struct atp *dev)
89390d95ef6SOliver Neukum {
89490d95ef6SOliver Neukum 	int error;
89590d95ef6SOliver Neukum 
89690d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
89790d95ef6SOliver Neukum 	if (error)
89890d95ef6SOliver Neukum 		return error;
89990d95ef6SOliver Neukum 
90090d95ef6SOliver Neukum 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
90190d95ef6SOliver Neukum 		return -EIO;
90290d95ef6SOliver Neukum 
90390d95ef6SOliver Neukum 	return 0;
90490d95ef6SOliver Neukum }
90590d95ef6SOliver Neukum 
906b5da20f8SDmitry Torokhov static int atp_suspend(struct usb_interface *iface, pm_message_t message)
907b5da20f8SDmitry Torokhov {
908b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
9095a6eb676SSoeren Sonnenburg 
910b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
911b5da20f8SDmitry Torokhov 	return 0;
912b5da20f8SDmitry Torokhov }
913b5da20f8SDmitry Torokhov 
914b5da20f8SDmitry Torokhov static int atp_resume(struct usb_interface *iface)
915b5da20f8SDmitry Torokhov {
916b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
9175a6eb676SSoeren Sonnenburg 
918b5da20f8SDmitry Torokhov 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
919b5da20f8SDmitry Torokhov 		return -EIO;
920b5da20f8SDmitry Torokhov 
921b5da20f8SDmitry Torokhov 	return 0;
922b5da20f8SDmitry Torokhov }
923b5da20f8SDmitry Torokhov 
92490d95ef6SOliver Neukum static int atp_reset_resume(struct usb_interface *iface)
92590d95ef6SOliver Neukum {
92690d95ef6SOliver Neukum 	struct atp *dev = usb_get_intfdata(iface);
92790d95ef6SOliver Neukum 
92890d95ef6SOliver Neukum 	return atp_recover(dev);
92990d95ef6SOliver Neukum }
93090d95ef6SOliver Neukum 
931b5da20f8SDmitry Torokhov static struct usb_driver atp_driver = {
932b5da20f8SDmitry Torokhov 	.name		= "appletouch",
933b5da20f8SDmitry Torokhov 	.probe		= atp_probe,
934b5da20f8SDmitry Torokhov 	.disconnect	= atp_disconnect,
935b5da20f8SDmitry Torokhov 	.suspend	= atp_suspend,
936b5da20f8SDmitry Torokhov 	.resume		= atp_resume,
93790d95ef6SOliver Neukum 	.reset_resume	= atp_reset_resume,
938b5da20f8SDmitry Torokhov 	.id_table	= atp_table,
939b5da20f8SDmitry Torokhov };
940b5da20f8SDmitry Torokhov 
941b5da20f8SDmitry Torokhov static int __init atp_init(void)
942b5da20f8SDmitry Torokhov {
943b5da20f8SDmitry Torokhov 	return usb_register(&atp_driver);
944b5da20f8SDmitry Torokhov }
945b5da20f8SDmitry Torokhov 
946b5da20f8SDmitry Torokhov static void __exit atp_exit(void)
947b5da20f8SDmitry Torokhov {
948b5da20f8SDmitry Torokhov 	usb_deregister(&atp_driver);
949b5da20f8SDmitry Torokhov }
950b5da20f8SDmitry Torokhov 
951b5da20f8SDmitry Torokhov module_init(atp_init);
952b5da20f8SDmitry Torokhov module_exit(atp_exit);
953