Hello! Here is a new version of my appletouch driver. If anybody has time, please test it and give some feedback!
The second-mouse-button-emulation now works with the 'fn' key pressed,
unfortunatly not with the 'ctrl' key...
The problem with scrolling still persist, but this version is almost
usable...
I need some feedback from users, who have access to an PowerBook, because I
want to be sure, these are still supported...
Regards
Sven
--
Sven Anders <[EMAIL PROTECTED]> () Ascii Ribbon Campaign
/\ Support plain text e-mail
ANDURAS service solutions AG
Innstraße 71 - 94036 Passau - Germany
Web: www.anduras.de - Tel: +49 (0)851-4 90 50-0 - Fax: +49 (0)851-4 90 50-55
Rechtsform: Aktiengesellschaft - Sitz: Passau - Amtsgericht Passau HRB 6032
Mitglieder des Vorstands: Sven Anders, Marcus Junker
Vorsitzender des Aufsichtsrats: Dipl. Kfm. Thomas Träger
/* Apple USB Touchpad (for post-February 2005 PowerBooks and MacBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman ([EMAIL PROTECTED])
* Copyright (C) 2005 Johannes Berg ([EMAIL PROTECTED])
* Copyright (C) 2005 Stelian Pop ([EMAIL PROTECTED])
* Copyright (C) 2005 Frank Arnold ([EMAIL PROTECTED])
* Copyright (C) 2005 Peter Osterlund ([EMAIL PROTECTED])
* Copyright (C) 2005 Michael Hanselmann ([EMAIL PROTECTED])
* Copyright (C) 2006 Nicolas Boichat ([EMAIL PROTECTED])
* Copyright (C) 2006 Jason Parekh ([EMAIL PROTECTED])
* Copyright (C) 2007 Sven Anders ([EMAIL PROTECTED])
*
* Thanks to Alex Harper <[EMAIL PROTECTED]> for his inputs.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb/input.h>
#include <asm/uaccess.h>
/* Debug level */
#define DEBUG 0
/* Device numbers */
#define USB_APPLETOUCH_MINOR_BASE 66
/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID 0x05AC
/* These names come from Info.plist in AppleUSBTrackpad.kext */
#define FOUNTAIN_ANSI_PRODUCT_ID 0x020E
#define FOUNTAIN_ISO_PRODUCT_ID 0x020F
#define FOUNTAIN_TP_ONLY_PRODUCT_ID 0x030A
#define GEYSER1_TP_ONLY_PRODUCT_ID 0x030B
#define GEYSER_ANSI_PRODUCT_ID 0x0214
#define GEYSER_ISO_PRODUCT_ID 0x0215
#define GEYSER_JIS_PRODUCT_ID 0x0216
/* MacBook devices */
#define GEYSER3_ANSI_PRODUCT_ID 0x0217
#define GEYSER3_ISO_PRODUCT_ID 0x0218
#define GEYSER3_JIS_PRODUCT_ID 0x0219
/*
* Geyser IV: same as Geyser III according to Info.plist in AppleUSBTrackpad.kext
* -> same IOClass (AppleUSBGrIIITrackpad), same acceleration tables
*/
#define GEYSER4_ANSI_PRODUCT_ID 0x021A
#define GEYSER4_ISO_PRODUCT_ID 0x021B
#define GEYSER4_JIS_PRODUCT_ID 0x021C
#define ATP_DEVICE(prod) \
.match_flags = USB_DEVICE_ID_MATCH_DEVICE | \
USB_DEVICE_ID_MATCH_INT_CLASS | \
USB_DEVICE_ID_MATCH_INT_PROTOCOL, \
.idVendor = APPLE_VENDOR_ID, \
.idProduct = (prod), \
.bInterfaceClass = 0x03, \
.bInterfaceProtocol = 0x02
/* table of devices that work with this driver */
static struct usb_device_id atp_table [] = {
{ ATP_DEVICE(FOUNTAIN_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(FOUNTAIN_ISO_PRODUCT_ID) },
{ ATP_DEVICE(FOUNTAIN_TP_ONLY_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER1_TP_ONLY_PRODUCT_ID) },
/* PowerBooks Oct 2005 */
{ ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
/* Core Duo MacBook & MacBook Pro */
{ ATP_DEVICE(GEYSER3_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER3_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER3_JIS_PRODUCT_ID) },
/* Core2 Duo MacBook & MacBook Pro */
{ ATP_DEVICE(GEYSER4_ANSI_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER4_ISO_PRODUCT_ID) },
{ ATP_DEVICE(GEYSER4_JIS_PRODUCT_ID) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE (usb, atp_table);
/*
* number of sensors. Note that only 16 instead of 26 X (horizontal)
* sensors exist on 12" and 15" PowerBooks. All models have 16 Y
* (vertical) sensors.
*/
#define ATP_XSENSORS 26
#define ATP_YSENSORS 16
/* amount of fuzz this touchpad generates */
#define ATP_FUZZ 16
/* maximum pressure this driver will report */
#define ATP_PRESSURE_FACTOR 2
#define ATP_MAX_PRESSURE (100*ATP_PRESSURE_FACTOR)
/*
* multiplication factor for the X and Y coordinates.
* We try to keep the touchpad aspect ratio while still doing only simple
* arithmetics.
* The factors below give coordinates like:
* 0 <= x < 960 on 12" and 15" Powerbooks
* 0 <= x < 1600 on 17" Powerbooks and 17" MacBook Pro
* 0 <= x < 1216 on 15" MacBook Pro
* 0 <= y < 646 on all Powerbooks
* 0 <= y < 774 on 15" MacBook Pro
*/
#define ATP_XFACT 64
#define ATP_YFACT 43 // 86
/*
* Thresholds for the touchpad sensors.
*
* Any sensors less than ATP_THRESHOLD is ignored.
* APT_START_THRESHOLD defines the pressure needed to start the moving.
* We need at least APT_FINGER_THRESHOLD to get a finger recognized.
* If we have ATP_PALM_THRESHOLD, we recognize it as an palm.
*/
#define ATP_THRESHOLD 2
#define ATP_START_THRESHOLD 7
#define ATP_FINGER_THRESHOLD 8
#define ATP_PALM_THRESHOLD 35
/*
* MacBook Pro (Geyser 3) initialization constants
*/
#define ATP_GEYSER3_MODE_READ_REQUEST_ID 1
#define ATP_GEYSER3_MODE_WRITE_REQUEST_ID 9
#define ATP_GEYSER3_MODE_REQUEST_VALUE 0x300
#define ATP_GEYSER3_MODE_REQUEST_INDEX 0
#define ATP_GEYSER3_MODE_VENDOR_VALUE 0x04
/*
* Meaning of the status bits
*/
#define ATP_STATUS_BIT_BUTTON 0x01 /* The button was pressed */
#define ATP_STATUS_BIT_UNKNOWN1 0x02 /* Unknown or unused */
#define ATP_STATUS_BIT_BASE_UPDATE 0x04 /* Update of the base values (untouched pad) */
#define ATP_STATUS_BIT_UNKNOWN2 0x08 /* Unknown or unused */
#define ATP_STATUS_BIT_FROM_RESET 0x10 /* Reset previously performed */
/* Type of touchpad */
typedef enum { TYPE_GEYSER_1, TYPE_GEYSER_2, TYPE_GEYSER_3_4 } touchpad_type;
/* Coordinate type */
typedef struct {
int x, y; /* x/y coordinate */
int x_pressure, y_pressure; /* pressure at this x/y coordinate */
int x_size, y_size; /* size of touch */
} atp_coord;
/* Structure to hold all of our device specific stuff */
struct atp {
char phys[64];
struct usb_device * udev; /* usb device */
struct urb * urb; /* usb request block */
unsigned char * data; /* transferred data */
int open; /* non-zero if opened */
struct input_dev *input; /* input dev */
touchpad_type type; /* type of touchpad */
int valid; /* are the sensors valid ? */
int idle_counter; /* idle counter */
int max_x; /* maximum x coordinate */
int max_y; /* maximum y coordinate */
int palm_left; /* palm left region border */
int palm_right; /* palm right region border */
atp_coord pos_old; /* last x/y positions we returned */
int scrolling; /* in scrolling state */
int old_x_coord[ATP_XSENSORS];
int old_y_coord[ATP_YSENSORS];
int old_x_count;
int old_y_count;
/* current value of the sensors */
unsigned int xy_cur[ATP_XSENSORS + ATP_YSENSORS];
/* last value of the sensors */
unsigned int xy_old[ATP_XSENSORS + ATP_YSENSORS];
/* accumulated sensors */
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
int threshold; /* currently used threshold */
int first; /* is this the first (re)touch? */
int ignored_palm; /* did we started a palm ignore? */
int overflowwarn; /* overflow warning printed? */
int datalen; /* size of an USB urb transfer */
struct work_struct reinit_thread; /* kernel thread (for re-init) */
int next;
};
#define dprintk(level, format, a...) \
do { \
if (debug > level) printk(format, ##a); \
} while (0)
MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann, Sven Anders");
MODULE_DESCRIPTION("Apple PowerBooks/MacBooks USB touchpad driver");
MODULE_LICENSE("GPL");
/*
* Modules parameters
*/
/* (until we have a working automatic detection) */
static int seventeen = 0;
module_param(seventeen, int, 0644);
MODULE_PARM_DESC(seventeen, "Is this a 17\" Powerbook or MacBook?");
static int threshold = ATP_THRESHOLD;
module_param(threshold, int, 0644);
MODULE_PARM_DESC(threshold, "Discards any change in data from a sensor "
"(trackpad has hundreds of these sensors) less than this value");
static int start_threshold = ATP_START_THRESHOLD;
module_param(start_threshold, int, 0644);
MODULE_PARM_DESC(start_threshold, "Pressure needed to start moving");
static int finger_threshold = ATP_FINGER_THRESHOLD;
module_param(finger_threshold, int, 0644);
MODULE_PARM_DESC(finger_threshold, "Least pressure that identifies a finger");
static int palm_threshold = ATP_PALM_THRESHOLD;
module_param(palm_threshold, int, 0644);
MODULE_PARM_DESC(palm_threshold, "Pressure which triggers the palm detection");
static int palm_detect = 1;
module_param(palm_detect, int, 0644);
MODULE_PARM_DESC(palm_detect, "Detect and ignore palms on the touchpad");
static int report_touchsize = 0;
module_param(report_touchsize, int, 0644);
MODULE_PARM_DESC(report_touchsize, "Report touch's size (tool-width) for use by synaptics driver");
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/* palm areas are defined as fractions of the pad in x direction */
/* Default: 4/13 * X-Size < X < 14/22 * X-Size */
#define palm_left_nomi palm_region[0] // default: 4
#define palm_left_div palm_region[1] // default: 13
#define palm_right_nomi palm_region[2] // default: 14
#define palm_right_div palm_region[3] // default: 22
static unsigned int palm_region[4] = { 4, 13, 14, 22 };
static unsigned int palm_region_count;
static struct kparam_array __param_arr_palm_region
= { ARRAY_SIZE(palm_region), &palm_region_count,
param_set_uint, param_get_uint,
sizeof(palm_region[0]), palm_region };
/* Palm area states */
#define ATP_PALM_AREA_ENABLED 0 /* Palm area is enabled */
#define ATP_PALM_AREA_DISABLED 1 /* Palm area is disabled (ignore touches) */
#define ATP_PALM_AREA_TIMEOUT 2 /* Palm area is disabled until timeout occures */
/* Timeout before reenabling the palm area after a keypress */
#define ATP_KEY_TIMEOUT_MSEC 1000 /* Timeout in milliseconds */
/* Is the palm area currently disabled? */
/* (It's disabled, if we pressed a key on the keyboard and enabled */
/* again, if we touch the inner region or a timeout occures) */
static int palm_area_state = ATP_PALM_AREA_ENABLED;
/* Is the modifier key pressed? */
static int modifier_pressed = 0;
/* Is the support devices open? */
static unsigned long device_opened = 0;
/* Timeout for pressed key palm ignore */
static unsigned long key_timeout = 0;
/* Validate and set palm region parameters */
static int atp_set_palm_region(const char *val, struct kernel_param *kp)
{
int result;
unsigned int palm_region_copy[4];
memcpy(palm_region_copy, palm_region, sizeof(palm_region));
result = param_array_set(val, kp);
if (palm_region_count != 4)
{
printk(KERN_ERR "%s: expecting 4 arguments\n", kp->name);
result = -EINVAL;
}
if ((palm_left_div == 0) || (palm_right_div == 0))
{
printk(KERN_ERR "%s: second and forth value must not be 0!\n", kp->name);
result = -EINVAL;
}
if (result != 0)
memcpy(palm_region, palm_region_copy, sizeof(palm_region));
return result;
}
module_param_call(palm_region, atp_set_palm_region, param_array_get,
&__param_arr_palm_region, 0644);
MODULE_PARM_DESC(palm_region, "Parameters to define palm region in fractions N/D of size (leftN,leftD,rightN,rightD)");
/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
static inline int atp_is_geyser_2(struct atp *dev)
{
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
return (productId == GEYSER_ANSI_PRODUCT_ID) ||
(productId == GEYSER_ISO_PRODUCT_ID) ||
(productId == GEYSER_JIS_PRODUCT_ID);
}
/* Checks if the device a Geyser 3 or 4 (ANSI, ISO, JIS) */
static inline int atp_is_geyser_3_or_4(struct atp *dev)
{
u16 productId = le16_to_cpu(dev->udev->descriptor.idProduct);
return (productId == GEYSER3_ANSI_PRODUCT_ID) ||
(productId == GEYSER3_ISO_PRODUCT_ID) ||
(productId == GEYSER3_JIS_PRODUCT_ID) ||
(productId == GEYSER4_ANSI_PRODUCT_ID) ||
(productId == GEYSER4_ISO_PRODUCT_ID) ||
(productId == GEYSER4_JIS_PRODUCT_ID);
}
/* Calculate the palm region */
static inline void calculate_palm_region(struct atp *dev)
{
/* the left and right border of the palm region */
dev->palm_left = (dev->max_x * palm_left_nomi) / palm_left_div;
dev->palm_right = (dev->max_x * palm_right_nomi) / palm_right_div;
}
/* Get all positions on one axis and return them */
static void atp_get_axis_pos(int *xy_sensors, int count,
int fact, int current_threshold,
int *coords, signed char *pressure, int *size,
int *num_coords, int *num_touched,
int *biggest_block, int *average)
{
int i;
int is_increasing = 0;
int is_decreasing = 0;
int was_increasing = 0;
int was_decreasing = 0;
int current_block = 0;
int highest_pressure = 0;
/* values to calculate mean */
int pcum = 0, psum = 0, pnum = 0;
/* clean return data */
memset(coords, 0, count);
memset(pressure, 0, count);
*num_coords = 0;
*num_touched = 0;
*biggest_block = 0;
*average = 0;
/* search for sensors */
for (i = 0; i < count; i++) {
/* count any sensors we touched on this axis */
if (xy_sensors[i] >= 2) (*num_touched)++;
/* search the biggest consecutively touched block */
if (xy_sensors[i] >= 2)
{
if ((i == 0) || (xy_sensors[i-1] >= 2))
current_block++;
if (current_block > *biggest_block)
*biggest_block = current_block;
}
else
current_block = 0;
/* are the pressure values increasing? */
is_increasing = ((xy_sensors[i] >= current_threshold) &&
((i == 0) || (xy_sensors[i-1] < xy_sensors[i])));
/* are the pressure values decreasing? */
is_decreasing = ((xy_sensors[i] >= current_threshold) &&
(i > 0) && (xy_sensors[i-1] >= xy_sensors[i]));
#if 0||DEBUG > 3
printk("appletouch: %i. INC=%i (%i), DEC=%i (%i) => %i\n",
i, is_increasing, was_increasing, is_decreasing,
was_decreasing, xy_sensors[i]);
#endif
/* store new found pos, if decreasing ended */
if ((was_decreasing && (!is_decreasing || is_increasing)) ||
(was_increasing && !is_decreasing && !is_increasing)) {
/* Did we found a pressure, that identifies a finger? */
if (highest_pressure >= finger_threshold)
{
/* Yes => store coordinate */
if (psum != 0)
coords[*num_coords] = pcum * fact / psum;
pressure[*num_coords] = psum; //highest_pressure;
size[*num_coords] = pnum;
*average += highest_pressure;
#if 0||DEBUG > 3
printk("appletouch: found %i (%i) => %i (%i) [%i]\n",
i, highest_pressure, coords[*num_coords], psum, pnum);
#endif
/* reset pressure store */
highest_pressure = 0;
/* increase coordinate/finger counter */
(*num_coords)++;
/* do we have two fingers closely? */
if (is_increasing && i > 0) {
/* Yes, init mean calc values with value of last sensor */
pcum = (xy_sensors[i-1] - current_threshold) * i;
psum = (xy_sensors[i-1] - current_threshold);
pnum = 1;
} else {
/* No, just reset mean calc values */
pcum = 0;
psum = 0;
pnum = 0;
}
}
}
/* store in/decrease state for later use in next loop */
was_decreasing = is_decreasing;
was_increasing = is_increasing;
/* ignore any sensor less than 'threshold' */
if (xy_sensors[i] < current_threshold)
continue;
/* Sum up the coordinates and pressures.
*
* Subtracts threshold so a high sensor that just passes the threshold
* won't skew the calculated absolute coordinate. Fixes an issue
* where slowly moving the mouse would occassionaly jump a number of
* pixels (let me restate--slowly moving the mouse makes this issue
* most apparent).
*/
pcum += (xy_sensors[i] - current_threshold) * (i+1);
psum += (xy_sensors[i] - current_threshold);
pnum++;
/* Save the highest value we've found */
if (xy_sensors[i] > highest_pressure)
highest_pressure = xy_sensors[i];
}
/* Calculate average pressure */
if (*num_coords != 0) *average /= *num_coords;
}
/* Calculate coordinates of all fingers, choose best and return it */
static void atp_calc_and_choose_coords(struct atp *dev,
atp_coord *xy_coord,
int *num_fingers,
int *x_count_ret, int *y_count_ret)
{
int x_coord[ATP_XSENSORS];
signed char x_pressure[ATP_XSENSORS];
int x_size[ATP_XSENSORS];
int y_coord[ATP_YSENSORS];
signed char y_pressure[ATP_YSENSORS];
int y_size[ATP_YSENSORS];
int x_count, y_count;
int x_touched, y_touched;
int x_consec, y_consec;
int x_average, y_average;
int i;
/* clear coordinate arrays */
memset(xy_coord, 0, sizeof(atp_coord));
*num_fingers = 0;
/* Get touched sensors on X and Y axis */
atp_get_axis_pos(dev->xy_acc, ATP_XSENSORS,
ATP_XFACT, dev->threshold,
x_coord, x_pressure, x_size,
&x_count, &x_touched, &x_consec, &x_average);
atp_get_axis_pos(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS,
ATP_YFACT, dev->threshold,
y_coord, y_pressure, y_size,
&y_count, &y_touched, &y_consec, &y_average);
*x_count_ret = x_count;
*y_count_ret = y_count;
#if 0||DEBUG > 1
printk("appletouch: pressure: X: ");
for (i=0; i < x_count; i++) printk("%i,", x_pressure[i]); printk(" Y: ");
for (i=0; i < y_count; i++) printk("%i,", y_pressure[i]); printk("\n");
printk("appletouch: ø pressure: X: %i / Y: %i\n", x_average, y_average);
printk("appletouch: touched: X: %i / Y: %i\n", x_touched, y_touched);
printk("appletouch: consec-block: X: %i / Y: %i\n", x_consec, y_consec);
printk("appletouch: count: X: %i / Y: %i\n", x_count, y_count);
printk("appletouch: coord: X: ");
for (i=0; i < x_count; i++) printk("%i,", x_coord[i]); printk(" Y: ");
for (i=0; i < y_count; i++) printk("%i,", y_coord[i]); printk("\n");
#endif
/* is the palm detection enabled? */
//if (palm_detect)
if (!dev->scrolling && palm_detect)
{
// big region or average pressure over 'palm_threshold' found?
if (x_consec > 5 ||
y_consec > 5 ||
x_touched >= 7 ||
y_touched >= 7 ||
x_average >= palm_threshold ||
y_average >= palm_threshold)
{
#if 1||DEBUG > 2
printk("appletouch: ignored palm on touchpad\n");
#endif
dev->ignored_palm = 1;
/* Disable palm region too */
palm_area_state = ATP_PALM_AREA_DISABLED;
printk("appletouch: palm area disabled!\n");
return;
}
}
if (!dev->ignored_palm)
{
/* Get first coordinate we found (as a start) */
xy_coord->x = x_coord[0];
xy_coord->y = y_coord[0];
xy_coord->x_pressure = x_pressure[0];
xy_coord->y_pressure = y_pressure[0];
xy_coord->x_size = x_size[0];
xy_coord->y_size = y_size[0];
/* How many finger do we have on the touchpad? */
if (x_count == 1 && y_count == 1) {
/* Only one finger on the touchpad, this is easy... */
/* We've already done this, just return with a finger count of one */
#if 0||DEBUG > 2
printk("** appletouch: IN X=%3d / Y=%3d / XP:%3d / YP:%3d / XS: %3d / YS: %3d\n",
xy_coord->x, xy_coord->y, xy_coord->x_pressure, xy_coord->y_pressure, xy_coord->x_size, xy_coord->y_size);
#endif
memcpy(dev->old_x_coord, x_coord, sizeof(dev->old_x_coord));
memcpy(dev->old_y_coord, y_coord, sizeof(dev->old_y_coord));
dev->old_x_count = x_count;
dev->old_y_count = y_count;
*num_fingers = 1;
dev->scrolling = 0;
}
else if (x_count > 0 && y_count > 0) {
/* We have more than one, get the nearest to the old */
for (i=1; i < x_count; i++)
{
if (abs(dev->pos_old.x - x_coord[i]) < abs(dev->pos_old.x - xy_coord->x))
{
xy_coord->x = x_coord[i];
xy_coord->x_pressure = x_pressure[i];
xy_coord->x_size = x_size[i];
}
}
for (i=1; i < y_count; i++)
{
if (abs(dev->pos_old.y - y_coord[i]) < abs(dev->pos_old.y - xy_coord->y))
{
xy_coord->y = y_coord[i];
xy_coord->y_pressure = y_pressure[i];
xy_coord->y_size = y_size[i];
}
}
printk("*#* appletouch: IN X=%3d / Y=%3d / XP:%3d / YP:%3d / XS: %3d / YS: %3d\n",
xy_coord->x, xy_coord->y, xy_coord->x_pressure, xy_coord->y_pressure, xy_coord->x_size, xy_coord->y_size);
if ((dev->old_x_count != x_count) || (dev->old_y_count != y_count))
{
memcpy(dev->old_x_coord, x_coord, sizeof(dev->old_x_coord));
memcpy(dev->old_y_coord, y_coord, sizeof(dev->old_y_coord));
dev->old_x_count = x_count;
dev->old_y_count = y_count;
}
else
{
int x_diff = 0;
int y_diff = 0;
for (i=0; i < x_count; i++)
x_diff += (x_coord[i] - dev->old_x_coord[i]);
for (i=0; i < y_count; i++)
y_diff += (y_coord[i] - dev->old_y_coord[i]);
xy_coord->x += (x_diff/x_count);
xy_coord->y += (y_diff/y_count);
if (xy_coord->x < 0) xy_coord->x = 0;
if (xy_coord->y < 0) xy_coord->y = 0;
}
// Are we scrolling (and are not in ?
if ((abs(dev->pos_old.x - xy_coord->x) > 2) ||
(abs(dev->pos_old.y - xy_coord->y) > 2))
dev->scrolling = 1;
/* If we have more than one finger on one coordinate,
get the maximum of it. This is the total count of fingers! */
*num_fingers = max(x_count, y_count);
#if 0||DEBUG > 1
printk("appletouch: X: %i of %i / Y: %i of %i -\n",
x_count, ATP_XSENSORS, y_count, ATP_YSENSORS);
printk("appletouch: num_fingers=%i\n", *num_fingers);
printk("appletouch: X: %3d P: %3d S: %3d\n", xy_coord->x, xy_coord->x_pressure, xy_coord->x_size);
printk("appletouch: Y: %3d P: %3d S: %3d\n", xy_coord->y, xy_coord->y_pressure, xy_coord->y_size);
#endif
}
} // (!dev->ignored_palm)
if (!dev->scrolling && palm_detect)
{
/* Ignore touches in the palm area, if it's not enabled */
if (palm_area_state != ATP_PALM_AREA_ENABLED)
{
for (i=0; i < y_count; i++)
{
if ((x_coord[i] < dev->palm_left) || (x_coord[i] > dev->palm_right))
{
#if 1||DEBUG > 2
printk("appletouch: ignored palm in defined palm region\n");
#endif
dev->ignored_palm = 1;
return;
}
}
}
}
}
static inline void atp_report_fingers(struct input_dev *input, int fingers)
{
input_report_key(input, BTN_TOOL_FINGER, fingers == 1);
input_report_key(input, BTN_TOOL_DOUBLETAP, fingers == 2);
input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2);
}
static void atp_complete(struct urb* urb)
{
int x, y;
atp_coord xy_coord;
int x_count, y_count;
int num_fingers, pressure, size;
int retval, i, j;
struct atp *dev = urb->context;
switch (urb->status) {
case 0:
/* success */
break;
case -EOVERFLOW:
if(!dev->overflowwarn) {
printk("appletouch: OVERFLOW with data "
"length %d, actual length is %d\n",
dev->datalen, dev->urb->actual_length);
dev->overflowwarn = 1;
}
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
/* This urb is terminated, clean up */
dbg("%s - urb shutting down with status: %d",
__FUNCTION__, urb->status);
return;
default:
dbg("%s - nonzero urb status received: %d",
__FUNCTION__, urb->status);
goto exit;
}
/* drop incomplete datasets */
if (dev->urb->actual_length != dev->datalen) {
dprintk(0,"appletouch: incomplete data package"
" (first byte: %d, length: %d).\n",
dev->data[0], dev->urb->actual_length);
goto exit;
}
/* reorder the sensors values */
if (dev->type == TYPE_GEYSER_3_4) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
* The values are laid out like this:
* -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ...
* '-' is an unused value.
*/
#if 0||DEBUG > 2
{
int i,a=1,b=0;
printk("raw (%i): Y=", dev->datalen);
for (i = 1; i < dev->datalen; i++)
{
if (i % 3 == a) printk("[");
if (b || a != 4) printk("%02x", (unsigned char)dev->data[i]);
if (i % 3 == a) printk("]");
if (i == 15) { if (b) printk(" ?="); a=4; }
if (i == 18) { printk(" X="); a=1; }
if (i == 48) { if (b) printk(" ?="); a=4; }
}
printk("\n");
}
#endif
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j + 1];
dev->xy_cur[i + 1] = dev->data[j + 2];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j + 1];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 2];
}
} else if (dev->type == TYPE_GEYSER_2) {
memset(dev->xy_cur, 0, sizeof(dev->xy_cur));
/*
* The values are laid out like this:
* Y1, Y2, -, Y3, Y4, -, ..., X1, X2, -, X3, X4, -, ...
* '-' is an unused value.
*/
/* read X values */
for (i = 0, j = 19; i < 20; i += 2, j += 3) {
dev->xy_cur[i] = dev->data[j];
dev->xy_cur[i + 1] = dev->data[j + 1];
}
/* read Y values */
for (i = 0, j = 1; i < 9; i += 2, j += 3) {
dev->xy_cur[ATP_XSENSORS + i] = dev->data[j];
dev->xy_cur[ATP_XSENSORS + i + 1] = dev->data[j + 1];
}
} else {
for (i = 0; i < 8; i++) {
/* X values */
dev->xy_cur[i ] = dev->data[5 * i + 2];
dev->xy_cur[i + 8] = dev->data[5 * i + 4];
dev->xy_cur[i + 16] = dev->data[5 * i + 42];
if (i < 2)
dev->xy_cur[i + 24] = dev->data[5 * i + 44];
/* Y values */
dev->xy_cur[i + 26] = dev->data[5 * i + 1];
dev->xy_cur[i + 34] = dev->data[5 * i + 3];
}
}
#if 0||DEBUG > 2
{
int i;
printk("xy_cur: X=");
for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
{
printk("%+02i", (unsigned char)dev->xy_cur[i]);
if (i == ATP_XSENSORS-1) printk(" Y=");
}
printk("\n");
}
{
int i;
printk("xy_old: X=");
for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
{
printk("%+02i", (unsigned char)dev->xy_old[i]);
if (i == ATP_XSENSORS-1) printk(" Y=");
}
printk("\n");
}
#endif
if (!dev->valid) {
/* first sample after init or resume */
dev->valid = 1;
dev->idle_counter = 0;
dev->threshold = start_threshold;
dev->ignored_palm = 0;
dev->first = 1;
dev->scrolling = 0;
memset(&dev->pos_old, 0, sizeof(dev->pos_old));
/* store first sample (possibly needed for older Geyser) */
if (dev->type != TYPE_GEYSER_3_4)
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
#ifdef DISABLED
/* 17" Powerbooks, 15" and 17" MacBooks have extra X sensors */
for (i = (dev->type == TYPE_GEYSER_2?15:16); i < ATP_XSENSORS; i++) {
if (!dev->xy_cur[i]) continue;
printk("appletouch: PowerBook 17\" or MacBookPro 17\" detected.\n");
printk("max_x = %i / max_y = %i\n", dev->max_x, dev->max_y);
if (dev->type == TYPE_GEYSER_2)
dev->max_x = ((20 - 1) * ATP_XFACT) - 1;
else
dev->max_x = ((ATP_XSENSORS - 1) * ATP_XFACT) - 1;
printk("max_x = %i / max_y = %i\n", dev->max_x, dev->max_y);
/* recalculate the palm region */
calculate_palm_region(dev);
input_set_abs_params(dev->input, ABS_X, 0, dev->max_x, ATP_FUZZ, 0);
break;
}
#endif
goto exit;
}
/* Just update the base values (i.e. touchpad in untouched state) */
if (dev->data[dev->datalen-1] & ATP_STATUS_BIT_BASE_UPDATE)
{
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
goto exit;
}
/* get the change of each sensor */
for (i = 0; i < ATP_XSENSORS + ATP_YSENSORS; i++) {
/* calculate the change */
dev->xy_acc[i] = dev->xy_cur[i] - dev->xy_old[i];
/* this is a round-robin value, so couple with that */
if (dev->xy_acc[i] > 127)
dev->xy_acc[i] -= 256;
if (dev->xy_acc[i] < -127)
dev->xy_acc[i] += 256;
/* Possibly this is needed for the older Geyser */
if (dev->type != TYPE_GEYSER_3_4)
{
/* store new 'untouched' value, if any new */
if (dev->xy_acc[i] < -1)
dev->xy_old[i] = dev->xy_cur[i];
}
/* prevent down-drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
#if 0||DEBUG > 2
{
int i;
printk("xy_acc: S=%u, X=", dev->data[dev->datalen-1]);
for (i = 0; i < ATP_XSENSORS+ATP_YSENSORS; i++)
{
printk("%+02i", (int)dev->xy_acc[i]);
if (i == ATP_XSENSORS-1) printk(" Y=");
}
printk("\n");
}
#endif
/* Renable palm area after the timeout occured */
if ((palm_area_state == ATP_PALM_AREA_TIMEOUT) && time_after(jiffies, key_timeout))
{
printk("appletouch: palm area (re)enabled after timeout\n");
palm_area_state = ATP_PALM_AREA_ENABLED;
}
/* calculate and choose coordinates */
atp_calc_and_choose_coords(dev, &xy_coord, &num_fingers, &x_count, &y_count);
x = xy_coord.x;
y = xy_coord.y;
pressure = (xy_coord.x_pressure + xy_coord.y_pressure) * ATP_PRESSURE_FACTOR / 2;
size = xy_coord.x_size + xy_coord.y_size;
/* save all coordinates we found */
memcpy(&dev->pos_old, &xy_coord, sizeof(dev->pos_old));
#if 0||DEBUG > 0
printk("fingers=%i / x=%i / y=%i\n", num_fingers, x, y);
#endif
/* is a touch reported and we have two valid coordinates? */
if ((num_fingers > 0) && x && y && !dev->ignored_palm)
{
dprintk(1,"appletouch: X: %3d Y: %3d P: %3d F: %d\n",
x, y, pressure, num_fingers);
/* If we touched the pad in the inner (non palm)
region, (re)enable the palm area */
if (dev->first && palm_detect &&
(x > dev->palm_left) && (x < dev->palm_right))
{
palm_area_state = ATP_PALM_AREA_ENABLED;
printk("appletouch: palm area (re)enabled\n");
}
/* Report this touch */
if ((dev->old_x_count > x_count) || (dev->old_y_count > y_count))
{
input_report_key(dev->input, BTN_TOUCH, 0);
atp_report_fingers(dev->input, num_fingers);
}
else
{
input_report_key(dev->input, BTN_TOUCH, 1);
input_report_abs(dev->input, ABS_X, x);
input_report_abs(dev->input, ABS_Y, y);
input_report_abs(dev->input, ABS_PRESSURE, pressure);
atp_report_fingers(dev->input, num_fingers);
if (report_touchsize)
input_report_abs(dev->input, ABS_TOOL_WIDTH, size / num_fingers);
}
dev->threshold = threshold;
dev->first = 0;
}
else if ((num_fingers == 0) && (x == 0) && (y == 0)) {
/* no touch reported => reset anything */
dev->threshold = start_threshold;
dev->first = 1;
dev->scrolling = 0;
dev->old_x_count = 0;
dev->old_y_count = 0;
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
/* report release */
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
// Idle counter
dev->idle_counter++;
// Wait for 10 more packages before suspending
if (dev->idle_counter > 10) {
// Get every 10th sample, reset counter
dev->idle_counter = 1;
// Reset Palm detector
dev->ignored_palm = 0;
/* Geyser 3/4 will continue to send packets continually after
the first touch. The function is called every 8 milliseconds
from interrups context, unless reinitialised. Do so if it's
been idle for a while in order to avoid waking the kernel up
several hundred times a second */
if (atp_is_geyser_3_or_4(dev))
schedule_work(&dev->reinit_thread);
}
}
//printk("appletouch: modifier_pressed: %i / button-value: %u\n", modifier_pressed, dev->data[dev->datalen-1]);
input_report_key(dev->input, modifier_pressed ? BTN_RIGHT : BTN_LEFT,
dev->data[dev->datalen - 1] & ATP_STATUS_BIT_BUTTON);
input_sync(dev->input);
exit:
retval = usb_submit_urb(dev->urb, GFP_ATOMIC);
if (retval) {
err("%s - usb_submit_urb failed with result %d",
__FUNCTION__, retval);
}
}
static int atp_open(struct input_dev *input)
{
struct atp *dev = input->private;
if (usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
dev->open = 1;
return 0;
}
static void atp_close(struct input_dev *input)
{
struct atp *dev = input->private;
usb_kill_urb(dev->urb);
dev->open = 0;
}
static int atp_geyser3_4_unknown_init(struct usb_device *udev)
{
#if 1||defined(GET_UNKNOWN_DATA_IS_UNUSED)
/*
* This request is made by the original Apple Touchpad driver.
* Does anybody have a clue, what this data is?
*/
#define RQ6_DATA_SIZE 0x59
char udata[RQ6_DATA_SIZE];
int usize;
usize = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
/* Request ID */ 6,
USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE,
/* RequestValue */ 0x2200,
/* RequestIndex */ 1,
&udata, RQ6_DATA_SIZE, 5000);
if (usize == RQ6_DATA_SIZE) {
int i;
printk("* Please send the following lines together with a description of your Mac\n"
"* (MacBook/MacBookPro, Screen-Size, Revision, Build-Year) to: [EMAIL PROTECTED]"
"* This will help to improve this driver...\n");
printk("* RequestID 6 data received:\n* ");
for (i=0; i < usize; i++)
printk("%02x ", (unsigned char)udata[i]); printk("\n");
printk("* Thank you!\n");
} else {
err("Could read data of requestid 6 from device");
return -EIO;
}
#endif
return 0;
}
static int atp_geyser3_4_init(struct usb_device *udev)
{
/*
* By default Geyser 3 device sends standard USB HID mouse
* packets (Report ID 2). This code changes device mode, so it
* sends raw sensor reports (Report ID 5).
*/
char data[8];
int size;
size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER3_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
if (size != 8) {
err("Could not do mode read request from device"
" (Geyser 3 mode)");
return -EIO;
}
/* Apply the mode switch */
data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;
size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);
if (size != 8) {
err("Could not do mode write request to device"
" (Geyser 3 mode)");
return -EIO;
}
return 0;
}
/* Reinitialise the device if it's a geyser 3 */
static void atp_reinit(struct work_struct *reinit_thread)
{
struct atp *dev = container_of(reinit_thread, struct atp, reinit_thread);
struct usb_device *udev = dev->udev;
dprintk(0,"appletouch: putting appletouch to sleep (reinit)\n");
atp_geyser3_4_init(udev);
}
static ssize_t atp_dev_write(struct file *file, const char __user *data,
size_t len, loff_t *ppos)
{
if (len) {
size_t i;
/* scan to see what happened */
for (i = 0; i != len; i++) {
char c;
if (get_user(c, data+i))
return -EFAULT;
if (c == '0') // normal key released
{
if (palm_area_state == ATP_PALM_AREA_DISABLED)
{
key_timeout = jiffies + msecs_to_jiffies(ATP_KEY_TIMEOUT_MSEC);
palm_area_state = ATP_PALM_AREA_TIMEOUT;
}
}
else if (c == '1') // normal key pressed
palm_area_state = ATP_PALM_AREA_DISABLED;
else if (c == '2') // modifier key released
modifier_pressed = 0;
else if (c == '3') // modifier key pressed
modifier_pressed = 1;
// Ignore special keys: (c == '4') and (c == '5')
}
}
return len;
}
static int atp_dev_open(struct inode *inode, struct file *file)
{
/* device can only be opened once */
if (test_and_set_bit(0, &device_opened))
return -EBUSY;
return nonseekable_open(inode, file);
}
static int atp_dev_release(struct inode *inode, struct file *file)
{
clear_bit(0, &device_opened);
palm_area_state = ATP_PALM_AREA_ENABLED;
modifier_pressed = 0;
key_timeout = 0;
return 0;
}
/* file operation pointers */
static const struct file_operations atp_dev_fops = {
.owner = THIS_MODULE,
.write = atp_dev_write,
.open = atp_dev_open,
.release = atp_dev_release,
};
/* class driver information */
static struct usb_class_driver atp_class = {
.name = "appletouch",
.fops = &atp_dev_fops,
.minor_base = USB_APPLETOUCH_MINOR_BASE,
};
static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
struct atp *dev;
struct input_dev *input_dev;
struct usb_device *udev = interface_to_usbdev(iface);
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, retval = -ENOMEM;
int x_sensors, y_sensors;
/* set up the endpoint information */
/* use only the first interrupt-in endpoint */
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
}
}
if (!int_in_endpointAddr) {
err("Could not find int-in endpoint");
return -EIO;
}
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(struct atp), GFP_KERNEL);
input_dev = input_allocate_device();
if (!dev || !input_dev) {
err("Out of memory");
goto err_free_devs;
}
/* Initialize dev struct */
dev->udev = udev;
dev->input = input_dev;
dev->overflowwarn = 0;
/* Detect type of touchpad */
if (atp_is_geyser_3_or_4(dev))
dev->type = TYPE_GEYSER_3_4;
else if (atp_is_geyser_2(dev))
dev->type = TYPE_GEYSER_2;
else
dev->type = TYPE_GEYSER_1;
/* Set data length */
if (dev->type == TYPE_GEYSER_1)
dev->datalen = 81;
else
dev->datalen = 64;
/* Switch Geyser3/4 device to raw sensor mode */
if (dev->type == TYPE_GEYSER_3_4) {
if (atp_geyser3_4_unknown_init(udev))
goto err_free_devs;
if (atp_geyser3_4_init(udev))
goto err_free_devs;
}
/* Create usb interrupt urb */
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb) {
retval = -ENOMEM;
goto err_free_devs;
}
dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
&dev->urb->transfer_dma);
if (!dev->data) {
retval = -ENOMEM;
goto err_free_urb;
}
usb_fill_int_urb(dev->urb, udev,
usb_rcvintpipe(udev, int_in_endpointAddr),
dev->data, dev->datalen, atp_complete, dev, 1);
usb_make_path(udev, dev->phys, sizeof(dev->phys));
strlcat(dev->phys, "/input0", sizeof(dev->phys));
input_dev->name = "appletouch";
input_dev->phys = dev->phys;
usb_to_input_id(dev->udev, &input_dev->id);
input_dev->cdev.dev = &iface->dev;
input_dev->private = dev;
input_dev->open = atp_open;
input_dev->close = atp_close;
/* Set number of sensors */
if (dev->type == TYPE_GEYSER_3_4) {
/*
* MacBook have 20 X sensors, 10 Y sensors
*/
x_sensors = 20;
y_sensors = 10;
/* 17" MacBooks have 26 X sensors */
//if (seventeen)
// x_sensors = 26;
} else if (dev->type == TYPE_GEYSER_2) {
/*
* Oct 2005 15" PowerBooks have 15 X sensors, 9 Y sensors
*/
x_sensors = 16;
y_sensors = 9;
/* Oct 2005 17" Powerbooks have 20 X sensors */
if (seventeen)
x_sensors = 20;
} else {
/*
* 12" and 15" PowerBooks have 15 X sensors, 9 Y sensors
*/
x_sensors = 16;
y_sensors = 9;
/* 17" Powerbooks have 26 X sensors */
if (seventeen)
x_sensors = 26;
}
/* calculate X/Y maximum coordinates */
dev->max_x = ((x_sensors - 1) * ATP_XFACT) - 1;
dev->max_y = ((y_sensors - 1) * ATP_YFACT) - 1;
/* calculate the palm region */
calculate_palm_region(dev);
/* init input-event parts */
set_bit(EV_ABS, input_dev->evbit);
/* set the input system data */
input_set_abs_params(input_dev, ABS_X, 0, dev->max_x, ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_Y, 0, dev->max_y, ATP_FUZZ, 0);
input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_MAX_PRESSURE, 0, 0);
/* The synaptics driver wants this! So, just to be sure it is set... */
/* (Is this set by default?) */
set_bit(EV_SYN, input_dev->evbit);
set_bit(EV_KEY, input_dev->evbit);
set_bit(BTN_TOUCH, input_dev->keybit);
set_bit(BTN_TOOL_FINGER, input_dev->keybit);
set_bit(BTN_TOOL_DOUBLETAP, input_dev->keybit);
set_bit(BTN_TOOL_TRIPLETAP, input_dev->keybit);
set_bit(BTN_LEFT, input_dev->keybit);
set_bit(BTN_RIGHT, input_dev->keybit);
if (report_touchsize)
set_bit(ABS_TOOL_WIDTH, input_dev->absbit);
input_register_device(dev->input);
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);
/* we can register the device now, as it is ready */
retval = usb_register_dev(iface, &atp_class);
if (retval) {
/* something prevented us from registering this device */
err("Unable to allocate minor number.");
usb_set_intfdata(iface, NULL);
goto err_free_urb;
}
/* report success */
dev_info(&iface->dev, "Appletouch device now attached\n");
/* initialize kernel thread for re-init out of interrupt context */
INIT_WORK(&dev->reinit_thread, atp_reinit);
return 0;
err_free_urb:
usb_free_urb(dev->urb);
err_free_devs:
usb_set_intfdata(iface, NULL);
kfree(dev);
input_free_device(input_dev);
return retval;
}
static void atp_disconnect(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
/* give back our minor */
usb_deregister_dev(iface, &atp_class);
/* cleanup allocated memory */
if (dev) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
cancel_work_sync(&dev->reinit_thread);
#else
flush_scheduled_work();
#endif
usb_kill_urb(dev->urb);
input_unregister_device(dev->input);
usb_buffer_free(dev->udev, dev->datalen,
dev->data, dev->urb->transfer_dma);
usb_free_urb(dev->urb);
kfree(dev);
}
printk(KERN_INFO "input: appletouch disconnected\n");
}
static int atp_suspend(struct usb_interface *iface, pm_message_t message)
{
struct atp *dev = usb_get_intfdata(iface);
usb_kill_urb(dev->urb);
dev->valid = 0;
return 0;
}
static int atp_resume(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
if (dev->open && usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
return 0;
}
static struct usb_driver atp_driver = {
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
.suspend = atp_suspend,
.resume = atp_resume,
.id_table = atp_table,
};
static int __init atp_init(void)
{
return usb_register(&atp_driver);
}
static void __exit atp_exit(void)
{
usb_deregister(&atp_driver);
}
module_init(atp_init);
module_exit(atp_exit);
/*
* appletouchd.c
*
* Copyright (c) 2007 Sven Anders <[EMAIL PROTECTED]>
*
* Appletouch driver support daemon
*/
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Should you need to contact me, the author, you can do so either by
* e-mail - mail your message to <[EMAIL PROTECTED]>, or by paper mail:
* Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
*/
#include <stdint.h>
#include <linux/input.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define DEBUG
#define BITS_PER_LONG (sizeof(long) * 8)
#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1)
#define OFF(x) ((x)%BITS_PER_LONG)
#define LONG(x) ((x)/BITS_PER_LONG)
#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1)
#define VENDOR_APPLE 0x5ac
#define VENDOR_KEYBOARD_MBP 0x21b
#define APPLE_KEYBOARD_EVENT_DEV "/dev/input/by-id/usb-Apple_Computer_Apple_Internal_Keyboard_._Trackpad-event-kbd"
#define APPLE_TOUCHPAD_DEV "/dev/appletouch"
#define VALUE_KEY_UP 0
#define VALUE_KEY_DOWN 1
#define VALUE_KEY_REPEAT 2
#define KEY_APPLE_FN 464 // This is special Apple 'fn' key
//#define MODIFIER_KEY KEY_LEFTCTRL // Does not work at all...
//#define MODIFIER_KEY KEY_LEFTALT // Does not work correctly
//#define MODIFIER_KEY KEY_LEFTMETA // Works
//#define MODIFIER_KEY KEY_RIGHTMETA // Works
#define MODIFIER_KEY KEY_APPLE_FN // Works
#define SPECIAL_KEYS { KEY_APPLE_FN, \
KEY_CAPSLOCK, \
KEY_LEFTCTRL, KEY_RIGHTCTRL, \
KEY_LEFTSHIFT, KEY_RIGHTSHIFT, \
KEY_LEFTALT, KEY_RIGHTALT, \
KEY_LEFTMETA, KEY_RIGHTMETA, \
-1}
int main (int argc, char **argv)
{
int fd, rd, i, wfd, j;
struct input_event ev[64];
unsigned short id[4];
unsigned long bit[EV_MAX][NBITS(KEY_MAX)];
int special_keys[] = SPECIAL_KEYS;
char name[256] = "Unknown";
if ((argc == 2) && (argv[1] != NULL)) {
printf("Usage: appletouchd\n");
printf("Appletouch kernel driver support daemon\n");
return 1;
}
if ((fd = open(APPLE_KEYBOARD_EVENT_DEV, O_RDONLY)) < 0) {
perror("appletouchd: opening " APPLE_KEYBOARD_EVENT_DEV);
return 1;
}
if ((wfd = open(APPLE_TOUCHPAD_DEV, O_WRONLY)) < 0) {
perror("appletouchd: opening " APPLE_TOUCHPAD_DEV);
return 1;
}
/* Check of an Apple computer with a known keyboard product id */
ioctl(fd, EVIOCGID, id);
ioctl(fd, EVIOCGNAME(sizeof(name)), name);
if (id[ID_VENDOR] != VENDOR_APPLE)
fprintf(stderr, "appletouchd: This program may be useless on a none Apple computer\n");
else if (id[ID_PRODUCT] != VENDOR_KEYBOARD_MBP)
fprintf(stderr, "appletouchd: The device '%s' is unknown. It may not work. Proceeding anyway...\n", name);
/* Search for a key event - must have an keyboard */
memset(bit, 0, sizeof(bit));
ioctl(fd, EVIOCGBIT(0, EV_MAX), bit[0]);
if (!test_bit(EV_KEY, bit[0]))
{
fprintf(stderr, "appletouchd: This device is not a keyboard\n");
return 1;
}
while (1)
{
rd = read(fd, ev, sizeof(struct input_event) * 64);
if (rd < (int) sizeof(struct input_event))
{
perror("\nappletouchd: error reading");
return 1;
}
for (i = 0; i < rd / sizeof(struct input_event); i++)
{
if (ev[i].type == EV_KEY)
{
/* Was the touchpad modifier key pressed? */
if (ev[i].code == MODIFIER_KEY)
{
/* Yes ... */
if (ev[i].value == VALUE_KEY_UP)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Touchpad modifier key released\n");
#endif
write(wfd,"2",1);
}
else if (ev[i].value == VALUE_KEY_DOWN)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Touchpad modifier key pressed\n");
#endif
write(wfd,"3",1);
}
}
else
{
/* No ... */
/* Was a special key pressed? */
for (j=0; special_keys[j] != -1; j++)
{
if (ev[i].code == special_keys[j])
{
if (ev[i].value == VALUE_KEY_UP)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Special key released\n");
#endif
write(wfd,"4",1);
}
else if (ev[i].value == VALUE_KEY_DOWN)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Special key pressed\n");
#endif
write(wfd,"5",1);
}
break;
}
}
/* No special key found? */
if (special_keys[j] == -1)
{
if (ev[i].value == VALUE_KEY_UP)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Some key released\n");
#endif
write(wfd,"0",1);
}
else if (ev[i].value == VALUE_KEY_DOWN)
{
#ifdef DEBUG
fprintf(stderr, "appletouchd: Some key pressed\n");
#endif
write(wfd,"1",1);
}
}
}
}
}
}
}
# Needed for Kernel compile
obj-m := appletouch.o
# Config
KSRC=/usr/src/linux
MODPATH=/lib/modules/`uname -r`/kernel/drivers/usb/input
# Targets
all: appletouch.ko appletouchd
appletouch.ko: appletouch.c
$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) modules
appletouchd: appletouchd.c
gcc -Wall -O2 -D_GNU_SOURCE -o appletouchd appletouchd.c
install: appletouch.ko appletouchd
@if [ ! -f appletouch.ko.orig ]; then \
cp $(MODPATH)/appletouch.ko appletouch.ko.orig; \
fi
install -m 644 appletouch.ko $(MODPATH)
#install -m 755 appletouchd /usr/sbin/
clean:
-rm *.o appletouchd Module.symvers
$(MAKE) -C $(KSRC) SUBDIRS=$(PWD) clean
begin:vcard fn:Sven Anders n:Anders;Sven org:ANDURAS AG;Research and Development adr;quoted-printable:;;Innstra=C3=9Fe 71;Passau;Bavaria;94036;Germany email;internet:[EMAIL PROTECTED] title:Dipl. Inf. tel;work:++49 (0)851 / 490 50 -0 tel;fax:++49 (0)851 / 590 50 - 55 x-mozilla-html:FALSE url:http://www.anduras.de version:2.1 end:vcard
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 [email protected] https://lists.sourceforge.net/lists/listinfo/mactel-linux-devel
