xref: /openbmc/linux/drivers/input/mouse/appletouch.c (revision 2d744b09199d2481c99563fdcf7f1c60f87fd965)
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 */
198*2d744b09SGreg Kroah-Hartman 	struct usb_interface	*intf;		/* usb interface */
199b5da20f8SDmitry Torokhov 	struct urb		*urb;		/* usb request block */
20082a196f4SSven Anders 	u8			*data;		/* transferred data */
201b5da20f8SDmitry Torokhov 	struct input_dev	*input;		/* input dev */
20209779678SStelian Pop 	const struct atp_info	*info;		/* touchpad model */
2037dce869fSJohannes Berg 	bool			open;
2047dce869fSJohannes Berg 	bool			valid;		/* are the samples valid? */
2057dce869fSJohannes Berg 	bool			size_detect_done;
2067dce869fSJohannes Berg 	bool			overflow_warned;
207b5da20f8SDmitry Torokhov 	int			x_old;		/* last reported x/y, */
208b5da20f8SDmitry Torokhov 	int			y_old;		/* used for smoothing */
2096e49c1a4SBenjamin Herrenschmidt 	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
2106e49c1a4SBenjamin Herrenschmidt 	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];
211b5da20f8SDmitry Torokhov 	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS];
2125a6eb676SSoeren Sonnenburg 	int			idlecount;	/* number of empty packets */
2135a6eb676SSoeren Sonnenburg 	struct work_struct	work;
214b5da20f8SDmitry Torokhov };
215b5da20f8SDmitry Torokhov 
216b5da20f8SDmitry Torokhov #define dbg_dump(msg, tab) \
217b5da20f8SDmitry Torokhov 	if (debug > 1) {						\
2187dce869fSJohannes Berg 		int __i;						\
2197dce869fSJohannes Berg 		printk(KERN_DEBUG "appletouch: %s", msg);		\
2207dce869fSJohannes Berg 		for (__i = 0; __i < ATP_XSENSORS + ATP_YSENSORS; __i++)	\
2217dce869fSJohannes Berg 			printk(" %02x", tab[__i]);			\
222b5da20f8SDmitry Torokhov 		printk("\n");						\
223b5da20f8SDmitry Torokhov 	}
224b5da20f8SDmitry Torokhov 
225b5da20f8SDmitry Torokhov #define dprintk(format, a...)						\
226b5da20f8SDmitry Torokhov 	do {								\
2277dce869fSJohannes Berg 		if (debug)						\
2287dce869fSJohannes Berg 			printk(KERN_DEBUG format, ##a);			\
229b5da20f8SDmitry Torokhov 	} while (0)
230b5da20f8SDmitry Torokhov 
2317dce869fSJohannes Berg MODULE_AUTHOR("Johannes Berg");
2327dce869fSJohannes Berg MODULE_AUTHOR("Stelian Pop");
2337dce869fSJohannes Berg MODULE_AUTHOR("Frank Arnold");
2347dce869fSJohannes Berg MODULE_AUTHOR("Michael Hanselmann");
2357dce869fSJohannes Berg MODULE_AUTHOR("Sven Anders");
2367dce869fSJohannes Berg MODULE_DESCRIPTION("Apple PowerBook and MacBook USB touchpad driver");
237b5da20f8SDmitry Torokhov MODULE_LICENSE("GPL");
238b5da20f8SDmitry Torokhov 
239b5da20f8SDmitry Torokhov /*
240b5da20f8SDmitry Torokhov  * Make the threshold a module parameter
241b5da20f8SDmitry Torokhov  */
242b5da20f8SDmitry Torokhov static int threshold = ATP_THRESHOLD;
243b5da20f8SDmitry Torokhov module_param(threshold, int, 0644);
2447dce869fSJohannes Berg MODULE_PARM_DESC(threshold, "Discard any change in data from a sensor"
2457dce869fSJohannes Berg 			    " (the trackpad has many of these sensors)"
2467dce869fSJohannes Berg 			    " less than this value.");
247b5da20f8SDmitry Torokhov 
2487dce869fSJohannes Berg static int debug;
249b5da20f8SDmitry Torokhov module_param(debug, int, 0644);
250b5da20f8SDmitry Torokhov MODULE_PARM_DESC(debug, "Activate debugging output");
251b5da20f8SDmitry Torokhov 
2525a6eb676SSoeren Sonnenburg /*
2532a3e480dSDmitry Torokhov  * By default newer Geyser devices send standard USB HID mouse
2545a6eb676SSoeren Sonnenburg  * packets (Report ID 2). This code changes device mode, so it
2555a6eb676SSoeren Sonnenburg  * sends raw sensor reports (Report ID 5).
2565a6eb676SSoeren Sonnenburg  */
25780f8594fSGreg Kroah-Hartman static int atp_geyser_init(struct atp *dev)
2585a6eb676SSoeren Sonnenburg {
25980f8594fSGreg Kroah-Hartman 	struct usb_device *udev = dev->udev;
2600385c5eeSBob Copeland 	char *data;
2615a6eb676SSoeren Sonnenburg 	int size;
2627dce869fSJohannes Berg 	int i;
2630385c5eeSBob Copeland 	int ret;
2640385c5eeSBob Copeland 
2650385c5eeSBob Copeland 	data = kmalloc(8, GFP_KERNEL);
2660385c5eeSBob Copeland 	if (!data) {
267*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "Out of memory\n");
2680385c5eeSBob Copeland 		return -ENOMEM;
2690385c5eeSBob Copeland 	}
2705a6eb676SSoeren Sonnenburg 
2715a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
2722a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_READ_REQUEST_ID,
2735a6eb676SSoeren Sonnenburg 			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2742a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2750385c5eeSBob Copeland 			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
2765a6eb676SSoeren Sonnenburg 
2775a6eb676SSoeren Sonnenburg 	if (size != 8) {
2787dce869fSJohannes Berg 		dprintk("atp_geyser_init: read error\n");
2797dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2807dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
2817dce869fSJohannes Berg 
282*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "Failed to read mode from device.\n");
2830385c5eeSBob Copeland 		ret = -EIO;
2840385c5eeSBob Copeland 		goto out_free;
2855a6eb676SSoeren Sonnenburg 	}
2865a6eb676SSoeren Sonnenburg 
2875a6eb676SSoeren Sonnenburg 	/* Apply the mode switch */
2882a3e480dSDmitry Torokhov 	data[0] = ATP_GEYSER_MODE_VENDOR_VALUE;
2895a6eb676SSoeren Sonnenburg 
2905a6eb676SSoeren Sonnenburg 	size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
2912a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_WRITE_REQUEST_ID,
2925a6eb676SSoeren Sonnenburg 			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
2932a3e480dSDmitry Torokhov 			ATP_GEYSER_MODE_REQUEST_VALUE,
2940385c5eeSBob Copeland 			ATP_GEYSER_MODE_REQUEST_INDEX, data, 8, 5000);
2955a6eb676SSoeren Sonnenburg 
2965a6eb676SSoeren Sonnenburg 	if (size != 8) {
2977dce869fSJohannes Berg 		dprintk("atp_geyser_init: write error\n");
2987dce869fSJohannes Berg 		for (i = 0; i < 8; i++)
2997dce869fSJohannes Berg 			dprintk("appletouch[%d]: %d\n", i, data[i]);
3007dce869fSJohannes Berg 
301*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev, "Failed to request geyser raw mode\n");
3020385c5eeSBob Copeland 		ret = -EIO;
3030385c5eeSBob Copeland 		goto out_free;
3045a6eb676SSoeren Sonnenburg 	}
3050385c5eeSBob Copeland 	ret = 0;
3060385c5eeSBob Copeland out_free:
3070385c5eeSBob Copeland 	kfree(data);
3080385c5eeSBob Copeland 	return ret;
3095a6eb676SSoeren Sonnenburg }
3105a6eb676SSoeren Sonnenburg 
3112a3e480dSDmitry Torokhov /*
3122a3e480dSDmitry Torokhov  * Reinitialise the device. This usually stops stream of empty packets
3132a3e480dSDmitry Torokhov  * coming from it.
3142a3e480dSDmitry Torokhov  */
3155a6eb676SSoeren Sonnenburg static void atp_reinit(struct work_struct *work)
3165a6eb676SSoeren Sonnenburg {
3175a6eb676SSoeren Sonnenburg 	struct atp *dev = container_of(work, struct atp, work);
3182a3e480dSDmitry Torokhov 	int retval;
3195a6eb676SSoeren Sonnenburg 
3207dce869fSJohannes Berg 	dprintk("appletouch: putting appletouch to sleep (reinit)\n");
32180f8594fSGreg Kroah-Hartman 	atp_geyser_init(dev);
3222a3e480dSDmitry Torokhov 
3232a3e480dSDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
3247dce869fSJohannes Berg 	if (retval)
325*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev,
3269c113dc6SGreg Kroah-Hartman 			"atp_reinit: usb_submit_urb failed with error %d\n",
3277dce869fSJohannes Berg 			retval);
3285a6eb676SSoeren Sonnenburg }
3295a6eb676SSoeren Sonnenburg 
330b5da20f8SDmitry Torokhov static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
331b5da20f8SDmitry Torokhov 			     int *z, int *fingers)
332b5da20f8SDmitry Torokhov {
333b5da20f8SDmitry Torokhov 	int i;
334b5da20f8SDmitry Torokhov 	/* values to calculate mean */
335b5da20f8SDmitry Torokhov 	int pcum = 0, psum = 0;
336b5da20f8SDmitry Torokhov 	int is_increasing = 0;
337b5da20f8SDmitry Torokhov 
338b5da20f8SDmitry Torokhov 	*fingers = 0;
339b5da20f8SDmitry Torokhov 
340b5da20f8SDmitry Torokhov 	for (i = 0; i < nb_sensors; i++) {
341b5da20f8SDmitry Torokhov 		if (xy_sensors[i] < threshold) {
342b5da20f8SDmitry Torokhov 			if (is_increasing)
343b5da20f8SDmitry Torokhov 				is_increasing = 0;
344b5da20f8SDmitry Torokhov 
345b5da20f8SDmitry Torokhov 			continue;
346b5da20f8SDmitry Torokhov 		}
347b5da20f8SDmitry Torokhov 
348b5da20f8SDmitry Torokhov 		/*
349b5da20f8SDmitry Torokhov 		 * Makes the finger detection more versatile.  For example,
350b5da20f8SDmitry Torokhov 		 * two fingers with no gap will be detected.  Also, my
351b5da20f8SDmitry Torokhov 		 * tests show it less likely to have intermittent loss
352b5da20f8SDmitry Torokhov 		 * of multiple finger readings while moving around (scrolling).
353b5da20f8SDmitry Torokhov 		 *
354b5da20f8SDmitry Torokhov 		 * Changes the multiple finger detection to counting humps on
355b5da20f8SDmitry Torokhov 		 * sensors (transitions from nonincreasing to increasing)
356b5da20f8SDmitry Torokhov 		 * instead of counting transitions from low sensors (no
357b5da20f8SDmitry Torokhov 		 * finger reading) to high sensors (finger above
358b5da20f8SDmitry Torokhov 		 * sensor)
359b5da20f8SDmitry Torokhov 		 *
360b5da20f8SDmitry Torokhov 		 * - Jason Parekh <jasonparekh@gmail.com>
361b5da20f8SDmitry Torokhov 		 */
3627dce869fSJohannes Berg 		if (i < 1 ||
3637dce869fSJohannes Berg 		    (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
364b5da20f8SDmitry Torokhov 			(*fingers)++;
365b5da20f8SDmitry Torokhov 			is_increasing = 1;
36605e882f8SJeremy Huddleston 		} else if (i > 0 && (xy_sensors[i - 1] - xy_sensors[i] > threshold)) {
367b5da20f8SDmitry Torokhov 			is_increasing = 0;
368b5da20f8SDmitry Torokhov 		}
369b5da20f8SDmitry Torokhov 
370b5da20f8SDmitry Torokhov 		/*
3717dce869fSJohannes Berg 		 * Subtracts threshold so a high sensor that just passes the
3727dce869fSJohannes Berg 		 * threshold won't skew the calculated absolute coordinate.
3737dce869fSJohannes Berg 		 * Fixes an issue where slowly moving the mouse would
3747dce869fSJohannes Berg 		 * occasionally jump a number of pixels (slowly moving the
3757dce869fSJohannes Berg 		 * finger makes this issue most apparent.)
376b5da20f8SDmitry Torokhov 		 */
377b5da20f8SDmitry Torokhov 		pcum += (xy_sensors[i] - threshold) * i;
378b5da20f8SDmitry Torokhov 		psum += (xy_sensors[i] - threshold);
379b5da20f8SDmitry Torokhov 	}
380b5da20f8SDmitry Torokhov 
381b5da20f8SDmitry Torokhov 	if (psum > 0) {
382b5da20f8SDmitry Torokhov 		*z = psum;
383b5da20f8SDmitry Torokhov 		return pcum * fact / psum;
384b5da20f8SDmitry Torokhov 	}
385b5da20f8SDmitry Torokhov 
386b5da20f8SDmitry Torokhov 	return 0;
387b5da20f8SDmitry Torokhov }
388b5da20f8SDmitry Torokhov 
389b5da20f8SDmitry Torokhov static inline void atp_report_fingers(struct input_dev *input, int fingers)
390b5da20f8SDmitry Torokhov {
391b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
392b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
393b5da20f8SDmitry Torokhov 	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
394b5da20f8SDmitry Torokhov }
395b5da20f8SDmitry Torokhov 
396d83d213dSSven Anders /* Check URB status and for correct length of data package */
397d83d213dSSven Anders 
398d83d213dSSven Anders #define ATP_URB_STATUS_SUCCESS		0
399d83d213dSSven Anders #define ATP_URB_STATUS_ERROR		1
400d83d213dSSven Anders #define ATP_URB_STATUS_ERROR_FATAL	2
401d83d213dSSven Anders 
402d83d213dSSven Anders static int atp_status_check(struct urb *urb)
403b5da20f8SDmitry Torokhov {
404b5da20f8SDmitry Torokhov 	struct atp *dev = urb->context;
40567946d13SGreg Kroah-Hartman 	struct input_dev *idev = dev->input;
406*2d744b09SGreg Kroah-Hartman 	struct usb_interface *intf = dev->intf;
407b5da20f8SDmitry Torokhov 
408b5da20f8SDmitry Torokhov 	switch (urb->status) {
409b5da20f8SDmitry Torokhov 	case 0:
410b5da20f8SDmitry Torokhov 		/* success */
411b5da20f8SDmitry Torokhov 		break;
412b5da20f8SDmitry Torokhov 	case -EOVERFLOW:
4137dce869fSJohannes Berg 		if (!dev->overflow_warned) {
414*2d744b09SGreg Kroah-Hartman 			dev_warn(&intf->dev,
41567946d13SGreg Kroah-Hartman 				"appletouch: OVERFLOW with data length %d, actual length is %d\n",
41609779678SStelian Pop 				dev->info->datalen, dev->urb->actual_length);
4177dce869fSJohannes Berg 			dev->overflow_warned = true;
418b5da20f8SDmitry Torokhov 		}
419b5da20f8SDmitry Torokhov 	case -ECONNRESET:
420b5da20f8SDmitry Torokhov 	case -ENOENT:
421b5da20f8SDmitry Torokhov 	case -ESHUTDOWN:
422b5da20f8SDmitry Torokhov 		/* This urb is terminated, clean up */
423*2d744b09SGreg Kroah-Hartman 		dev_dbg(&intf->dev,
42467946d13SGreg Kroah-Hartman 			"atp_complete: urb shutting down with status: %d\n",
4257dce869fSJohannes Berg 			urb->status);
426d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR_FATAL;
427d83d213dSSven Anders 
428b5da20f8SDmitry Torokhov 	default:
429*2d744b09SGreg Kroah-Hartman 		dev_dbg(&intf->dev,
43067946d13SGreg Kroah-Hartman 			"atp_complete: nonzero urb status received: %d\n",
4317dce869fSJohannes Berg 			urb->status);
432d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
433b5da20f8SDmitry Torokhov 	}
434b5da20f8SDmitry Torokhov 
435b5da20f8SDmitry Torokhov 	/* drop incomplete datasets */
43609779678SStelian Pop 	if (dev->urb->actual_length != dev->info->datalen) {
437b5da20f8SDmitry Torokhov 		dprintk("appletouch: incomplete data package"
438b5da20f8SDmitry Torokhov 			" (first byte: %d, length: %d).\n",
439b5da20f8SDmitry Torokhov 			dev->data[0], dev->urb->actual_length);
440d83d213dSSven Anders 		return ATP_URB_STATUS_ERROR;
441b5da20f8SDmitry Torokhov 	}
442b5da20f8SDmitry Torokhov 
443d83d213dSSven Anders 	return ATP_URB_STATUS_SUCCESS;
444d83d213dSSven Anders }
445b5da20f8SDmitry Torokhov 
44609779678SStelian Pop static void atp_detect_size(struct atp *dev)
44709779678SStelian Pop {
44809779678SStelian Pop 	int i;
44909779678SStelian Pop 
45009779678SStelian Pop 	/* 17" Powerbooks have extra X sensors */
45109779678SStelian Pop 	for (i = dev->info->xsensors; i < ATP_XSENSORS; i++) {
45209779678SStelian Pop 		if (dev->xy_cur[i]) {
45309779678SStelian Pop 
454*2d744b09SGreg Kroah-Hartman 			dev_info(&dev->intf->dev,
45567946d13SGreg Kroah-Hartman 				"appletouch: 17\" model detected.\n");
45609779678SStelian Pop 
45709779678SStelian Pop 			input_set_abs_params(dev->input, ABS_X, 0,
45809779678SStelian Pop 					     (dev->info->xsensors_17 - 1) *
45909779678SStelian Pop 							dev->info->xfact - 1,
46009779678SStelian Pop 					     ATP_FUZZ, 0);
46109779678SStelian Pop 			break;
46209779678SStelian Pop 		}
46309779678SStelian Pop 	}
46409779678SStelian Pop }
46509779678SStelian Pop 
466b5da20f8SDmitry Torokhov /*
467d83d213dSSven Anders  * USB interrupt callback functions
468b5da20f8SDmitry Torokhov  */
469b5da20f8SDmitry Torokhov 
470d83d213dSSven Anders /* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */
471d83d213dSSven Anders 
472d83d213dSSven Anders static void atp_complete_geyser_1_2(struct urb *urb)
473d83d213dSSven Anders {
474d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
475d83d213dSSven Anders 	int retval, i, j;
476d83d213dSSven Anders 	int key;
477d83d213dSSven Anders 	struct atp *dev = urb->context;
478d83d213dSSven Anders 	int status = atp_status_check(urb);
479d83d213dSSven Anders 
480d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
481d83d213dSSven Anders 		return;
482d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
483d83d213dSSven Anders 		goto exit;
484d83d213dSSven Anders 
485d83d213dSSven Anders 	/* reorder the sensors values */
48609779678SStelian Pop 	if (dev->info == &geyser2_info) {
487b5da20f8SDmitry Torokhov 		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
488b5da20f8SDmitry Torokhov 
489b5da20f8SDmitry Torokhov 		/*
490b5da20f8SDmitry Torokhov 		 * The values are laid out like this:
491b5da20f8SDmitry Torokhov 		 * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
492b5da20f8SDmitry Torokhov 		 * '-' is an unused value.
493b5da20f8SDmitry Torokhov 		 */
494b5da20f8SDmitry Torokhov 
495b5da20f8SDmitry Torokhov 		/* read X values */
496b5da20f8SDmitry Torokhov 		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
497b5da20f8SDmitry Torokhov 			dev->xy_cur[i] = dev->data[j];
498b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 1] = dev->data[j + 1];
499b5da20f8SDmitry Torokhov 		}
500b5da20f8SDmitry Torokhov 
501b5da20f8SDmitry Torokhov 		/* read Y values */
502b5da20f8SDmitry Torokhov 		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
503b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
504b5da20f8SDmitry Torokhov 			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
505b5da20f8SDmitry Torokhov 		}
506b5da20f8SDmitry Torokhov 	} else {
507b5da20f8SDmitry Torokhov 		for (i = 0; i < 8; i++) {
508b5da20f8SDmitry Torokhov 			/* X values */
5097dce869fSJohannes Berg 			dev->xy_cur[i +  0] = dev->data[5 * i +  2];
510b5da20f8SDmitry Torokhov 			dev->xy_cur[i +  8] = dev->data[5 * i +  4];
511b5da20f8SDmitry Torokhov 			dev->xy_cur[i + 16] = dev->data[5 * i + 42];
512b5da20f8SDmitry Torokhov 			if (i < 2)
513b5da20f8SDmitry Torokhov 				dev->xy_cur[i + 24] = dev->data[5 * i + 44];
514b5da20f8SDmitry Torokhov 
515b5da20f8SDmitry Torokhov 			/* Y values */
51609779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[5 * i +  1];
51709779678SStelian Pop 			dev->xy_cur[ATP_XSENSORS + i + 8] = dev->data[5 * i + 3];
518b5da20f8SDmitry Torokhov 		}
519b5da20f8SDmitry Torokhov 	}
520b5da20f8SDmitry Torokhov 
521b5da20f8SDmitry Torokhov 	dbg_dump("sample", dev->xy_cur);
522b5da20f8SDmitry Torokhov 
523b5da20f8SDmitry Torokhov 	if (!dev->valid) {
524b5da20f8SDmitry Torokhov 		/* first sample */
5257dce869fSJohannes Berg 		dev->valid = true;
526b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
527d83d213dSSven Anders 
528d83d213dSSven Anders 		/* Store first sample */
529b5da20f8SDmitry Torokhov 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
530b5da20f8SDmitry Torokhov 
531d83d213dSSven Anders 		/* Perform size detection, if not done already */
53209779678SStelian Pop 		if (unlikely(!dev->size_detect_done)) {
53309779678SStelian Pop 			atp_detect_size(dev);
5342a3e480dSDmitry Torokhov 			dev->size_detect_done = 1;
535b5da20f8SDmitry Torokhov 			goto exit;
536b5da20f8SDmitry Torokhov 		}
537d83d213dSSven Anders 	}
538d83d213dSSven Anders 
539d83d213dSSven Anders 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
540d83d213dSSven Anders 		/* accumulate the change */
5416e49c1a4SBenjamin Herrenschmidt 		signed char change = dev->xy_old[i] - dev->xy_cur[i];
542d83d213dSSven Anders 		dev->xy_acc[i] -= change;
543d83d213dSSven Anders 
544d83d213dSSven Anders 		/* prevent down drifting */
545d83d213dSSven Anders 		if (dev->xy_acc[i] < 0)
546d83d213dSSven Anders 			dev->xy_acc[i] = 0;
547d83d213dSSven Anders 	}
548d83d213dSSven Anders 
549d83d213dSSven Anders 	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
550d83d213dSSven Anders 
551d83d213dSSven Anders 	dbg_dump("accumulator", dev->xy_acc);
552d83d213dSSven Anders 
553d83d213dSSven Anders 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
55409779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
555d83d213dSSven Anders 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
55609779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
55709779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
558d83d213dSSven Anders 
559d83d213dSSven Anders 	if (x && y) {
560d83d213dSSven Anders 		if (dev->x_old != -1) {
561d83d213dSSven Anders 			x = (dev->x_old * 3 + x) >> 2;
562d83d213dSSven Anders 			y = (dev->y_old * 3 + y) >> 2;
563d83d213dSSven Anders 			dev->x_old = x;
564d83d213dSSven Anders 			dev->y_old = y;
565d83d213dSSven Anders 
566d83d213dSSven Anders 			if (debug > 1)
567d83d213dSSven Anders 				printk(KERN_DEBUG "appletouch: "
568d83d213dSSven Anders 					"X: %3d Y: %3d Xz: %3d Yz: %3d\n",
569d83d213dSSven Anders 					x, y, x_z, y_z);
570d83d213dSSven Anders 
571d83d213dSSven Anders 			input_report_key(dev->input, BTN_TOUCH, 1);
572d83d213dSSven Anders 			input_report_abs(dev->input, ABS_X, x);
573d83d213dSSven Anders 			input_report_abs(dev->input, ABS_Y, y);
574d83d213dSSven Anders 			input_report_abs(dev->input, ABS_PRESSURE,
575d83d213dSSven Anders 					 min(ATP_PRESSURE, x_z + y_z));
576d83d213dSSven Anders 			atp_report_fingers(dev->input, max(x_f, y_f));
577d83d213dSSven Anders 		}
578d83d213dSSven Anders 		dev->x_old = x;
579d83d213dSSven Anders 		dev->y_old = y;
580d83d213dSSven Anders 
581d83d213dSSven Anders 	} else if (!x && !y) {
582d83d213dSSven Anders 
583d83d213dSSven Anders 		dev->x_old = dev->y_old = -1;
584d83d213dSSven Anders 		input_report_key(dev->input, BTN_TOUCH, 0);
585d83d213dSSven Anders 		input_report_abs(dev->input, ABS_PRESSURE, 0);
586d83d213dSSven Anders 		atp_report_fingers(dev->input, 0);
587d83d213dSSven Anders 
588d83d213dSSven Anders 		/* reset the accumulator on release */
589d83d213dSSven Anders 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
590d83d213dSSven Anders 	}
591d83d213dSSven Anders 
592d83d213dSSven Anders 	input_report_key(dev->input, BTN_LEFT, key);
593d83d213dSSven Anders 	input_sync(dev->input);
594d83d213dSSven Anders 
595d83d213dSSven Anders  exit:
596d83d213dSSven Anders 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
597d83d213dSSven Anders 	if (retval)
598*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev,
5999c113dc6SGreg Kroah-Hartman 			"atp_complete: usb_submit_urb failed with result %d\n",
600d83d213dSSven Anders 			retval);
601d83d213dSSven Anders }
602d83d213dSSven Anders 
603d83d213dSSven Anders /* Interrupt function for older touchpads: GEYSER3/GEYSER4 */
604d83d213dSSven Anders 
605d83d213dSSven Anders static void atp_complete_geyser_3_4(struct urb *urb)
606d83d213dSSven Anders {
607d83d213dSSven Anders 	int x, y, x_z, y_z, x_f, y_f;
608d83d213dSSven Anders 	int retval, i, j;
609d83d213dSSven Anders 	int key;
610d83d213dSSven Anders 	struct atp *dev = urb->context;
611d83d213dSSven Anders 	int status = atp_status_check(urb);
612d83d213dSSven Anders 
613d83d213dSSven Anders 	if (status == ATP_URB_STATUS_ERROR_FATAL)
614d83d213dSSven Anders 		return;
615d83d213dSSven Anders 	else if (status == ATP_URB_STATUS_ERROR)
616d83d213dSSven Anders 		goto exit;
617d83d213dSSven Anders 
618d83d213dSSven Anders 	/* Reorder the sensors values:
619d83d213dSSven Anders 	 *
620d83d213dSSven Anders 	 * The values are laid out like this:
621d83d213dSSven Anders 	 * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
622d83d213dSSven Anders 	 * '-' is an unused value.
623d83d213dSSven Anders 	 */
624d83d213dSSven Anders 
625d83d213dSSven Anders 	/* read X values */
626d83d213dSSven Anders 	for (i = 0, j = 19; i < 20; i += 2, j += 3) {
627d83d213dSSven Anders 		dev->xy_cur[i] = dev->data[j + 1];
628d83d213dSSven Anders 		dev->xy_cur[i + 1] = dev->data[j + 2];
629d83d213dSSven Anders 	}
630d83d213dSSven Anders 	/* read Y values */
631d83d213dSSven Anders 	for (i = 0, j = 1; i < 9; i += 2, j += 3) {
632d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
633d83d213dSSven Anders 		dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
634d83d213dSSven Anders 	}
635d83d213dSSven Anders 
636d83d213dSSven Anders 	dbg_dump("sample", dev->xy_cur);
637d83d213dSSven Anders 
63882a196f4SSven Anders 	/* Just update the base values (i.e. touchpad in untouched state) */
63909779678SStelian Pop 	if (dev->data[dev->info->datalen - 1] & ATP_STATUS_BASE_UPDATE) {
640d83d213dSSven Anders 
641d745b532SJoe Perches 		dprintk("appletouch: updated base values\n");
64282a196f4SSven Anders 
64382a196f4SSven Anders 		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
644d83d213dSSven Anders 		goto exit;
645d83d213dSSven Anders 	}
646b5da20f8SDmitry Torokhov 
647b5da20f8SDmitry Torokhov 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
64882a196f4SSven Anders 		/* calculate the change */
64982a196f4SSven Anders 		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
65082a196f4SSven Anders 
65182a196f4SSven Anders 		/* this is a round-robin value, so couple with that */
65282a196f4SSven Anders 		if (dev->xy_acc[i] > 127)
65382a196f4SSven Anders 			dev->xy_acc[i] -= 256;
65482a196f4SSven Anders 
65582a196f4SSven Anders 		if (dev->xy_acc[i] < -127)
65682a196f4SSven Anders 			dev->xy_acc[i] += 256;
657b5da20f8SDmitry Torokhov 
658b5da20f8SDmitry Torokhov 		/* prevent down drifting */
659b5da20f8SDmitry Torokhov 		if (dev->xy_acc[i] < 0)
660b5da20f8SDmitry Torokhov 			dev->xy_acc[i] = 0;
661b5da20f8SDmitry Torokhov 	}
662b5da20f8SDmitry Torokhov 
663b5da20f8SDmitry Torokhov 	dbg_dump("accumulator", dev->xy_acc);
664b5da20f8SDmitry Torokhov 
665b5da20f8SDmitry Torokhov 	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
66609779678SStelian Pop 			      dev->info->xfact, &x_z, &x_f);
667b5da20f8SDmitry Torokhov 	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
66809779678SStelian Pop 			      dev->info->yfact, &y_z, &y_f);
66909779678SStelian Pop 	key = dev->data[dev->info->datalen - 1] & ATP_STATUS_BUTTON;
670b5da20f8SDmitry Torokhov 
671b5da20f8SDmitry Torokhov 	if (x && y) {
672b5da20f8SDmitry Torokhov 		if (dev->x_old != -1) {
673b5da20f8SDmitry Torokhov 			x = (dev->x_old * 3 + x) >> 2;
674b5da20f8SDmitry Torokhov 			y = (dev->y_old * 3 + y) >> 2;
675b5da20f8SDmitry Torokhov 			dev->x_old = x;
676b5da20f8SDmitry Torokhov 			dev->y_old = y;
677b5da20f8SDmitry Torokhov 
678b5da20f8SDmitry Torokhov 			if (debug > 1)
6792a3e480dSDmitry Torokhov 				printk(KERN_DEBUG "appletouch: X: %3d Y: %3d "
680b5da20f8SDmitry Torokhov 				       "Xz: %3d Yz: %3d\n",
681b5da20f8SDmitry Torokhov 				       x, y, x_z, y_z);
682b5da20f8SDmitry Torokhov 
683b5da20f8SDmitry Torokhov 			input_report_key(dev->input, BTN_TOUCH, 1);
684b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_X, x);
685b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_Y, y);
686b5da20f8SDmitry Torokhov 			input_report_abs(dev->input, ABS_PRESSURE,
687b5da20f8SDmitry Torokhov 					 min(ATP_PRESSURE, x_z + y_z));
688b5da20f8SDmitry Torokhov 			atp_report_fingers(dev->input, max(x_f, y_f));
689b5da20f8SDmitry Torokhov 		}
690b5da20f8SDmitry Torokhov 		dev->x_old = x;
691b5da20f8SDmitry Torokhov 		dev->y_old = y;
6925a6eb676SSoeren Sonnenburg 
6935a6eb676SSoeren Sonnenburg 	} else if (!x && !y) {
694b5da20f8SDmitry Torokhov 
695b5da20f8SDmitry Torokhov 		dev->x_old = dev->y_old = -1;
696b5da20f8SDmitry Torokhov 		input_report_key(dev->input, BTN_TOUCH, 0);
697b5da20f8SDmitry Torokhov 		input_report_abs(dev->input, ABS_PRESSURE, 0);
698b5da20f8SDmitry Torokhov 		atp_report_fingers(dev->input, 0);
699b5da20f8SDmitry Torokhov 
700b5da20f8SDmitry Torokhov 		/* reset the accumulator on release */
701b5da20f8SDmitry Torokhov 		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
702937ad5c1SSoeren Sonnenburg 	}
7035a6eb676SSoeren Sonnenburg 
70446249ea6SAnton Ekblad 	input_report_key(dev->input, BTN_LEFT, key);
70546249ea6SAnton Ekblad 	input_sync(dev->input);
70646249ea6SAnton Ekblad 
7072a3e480dSDmitry Torokhov 	/*
708d83d213dSSven Anders 	 * Geysers 3/4 will continue to send packets continually after
7092a3e480dSDmitry Torokhov 	 * the first touch unless reinitialised. Do so if it's been
7102a3e480dSDmitry Torokhov 	 * idle for a while in order to avoid waking the kernel up
711d83d213dSSven Anders 	 * several hundred times a second.
7122a3e480dSDmitry Torokhov 	 */
713d83d213dSSven Anders 
7147dce869fSJohannes Berg 	/*
7157dce869fSJohannes Berg 	 * Button must not be pressed when entering suspend,
7167dce869fSJohannes Berg 	 * otherwise we will never release the button.
7177dce869fSJohannes Berg 	 */
718937ad5c1SSoeren Sonnenburg 	if (!x && !y && !key) {
7195a6eb676SSoeren Sonnenburg 		dev->idlecount++;
7205a6eb676SSoeren Sonnenburg 		if (dev->idlecount == 10) {
72182a196f4SSven Anders 			dev->x_old = dev->y_old = -1;
72282a196f4SSven Anders 			dev->idlecount = 0;
7235a6eb676SSoeren Sonnenburg 			schedule_work(&dev->work);
7242a3e480dSDmitry Torokhov 			/* Don't resubmit urb here, wait for reinit */
7252a3e480dSDmitry Torokhov 			return;
7265a6eb676SSoeren Sonnenburg 		}
72746249ea6SAnton Ekblad 	} else
728937ad5c1SSoeren Sonnenburg 		dev->idlecount = 0;
729b5da20f8SDmitry Torokhov 
730b5da20f8SDmitry Torokhov  exit:
731b5da20f8SDmitry Torokhov 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
7327dce869fSJohannes Berg 	if (retval)
733*2d744b09SGreg Kroah-Hartman 		dev_err(&dev->intf->dev,
7349c113dc6SGreg Kroah-Hartman 			"atp_complete: usb_submit_urb failed with result %d\n",
7357dce869fSJohannes Berg 			retval);
736b5da20f8SDmitry Torokhov }
737b5da20f8SDmitry Torokhov 
738b5da20f8SDmitry Torokhov static int atp_open(struct input_dev *input)
739b5da20f8SDmitry Torokhov {
740b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
741b5da20f8SDmitry Torokhov 
742b5da20f8SDmitry Torokhov 	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
743b5da20f8SDmitry Torokhov 		return -EIO;
744b5da20f8SDmitry Torokhov 
745b5da20f8SDmitry Torokhov 	dev->open = 1;
746b5da20f8SDmitry Torokhov 	return 0;
747b5da20f8SDmitry Torokhov }
748b5da20f8SDmitry Torokhov 
749b5da20f8SDmitry Torokhov static void atp_close(struct input_dev *input)
750b5da20f8SDmitry Torokhov {
751b5da20f8SDmitry Torokhov 	struct atp *dev = input_get_drvdata(input);
752b5da20f8SDmitry Torokhov 
753b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
7545a6eb676SSoeren Sonnenburg 	cancel_work_sync(&dev->work);
755b5da20f8SDmitry Torokhov 	dev->open = 0;
756b5da20f8SDmitry Torokhov }
757b5da20f8SDmitry Torokhov 
75890d95ef6SOliver Neukum static int atp_handle_geyser(struct atp *dev)
75990d95ef6SOliver Neukum {
76009779678SStelian Pop 	if (dev->info != &fountain_info) {
76190d95ef6SOliver Neukum 		/* switch to raw sensor mode */
76280f8594fSGreg Kroah-Hartman 		if (atp_geyser_init(dev))
76390d95ef6SOliver Neukum 			return -EIO;
76490d95ef6SOliver Neukum 
765*2d744b09SGreg Kroah-Hartman 		dev_info(&dev->intf->dev, "Geyser mode initialized.\n");
76690d95ef6SOliver Neukum 	}
76790d95ef6SOliver Neukum 
76890d95ef6SOliver Neukum 	return 0;
76990d95ef6SOliver Neukum }
77090d95ef6SOliver Neukum 
7717dce869fSJohannes Berg static int atp_probe(struct usb_interface *iface,
7727dce869fSJohannes Berg 		     const struct usb_device_id *id)
773b5da20f8SDmitry Torokhov {
774b5da20f8SDmitry Torokhov 	struct atp *dev;
775b5da20f8SDmitry Torokhov 	struct input_dev *input_dev;
776b5da20f8SDmitry Torokhov 	struct usb_device *udev = interface_to_usbdev(iface);
777b5da20f8SDmitry Torokhov 	struct usb_host_interface *iface_desc;
778b5da20f8SDmitry Torokhov 	struct usb_endpoint_descriptor *endpoint;
779b5da20f8SDmitry Torokhov 	int int_in_endpointAddr = 0;
780b5da20f8SDmitry Torokhov 	int i, error = -ENOMEM;
78109779678SStelian Pop 	const struct atp_info *info = (const struct atp_info *)id->driver_info;
782b5da20f8SDmitry Torokhov 
783b5da20f8SDmitry Torokhov 	/* set up the endpoint information */
784b5da20f8SDmitry Torokhov 	/* use only the first interrupt-in endpoint */
785b5da20f8SDmitry Torokhov 	iface_desc = iface->cur_altsetting;
786b5da20f8SDmitry Torokhov 	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
787b5da20f8SDmitry Torokhov 		endpoint = &iface_desc->endpoint[i].desc;
788b5da20f8SDmitry Torokhov 		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
789b5da20f8SDmitry Torokhov 			/* we found an interrupt in endpoint */
790b5da20f8SDmitry Torokhov 			int_in_endpointAddr = endpoint->bEndpointAddress;
791b5da20f8SDmitry Torokhov 			break;
792b5da20f8SDmitry Torokhov 		}
793b5da20f8SDmitry Torokhov 	}
794b5da20f8SDmitry Torokhov 	if (!int_in_endpointAddr) {
7959c113dc6SGreg Kroah-Hartman 		dev_err(&iface->dev, "Could not find int-in endpoint\n");
796b5da20f8SDmitry Torokhov 		return -EIO;
797b5da20f8SDmitry Torokhov 	}
798b5da20f8SDmitry Torokhov 
799b5da20f8SDmitry Torokhov 	/* allocate memory for our device state and initialize it */
800b5da20f8SDmitry Torokhov 	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
801b5da20f8SDmitry Torokhov 	input_dev = input_allocate_device();
802b5da20f8SDmitry Torokhov 	if (!dev || !input_dev) {
8039c113dc6SGreg Kroah-Hartman 		dev_err(&iface->dev, "Out of memory\n");
804b5da20f8SDmitry Torokhov 		goto err_free_devs;
805b5da20f8SDmitry Torokhov 	}
806b5da20f8SDmitry Torokhov 
807b5da20f8SDmitry Torokhov 	dev->udev = udev;
808*2d744b09SGreg Kroah-Hartman 	dev->intf = iface;
809b5da20f8SDmitry Torokhov 	dev->input = input_dev;
81009779678SStelian Pop 	dev->info = info;
8117dce869fSJohannes Berg 	dev->overflow_warned = false;
812b5da20f8SDmitry Torokhov 
813b5da20f8SDmitry Torokhov 	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
814b5da20f8SDmitry Torokhov 	if (!dev->urb)
815b5da20f8SDmitry Torokhov 		goto err_free_devs;
816b5da20f8SDmitry Torokhov 
817997ea58eSDaniel Mack 	dev->data = usb_alloc_coherent(dev->udev, dev->info->datalen, GFP_KERNEL,
818b5da20f8SDmitry Torokhov 				       &dev->urb->transfer_dma);
819b5da20f8SDmitry Torokhov 	if (!dev->data)
820b5da20f8SDmitry Torokhov 		goto err_free_urb;
821b5da20f8SDmitry Torokhov 
822b5da20f8SDmitry Torokhov 	usb_fill_int_urb(dev->urb, udev,
823b5da20f8SDmitry Torokhov 			 usb_rcvintpipe(udev, int_in_endpointAddr),
82409779678SStelian Pop 			 dev->data, dev->info->datalen,
82509779678SStelian Pop 			 dev->info->callback, dev, 1);
826b5da20f8SDmitry Torokhov 
82790d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
82890d95ef6SOliver Neukum 	if (error)
82990d95ef6SOliver Neukum 		goto err_free_buffer;
83090d95ef6SOliver Neukum 
831b5da20f8SDmitry Torokhov 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
832b5da20f8SDmitry Torokhov 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
833b5da20f8SDmitry Torokhov 
834b5da20f8SDmitry Torokhov 	input_dev->name = "appletouch";
835b5da20f8SDmitry Torokhov 	input_dev->phys = dev->phys;
836b5da20f8SDmitry Torokhov 	usb_to_input_id(dev->udev, &input_dev->id);
837b5da20f8SDmitry Torokhov 	input_dev->dev.parent = &iface->dev;
838b5da20f8SDmitry Torokhov 
839b5da20f8SDmitry Torokhov 	input_set_drvdata(input_dev, dev);
840b5da20f8SDmitry Torokhov 
841b5da20f8SDmitry Torokhov 	input_dev->open = atp_open;
842b5da20f8SDmitry Torokhov 	input_dev->close = atp_close;
843b5da20f8SDmitry Torokhov 
844b5da20f8SDmitry Torokhov 	set_bit(EV_ABS, input_dev->evbit);
845b5da20f8SDmitry Torokhov 
846b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_X, 0,
84709779678SStelian Pop 			     (dev->info->xsensors - 1) * dev->info->xfact - 1,
8487dce869fSJohannes Berg 			     ATP_FUZZ, 0);
849b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_Y, 0,
85009779678SStelian Pop 			     (dev->info->ysensors - 1) * dev->info->yfact - 1,
8517dce869fSJohannes Berg 			     ATP_FUZZ, 0);
852b5da20f8SDmitry Torokhov 	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
853b5da20f8SDmitry Torokhov 
854b5da20f8SDmitry Torokhov 	set_bit(EV_KEY, input_dev->evbit);
855b5da20f8SDmitry Torokhov 	set_bit(BTN_TOUCH, input_dev->keybit);
856b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
857b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
858b5da20f8SDmitry Torokhov 	set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
859b5da20f8SDmitry Torokhov 	set_bit(BTN_LEFT, input_dev->keybit);
860b5da20f8SDmitry Torokhov 
861b5da20f8SDmitry Torokhov 	error = input_register_device(dev->input);
862b5da20f8SDmitry Torokhov 	if (error)
863b5da20f8SDmitry Torokhov 		goto err_free_buffer;
864b5da20f8SDmitry Torokhov 
865b5da20f8SDmitry Torokhov 	/* save our data pointer in this interface device */
866b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, dev);
867b5da20f8SDmitry Torokhov 
8685a6eb676SSoeren Sonnenburg 	INIT_WORK(&dev->work, atp_reinit);
8695a6eb676SSoeren Sonnenburg 
870b5da20f8SDmitry Torokhov 	return 0;
871b5da20f8SDmitry Torokhov 
872b5da20f8SDmitry Torokhov  err_free_buffer:
873997ea58eSDaniel Mack 	usb_free_coherent(dev->udev, dev->info->datalen,
874b5da20f8SDmitry Torokhov 			  dev->data, dev->urb->transfer_dma);
875b5da20f8SDmitry Torokhov  err_free_urb:
876b5da20f8SDmitry Torokhov 	usb_free_urb(dev->urb);
877b5da20f8SDmitry Torokhov  err_free_devs:
878b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
879b5da20f8SDmitry Torokhov 	kfree(dev);
880b5da20f8SDmitry Torokhov 	input_free_device(input_dev);
881b5da20f8SDmitry Torokhov 	return error;
882b5da20f8SDmitry Torokhov }
883b5da20f8SDmitry Torokhov 
884b5da20f8SDmitry Torokhov static void atp_disconnect(struct usb_interface *iface)
885b5da20f8SDmitry Torokhov {
886b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
887b5da20f8SDmitry Torokhov 
888b5da20f8SDmitry Torokhov 	usb_set_intfdata(iface, NULL);
889b5da20f8SDmitry Torokhov 	if (dev) {
890b5da20f8SDmitry Torokhov 		usb_kill_urb(dev->urb);
891b5da20f8SDmitry Torokhov 		input_unregister_device(dev->input);
892997ea58eSDaniel Mack 		usb_free_coherent(dev->udev, dev->info->datalen,
893b5da20f8SDmitry Torokhov 				  dev->data, dev->urb->transfer_dma);
894b5da20f8SDmitry Torokhov 		usb_free_urb(dev->urb);
895b5da20f8SDmitry Torokhov 		kfree(dev);
896b5da20f8SDmitry Torokhov 	}
897*2d744b09SGreg Kroah-Hartman 	dev_info(&iface->dev, "input: appletouch disconnected\n");
898b5da20f8SDmitry Torokhov }
899b5da20f8SDmitry Torokhov 
90090d95ef6SOliver Neukum static int atp_recover(struct atp *dev)
90190d95ef6SOliver Neukum {
90290d95ef6SOliver Neukum 	int error;
90390d95ef6SOliver Neukum 
90490d95ef6SOliver Neukum 	error = atp_handle_geyser(dev);
90590d95ef6SOliver Neukum 	if (error)
90690d95ef6SOliver Neukum 		return error;
90790d95ef6SOliver Neukum 
90890d95ef6SOliver Neukum 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
90990d95ef6SOliver Neukum 		return -EIO;
91090d95ef6SOliver Neukum 
91190d95ef6SOliver Neukum 	return 0;
91290d95ef6SOliver Neukum }
91390d95ef6SOliver Neukum 
914b5da20f8SDmitry Torokhov static int atp_suspend(struct usb_interface *iface, pm_message_t message)
915b5da20f8SDmitry Torokhov {
916b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
9175a6eb676SSoeren Sonnenburg 
918b5da20f8SDmitry Torokhov 	usb_kill_urb(dev->urb);
919b5da20f8SDmitry Torokhov 	return 0;
920b5da20f8SDmitry Torokhov }
921b5da20f8SDmitry Torokhov 
922b5da20f8SDmitry Torokhov static int atp_resume(struct usb_interface *iface)
923b5da20f8SDmitry Torokhov {
924b5da20f8SDmitry Torokhov 	struct atp *dev = usb_get_intfdata(iface);
9255a6eb676SSoeren Sonnenburg 
926b5da20f8SDmitry Torokhov 	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
927b5da20f8SDmitry Torokhov 		return -EIO;
928b5da20f8SDmitry Torokhov 
929b5da20f8SDmitry Torokhov 	return 0;
930b5da20f8SDmitry Torokhov }
931b5da20f8SDmitry Torokhov 
93290d95ef6SOliver Neukum static int atp_reset_resume(struct usb_interface *iface)
93390d95ef6SOliver Neukum {
93490d95ef6SOliver Neukum 	struct atp *dev = usb_get_intfdata(iface);
93590d95ef6SOliver Neukum 
93690d95ef6SOliver Neukum 	return atp_recover(dev);
93790d95ef6SOliver Neukum }
93890d95ef6SOliver Neukum 
939b5da20f8SDmitry Torokhov static struct usb_driver atp_driver = {
940b5da20f8SDmitry Torokhov 	.name		= "appletouch",
941b5da20f8SDmitry Torokhov 	.probe		= atp_probe,
942b5da20f8SDmitry Torokhov 	.disconnect	= atp_disconnect,
943b5da20f8SDmitry Torokhov 	.suspend	= atp_suspend,
944b5da20f8SDmitry Torokhov 	.resume		= atp_resume,
94590d95ef6SOliver Neukum 	.reset_resume	= atp_reset_resume,
946b5da20f8SDmitry Torokhov 	.id_table	= atp_table,
947b5da20f8SDmitry Torokhov };
948b5da20f8SDmitry Torokhov 
94908642e7cSGreg Kroah-Hartman module_usb_driver(atp_driver);
950