Hello!

Here is a new version of my appletouch driver.
If anybody has time, please test it and give some feedback!

The second-mouse-button-emulation now works with the 'fn' key pressed,
unfortunatly not with the 'ctrl' key...

The problem with scrolling still persist, but this version is almost
usable...

I need some feedback from users, who have access to an PowerBook, because I
want to be sure, these are still supported...

Regards
 Sven

-- 
 Sven Anders <[EMAIL PROTECTED]>                 () Ascii Ribbon Campaign
                                                 /\ Support plain text e-mail
 ANDURAS service solutions AG
 Innstraße 71 - 94036 Passau - Germany
 Web: www.anduras.de - Tel: +49 (0)851-4 90 50-0 - Fax: +49 (0)851-4 90 50-55

Rechtsform: Aktiengesellschaft - Sitz: Passau - Amtsgericht Passau HRB 6032
Mitglieder des Vorstands: Sven Anders, Marcus Junker
Vorsitzender des Aufsichtsrats: Dipl. Kfm. Thomas Träger
/* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
 *
 * Copyright (C) 2001-2004 Greg Kroah-Hartman ([EMAIL PROTECTED])
 * Copyright (C) 2005      Johannes Berg ([EMAIL PROTECTED])
 * Copyright (C) 2005      Stelian Pop ([EMAIL PROTECTED])
 * Copyright (C) 2005      Frank Arnold ([EMAIL PROTECTED])
 * Copyright (C) 2005      Peter Osterlund ([EMAIL PROTECTED])
 * Copyright (C) 2005      Michael Hanselmann ([EMAIL PROTECTED])
 * Copyright (C) 2006      Nicolas Boichat ([EMAIL PROTECTED])
 * Copyright (C) 2006      Jason Parekh ([EMAIL PROTECTED])
 * Copyright (C) 2007      Sven Anders ([EMAIL PROTECTED])
 *
 * Thanks to Alex Harper <[EMAIL PROTECTED]> for his inputs.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#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

/* These names come from Info.plist in AppleUSBTrackpad.kext */
#define FOUNTAIN_ANSI_PRODUCT_ID	0x020E
#define FOUNTAIN_ISO_PRODUCT_ID		0x020F

#define FOUNTAIN_TP_ONLY_PRODUCT_ID	0x030A

#define GEYSER1_TP_ONLY_PRODUCT_ID	0x030B

#define GEYSER_ANSI_PRODUCT_ID		0x0214
#define GEYSER_ISO_PRODUCT_ID		0x0215
#define GEYSER_JIS_PRODUCT_ID		0x0216

/* MacBook devices */
#define GEYSER3_ANSI_PRODUCT_ID		0x0217
#define GEYSER3_ISO_PRODUCT_ID		0x0218
#define GEYSER3_JIS_PRODUCT_ID		0x0219

/*
 * 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 ATP_DEVICE(prod)					\
	.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,				\
	.bInterfaceProtocol = 0x02

/* table of devices that work with this driver */
static struct usb_device_id atp_table [] = {
	{ ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
	{ ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
	{ ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },

	/* PowerBooks Oct 2005 */
	{ ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },

	/* Core Duo MacBook & MacBook Pro */
	{ ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },

	/* Core2 Duo MacBook & MacBook Pro */
	{ ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
	{ ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },

	/* Terminating entry */
	{ }
};
MODULE_DEVICE_TABLE (usb, atp_table);

/*
 * number of sensors. Note that only 16 instead of 26 X (horizontal)
 * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
 * (vertical) sensors.
 */
#define ATP_XSENSORS	26
#define ATP_YSENSORS	16

/* amount of fuzz this touchpad generates */
#define ATP_FUZZ	16

/* maximum pressure this driver will report */
#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 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 // 86

/*
 * 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		2
#define ATP_START_THRESHOLD	7
#define ATP_FINGER_THRESHOLD	8
#define ATP_PALM_THRESHOLD	35

/* 
 * 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 */
	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			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 dprintk(level, format, a...)					\
	do {								\
		if (debug > level) printk(format, ##a);				\
	} while (0)

MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann, Sven Anders");
MODULE_DESCRIPTION("Apple PowerBooks/MacBooks USB touchpad driver");
MODULE_LICENSE("GPL");

/*
 * 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");

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)
{
	u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);

	return (productId == GEYSER_ANSI_PRODUCT_ID) ||
		(productId == GEYSER_ISO_PRODUCT_ID) ||
		(productId == GEYSER_JIS_PRODUCT_ID);
}

/* 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);

	return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
		(productId == GEYSER3_ISO_PRODUCT_ID) ||
		(productId == GEYSER3_JIS_PRODUCT_ID) ||
		(productId == GEYSER4_ANSI_PRODUCT_ID) ||
		(productId == GEYSER4_ISO_PRODUCT_ID) ||
		(productId == GEYSER4_JIS_PRODUCT_ID);
}

/* 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;
	int is_increasing = 0;
	int is_decreasing = 0;
	int was_increasing = 0;
	int was_decreasing = 0;
	int current_block = 0;
	int highest_pressure = 0;

	/* values to calculate mean */
	int pcum = 0, psum = 0, pnum = 0;

	/* 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;

		/* 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] - 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];

	}

	/* 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;
		}
	}

	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)
{
	input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
	input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
	input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}

static void atp_complete(struct urb* urb)
{
	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;
	}

	/* drop incomplete datasets */
	if (dev->urb->actual_length != dev->datalen) {
		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 (dev->type == TYPE_GEYSER_3_4) {
		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));

		/*
		 * The values are laid out like this:
		 * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
		 * '-' 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];
			dev->xy_cur[i + 1] = dev->data[j + 2];
		}
		/* read Y values */
		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
		}

	} else if (dev->type == TYPE_GEYSER_2) {
		memset(dev->xy_cur, 0, sizeof(dev->xy_cur));

		/*
		 * The values are laid out like this:
		 * Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
		 * '-' is an unused value.
		 */

		/* read X values */
		for (i = 0, j = 19; i < 20; i += 2, j += 3) {
			dev->xy_cur[i] = dev->data[j];
			dev->xy_cur[i + 1] = dev->data[j + 1];
		}

		/* read Y values */
		for (i = 0, j = 1; i < 9; i += 2, j += 3) {
			dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
			dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
		}
	} else {
		for (i = 0; i < 8; i++) {
			/* X values */
			dev->xy_cur[i     ] = dev->data[5 * i +  2];
			dev->xy_cur[i +  8] = dev->data[5 * i +  4];
			dev->xy_cur[i + 16] = dev->data[5 * i + 42];
			if (i < 2)
				dev->xy_cur[i + 24] = dev->data[5 * i + 44];

			/* Y values */
			dev->xy_cur[i + 26] = dev->data[5 * i +  1];
			dev->xy_cur[i + 34] = dev->data[5 * i +  3];
		}
	}

#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 after init or resume */
		dev->valid = 1;
		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: 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
				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++) {
		/* calculate the change */
		dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];

		/* 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;
	}

#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;
	}

	/* calculate and choose coordinates */
	atp_calc_and_choose_coords(dev, &xy_coord, &num_fingers, &x_count, &y_count);

	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, pressure);
			atp_report_fingers(dev->input, num_fingers);
			if (report_touchsize)
				input_report_abs(dev->input, ABS_TOOL_WIDTH, size / num_fingers);
		}

		dev->threshold = threshold;
		dev->first = 0;
	}
	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->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);

		// 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);
		}
	}

	//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);

exit:
	retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
	if (retval) {
		err("%s - usb_submit_urb failed with result %d",
				__FUNCTION__, retval);
	}
}

static int atp_open(struct input_dev *input)
{
	struct atp *dev = input->private;

	if (usb_submit_urb(dev->urb, GFP_ATOMIC))
		return -EIO;

	dev->open = 1;
	return 0;
}

static void atp_close(struct input_dev *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: [EMAIL PROTECTED]"
		       "* 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;
	struct input_dev *input_dev;
	struct usb_device *udev = interface_to_usbdev(iface);
	struct usb_host_interface *iface_desc;
	struct usb_endpoint_descriptor *endpoint;
	int int_in_endpointAddr = 0;
	int i, retval = -ENOMEM;
	int x_sensors, y_sensors;

	/* set up the endpoint information */
	/* use only the first interrupt-in endpoint */
	iface_desc = iface->cur_altsetting;
	for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
		endpoint = &iface_desc->endpoint[i].desc;
		if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
			/* we found an interrupt in endpoint */
			int_in_endpointAddr = endpoint->bEndpointAddress;
			break;
		}
	}
	if (!int_in_endpointAddr) {
		err("Could not find int-in endpoint");
		return -EIO;
	}

	/* allocate memory for our device state and initialize it */
	dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
	input_dev = input_allocate_device();
	if (!dev || !input_dev) {
		err("Out of memory");
		goto err_free_devs;
	}

	/* Initialize dev struct */
	dev->udev = udev;
	dev->input = input_dev;
	dev->overflowwarn = 0;

	/* 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->type = TYPE_GEYSER_2;
	else
		dev->type = TYPE_GEYSER_1;

	/* Set data length */
	if (dev->type == TYPE_GEYSER_1)
		dev->datalen = 81;
	else
		dev->datalen = 64;

	/* 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;

		if (atp_geyser3_4_init(udev))
			goto err_free_devs;
	}

	/* Create usb interrupt urb */
	dev->urb = usb_alloc_urb(0, GFP_KERNEL);
	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) {
		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_make_path(udev, dev->phys, sizeof(dev->phys));
	strlcat(dev->phys, "/input0", sizeof(dev->phys));

	input_dev->name = "appletouch";
	input_dev->phys = dev->phys;
	usb_to_input_id(dev->udev, &input_dev->id);
	input_dev->cdev.dev = &iface->dev;

	input_dev->private = dev;
	input_dev->open = atp_open;
	input_dev->close = atp_close;

	/* Set number of sensors */
	if (dev->type == TYPE_GEYSER_3_4) {
		/*
		 * MacBook have 20 X sensors, 10 Y sensors
		 */
		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, 9 Y sensors
		 */
		x_sensors = 16;
		y_sensors = 9;

		/* Oct 2005 17" Powerbooks have 20 X sensors */
		if (seventeen)
			x_sensors = 20;
	} else {
		/*
		 * 12" and 15" PowerBooks have 15 X sensors, 9 Y sensors
		 */
		x_sensors = 16;
		y_sensors = 9;

		/* 17" Powerbooks have 26 X sensors */
		if (seventeen)
			x_sensors = 26;
	}

	/* 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);
	set_bit(BTN_TOOL_FINGER, input_dev->keybit);
	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);

	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_urb:
	usb_free_urb(dev->urb);
err_free_devs:
	usb_set_intfdata(iface, NULL);
	kfree(dev);
	input_free_device(input_dev);
	return retval;
}

static void atp_disconnect(struct usb_interface *iface)
{
	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,
				dev->data, dev->urb->transfer_dma);
		usb_free_urb(dev->urb);
		kfree(dev);
	}
	printk(KERN_INFO "input: appletouch disconnected\n");
}

static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
	struct atp *dev = usb_get_intfdata(iface);
	usb_kill_urb(dev->urb);
	dev->valid = 0;
	return 0;
}

static int atp_resume(struct usb_interface *iface)
{
	struct atp *dev = usb_get_intfdata(iface);
	if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
		return -EIO;

	return 0;
}

static struct usb_driver atp_driver = {
	.name		= "appletouch",
	.probe		= atp_probe,
	.disconnect	= atp_disconnect,
	.suspend	= atp_suspend,
	.resume		= atp_resume,
	.id_table	= atp_table,
};

static int __init atp_init(void)
{
	return usb_register(&atp_driver);
}

static void __exit atp_exit(void)
{
	usb_deregister(&atp_driver);
}

module_init(atp_init);
module_exit(atp_exit);
/*
 * appletouchd.c
 *
 *  Copyright (c) 2007 Sven Anders <[EMAIL PROTECTED]>
 *
 *  Appletouch driver support daemon
 */

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 * 
 * Should you need to contact me, the author, you can do so either by
 * e-mail - mail your message to <[EMAIL PROTECTED]>, or by paper mail:
 * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
 */

#include <stdint.h>

#include <linux/input.h>

#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

#define DEBUG

#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x)  ((x)%BITS_PER_LONG)
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array)	((array[LONG(bit)] >> OFF(bit)) & 1)

#define VENDOR_APPLE 		0x5ac
#define VENDOR_KEYBOARD_MBP 	0x21b

#define APPLE_KEYBOARD_EVENT_DEV "/dev/input/by-id/usb-Apple_Computer_Apple_Internal_Keyboard_._Trackpad-event-kbd"
#define APPLE_TOUCHPAD_DEV       "/dev/appletouch"

#define VALUE_KEY_UP		0
#define VALUE_KEY_DOWN		1
#define VALUE_KEY_REPEAT	2

#define KEY_APPLE_FN		464		// This is special Apple 'fn' key

//#define MODIFIER_KEY 		KEY_LEFTCTRL	// Does not work at all...
//#define MODIFIER_KEY 		KEY_LEFTALT	// Does not work correctly
//#define MODIFIER_KEY 		KEY_LEFTMETA	// Works
//#define MODIFIER_KEY 		KEY_RIGHTMETA	// Works
#define MODIFIER_KEY 		KEY_APPLE_FN	// Works

#define SPECIAL_KEYS		{ KEY_APPLE_FN, \
				  KEY_CAPSLOCK, \
				  KEY_LEFTCTRL, KEY_RIGHTCTRL, \
				  KEY_LEFTSHIFT, KEY_RIGHTSHIFT, \
				  KEY_LEFTALT, KEY_RIGHTALT, \
				  KEY_LEFTMETA, KEY_RIGHTMETA, \
				  -1}


int main (int argc, char **argv)
{
  int fd, rd, i, wfd, j;
  struct input_event ev[64];
  unsigned short id[4];
  unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
  int special_keys[] = SPECIAL_KEYS;
  char name[256] = "Unknown";
  
  if ((argc == 2) && (argv[1] != NULL)) {
    printf("Usage: appletouchd\n");
    printf("Appletouch kernel driver support daemon\n");
    return 1;
  }
  
  if ((fd = open(APPLE_KEYBOARD_EVENT_DEV, O_RDONLY)) < 0) {
    perror("appletouchd: opening " APPLE_KEYBOARD_EVENT_DEV);
    return 1;
  }

  if ((wfd = open(APPLE_TOUCHPAD_DEV, O_WRONLY)) < 0) {
    perror("appletouchd: opening " APPLE_TOUCHPAD_DEV);
    return 1;
  }
  
  /* Check of an Apple computer with a known keyboard product id */
  
  ioctl(fd, EVIOCGID, id);
  ioctl(fd, EVIOCGNAME(sizeof(name)), name);
  if (id[ID_VENDOR] != VENDOR_APPLE)
    fprintf(stderr, "appletouchd: This program may be useless on a none Apple computer\n");
  else if (id[ID_PRODUCT] != VENDOR_KEYBOARD_MBP)
    fprintf(stderr, "appletouchd: The device '%s' is unknown. It may not work. Proceeding anyway...\n", name);
  
  /* Search for a key event - must have an keyboard */
  memset(bit, 0, sizeof(bit));
  ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
  if (!test_bit(EV_KEY, bit[0]))
  {
    fprintf(stderr, "appletouchd: This device is not a keyboard\n");
    return 1;
  }
  
  while (1)
  {
    rd = read(fd, ev, sizeof(struct input_event) * 64);
  
    if (rd < (int) sizeof(struct input_event))
    {
      perror("\nappletouchd: error reading");
      return 1;
    }
  
    for (i = 0; i < rd / sizeof(struct input_event); i++)
    {
      if (ev[i].type == EV_KEY)
      {
	/* Was the touchpad modifier key pressed? */
      	if (ev[i].code == MODIFIER_KEY)
        {
	  /* Yes ... */
          if (ev[i].value == VALUE_KEY_UP)
          {
#ifdef DEBUG
            fprintf(stderr, "appletouchd: Touchpad modifier key released\n");
#endif
            write(wfd,"2",1);
          }
          else if (ev[i].value == VALUE_KEY_DOWN)
          {
#ifdef DEBUG
            fprintf(stderr, "appletouchd: Touchpad modifier key pressed\n");
#endif
            write(wfd,"3",1);
          }
        }
        else
        {
	  /* No ... */

	  /* Was a special key pressed? */
	  for (j=0; special_keys[j] != -1; j++)
	  {
	    if (ev[i].code == special_keys[j])
	    {
	      if (ev[i].value == VALUE_KEY_UP)
	      {
#ifdef DEBUG
		fprintf(stderr, "appletouchd: Special key released\n");
#endif
		write(wfd,"4",1);
	      }
	      else if (ev[i].value == VALUE_KEY_DOWN)
	      {
#ifdef DEBUG
		fprintf(stderr, "appletouchd: Special key pressed\n");
#endif
		write(wfd,"5",1);
	      }
	      break;
	    }
	  }
	  
	  /* No special key found? */
	  if (special_keys[j] == -1)
	  {
	    if (ev[i].value == VALUE_KEY_UP)
	    {
#ifdef DEBUG
	      fprintf(stderr, "appletouchd: Some key released\n");
#endif
	      write(wfd,"0",1);
	    }
	    else if (ev[i].value == VALUE_KEY_DOWN)
	    {
#ifdef DEBUG
	      fprintf(stderr, "appletouchd: Some key pressed\n");
#endif
	      write(wfd,"1",1);
	    }
          }
        }
      }
    }
  }
}
# Needed for Kernel compile
obj-m := appletouch.o

# Config
KSRC=/usr/src/linux
MODPATH=/lib/modules/`uname -r`/kernel/drivers/usb/input

# Targets
all: appletouch.ko appletouchd

appletouch.ko: appletouch.c
        $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules

appletouchd: appletouchd.c
        gcc -Wall -O2 -D_GNU_SOURCE -o appletouchd appletouchd.c 

install: appletouch.ko appletouchd
        @if [ ! -f appletouch.ko.orig ]; then \
          cp $(MODPATH)/appletouch.ko appletouch.ko.orig; \
        fi
        install -m 644 appletouch.ko $(MODPATH)
        #install -m 755 appletouchd /usr/sbin/

clean:
        -rm *.o appletouchd Module.symvers
        $(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean
begin:vcard
fn:Sven Anders
n:Anders;Sven
org:ANDURAS AG;Research and Development
adr;quoted-printable:;;Innstra=C3=9Fe 71;Passau;Bavaria;94036;Germany
email;internet:[EMAIL PROTECTED]
title:Dipl. Inf.
tel;work:++49 (0)851 / 490 50 -0
tel;fax:++49 (0)851 / 590 50 - 55
x-mozilla-html:FALSE
url:http://www.anduras.de
version:2.1
end:vcard

Attachment: signature.asc
Description: OpenPGP digital signature

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Mactel-linux-devel mailing list
Mactel-linux-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mactel-linux-devel

Reply via email to