Input: appletouch - add support for Geyser 2
Michael Hanselmann [Wed, 21 Dec 2005 05:50:23 +0000 (00:50 -0500)]
This patch adds support for the Geyser 2 touchpads used on post Oct 2005
Apple PowerBooks to the appletouch driver.

Signed-off-by: Michael Hanselmann <linux-kernel@hansmi.ch>
Acked-by: Rene Nussbaumer <linux-kernel@killerfox.forkbomb.ch>
Acked-by: Johannes Berg <johannes@sipsolutions.net>
Acked-by: Stelian Pop <stelian@popies.net>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

Documentation/input/appletouch.txt
drivers/usb/input/appletouch.c

index b48d11d..4f7c633 100644 (file)
@@ -3,7 +3,7 @@ Apple Touchpad Driver (appletouch)
        Copyright (C) 2005 Stelian Pop <stelian@popies.net>
 
 appletouch is a Linux kernel driver for the USB touchpad found on post
-February 2005 Apple Alu Powerbooks.
+February 2005 and October 2005 Apple Aluminium Powerbooks.
 
 This driver is derived from Johannes Berg's appletrackpad driver[1], but it has
 been improved in some areas:
@@ -13,7 +13,8 @@ been improved in some areas:
 
 Credits go to Johannes Berg for reverse-engineering the touchpad protocol,
 Frank Arnold for further improvements, and Alex Harper for some additional
-information about the inner workings of the touchpad sensors.
+information about the inner workings of the touchpad sensors. Michael
+Hanselmann added support for the October 2005 models.
 
 Usage:
 ------
index 15840db..6cce852 100644 (file)
@@ -6,6 +6,7 @@
  * Copyright (C) 2005      Stelian Pop (stelian@popies.net)
  * Copyright (C) 2005      Frank Arnold (frank@scirocco-5v-turbo.de)
  * Copyright (C) 2005      Peter Osterlund (petero2@telia.com)
+ * Copyright (C) 2005      Michael Hanselmann (linux-kernel@hansmi.ch)
  *
  * Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
  *
 /* 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 GEYSER_ANSI_PRODUCT_ID 0x0214
+#define GEYSER_ISO_PRODUCT_ID  0x0215
+#define GEYSER_JIS_PRODUCT_ID  0x0216
+
 #define ATP_DEVICE(prod)                                       \
        .match_flags = USB_DEVICE_ID_MATCH_DEVICE |             \
                       USB_DEVICE_ID_MATCH_INT_CLASS |          \
@@ -53,13 +59,17 @@ static struct usb_device_id atp_table [] = {
        { ATP_DEVICE(0x020F) },
        { ATP_DEVICE(0x030A) },
        { ATP_DEVICE(0x030B) },
-       { }                                     /* Terminating entry */
+
+       /* PowerBooks Oct 2005 */
+       { ATP_DEVICE(GEYSER_ANSI_PRODUCT_ID) },
+       { ATP_DEVICE(GEYSER_ISO_PRODUCT_ID) },
+       { ATP_DEVICE(GEYSER_JIS_PRODUCT_ID) },
+
+       /* Terminating entry */
+       { }
 };
 MODULE_DEVICE_TABLE (usb, atp_table);
 
-/* size of a USB urb transfer */
-#define ATP_DATASIZE   81
-
 /*
  * number of sensors. Note that only 16 instead of 26 X (horizontal)
  * sensors exist on 12" and 15" PowerBooks. All models have 16 Y
@@ -108,6 +118,8 @@ struct atp {
        signed char             xy_old[ATP_XSENSORS + ATP_YSENSORS];
                                                /* accumulated sensors */
        int                     xy_acc[ATP_XSENSORS + ATP_YSENSORS];
+       int                     overflowwarn;   /* overflow warning printed? */
+       int                     datalen;        /* size of an USB urb transfer */
 };
 
 #define dbg_dump(msg, tab) \
@@ -124,7 +136,7 @@ struct atp {
                if (debug) printk(format, ##a);                         \
        } while (0)
 
-MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold");
+MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Michael Hanselmann");
 MODULE_DESCRIPTION("Apple PowerBooks USB touchpad driver");
 MODULE_LICENSE("GPL");
 
@@ -132,6 +144,16 @@ static int debug = 1;
 module_param(debug, int, 0644);
 MODULE_PARM_DESC(debug, "Activate debugging output");
 
+/* Checks if the device a Geyser 2 (ANSI, ISO, JIS) */
+static inline int atp_is_geyser_2(struct atp *dev)
+{
+       int16_t productId = le16_to_cpu(dev->udev->descriptor.idProduct);
+
+       return (productId == GEYSER_ANSI_PRODUCT_ID) ||
+               (productId == GEYSER_ISO_PRODUCT_ID) ||
+               (productId == GEYSER_JIS_PRODUCT_ID);
+}
+
 static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
                             int *z, int *fingers)
 {
@@ -168,13 +190,20 @@ static inline void atp_report_fingers(struct input_dev *input, int fingers)
 static void atp_complete(struct urb* urb, struct pt_regs* regs)
 {
        int x, y, x_z, y_z, x_f, y_f;
-       int retval, i;
+       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:
@@ -189,23 +218,45 @@ static void atp_complete(struct urb* urb, struct pt_regs* regs)
        }
 
        /* drop incomplete datasets */
-       if (dev->urb->actual_length != ATP_DATASIZE) {
+       if (dev->urb->actual_length != dev->datalen) {
                dprintk("appletouch: incomplete data package.\n");
                goto exit;
        }
 
        /* reorder the sensors values */
-       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 (atp_is_geyser_2(dev)) {
+               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];
+               }
        }
 
        dbg_dump("sample", dev->xy_cur);
@@ -216,16 +267,24 @@ static void atp_complete(struct urb* urb, struct pt_regs* regs)
                dev->x_old = dev->y_old = -1;
                memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
 
-               /* 17" Powerbooks have 10 extra X sensors */
-               for (i = 16; i < ATP_XSENSORS; i++)
-                       if (dev->xy_cur[i]) {
-                               printk("appletouch: 17\" model detected.\n");
+               /* 17" Powerbooks have extra X sensors */
+               for (i = (atp_is_geyser_2(dev)?15:16); i < ATP_XSENSORS; i++) {
+                       if (!dev->xy_cur[i]) continue;
+
+                       printk("appletouch: 17\" model detected.\n");
+                       if(atp_is_geyser_2(dev))
+                               input_set_abs_params(dev->input, ABS_X, 0,
+                                                    (20 - 1) *
+                                                    ATP_XFACT - 1,
+                                                    ATP_FUZZ, 0);
+                       else
                                input_set_abs_params(dev->input, ABS_X, 0,
                                                     (ATP_XSENSORS - 1) *
                                                     ATP_XFACT - 1,
                                                     ATP_FUZZ, 0);
-                               break;
-                       }
+
+                       break;
+               }
 
                goto exit;
        }
@@ -282,7 +341,8 @@ static void atp_complete(struct urb* urb, struct pt_regs* regs)
                memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
        }
 
-       input_report_key(dev->input, BTN_LEFT, !!dev->data[80]);
+       input_report_key(dev->input, BTN_LEFT,
+                        !!dev->data[dev->datalen - 1]);
 
        input_sync(dev->input);
 
@@ -353,6 +413,8 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
 
        dev->udev = udev;
        dev->input = input_dev;
+       dev->overflowwarn = 0;
+       dev->datalen = (atp_is_geyser_2(dev)?64:81);
 
        dev->urb = usb_alloc_urb(0, GFP_KERNEL);
        if (!dev->urb) {
@@ -360,7 +422,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
                goto err_free_devs;
        }
 
-       dev->data = usb_buffer_alloc(dev->udev, ATP_DATASIZE, GFP_KERNEL,
+       dev->data = usb_buffer_alloc(dev->udev, dev->datalen, GFP_KERNEL,
                                     &dev->urb->transfer_dma);
        if (!dev->data) {
                retval = -ENOMEM;
@@ -369,7 +431,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
 
        usb_fill_int_urb(dev->urb, udev,
                         usb_rcvintpipe(udev, int_in_endpointAddr),
-                        dev->data, ATP_DATASIZE, atp_complete, dev, 1);
+                        dev->data, dev->datalen, atp_complete, dev, 1);
 
        usb_make_path(udev, dev->phys, sizeof(dev->phys));
        strlcat(dev->phys, "/input0", sizeof(dev->phys));
@@ -385,14 +447,25 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
 
        set_bit(EV_ABS, input_dev->evbit);
 
-       /*
-        * 12" and 15" Powerbooks only have 16 x sensors,
-        * 17" models are detected later.
-        */
-       input_set_abs_params(input_dev, ABS_X, 0,
-                            (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
-       input_set_abs_params(input_dev, ABS_Y, 0,
-                            (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
+       if (atp_is_geyser_2(dev)) {
+               /*
+                * Oct 2005 15" PowerBooks have 15 X sensors, 17" are detected
+                * later.
+                */
+               input_set_abs_params(input_dev, ABS_X, 0,
+                                    ((15 - 1) * ATP_XFACT) - 1, ATP_FUZZ, 0);
+               input_set_abs_params(input_dev, ABS_Y, 0,
+                                    ((9 - 1) * ATP_YFACT) - 1, ATP_FUZZ, 0);
+       } else {
+               /*
+                * 12" and 15" Powerbooks only have 16 x sensors,
+                * 17" models are detected later.
+                */
+               input_set_abs_params(input_dev, ABS_X, 0,
+                                    (16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
+               input_set_abs_params(input_dev, ABS_Y, 0,
+                                    (ATP_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
+       }
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
 
        set_bit(EV_KEY, input_dev->evbit);
@@ -427,7 +500,7 @@ static void atp_disconnect(struct usb_interface *iface)
                usb_kill_urb(dev->urb);
                input_unregister_device(dev->input);
                usb_free_urb(dev->urb);
-               usb_buffer_free(dev->udev, ATP_DATASIZE,
+               usb_buffer_free(dev->udev, dev->datalen,
                                dev->data, dev->urb->transfer_dma);
                kfree(dev);
        }