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 Mactel-linux-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/mactel-linux-devel