[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Re: new appletouch alpha version



On Thu, 2007-06-28 at 13:07 +0200, Johannes Berg wrote:
> On Thu, 2007-06-28 at 13:02 +0200, Soeren Sonnenburg wrote:
> 
> > As he/we don't own powerpc hardware anymore and we don't want to break
> > appletouch powerpc support, could someone with a newer powerbook give
> > this driver a go and report the results to sven ?
> 
> The proper way to do this is to send *patches* to the existing driver to
> all people listed in the driver as well as linux-usb and/or linux-input
> lists.
> 
> Until then, I'm not willing to look at a completely new file when I
> can't even tell what it changes over the old driver.

It really changes a lot. but as you wish diff wrt. to current git CC'ing
all (former) authors is attached.

Soeren
-- 
Sometimes, there's a moment as you're waking, when you become aware of
the real world around you, but you're still dreaming.
--- drivers/input/mouse/appletouch.c	2007-06-28 13:10:22.000000000 +0200
+++ /home/sonne/Documents/open/src/mbp/appletouch/appletouch.c	2007-06-28 07:14:49.000000000 +0200
@@ -1,5 +1,4 @@
-/*
- * Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
+/* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
  *
  * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
  * Copyright (C) 2005      Johannes Berg (johannes@sipsolutions.net)
@@ -8,6 +7,8 @@
  * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
  * Copyright (C) 2005      Michael Hanselmann (linux-kernel@hansmi.ch)
  * Copyright (C) 2006      Nicolas Boichat (nicolas@boichat.ch)
+ * Copyright (C) 2006      Jason Parekh (jasonparekh@gmail.com)
+ * Copyright (C) 2007      Sven Anders (anders@anduras.de)
  *
  * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
  *
@@ -27,6 +28,7 @@
  *
  */
 
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -34,8 +36,16 @@
 #include <linux/module.h>
 #include <linux/usb/input.h>
 
+#include <asm/uaccess.h>
+
+/* Debug level */
+#define DEBUG 0
+
+/* Device numbers */
+#define USB_APPLETOUCH_MINOR_BASE	66
+
 /* Apple has powerbooks which have the keyboard with different Product IDs */
-#define APPLE_VENDOR_ID		0x05AC
+#define APPLE_VENDOR_ID			0x05AC
 
 /* These names come from Info.plist in AppleUSBTrackpad.kext */
 #define FOUNTAIN_ANSI_PRODUCT_ID	0x020E
@@ -58,14 +68,14 @@
  * Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
  * -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
  */
-#define GEYSER4_ANSI_PRODUCT_ID	0x021A
-#define GEYSER4_ISO_PRODUCT_ID	0x021B
-#define GEYSER4_JIS_PRODUCT_ID	0x021C
+#define GEYSER4_ANSI_PRODUCT_ID		0x021A
+#define GEYSER4_ISO_PRODUCT_ID		0x021B
+#define GEYSER4_JIS_PRODUCT_ID		0x021C
 
 #define ATP_DEVICE(prod)					\
-	.match_flags = USB_DEVICE_ID_MATCH_DEVICE |		\
-		       USB_DEVICE_ID_MATCH_INT_CLASS |		\
-		       USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
+	.match_flags =	USB_DEVICE_ID_MATCH_DEVICE |		\
+			USB_DEVICE_ID_MATCH_INT_CLASS |		\
+			USB_DEVICE_ID_MATCH_INT_PROTOCOL,	\
 	.idVendor = APPLE_VENDOR_ID,				\
 	.idProduct = (prod),					\
 	.bInterfaceClass = 0x03,				\
@@ -110,82 +120,218 @@
 #define ATP_FUZZ	16
 
 /* maximum pressure this driver will report */
-#define ATP_PRESSURE	300
+#define ATP_PRESSURE_FACTOR	2
+#define ATP_MAX_PRESSURE	(100*ATP_PRESSURE_FACTOR)
+
 /*
  * multiplication factor for the X and Y coordinates.
  * We try to keep the touchpad aspect ratio while still doing only simple
  * arithmetics.
  * The factors below give coordinates like:
  *	0 <= x <  960 on 12" and 15" Powerbooks
- *	0 <= x < 1600 on 17" Powerbooks
- *	0 <= y <  646
+ *	0 <= x < 1600 on 17" Powerbooks and 17" MacBook Pro
+ *	0 <= x < 1216 on 15" MacBook Pro
+ *	0 <= y <  646 on all Powerbooks
+ *	0 <= y <  774 on 15" MacBook Pro
  */
 #define ATP_XFACT	64
-#define ATP_YFACT	43
+#define ATP_YFACT	43 // 86
 
 /*
- * Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
- * ignored.
+ * Thresholds for the touchpad sensors.
+ *
+ * Any sensors less than ATP_THRESHOLD is ignored. 
+ * APT_START_THRESHOLD defines the pressure needed to start the moving.
+ * We need at least APT_FINGER_THRESHOLD to get a finger recognized.
+ * If we have ATP_PALM_THRESHOLD, we recognize it as an palm.
  */
-#define ATP_THRESHOLD	 5
+#define ATP_THRESHOLD		2
+#define ATP_START_THRESHOLD	7
+#define ATP_FINGER_THRESHOLD	8
+#define ATP_PALM_THRESHOLD	35
 
-/* MacBook Pro (Geyser 3 & 4) initialization constants */
-#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
-#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
-#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
-#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
-#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
+/* 
+ * MacBook Pro (Geyser 3) initialization constants 
+ */
+#define ATP_GEYSER3_MODE_READ_REQUEST_ID	1
+#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID	9
+#define ATP_GEYSER3_MODE_REQUEST_VALUE		0x300
+#define ATP_GEYSER3_MODE_REQUEST_INDEX		0
+#define ATP_GEYSER3_MODE_VENDOR_VALUE		0x04
+
+/*
+ * Meaning of the status bits
+ */
+#define ATP_STATUS_BIT_BUTTON		0x01	/* The button was pressed */
+#define ATP_STATUS_BIT_UNKNOWN1		0x02	/* Unknown or unused */
+#define ATP_STATUS_BIT_BASE_UPDATE	0x04	/* Update of the base values (untouched pad) */
+#define ATP_STATUS_BIT_UNKNOWN2		0x08	/* Unknown or unused */
+#define ATP_STATUS_BIT_FROM_RESET	0x10	/* Reset previously performed */
+
+/* Type of touchpad */
+typedef enum { TYPE_GEYSER_1, TYPE_GEYSER_2, TYPE_GEYSER_3_4 } touchpad_type;
+
+/* Coordinate type */
+typedef struct {
+	int x, y;			/* x/y coordinate */
+	int x_pressure, y_pressure;	/* pressure at this x/y coordinate */
+	int x_size, y_size;		/* size of touch */
+} atp_coord;
 
 /* Structure to hold all of our device specific stuff */
 struct atp {
 	char			phys[64];
 	struct usb_device *	udev;		/* usb device */
 	struct urb *		urb;		/* usb request block */
-	signed char *		data;		/* transferred data */
+	unsigned char *		data;		/* transferred data */
 	int			open;		/* non-zero if opened */
 	struct input_dev	*input;		/* input dev */
+	touchpad_type		type;		/* type of touchpad */
 	int			valid;		/* are the sensors valid ? */
-	int			x_old;		/* last reported x/y, */
-	int			y_old;		/* used for smoothing */
-						/* current value of the sensors */
-	signed char		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
-						/* last value of the sensors */
-	signed char		xy_old[ATP_XSENSORS + ATP_YSENSORS];
-						/* accumulated sensors */
+	int			idle_counter;	/* idle counter  */	
+	int			max_x;		/* maximum x coordinate */
+	int			max_y;		/* maximum y coordinate */
+	int			palm_left;	/* palm left region border */
+	int			palm_right;	/* palm right region border */
+	atp_coord		pos_old;	/* last x/y positions we returned */
+	int			scrolling;	/* in scrolling state */
+
+	int			old_x_coord[ATP_XSENSORS];
+	int			old_y_coord[ATP_YSENSORS];
+	int			old_x_count;
+	int			old_y_count;
+	/* current value of the sensors */
+	unsigned int		xy_cur[ATP_XSENSORS + ATP_YSENSORS];
+	/* last value of the sensors */
+	unsigned int		xy_old[ATP_XSENSORS + ATP_YSENSORS];
+	/* accumulated sensors */
 	int			xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+	int			threshold;	/* currently used threshold */
+	int			first;		/* is this the first (re)touch? */
+	int			ignored_palm;	/* did we started a palm ignore? */
 	int			overflowwarn;	/* overflow warning printed? */
 	int			datalen;	/* size of an USB urb transfer */
+	struct work_struct	reinit_thread;	/* kernel thread (for re-init) */
+	int next;
 };
 
-#define dbg_dump(msg, tab) \
-	if (debug > 1) {						\
-		int i;							\
-		printk("appletouch: %s %lld", msg, (long long)jiffies); \
-		for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++)	\
-			printk(" %02x", tab[i]);			\
-		printk("\n");						\
-	}
-
-#define dprintk(format, a...)						\
+#define dprintk(level, format, a...)					\
 	do {								\
-		if (debug) printk(format, ##a);				\
+		if (debug > level) printk(format, ##a);				\
 	} while (0)
 
-MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann");
-MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
+MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann, Sven Anders");
+MODULE_DESCRIPTION("Apple PowerBooks/MacBooks USB touchpad driver");
 MODULE_LICENSE("GPL");
 
 /*
- * Make the threshold a module parameter
+ * Modules parameters
  */
+
+/* (until we have a working automatic detection) */
+static int seventeen = 0;
+module_param(seventeen, int, 0644);
+MODULE_PARM_DESC(seventeen, "Is this a 17\" Powerbook or MacBook?");
+
 static int threshold = ATP_THRESHOLD;
 module_param(threshold, int, 0644);
-MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor (trackpad has hundreds of these sensors) less than this value");
+MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor "
+		"(trackpad has hundreds of these sensors) less than this value");
+
+static int start_threshold = ATP_START_THRESHOLD;
+module_param(start_threshold, int, 0644);
+MODULE_PARM_DESC(start_threshold, "Pressure needed to start moving");
+
+static int finger_threshold = ATP_FINGER_THRESHOLD;
+module_param(finger_threshold, int, 0644);
+MODULE_PARM_DESC(finger_threshold, "Least pressure that identifies a finger");
+
+static int palm_threshold = ATP_PALM_THRESHOLD;
+module_param(palm_threshold, int, 0644);
+MODULE_PARM_DESC(palm_threshold, "Pressure which triggers the palm detection");
+
+static int palm_detect = 1;
+module_param(palm_detect, int, 0644);
+MODULE_PARM_DESC(palm_detect, "Detect and ignore palms on the touchpad");
+
+static int report_touchsize = 0;
+module_param(report_touchsize, int, 0644);
+MODULE_PARM_DESC(report_touchsize, "Report touch's size (tool-width) for use by synaptics driver");
 
 static int debug = 1;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Activate debugging output");
 
+/* palm areas are defined as fractions of the pad in x direction */
+/* Default: 4/13 * X-Size < X < 14/22 * X-Size */
+
+#define palm_left_nomi palm_region[0]  // default: 4
+#define palm_left_div  palm_region[1]  // default: 13
+#define palm_right_nomi palm_region[2] // default: 14
+#define palm_right_div  palm_region[3] // default: 22
+
+static unsigned int palm_region[4] = { 4, 13, 14, 22 };
+static unsigned int palm_region_count;
+
+static struct kparam_array __param_arr_palm_region
+= { ARRAY_SIZE(palm_region), &palm_region_count, 
+	param_set_uint, param_get_uint,
+	sizeof(palm_region[0]), palm_region };
+
+/* Palm area states */
+#define ATP_PALM_AREA_ENABLED	0	/* Palm area is enabled */
+#define ATP_PALM_AREA_DISABLED	1	/* Palm area is disabled (ignore touches) */
+#define ATP_PALM_AREA_TIMEOUT	2	/* Palm area is disabled until timeout occures */
+
+/* Timeout before reenabling the palm area after a keypress */
+#define ATP_KEY_TIMEOUT_MSEC	1000	/* Timeout in milliseconds */
+
+/* Is the palm area currently disabled? */
+/* (It's disabled, if we pressed a key on the keyboard and enabled */
+/*  again, if we touch the inner region or a timeout occures) */
+static int palm_area_state = ATP_PALM_AREA_ENABLED;
+
+/* Is the modifier key pressed? */
+static int modifier_pressed = 0;
+
+/* Is the support devices open? */
+static unsigned long device_opened = 0;
+
+/* Timeout for pressed key palm ignore */
+static unsigned long key_timeout = 0;
+
+/* Validate and set palm region parameters */
+static int atp_set_palm_region(const char *val, struct kernel_param *kp)
+{
+	int result;
+	unsigned int palm_region_copy[4];
+
+	memcpy(palm_region_copy, palm_region, sizeof(palm_region));
+
+	result = param_array_set(val, kp);
+
+	if (palm_region_count != 4)
+	{
+		printk(KERN_ERR "%s: expecting 4 arguments\n", kp->name);
+		result = -EINVAL;
+	}
+
+	if ((palm_left_div == 0) || (palm_right_div == 0))
+	{
+		printk(KERN_ERR "%s: second and forth value must not be 0!\n", kp->name);
+		result = -EINVAL;
+	}
+
+	if (result != 0)
+		memcpy(palm_region, palm_region_copy, sizeof(palm_region));
+
+	return result;
+}
+
+module_param_call(palm_region, atp_set_palm_region, param_array_get,
+		&__param_arr_palm_region, 0644);
+MODULE_PARM_DESC(palm_region, "Parameters to define palm region in fractions N/D of size (leftN,leftD,rightN,rightD)");
+
 /* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
 static inline int atp_is_geyser_2(struct atp *dev)
 {
@@ -196,7 +342,8 @@
 		(productId == GEYSER_JIS_PRODUCT_ID);
 }
 
-static inline int atp_is_geyser_3(struct atp *dev)
+/* Checks if the device a Geyser 3 or 4 (ANSI, ISO, JIS) */
+static inline int atp_is_geyser_3_or_4(struct atp *dev)
 {
 	u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
 
@@ -208,62 +355,334 @@
 		(productId == GEYSER4_JIS_PRODUCT_ID);
 }
 
-static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
-			     int *z, int *fingers)
+/* Calculate the palm region */
+static inline void calculate_palm_region(struct atp *dev)
+{
+	/* the left and right border of the palm region */
+	dev->palm_left = (dev->max_x * palm_left_nomi) / palm_left_div;
+	dev->palm_right = (dev->max_x * palm_right_nomi) / palm_right_div;
+}
+
+/* Get all positions on one axis and return them */
+static void atp_get_axis_pos(int *xy_sensors, int count, 
+		int fact, int current_threshold,
+		int *coords, signed char *pressure, int *size,
+		int *num_coords, int *num_touched, 
+		int *biggest_block, int *average)
 {
 	int i;
-	/* values to calculate mean */
-	int pcum = 0, psum = 0;
 	int is_increasing = 0;
+	int is_decreasing = 0;
+	int was_increasing = 0;
+	int was_decreasing = 0;
+	int current_block = 0;
+	int highest_pressure = 0;
 
-	*fingers = 0;
-
-	for (i = 0; i < nb_sensors; i++) {
-		if (xy_sensors[i] < threshold) {
-			if (is_increasing)
-				is_increasing = 0;
+	/* values to calculate mean */
+	int pcum = 0, psum = 0, pnum = 0;
 
-			continue;
+	/* clean return data */
+	memset(coords, 0, count);
+	memset(pressure, 0, count);
+	*num_coords = 0;
+	*num_touched = 0;
+	*biggest_block = 0;
+	*average = 0;
+
+	/* search for sensors */
+	for (i = 0; i < count; i++) {
+
+		/* count any sensors we touched on this axis */
+		if (xy_sensors[i] >= 2) (*num_touched)++;
+
+		/* search the biggest consecutively touched block */
+		if (xy_sensors[i] >= 2)
+		{
+			if ((i == 0) || (xy_sensors[i-1] >= 2))
+				current_block++;
+			if (current_block > *biggest_block)
+				*biggest_block = current_block;
 		}
+		else
+			current_block = 0;
 
-		/*
-		 * Makes the finger detection more versatile.  For example,
-		 * two fingers with no gap will be detected.  Also, my
-		 * tests show it less likely to have intermittent loss
-		 * of multiple finger readings while moving around (scrolling).
-		 *
-		 * Changes the multiple finger detection to counting humps on
-		 * sensors (transitions from nonincreasing to increasing)
-		 * instead of counting transitions from low sensors (no
-		 * finger reading) to high sensors (finger above
-		 * sensor)
-		 *
-		 * - Jason Parekh <jasonparekh@gmail.com>
-		 */
-		if (i < 1 || (!is_increasing && xy_sensors[i - 1] < xy_sensors[i])) {
-			(*fingers)++;
-			is_increasing = 1;
-		} else if (i > 0 && xy_sensors[i - 1] >= xy_sensors[i]) {
-			is_increasing = 0;
+		/* are the pressure values increasing? */
+		is_increasing = ((xy_sensors[i] >= current_threshold) &&
+				((i == 0) || (xy_sensors[i-1] < xy_sensors[i])));
+
+		/* are the pressure values decreasing? */
+		is_decreasing = ((xy_sensors[i] >= current_threshold) && 
+				(i > 0)  && (xy_sensors[i-1] >= xy_sensors[i]));
+
+#if 0||DEBUG > 3
+		printk("appletouch: %i. INC=%i (%i), DEC=%i (%i) => %i\n", 
+				i, is_increasing, was_increasing, is_decreasing, 
+				was_decreasing, xy_sensors[i]);
+#endif
+
+		/* store new found pos, if decreasing ended */
+		if ((was_decreasing && (!is_decreasing || is_increasing)) ||
+			(was_increasing && !is_decreasing && !is_increasing)) {
+			/* Did we found a pressure, that identifies a finger? */
+			if (highest_pressure >= finger_threshold)
+			{
+				/* Yes => store coordinate */
+				if (psum != 0)
+					coords[*num_coords] = pcum * fact / psum;
+
+				pressure[*num_coords] = psum; //highest_pressure;
+				size[*num_coords] = pnum;
+				*average += highest_pressure;
+
+#if 0||DEBUG > 3
+				printk("appletouch: found %i (%i) => %i (%i) [%i]\n", 
+						i, highest_pressure, coords[*num_coords], psum, pnum);
+#endif
+				/* reset pressure store */
+				highest_pressure = 0;
+
+				/* increase coordinate/finger counter */
+				(*num_coords)++;
+
+				/* do we have two fingers closely? */
+				if (is_increasing && i > 0) {
+					/* Yes, init mean calc values with value of last sensor */
+					pcum = (xy_sensors[i-1] - current_threshold) * i;
+					psum = (xy_sensors[i-1] - current_threshold);
+					pnum = 1;
+
+				} else {
+					/* No, just reset mean calc values */
+					pcum = 0;
+					psum = 0;
+					pnum = 0;
+				}
+			}
 		}
 
-		/*
+		/* store in/decrease state for later use in next loop */
+		was_decreasing = is_decreasing;
+		was_increasing = is_increasing;
+
+		/* ignore any sensor less than 'threshold' */
+		if (xy_sensors[i] < current_threshold)
+			continue;
+
+		/* Sum up the coordinates and pressures.
+		 *
 		 * Subtracts threshold so a high sensor that just passes the threshold
 		 * won't skew the calculated absolute coordinate.  Fixes an issue
 		 * where slowly moving the mouse would occassionaly jump a number of
 		 * pixels (let me restate--slowly moving the mouse makes this issue
 		 * most apparent).
 		 */
-		pcum += (xy_sensors[i] - threshold) * i;
-		psum += (xy_sensors[i] - threshold);
+		pcum += (xy_sensors[i] - current_threshold) * (i+1);
+		psum += (xy_sensors[i] - current_threshold);
+		pnum++;
+
+		/* Save the highest value we've found */
+		if (xy_sensors[i] > highest_pressure)
+			highest_pressure = xy_sensors[i];
+
 	}
 
-	if (psum > 0) {
-		*z = psum;
-		return pcum * fact / psum;
+	/* Calculate average pressure */
+	if (*num_coords != 0) *average /= *num_coords;
+}
+
+/* Calculate coordinates of all fingers, choose best and return it */
+
+static void atp_calc_and_choose_coords(struct atp *dev, 
+		atp_coord *xy_coord,
+		int *num_fingers,
+		int *x_count_ret, int *y_count_ret)
+
+{
+	int x_coord[ATP_XSENSORS];
+	signed char x_pressure[ATP_XSENSORS];
+	int x_size[ATP_XSENSORS];
+	int y_coord[ATP_YSENSORS];
+	signed char y_pressure[ATP_YSENSORS]; 
+	int y_size[ATP_YSENSORS]; 
+	int x_count, y_count;
+	int x_touched, y_touched;
+	int x_consec, y_consec;
+	int x_average, y_average;
+	int i;
+
+	/* clear coordinate arrays */
+	memset(xy_coord, 0, sizeof(atp_coord));
+	*num_fingers = 0;
+
+	/* Get touched sensors on X and Y axis */
+	atp_get_axis_pos(dev->xy_acc, ATP_XSENSORS, 
+			ATP_XFACT, dev->threshold,
+			x_coord, x_pressure, x_size,
+			&x_count, &x_touched, &x_consec, &x_average);
+
+	atp_get_axis_pos(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, 
+			ATP_YFACT, dev->threshold,
+			y_coord, y_pressure, y_size,
+			&y_count, &y_touched, &y_consec, &y_average);
+
+	*x_count_ret = x_count;
+	*y_count_ret = y_count;
+
+#if 0||DEBUG > 1
+	printk("appletouch: pressure: X: ");
+	for (i=0; i < x_count; i++) printk("%i,", x_pressure[i]); printk(" Y: ");
+	for (i=0; i < y_count; i++) printk("%i,", y_pressure[i]); printk("\n");
+	printk("appletouch: ø pressure: X: %i / Y: %i\n", x_average, y_average);
+	printk("appletouch: touched: X: %i / Y: %i\n", x_touched, y_touched);
+	printk("appletouch: consec-block: X: %i / Y: %i\n", x_consec, y_consec);
+	printk("appletouch: count: X: %i / Y: %i\n", x_count, y_count);
+	printk("appletouch: coord: X: ");
+	for (i=0; i < x_count; i++) printk("%i,", x_coord[i]); printk(" Y: ");
+	for (i=0; i < y_count; i++) printk("%i,", y_coord[i]); printk("\n");
+#endif
+
+	/* is the palm detection enabled? */
+	//if (palm_detect)
+	if (!dev->scrolling && palm_detect)
+	{
+		// big region or average pressure over 'palm_threshold' found?
+		if (x_consec > 5 || 
+		    y_consec > 5 ||
+		    x_touched >= 7 || 
+		    y_touched >= 7 ||
+		    x_average >= palm_threshold || 
+		    y_average >= palm_threshold)
+		{
+#if 1||DEBUG > 2
+			printk("appletouch: ignored palm on touchpad\n");
+#endif
+			dev->ignored_palm = 1;
+
+			/* Disable palm region too */
+			palm_area_state = ATP_PALM_AREA_DISABLED;
+			printk("appletouch: palm area disabled!\n");
+			return;
+		}
 	}
 
-	return 0;
+	if (!dev->ignored_palm)
+	{
+
+		/* Get first coordinate we found (as a start) */
+		xy_coord->x = x_coord[0];
+		xy_coord->y = y_coord[0];
+		xy_coord->x_pressure = x_pressure[0];
+		xy_coord->y_pressure = y_pressure[0];
+		xy_coord->x_size = x_size[0];
+		xy_coord->y_size = y_size[0];
+
+		/* How many finger do we have on the touchpad? */
+
+		if (x_count == 1 && y_count == 1) {
+
+			/* Only one finger on the touchpad, this is easy... */
+			/* We've already done this, just return with a finger count of one */
+#if 0||DEBUG > 2		
+			printk("** appletouch: IN X=%3d / Y=%3d / XP:%3d / YP:%3d / XS: %3d / YS: %3d\n",
+					xy_coord->x, xy_coord->y, xy_coord->x_pressure, xy_coord->y_pressure, xy_coord->x_size, xy_coord->y_size);
+#endif
+
+
+			memcpy(dev->old_x_coord, x_coord, sizeof(dev->old_x_coord));
+			memcpy(dev->old_y_coord, y_coord, sizeof(dev->old_y_coord));
+			dev->old_x_count = x_count;
+			dev->old_y_count = y_count;
+
+			*num_fingers = 1;
+			dev->scrolling = 0;
+		}
+		else if (x_count > 0 && y_count > 0) {
+
+			/* We have more than one, get the nearest to the old */
+
+			for (i=1; i < x_count; i++)
+			{
+				if (abs(dev->pos_old.x - x_coord[i]) < abs(dev->pos_old.x - xy_coord->x))
+				{
+					xy_coord->x = x_coord[i];
+					xy_coord->x_pressure = x_pressure[i];
+					xy_coord->x_size = x_size[i];
+				}
+			}
+
+			for (i=1; i < y_count; i++)
+			{
+				if (abs(dev->pos_old.y - y_coord[i]) < abs(dev->pos_old.y - xy_coord->y))
+				{
+					xy_coord->y = y_coord[i];
+					xy_coord->y_pressure = y_pressure[i];
+					xy_coord->y_size = y_size[i];
+				}
+			}
+
+			printk("*#* appletouch: IN X=%3d / Y=%3d / XP:%3d / YP:%3d / XS: %3d / YS: %3d\n",
+					xy_coord->x, xy_coord->y, xy_coord->x_pressure, xy_coord->y_pressure, xy_coord->x_size, xy_coord->y_size);
+
+			if ((dev->old_x_count != x_count) || (dev->old_y_count != y_count))
+			{
+				memcpy(dev->old_x_coord, x_coord, sizeof(dev->old_x_coord));
+				memcpy(dev->old_y_coord, y_coord, sizeof(dev->old_y_coord));
+				dev->old_x_count = x_count;
+				dev->old_y_count = y_count;
+			}
+			else
+			{
+				int x_diff = 0;
+				int y_diff = 0;
+				for (i=0; i < x_count; i++)
+					x_diff += (x_coord[i] - dev->old_x_coord[i]);
+				for (i=0; i < y_count; i++)
+					y_diff += (y_coord[i] - dev->old_y_coord[i]);
+
+				xy_coord->x += (x_diff/x_count);
+				xy_coord->y += (y_diff/y_count);
+
+				if (xy_coord->x < 0) xy_coord->x = 0;
+				if (xy_coord->y < 0) xy_coord->y = 0;
+			}
+
+			// Are we scrolling (and are not in ?
+			if ((abs(dev->pos_old.x - xy_coord->x) > 2) ||
+			    (abs(dev->pos_old.y - xy_coord->y) > 2))
+				dev->scrolling = 1;
+
+			/* If we have more than one finger on one coordinate,
+			   get the maximum of it. This is the total count of fingers! */
+			*num_fingers = max(x_count, y_count);
+
+#if 0||DEBUG > 1
+			printk("appletouch: X: %i of %i / Y: %i of %i -\n", 
+				x_count, ATP_XSENSORS, y_count, ATP_YSENSORS);
+			printk("appletouch: num_fingers=%i\n", *num_fingers);
+			printk("appletouch: X: %3d P: %3d S: %3d\n", xy_coord->x, xy_coord->x_pressure, xy_coord->x_size);
+			printk("appletouch: Y: %3d P: %3d S: %3d\n", xy_coord->y, xy_coord->y_pressure, xy_coord->y_size);
+#endif
+		}
+	} // (!dev->ignored_palm)
+
+	if (!dev->scrolling && palm_detect)
+	{
+		/* Ignore touches in the palm area, if it's not enabled */
+		if (palm_area_state != ATP_PALM_AREA_ENABLED)
+		{
+			for (i=0; i < y_count; i++)
+			{
+				if ((x_coord[i] < dev->palm_left) || (x_coord[i] > dev->palm_right))
+				{
+#if 1||DEBUG > 2
+					printk("appletouch: ignored palm in defined palm region\n");
+#endif
+					dev->ignored_palm = 1;
+					return;
+				}
+			}
+		}
+	}
 }
 
 static inline void atp_report_fingers(struct input_dev *input, int fingers)
@@ -275,44 +694,47 @@
 
 static void atp_complete(struct urb* urb)
 {
-	int x, y, x_z, y_z, x_f, y_f;
+	int x, y;
+	atp_coord xy_coord;
+	int x_count, y_count;
+	int num_fingers, pressure, size;
 	int retval, i, j;
 	struct atp *dev = urb->context;
 
 	switch (urb->status) {
-	case 0:
-		/* success */
-		break;
-	case -EOVERFLOW:
-		if(!dev->overflowwarn) {
-			printk("appletouch: OVERFLOW with data "
-				"length %d, actual length is %d\n",
-				dev->datalen, dev->urb->actual_length);
-			dev->overflowwarn = 1;
-		}
-	case -ECONNRESET:
-	case -ENOENT:
-	case -ESHUTDOWN:
-		/* This urb is terminated, clean up */
-		dbg("%s - urb shutting down with status: %d",
-		    __FUNCTION__, urb->status);
-		return;
-	default:
-		dbg("%s - nonzero urb status received: %d",
-		    __FUNCTION__, urb->status);
-		goto exit;
+		case 0:
+			/* success */
+			break;
+		case -EOVERFLOW:
+			if(!dev->overflowwarn) {
+				printk("appletouch: OVERFLOW with data "
+						"length %d, actual length is %d\n",
+						dev->datalen, dev->urb->actual_length);
+				dev->overflowwarn = 1;
+			}
+		case -ECONNRESET:
+		case -ENOENT:
+		case -ESHUTDOWN:
+			/* This urb is terminated, clean up */
+			dbg("%s - urb shutting down with status: %d",
+					__FUNCTION__, urb->status);
+			return;
+		default:
+			dbg("%s - nonzero urb status received: %d",
+					__FUNCTION__, urb->status);
+			goto exit;
 	}
 
 	/* drop incomplete datasets */
 	if (dev->urb->actual_length != dev->datalen) {
-		dprintk("appletouch: incomplete data package"
-			" (first byte: %d, length: %d).\n",
-			dev->data[0], dev->urb->actual_length);
+		dprintk(0,"appletouch: incomplete data package"
+				" (first byte: %d, length: %d).\n",
+				dev->data[0], dev->urb->actual_length);
 		goto exit;
 	}
 
 	/* reorder the sensors values */
-	if (atp_is_geyser_3(dev)) {
+	if (dev->type == TYPE_GEYSER_3_4) {
 		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
 
 		/*
@@ -321,6 +743,22 @@
 		 * '-' is an unused value.
 		 */
 
+#if 0||DEBUG > 2
+		{
+			int i,a=1,b=0;
+			printk("raw (%i): Y=", dev->datalen);
+			for (i = 1; i < dev->datalen; i++)
+			{
+				if (i % 3 == a) printk("[");
+				if (b || a != 4) printk("%02x", (unsigned char)dev->data[i]);
+				if (i % 3 == a) printk("]");
+				if (i == 15) { if (b) printk(" ?="); a=4; }
+				if (i == 18) { printk(" X="); a=1; }
+				if (i == 48) { if (b) printk(" ?="); a=4; }
+			}
+			printk("\n");
+		}
+#endif
 		/* read X values */
 		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
 			dev->xy_cur[i] = dev->data[j + 1];
@@ -331,7 +769,8 @@
 			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
 			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
 		}
-	} else if (atp_is_geyser_2(dev)) {
+
+	} else if (dev->type == TYPE_GEYSER_2) {
 		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
 
 		/*
@@ -366,93 +805,214 @@
 		}
 	}
 
-	dbg_dump("sample", dev->xy_cur);
+#if 0||DEBUG > 2
+
+	{
+		int i;
+		printk("xy_cur: X=");
+		for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
+		{
+			printk("%+02i", (unsigned char)dev->xy_cur[i]);
+			if (i == ATP_XSENSORS-1) printk(" Y=");
+		}
+		printk("\n");
+	}
+	{
+		int i;
+		printk("xy_old: X=");
+		for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
+		{
+			printk("%+02i", (unsigned char)dev->xy_old[i]);
+			if (i == ATP_XSENSORS-1) printk(" Y=");
+		}
+		printk("\n");
+	}
+#endif
 
 	if (!dev->valid) {
-		/* first sample */
+		/* first sample after init or resume */
 		dev->valid = 1;
-		dev->x_old = dev->y_old = -1;
-		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
-
-		if (atp_is_geyser_3(dev)) /* No 17" Macbooks (yet) */
-			goto exit;
-
-		/* 17" Powerbooks have extra X sensors */
-		for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
+		dev->idle_counter = 0;
+		dev->threshold = start_threshold;
+		dev->ignored_palm = 0;
+		dev->first = 1;
+		dev->scrolling = 0;
+		memset(&dev->pos_old, 0, sizeof(dev->pos_old));
+
+		/* store first sample (possibly needed for older Geyser) */
+		if (dev->type != TYPE_GEYSER_3_4)
+		  memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+#ifdef DISABLED
+		/* 17" Powerbooks, 15" and 17" MacBooks have extra X sensors */
+		for (i = (dev->type == TYPE_GEYSER_2?15:16); i < ATP_XSENSORS; i++) {
 			if (!dev->xy_cur[i]) continue;
-
-			printk("appletouch: 17\" model detected.\n");
-			if(atp_is_geyser_2(dev))
-				input_set_abs_params(dev->input, ABS_X, 0,
-						     (20 - 1) *
-						     ATP_XFACT - 1,
-						     ATP_FUZZ, 0);
+			printk("appletouch: PowerBook 17\" or MacBookPro 17\" detected.\n");
+			printk("max_x = %i / max_y = %i\n", dev->max_x, dev->max_y);
+			if (dev->type == TYPE_GEYSER_2)
+				dev->max_x = ((20 - 1) * ATP_XFACT) - 1;
 			else
-				input_set_abs_params(dev->input, ABS_X, 0,
-						     (ATP_XSENSORS - 1) *
-						     ATP_XFACT - 1,
-						     ATP_FUZZ, 0);
+				dev->max_x = ((ATP_XSENSORS - 1) * ATP_XFACT) - 1;
 
+			printk("max_x = %i / max_y = %i\n", dev->max_x, dev->max_y);
+
+			/* recalculate the palm region */
+			calculate_palm_region(dev);
+
+			input_set_abs_params(dev->input, ABS_X, 0, dev->max_x, ATP_FUZZ, 0);
 			break;
 		}
+#endif
+		goto exit;
+	}
 
+	/* Just update the base values (i.e. touchpad in untouched state) */
+	if (dev->data[dev->datalen-1] & ATP_STATUS_BIT_BASE_UPDATE)
+	{
+		memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
 		goto exit;
 	}
 
+	/* get the change of each sensor */
+
 	for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
-		/* accumulate the change */
-		signed char change = dev->xy_old[i] - dev->xy_cur[i];
-		dev->xy_acc[i] -= change;
+		/* calculate the change */
+		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
 
-		/* prevent down drifting */
-		if (dev->xy_acc[i] < 0)
+		/* this is a round-robin value, so couple with that */
+		if (dev->xy_acc[i] > 127)
+			dev->xy_acc[i] -= 256;
+
+		if (dev->xy_acc[i] < -127)
+			dev->xy_acc[i] += 256;
+
+		/* Possibly this is needed for the older Geyser */
+		if (dev->type != TYPE_GEYSER_3_4)
+		{
+			/* store new 'untouched' value, if any new */
+			if (dev->xy_acc[i] < -1)
+				dev->xy_old[i] = dev->xy_cur[i];
+		}
+
+		/* prevent down-drifting */
+		if (dev->xy_acc[i] < 0) 
 			dev->xy_acc[i] = 0;
 	}
 
-	memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
+#if 0||DEBUG > 2
+	{
+		int i;
+		printk("xy_acc: S=%u, X=", dev->data[dev->datalen-1]);
+		for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
+		{
+			printk("%+02i", (int)dev->xy_acc[i]);
+			if (i == ATP_XSENSORS-1) printk(" Y=");
+		}
+		printk("\n");
+	}
+#endif
+
+	/* Renable palm area after the timeout occured */
+	if ((palm_area_state == ATP_PALM_AREA_TIMEOUT) && time_after(jiffies, key_timeout))
+	{
+		printk("appletouch: palm area (re)enabled after timeout\n");
+		palm_area_state = ATP_PALM_AREA_ENABLED;
+	}
 
-	dbg_dump("accumulator", dev->xy_acc);
+	/* calculate and choose coordinates */
+	atp_calc_and_choose_coords(dev, &xy_coord, &num_fingers, &x_count, &y_count);
 
-	x = atp_calculate_abs(dev->xy_acc, ATP_XSENSORS,
-			      ATP_XFACT, &x_z, &x_f);
-	y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
-			      ATP_YFACT, &y_z, &y_f);
-
-	if (x && y) {
-		if (dev->x_old != -1) {
-			x = (dev->x_old * 3 + x) >> 2;
-			y = (dev->y_old * 3 + y) >> 2;
-			dev->x_old = x;
-			dev->y_old = y;
-
-			if (debug > 1)
-				printk("appletouch: X: %3d Y: %3d "
-				       "Xz: %3d Yz: %3d\n",
-				       x, y, x_z, y_z);
+	x = xy_coord.x;
+	y = xy_coord.y;
+	pressure = (xy_coord.x_pressure + xy_coord.y_pressure) * ATP_PRESSURE_FACTOR / 2;
+	size = xy_coord.x_size + xy_coord.y_size;
+
+	/* save all coordinates we found */
+	memcpy(&dev->pos_old, &xy_coord, sizeof(dev->pos_old));
+
+#if 0||DEBUG > 0
+	printk("fingers=%i / x=%i / y=%i\n", num_fingers, x, y);
+#endif
+
+	/* is a touch reported and we have two valid coordinates? */
+	if ((num_fingers > 0) && x && y && !dev->ignored_palm)
+	{ 
+		dprintk(1,"appletouch: X: %3d Y: %3d P: %3d F: %d\n",
+				x, y, pressure, num_fingers);
+
+		/* If we touched the pad in the inner (non palm) 
+		   region, (re)enable the palm area */
+		if (dev->first && palm_detect &&
+		    (x > dev->palm_left) && (x < dev->palm_right))
+		{
+			palm_area_state = ATP_PALM_AREA_ENABLED;
+			printk("appletouch: palm area (re)enabled\n");
+		}
 
+		/* Report this touch */
+		if ((dev->old_x_count > x_count) || (dev->old_y_count > y_count))
+		{
+			input_report_key(dev->input, BTN_TOUCH, 0);
+			atp_report_fingers(dev->input, num_fingers);
+		}
+		else
+		{
 			input_report_key(dev->input, BTN_TOUCH, 1);
 			input_report_abs(dev->input, ABS_X, x);
 			input_report_abs(dev->input, ABS_Y, y);
-			input_report_abs(dev->input, ABS_PRESSURE,
-					 min(ATP_PRESSURE, x_z + y_z));
-			atp_report_fingers(dev->input, max(x_f, y_f));
+			input_report_abs(dev->input, ABS_PRESSURE, pressure);
+			atp_report_fingers(dev->input, num_fingers);
+			if (report_touchsize)
+				input_report_abs(dev->input, ABS_TOOL_WIDTH, size / num_fingers);
 		}
-		dev->x_old = x;
-		dev->y_old = y;
+
+		dev->threshold = threshold;
+		dev->first = 0;
 	}
-	else if (!x && !y) {
+	else if ((num_fingers == 0) && (x == 0) && (y == 0)) {
+
+		/* no touch reported => reset anything */
+		dev->threshold = start_threshold;
+		dev->first = 1;
+		dev->scrolling = 0;
 
-		dev->x_old = dev->y_old = -1;
+		dev->old_x_count = 0;
+		dev->old_y_count = 0;
+
+		/* reset the accumulator on release */
+		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+
+		/* report release */
 		input_report_key(dev->input, BTN_TOUCH, 0);
 		input_report_abs(dev->input, ABS_PRESSURE, 0);
 		atp_report_fingers(dev->input, 0);
 
-		/* reset the accumulator on release */
-		memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
+		// Idle counter
+		dev->idle_counter++;
+
+		// Wait for 10 more packages before suspending
+		if (dev->idle_counter > 10) {
+
+			// Get every 10th sample, reset counter
+			dev->idle_counter = 1;
+
+			// Reset Palm detector
+			dev->ignored_palm = 0;
+
+			/* Geyser 3/4 will continue to send packets continually after
+			   the first touch. The function is called every 8 milliseconds
+			   from interrups context, unless reinitialised. Do so if it's
+			   been idle for a while in order to avoid waking the kernel up
+			   several hundred times a second */
+
+			if (atp_is_geyser_3_or_4(dev))
+				schedule_work(&dev->reinit_thread);
+		}
 	}
 
-	input_report_key(dev->input, BTN_LEFT,
-			 !!dev->data[dev->datalen - 1]);
+	//printk("appletouch: modifier_pressed: %i / button-value: %u\n", modifier_pressed, dev->data[dev->datalen-1]);
+
+	input_report_key(dev->input, modifier_pressed ? BTN_RIGHT : BTN_LEFT,
+			dev->data[dev->datalen - 1] & ATP_STATUS_BIT_BUTTON);
 
 	input_sync(dev->input);
 
@@ -460,13 +1020,13 @@
 	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
 	if (retval) {
 		err("%s - usb_submit_urb failed with result %d",
-		    __FUNCTION__, retval);
+				__FUNCTION__, retval);
 	}
 }
 
 static int atp_open(struct input_dev *input)
 {
-	struct atp *dev = input_get_drvdata(input);
+	struct atp *dev = input->private;
 
 	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
 		return -EIO;
@@ -477,12 +1037,164 @@
 
 static void atp_close(struct input_dev *input)
 {
-	struct atp *dev = input_get_drvdata(input);
+	struct atp *dev = input->private;
 
 	usb_kill_urb(dev->urb);
 	dev->open = 0;
 }
 
+static int atp_geyser3_4_unknown_init(struct usb_device *udev)
+{
+#if 1||defined(GET_UNKNOWN_DATA_IS_UNUSED)
+	/*
+	 * This request is made by the original Apple Touchpad driver.
+	 * Does anybody have a clue, what this data is? 
+	 */
+
+#define RQ6_DATA_SIZE  0x59
+	char udata[RQ6_DATA_SIZE];
+	int usize;
+
+	usize = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			/* Request ID */ 6,
+			USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE,
+			/* RequestValue */ 0x2200, 
+			/* RequestIndex */ 1, 
+			&udata, RQ6_DATA_SIZE, 5000);
+
+	if (usize == RQ6_DATA_SIZE) {
+		int i;
+		printk("* Please send the following lines together with a description of your Mac\n"
+		       "* (MacBook/MacBookPro, Screen-Size, Revision, Build-Year) to: anders@anduras.de\n"
+		       "* This will help to improve this driver...\n");
+		printk("* RequestID 6 data received:\n* ");
+		for (i=0; i < usize; i++) 
+			printk("%02x ", (unsigned char)udata[i]); printk("\n");
+		printk("* Thank you!\n");
+	} else {
+		err("Could read data of requestid 6 from device");
+		return -EIO;
+	}
+#endif
+	return 0;
+}
+
+static int atp_geyser3_4_init(struct usb_device *udev)
+{
+	/*
+	 * By default Geyser 3 device sends standard USB HID mouse
+	 * packets (Report ID 2). This code changes device mode, so it
+	 * sends raw sensor reports (Report ID 5).
+	 */
+	char data[8];
+	int size;
+
+	size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
+			ATP_GEYSER3_MODE_READ_REQUEST_ID,
+			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			ATP_GEYSER3_MODE_REQUEST_VALUE,
+			ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+	if (size != 8) {
+		err("Could not do mode read request from device"
+				" (Geyser 3 mode)");
+		return -EIO;
+	}
+
+	/* Apply the mode switch */
+	data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
+
+	size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+			ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
+			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+			ATP_GEYSER3_MODE_REQUEST_VALUE,
+			ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+
+	if (size != 8) {
+		err("Could not do mode write request to device"
+				" (Geyser 3 mode)");
+		return -EIO;
+	}
+	return 0;
+}
+
+/* Reinitialise the device if it's a geyser 3 */
+static void atp_reinit(struct work_struct *reinit_thread)
+{
+	struct atp *dev = container_of(reinit_thread, struct atp, reinit_thread);
+	struct usb_device *udev = dev->udev;
+
+	dprintk(0,"appletouch: putting appletouch to sleep (reinit)\n");
+	atp_geyser3_4_init(udev);
+}
+
+static ssize_t atp_dev_write(struct file *file, const char __user *data,
+		size_t len, loff_t *ppos)
+{
+	if (len) {
+		size_t i;
+
+		/* scan to see what happened */
+		for (i = 0; i != len; i++) {
+			char c;
+			if (get_user(c, data+i))
+				return -EFAULT;
+
+			if (c == '0')       // normal key released
+			{
+				if (palm_area_state == ATP_PALM_AREA_DISABLED)
+				{
+					key_timeout = jiffies + msecs_to_jiffies(ATP_KEY_TIMEOUT_MSEC);
+					palm_area_state = ATP_PALM_AREA_TIMEOUT;
+				}
+			}
+			else if (c == '1')  // normal key pressed
+				palm_area_state = ATP_PALM_AREA_DISABLED;
+			else if (c == '2')  // modifier key released
+				modifier_pressed = 0;
+			else if (c == '3')  // modifier key pressed
+				modifier_pressed = 1;
+
+			// Ignore special keys: (c == '4') and (c == '5') 
+		}
+	}
+
+	return len;
+}
+
+static int atp_dev_open(struct inode *inode, struct file *file)
+{
+	/* device can only be opened once */
+	if (test_and_set_bit(0, &device_opened))
+		return -EBUSY;
+
+	return nonseekable_open(inode, file);
+}
+
+static int atp_dev_release(struct inode *inode, struct file *file)
+{
+	clear_bit(0, &device_opened);
+	palm_area_state = ATP_PALM_AREA_ENABLED;
+	modifier_pressed = 0;
+	key_timeout = 0;
+	return 0;
+}
+
+/* file operation pointers */
+static const struct file_operations atp_dev_fops = {
+	.owner = THIS_MODULE,
+	.write = atp_dev_write,
+	.open = atp_dev_open,
+	.release = atp_dev_release,
+};
+
+/* class driver information */
+static struct usb_class_driver atp_class = {
+	.name = "appletouch",
+	.fops = &atp_dev_fops,
+	.minor_base = USB_APPLETOUCH_MINOR_BASE,
+};
+
 static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
 {
 	struct atp *dev;
@@ -491,7 +1203,8 @@
 	struct usb_host_interface *iface_desc;
 	struct usb_endpoint_descriptor *endpoint;
 	int int_in_endpointAddr = 0;
-	int i, error = -ENOMEM;
+	int i, retval = -ENOMEM;
+	int x_sensors, y_sensors;
 
 	/* set up the endpoint information */
 	/* use only the first interrupt-in endpoint */
@@ -517,66 +1230,52 @@
 		goto err_free_devs;
 	}
 
+	/* Initialize dev struct */
 	dev->udev = udev;
 	dev->input = input_dev;
 	dev->overflowwarn = 0;
-	if (atp_is_geyser_3(dev))
-		dev->datalen = 64;
+
+	/* Detect type of touchpad */
+	if (atp_is_geyser_3_or_4(dev))
+		dev->type = TYPE_GEYSER_3_4;
 	else if (atp_is_geyser_2(dev))
-		dev->datalen = 64;
+		dev->type = TYPE_GEYSER_2;
 	else
-		dev->datalen = 81;
-
-	if (atp_is_geyser_3(dev)) {
-		/*
-		 * By default Geyser 3 device sends standard USB HID mouse
-		 * packets (Report ID 2). This code changes device mode, so it
-		 * sends raw sensor reports (Report ID 5).
-		 */
-		char data[8];
-		int size;
+		dev->type = TYPE_GEYSER_1;
 
-		size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
-			ATP_GEYSER3_MODE_READ_REQUEST_ID,
-			USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-			ATP_GEYSER3_MODE_REQUEST_VALUE,
-			ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
+	/* Set data length */
+	if (dev->type == TYPE_GEYSER_1)
+		dev->datalen = 81;
+	else
+		dev->datalen = 64;
 
-		if (size != 8) {
-			err("Could not do mode read request from device"
-							" (Geyser 3 mode)");
+	/* Switch Geyser3/4 device to raw sensor mode */
+	if (dev->type == TYPE_GEYSER_3_4) {
+		if (atp_geyser3_4_unknown_init(udev))
 			goto err_free_devs;
-		}
-
-		/* Apply the mode switch */
-		data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
 
-		size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
-			ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
-			USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
-			ATP_GEYSER3_MODE_REQUEST_VALUE,
-			ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
-
-		if (size != 8) {
-			err("Could not do mode write request to device"
-							" (Geyser 3 mode)");
+		if (atp_geyser3_4_init(udev))
 			goto err_free_devs;
-		}
-		printk("appletouch Geyser 3 inited.\n");
 	}
 
+	/* Create usb interrupt urb */
 	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!dev->urb)
+	if (!dev->urb) {
+		retval = -ENOMEM;
 		goto err_free_devs;
+	}
 
 	dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
-				     &dev->urb->transfer_dma);
-	if (!dev->data)
+			&dev->urb->transfer_dma);
+	if (!dev->data) {
+		retval = -ENOMEM;
 		goto err_free_urb;
+	}
 
 	usb_fill_int_urb(dev->urb, udev,
-			 usb_rcvintpipe(udev, int_in_endpointAddr),
-			 dev->data, dev->datalen, atp_complete, dev, 1);
+			usb_rcvintpipe(udev, int_in_endpointAddr),
+			dev->data, dev->datalen, atp_complete, dev, 1);
+
 
 	usb_make_path(udev, dev->phys, sizeof(dev->phys));
 	strlcat(dev->phys, "/input0", sizeof(dev->phys));
@@ -584,43 +1283,64 @@
 	input_dev->name = "appletouch";
 	input_dev->phys = dev->phys;
 	usb_to_input_id(dev->udev, &input_dev->id);
-	input_dev->dev.parent = &iface->dev;
-
-	input_set_drvdata(input_dev, dev);
+	input_dev->cdev.dev = &iface->dev;
 
+	input_dev->private = dev;
 	input_dev->open = atp_open;
 	input_dev->close = atp_close;
 
-	set_bit(EV_ABS, input_dev->evbit);
-
-	if (atp_is_geyser_3(dev)) {
+	/* Set number of sensors */
+	if (dev->type == TYPE_GEYSER_3_4) {
 		/*
 		 * MacBook have 20 X sensors, 10 Y sensors
 		 */
-		input_set_abs_params(input_dev, ABS_X, 0,
-				     ((20 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
-		input_set_abs_params(input_dev, ABS_Y, 0,
-				     ((10 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
-	} else if (atp_is_geyser_2(dev)) {
+		x_sensors = 20;
+		y_sensors = 10;
+
+		/* 17" MacBooks have 26 X sensors */
+		//if (seventeen)
+		//  x_sensors = 26;
+
+	} else	if (dev->type == TYPE_GEYSER_2) {
 		/*
-		 * Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
-		 * later.
+		 * Oct 2005 15" PowerBooks have 15 X sensors, 9 Y sensors
 		 */
-		input_set_abs_params(input_dev, ABS_X, 0,
-				     ((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
-		input_set_abs_params(input_dev, ABS_Y, 0,
-				     ((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+		x_sensors = 16;
+		y_sensors = 9;
+
+		/* Oct 2005 17" Powerbooks have 20 X sensors */
+		if (seventeen)
+			x_sensors = 20;
 	} else {
 		/*
-		 * 12" and 15" Powerbooks only have 16 x sensors,
-		 * 17" models are detected later.
+		 * 12" and 15" PowerBooks have 15 X sensors, 9 Y sensors
 		 */
-		input_set_abs_params(input_dev, ABS_X, 0,
-				     (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
-		input_set_abs_params(input_dev, ABS_Y, 0,
-				     (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
+		x_sensors = 16;
+		y_sensors = 9;
+
+		/* 17" Powerbooks have 26 X sensors */
+		if (seventeen)
+			x_sensors = 26;
 	}
-	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
+
+	/* calculate X/Y maximum coordinates */
+	dev->max_x = ((x_sensors - 1) * ATP_XFACT) - 1;
+	dev->max_y = ((y_sensors - 1) * ATP_YFACT) - 1;
+
+	/* calculate the palm region */
+	calculate_palm_region(dev);
+
+	/* init input-event parts */
+	set_bit(EV_ABS, input_dev->evbit);
+
+	/* set the input system data */
+	input_set_abs_params(input_dev, ABS_X, 0, dev->max_x, ATP_FUZZ, 0);
+	input_set_abs_params(input_dev, ABS_Y, 0, dev->max_y, ATP_FUZZ, 0);
+	input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_MAX_PRESSURE, 0, 0);
+
+	/* The synaptics driver wants this! So, just to be sure it is set... */
+	/* (Is this set by default?) */
+	set_bit(EV_SYN, input_dev->evbit);
 
 	set_bit(EV_KEY, input_dev->evbit);
 	set_bit(BTN_TOUCH, input_dev->keybit);
@@ -628,26 +1348,39 @@
 	set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
 	set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
 	set_bit(BTN_LEFT, input_dev->keybit);
+	set_bit(BTN_RIGHT, input_dev->keybit);
+	if (report_touchsize)
+		set_bit(ABS_TOOL_WIDTH, input_dev->absbit);
 
-	error = input_register_device(dev->input);
-	if (error)
-		goto err_free_buffer;
+	input_register_device(dev->input);
 
 	/* save our data pointer in this interface device */
 	usb_set_intfdata(iface, dev);
 
+	/* we can register the device now, as it is ready */
+	retval = usb_register_dev(iface, &atp_class);
+	if (retval) {
+		/* something prevented us from registering this device */
+		err("Unable to allocate minor number.");
+		usb_set_intfdata(iface, NULL);
+		goto err_free_urb;
+	}
+
+	/* report success */
+	dev_info(&iface->dev, "Appletouch device now attached\n");
+
+	/* initialize kernel thread for re-init out of interrupt context */
+	INIT_WORK(&dev->reinit_thread, atp_reinit);
+
 	return 0;
 
- err_free_buffer:
-	usb_buffer_free(dev->udev, dev->datalen,
-			dev->data, dev->urb->transfer_dma);
- err_free_urb:
+err_free_urb:
 	usb_free_urb(dev->urb);
- err_free_devs:
+err_free_devs:
 	usb_set_intfdata(iface, NULL);
 	kfree(dev);
 	input_free_device(input_dev);
-	return error;
+	return retval;
 }
 
 static void atp_disconnect(struct usb_interface *iface)
@@ -655,7 +1388,17 @@
 	struct atp *dev = usb_get_intfdata(iface);
 
 	usb_set_intfdata(iface, NULL);
+
+	/* give back our minor */
+	usb_deregister_dev(iface, &atp_class);
+
+	/* cleanup allocated memory */
 	if (dev) {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
+		cancel_work_sync(&dev->reinit_thread);
+#else
+		flush_scheduled_work();
+#endif
 		usb_kill_urb(dev->urb);
 		input_unregister_device(dev->input);
 		usb_buffer_free(dev->udev, dev->datalen,

Reply to: