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