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