Hello!

On the last four month I tried to improve the appletouch driver.
This is the first version of my new driver.
The driver is developed for Linux kernel 2.6.21 or newer.

I tried to implement a palm detection, make the driver more robust and
add correct suspend (the PowerTop problem).

First: If you want to use the touchpad, use the newest X11 Synaptics
       driver from GIT. The version 0.14.6 is NOT new enough!

To improve the palm detection, I implemented for testing a little
helper daemon (appletouchd.c), which will report pressed keys
to the driver and ignore touches then. Additionally I tried with this
daemon to enable the Ctrl modifier key to emulate the second touchpad
button, but it does not work correctly.
Alternatively you can use the "syndaemon" helper.

I would like to implement the reporting of keys to the driver in
kernel space, but I have no clue how to do this.

This version has many debug outputs, which you can enable if you
want. Feel free to play with the driver and give some feedback.
Don't expect the driver to be full usable. I have to clean it up
and optimize some parts...

Known problems:
 - After two finger scrolling with one finger fixed and the other
   used for scrolling, the pointer jumps some pixels.
 - When using the helper daemon, pressing the Ctrl key together with
   the touchpad button, it well generate a second button event, but
   in some way the keyboard will block it. You can see it via evtest,
   but it never reaches the GUI...

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) 2006-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/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_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.
 */
#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			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 needed for 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 };

/* Is the palm detect currently enabled? */
/* (It's enabled, if we pressed a key on the keyboard and */
/*  disabled again, if we touch the inner region) */
static int palm_detect_active = 0;

/* Is the modifier key pressed? */
static int modifier_pressed = 0;

/* Is the support devices open? */
static unsigned long device_opened = 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;

		    /* Activate palm region too */
		    palm_detect_active = 1;
		    printk("appletouch: palm region 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
	}
	} // ignored_palm

	if (!dev->scrolling && palm_detect)
	{
	      /* Ignore touches in the palm region, if activated */
	      if (palm_detect_active)
	      {
		    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 num_fingers, pressure, size;
	int retval, i, j;
	struct atp *dev = urb->context;

	//dprintk(0,"appletouch: in 'atp_complete'...\n");

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

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

	int x_count, y_count;

        /* calculate and choose coordinates */
	//atp_calc_and_choose_coords(dev, &xy_coord, &num_fingers);
	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-region too */
			if (dev->first && palm_detect &&
			    (x > dev->palm_left) && (x < dev->palm_right))
			{
			    palm_detect_active = 0;
			    printk("appletouch: palm region (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->pos_old.x = x;
			//dev->pos_old.y = y;
			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->valid++;

		// Wait for 10 more packages before suspending
		if (dev->valid > 10) {

		        // Enable palm region for normal touches
		        //palm_detect_active = 0;
		        //printk("appletouch: palm region (re)enabled\n");
		        
		        // Get every 100th sample, reset counter
		        dev->valid = 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_init(struct usb_device *udev)
{

#if 0||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("RequestID 6 data received (1):\n");
	        for (i=0; i < size; i++) 
		  printk("%02x ", (unsigned char)udata[i]); printk("\n");
	} else {
		err("Could read data of requestid 6 from device");
		return -EIO;
	}
#endif

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

			dprintk(0, "appletouch: got message: %c\n", c);

                        if (c == '0')                  
			        palm_detect_active = 0; // normal key released
                        else if (c == '1')
			        palm_detect_active = 1; // normal key pressed
                        else if (c == '2')
			        modifier_pressed = 0;   // modifier released
                        else if (c == '3')
			        modifier_pressed = 1;   // modifier pressed
                }
        }

        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_detect_active = 0;
	modifier_pressed = 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;

	/* Perform special initializations */
	if (dev->type == TYPE_GEYSER_3_4) {
		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_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) {
		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 MODIFIER_KEY 		KEY_LEFTCTRL

int main (int argc, char **argv)
{
  int fd, rd, i, wfd;
  struct input_event ev[64];
  unsigned short id[4];
  unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
  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)
      {
      	if (ev[i].code == MODIFIER_KEY)
        {
          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
        {
          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);
          }
        }
      }
    }
  }
}
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