Skip to content

Commit

Permalink
Input: appletouch - add support for Geyser 2
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Michael Hanselmann authored and Dmitry Torokhov committed Dec 21, 2005
1 parent 041387d commit e1e02c9
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 38 deletions.
5 changes: 3 additions & 2 deletions Documentation/input/appletouch.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
------
Expand Down
145 changes: 109 additions & 36 deletions drivers/usb/input/appletouch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -38,6 +39,11 @@
/* 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 | \
Expand All @@ -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
Expand Down Expand Up @@ -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) \
Expand All @@ -124,14 +136,24 @@ 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");

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)
{
Expand Down Expand Up @@ -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:
Expand All @@ -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);
Expand All @@ -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;
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -353,14 +413,16 @@ 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) {
retval = -ENOMEM;
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;
Expand All @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down

0 comments on commit e1e02c9

Please sign in to comment.