From: Michael Hanselmann <linux-kernel@hansmi.ch>
To: Parag Warudkar <kernel-stuff@comcast.net>
Cc: linuxppc-dev@ozlabs.org, debian-powerpc@lists.debian.org,
linux-kernel <linux-kernel@vger.kernel.org>
Subject: Re: PowerBook5,8 - TrackPad update
Date: Tue, 29 Nov 2005 01:06:15 +0100 [thread overview]
Message-ID: <20051129000615.GA20843@hansmi.ch> (raw)
In-Reply-To: <70210ED5-37CA-40BC-8293-FF1DAA3E8BD5@comcast.net>
[-- Attachment #1.1: Type: text/plain, Size: 988 bytes --]
Hello Parag
> Attached is the code which takes care of above 3 - tested but it
> should be considered half baked for obvious reasons and
I hacked your code some more to make it work on my October 2005
PowerBook 1.67 GHz. The product ID I have, 0x0215, was in none of the
available drivers and the data format is somewhat different.
You find my hacked version attached -- be aware that in its current form
it will not work with any touchpad except 0x0215.
> in addition I added some relayfs write calls [...]
I wrapped them into #if's, so one is not required to have relayfs in the
kernel to use the driver.
> The code might break the existing (old) trackpads as the detection
> might not be correct.
My changes do that definitively, but it's only a hack.
As far as I see it, all methods can be built into one driver. Is there
already someone working on combining them?
Greets,
Michael
--
Gentoo Linux Developer using m0n0wall | http://hansmi.ch/
[-- Attachment #1.2: appletouch.c --]
[-- Type: text/x-csrc, Size: 16927 bytes --]
/*
* Apple USB Touchpad (for post-February 2005 PowerBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
* 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 Parag Warudkar (parag.warudkar@gmail.com)
*
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
* Nov 2005 - Parag Warudkar
* o Add preliminary support for Oct 2005 Powerbooks
* (Doesn't work fully yet)
* o Converted to use input_device_allocate()
* o Added ability to export data via relayfs
*
* 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/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb_input.h>
#include <linux/debugfs.h>
#include <asm/prom.h>
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
#include <linux/relayfs_fs.h>
#endif
#if !defined(PPC) && !defined(POWERPC)
#error "Module intended for PowerPC based PowerBooks!"
#endif
/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID 0x05AC
#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 [] = {
/* Earlier PowerBooks */
{ ATP_DEVICE(0x020E) },
{ ATP_DEVICE(0x020F) },
{ ATP_DEVICE(0x030A) },
{ ATP_DEVICE(0x030B) },
/* PowerBooks Late Oct 2005 */
{ ATP_DEVICE(0x0214) },
{ ATP_DEVICE(0x0215) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE (usb, atp_table);
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
struct rchan* rch = NULL;
struct rchan_callbacks* rcb = NULL;
#endif
/*
* 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_MAX_XSENSORS 15
#define ATP_MAX_YSENSORS 7
/* amount of fuzz this touchpad generates */
#define ATP_FUZZ 16
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
/*
* 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
* 0 <= y < 646
*/
#define ATP_XFACT 64
#define ATP_YFACT 43
/*
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
* ignored.
*/
#define ATP_THRESHOLD 5
typedef enum PB_MAC_TYPE {
PowerBook54,
PowerBook56,
PowerBook58,
PowerBook59,
Unknown
} PB_MAC_TYPE;
/* Structure to hold all of our device specific stuff */
struct atp {
struct usb_device * udev; /* usb device */
struct urb * urb; /* usb request block */
signed char * data; /* transferred data */
int open; /* non-zero if opened */
struct input_dev *input; /* input dev */
int valid; /* are the sensors valid ? */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
/* current value of the sensors */
signed char xy_cur[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
/* last value of the sensors */
signed char xy_old[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
/* accumulated sensors */
int xy_acc[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
PB_MAC_TYPE mtype;
};
#define dbg_dump(msg, tab) \
if (debug > 1) { \
int i; \
printk("appletouch: %s %lld", msg, (long long)jiffies); \
for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) \
printk(" %02x", tab[i]); \
printk("\n"); \
}
#define dprintk(format, a...) \
do { \
if (debug) printk(format, ##a); \
} while (0)
MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Parag Warudkar");
MODULE_DESCRIPTION("Apple Al PowerBook USB touchpad driver");
MODULE_LICENSE("GPL");
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/* USB URB buffer length - 81 works on Feb 05 models.
* Oct 05 models fail with EOVERFLOW, detected and corrected
* separately.
*/
//static int data_len = 81;
static int data_len = 80;
module_param(data_len, int, 0644);
MODULE_PARM_DESC(data_len, "Specify USB URB buffer length (auto-detected normally)");
PB_MAC_TYPE atp_machine_detect(void)
{
if(machine_is_compatible("PowerBook5,9"))
return PowerBook59;
if(machine_is_compatible("PowerBook5,8"))
return PowerBook58;
if(machine_is_compatible("PowerBook5,6"))
return PowerBook56;
if (machine_is_compatible("PowerBook5,4"))
return PowerBook54;
return Unknown;
}
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
int i;
/* values to calculate mean */
int pcum = 0, psum = 0;
*fingers = 0;
for (i = 0; i < nb_sensors; i++) {
if (xy_sensors[i] < ATP_THRESHOLD)
continue;
if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
(*fingers)++;
pcum += xy_sensors[i] * i;
psum += xy_sensors[i];
}
if (psum > 0) {
*z = psum;
return pcum * fact / psum;
}
return 0;
}
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, struct pt_regs* regs)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i;
struct atp *dev = urb->context;
static int ctr = 1;
switch (urb->status) {
case 0:
/* success */
break;
case -EOVERFLOW:
if(ctr){
printk("appletouch: OVERFLOW with data length %d\n", data_len);
ctr = 0;
}
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:
goto exit;
}
/* drop incomplete datasets */
if (dev->urb->actual_length != data_len) {
printk(KERN_WARNING "appletouch: incomplete data package length %d\n", dev->urb->actual_length);
goto exit;
}
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
if ( dev->data ) {
relay_write(rch, dev->data, dev->urb->actual_length);
}
#endif
dev->xy_cur[0] = dev->data[19];
dev->xy_cur[1] = dev->data[20];
dev->xy_cur[2] = dev->data[22];
dev->xy_cur[3] = dev->data[23];
dev->xy_cur[4] = dev->data[25];
dev->xy_cur[5] = dev->data[26];
dev->xy_cur[6] = dev->data[28];
dev->xy_cur[7] = dev->data[29];
dev->xy_cur[8] = dev->data[31];
dev->xy_cur[9] = dev->data[32];
dev->xy_cur[10] = dev->data[34];
dev->xy_cur[11] = dev->data[35];
dev->xy_cur[12] = dev->data[37];
dev->xy_cur[13] = dev->data[38];
dev->xy_cur[14] = dev->data[40];
dev->xy_cur[15] = dev->data[1];
dev->xy_cur[16] = dev->data[2];
dev->xy_cur[17] = dev->data[4];
dev->xy_cur[18] = dev->data[5];
dev->xy_cur[19] = dev->data[7];
dev->xy_cur[20] = dev->data[8];
dev->xy_cur[21] = dev->data[10];
/*
for(i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
printk("%2x ", dev->xy_cur[i]);
}
printk("\n");
*/
/* 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];
}*/
//dbg_dump("sample", dev->xy_cur);
if (!dev->valid) {
/* first sample */
dev->valid = 1;
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_MAX_XSENSORS; i++)
if (dev->xy_cur[i]) {
printk("appletouch: 17\" model detected.\n");
input_set_abs_params(dev->input, ABS_X, 0,
((ATP_MAX_XSENSORS - 1) * ATP_XFACT) - 1,
ATP_FUZZ, 0);
break;
}*/
goto exit;
}
for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
/* accumulate the change */
signed char change = dev->xy_old[i] - dev->xy_cur[i];
dev->xy_acc[i] -= change;
/* prevent down drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
//dbg_dump("accumulator", dev->xy_acc);
x = atp_calculate_abs(dev->xy_acc, ATP_MAX_XSENSORS,
ATP_XFACT, &x_z, &x_f);
y = atp_calculate_abs(dev->xy_acc + ATP_MAX_XSENSORS, ATP_MAX_YSENSORS,
ATP_YFACT, &y_z, &y_f);
if (x && y) {
if (dev->x_old != -1) {
x = (dev->x_old * 3 + x) >> 2;
y = (dev->y_old * 3 + y) >> 2;
dev->x_old = x;
dev->y_old = y;
if (debug > 1)
printk("appletouch: X: %3d Y: %3d "
"Xz: %3d Yz: %3d\n",
x, y, x_z, y_z);
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,
min(ATP_PRESSURE, x_z + y_z));
atp_report_fingers(dev->input, max(x_f, y_f));
}
dev->x_old = x;
dev->y_old = y;
}
else if (!x && !y) {
dev->x_old = dev->y_old = -1;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
input_report_key(dev->input, BTN_LEFT, !!dev->data[data_len-1]);
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)) {
printk("atp: usb_submit_urb Error\n");
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_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
struct atp *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, retval = -ENOMEM;
int regret=0;
printk(KERN_INFO "AppleTouch: Inside atp_probe\n");
/* allocate memory for our device state and initialize it */
dev = kmalloc(sizeof(struct atp), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto err_kmalloc;
}
memset(dev, 0, sizeof(struct atp));
dev->mtype = atp_machine_detect();
if(dev->mtype == Unknown)
{
printk(KERN_INFO "appletouch: Unknown machine type!\n");
return -ENODEV;
}
if(dev->mtype > 1)
{
if(data_len < 256)
{
printk(KERN_INFO "appletouch: Setting URB buffer length to 256\n");
data_len = 256;
}
}
printk("appletouch: Machine type is: %d\n", dev->mtype);
dev->udev = interface_to_usbdev(iface);
/* 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 &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_INT)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
printk(KERN_INFO "AppleTouch: atp_probe found interrupt in endpoint: %d\n", int_in_endpointAddr);
break;
}
}
if (!int_in_endpointAddr) {
retval = -EIO;
err("Could not find int-in endpoint");
goto err_endpoint;
}
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb) {
retval = -ENOMEM;
goto err_usballoc;
}
dev->data = usb_buffer_alloc(dev->udev, data_len, GFP_KERNEL,
&dev->urb->transfer_dma);
if (!dev->data) {
retval = -ENOMEM;
goto err_usbbufalloc;
}
usb_fill_int_urb(dev->urb, dev->udev,
usb_rcvintpipe(dev->udev, int_in_endpointAddr),
dev->data, data_len, atp_complete, dev, 1);
dev->input = input_allocate_device();
dev->input->name = "appletouch";
dev->input->dev = &iface->dev;
dev->input->private = dev;
dev->input->open = atp_open;
dev->input->close = atp_close;
usb_to_input_id(dev->udev, &dev->input->id);
set_bit(EV_ABS, dev->input->evbit);
/*
* 12" and 15" Powerbooks only have 16 x sensors,
* 17" models are detected later.
*/
input_set_abs_params(dev->input, ABS_X, 0,
(16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
input_set_abs_params(dev->input, ABS_Y, 0,
(ATP_MAX_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
input_set_abs_params(dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, dev->input->evbit);
set_bit(BTN_TOUCH, dev->input->keybit);
set_bit(BTN_TOOL_FINGER, dev->input->keybit);
set_bit(BTN_TOOL_DOUBLETAP, dev->input->keybit);
set_bit(BTN_TOOL_TRIPLETAP, dev->input->keybit);
set_bit(BTN_LEFT, dev->input->keybit);
regret = input_register_device(dev->input);
printk(KERN_INFO "input: appletouch connected, Reg code: %d\n", regret);
return 0;
err_usbbufalloc:
usb_free_urb(dev->urb);
err_usballoc:
usb_set_intfdata(iface, NULL);
err_endpoint:
kfree(dev);
err_kmalloc:
return retval;
}
static void atp_disconnect(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
if (dev) {
usb_kill_urb(dev->urb);
input_unregister_device(dev->input);
usb_free_urb(dev->urb);
usb_buffer_free(dev->udev, data_len,
dev->data, dev->urb->transfer_dma);
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 = {
.owner = THIS_MODULE,
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
.suspend = atp_suspend,
.resume = atp_resume,
.id_table = atp_table,
};
static int __init atp_init(void)
{
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
rcb = kmalloc(sizeof(struct rchan_callbacks), GFP_KERNEL);
rcb->subbuf_start = NULL;
rcb->buf_mapped = NULL;
rcb->buf_unmapped = NULL;
rch = relay_open("atpdata", NULL, 256, 256, NULL);
if (!rch) return -ENOMEM;
printk("appletouch: Relayfs enabled.\n");
#endif
return usb_register(&atp_driver);
}
static void __exit atp_exit(void)
{
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
relay_close(rch);
kfree(rcb);
#endif
usb_deregister(&atp_driver);
}
module_init(atp_init);
module_exit(atp_exit);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
WARNING: multiple messages have this Message-ID (diff)
From: Michael Hanselmann <linux-kernel@hansmi.ch>
To: Parag Warudkar <kernel-stuff@comcast.net>
Cc: debian-powerpc@lists.debian.org,
linux-kernel <linux-kernel@vger.kernel.org>,
linuxppc-dev@ozlabs.org
Subject: Re: PowerBook5,8 - TrackPad update
Date: Tue, 29 Nov 2005 01:06:15 +0100 [thread overview]
Message-ID: <20051129000615.GA20843@hansmi.ch> (raw)
In-Reply-To: <70210ED5-37CA-40BC-8293-FF1DAA3E8BD5@comcast.net>
[-- Attachment #1.1: Type: text/plain, Size: 988 bytes --]
Hello Parag
> Attached is the code which takes care of above 3 - tested but it
> should be considered half baked for obvious reasons and
I hacked your code some more to make it work on my October 2005
PowerBook 1.67 GHz. The product ID I have, 0x0215, was in none of the
available drivers and the data format is somewhat different.
You find my hacked version attached -- be aware that in its current form
it will not work with any touchpad except 0x0215.
> in addition I added some relayfs write calls [...]
I wrapped them into #if's, so one is not required to have relayfs in the
kernel to use the driver.
> The code might break the existing (old) trackpads as the detection
> might not be correct.
My changes do that definitively, but it's only a hack.
As far as I see it, all methods can be built into one driver. Is there
already someone working on combining them?
Greets,
Michael
--
Gentoo Linux Developer using m0n0wall | http://hansmi.ch/
[-- Attachment #1.2: appletouch.c --]
[-- Type: text/x-csrc, Size: 16927 bytes --]
/*
* Apple USB Touchpad (for post-February 2005 PowerBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2005 Johannes Berg (johannes@sipsolutions.net)
* 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 Parag Warudkar (parag.warudkar@gmail.com)
*
* Thanks to Alex Harper <basilisk@foobox.net> for his inputs.
* Nov 2005 - Parag Warudkar
* o Add preliminary support for Oct 2005 Powerbooks
* (Doesn't work fully yet)
* o Converted to use input_device_allocate()
* o Added ability to export data via relayfs
*
* 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/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/input.h>
#include <linux/usb_input.h>
#include <linux/debugfs.h>
#include <asm/prom.h>
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
#include <linux/relayfs_fs.h>
#endif
#if !defined(PPC) && !defined(POWERPC)
#error "Module intended for PowerPC based PowerBooks!"
#endif
/* Apple has powerbooks which have the keyboard with different Product IDs */
#define APPLE_VENDOR_ID 0x05AC
#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 [] = {
/* Earlier PowerBooks */
{ ATP_DEVICE(0x020E) },
{ ATP_DEVICE(0x020F) },
{ ATP_DEVICE(0x030A) },
{ ATP_DEVICE(0x030B) },
/* PowerBooks Late Oct 2005 */
{ ATP_DEVICE(0x0214) },
{ ATP_DEVICE(0x0215) },
/* Terminating entry */
{ }
};
MODULE_DEVICE_TABLE (usb, atp_table);
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
struct rchan* rch = NULL;
struct rchan_callbacks* rcb = NULL;
#endif
/*
* 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_MAX_XSENSORS 15
#define ATP_MAX_YSENSORS 7
/* amount of fuzz this touchpad generates */
#define ATP_FUZZ 16
/* maximum pressure this driver will report */
#define ATP_PRESSURE 300
/*
* 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
* 0 <= y < 646
*/
#define ATP_XFACT 64
#define ATP_YFACT 43
/*
* Threshold for the touchpad sensors. Any change less than ATP_THRESHOLD is
* ignored.
*/
#define ATP_THRESHOLD 5
typedef enum PB_MAC_TYPE {
PowerBook54,
PowerBook56,
PowerBook58,
PowerBook59,
Unknown
} PB_MAC_TYPE;
/* Structure to hold all of our device specific stuff */
struct atp {
struct usb_device * udev; /* usb device */
struct urb * urb; /* usb request block */
signed char * data; /* transferred data */
int open; /* non-zero if opened */
struct input_dev *input; /* input dev */
int valid; /* are the sensors valid ? */
int x_old; /* last reported x/y, */
int y_old; /* used for smoothing */
/* current value of the sensors */
signed char xy_cur[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
/* last value of the sensors */
signed char xy_old[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
/* accumulated sensors */
int xy_acc[ATP_MAX_XSENSORS + ATP_MAX_YSENSORS];
PB_MAC_TYPE mtype;
};
#define dbg_dump(msg, tab) \
if (debug > 1) { \
int i; \
printk("appletouch: %s %lld", msg, (long long)jiffies); \
for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) \
printk(" %02x", tab[i]); \
printk("\n"); \
}
#define dprintk(format, a...) \
do { \
if (debug) printk(format, ##a); \
} while (0)
MODULE_AUTHOR("Johannes Berg, Stelian Pop, Frank Arnold, Parag Warudkar");
MODULE_DESCRIPTION("Apple Al PowerBook USB touchpad driver");
MODULE_LICENSE("GPL");
static int debug = 1;
module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "Activate debugging output");
/* USB URB buffer length - 81 works on Feb 05 models.
* Oct 05 models fail with EOVERFLOW, detected and corrected
* separately.
*/
//static int data_len = 81;
static int data_len = 80;
module_param(data_len, int, 0644);
MODULE_PARM_DESC(data_len, "Specify USB URB buffer length (auto-detected normally)");
PB_MAC_TYPE atp_machine_detect(void)
{
if(machine_is_compatible("PowerBook5,9"))
return PowerBook59;
if(machine_is_compatible("PowerBook5,8"))
return PowerBook58;
if(machine_is_compatible("PowerBook5,6"))
return PowerBook56;
if (machine_is_compatible("PowerBook5,4"))
return PowerBook54;
return Unknown;
}
static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
int i;
/* values to calculate mean */
int pcum = 0, psum = 0;
*fingers = 0;
for (i = 0; i < nb_sensors; i++) {
if (xy_sensors[i] < ATP_THRESHOLD)
continue;
if ((i - 1 < 0) || (xy_sensors[i - 1] < ATP_THRESHOLD))
(*fingers)++;
pcum += xy_sensors[i] * i;
psum += xy_sensors[i];
}
if (psum > 0) {
*z = psum;
return pcum * fact / psum;
}
return 0;
}
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, struct pt_regs* regs)
{
int x, y, x_z, y_z, x_f, y_f;
int retval, i;
struct atp *dev = urb->context;
static int ctr = 1;
switch (urb->status) {
case 0:
/* success */
break;
case -EOVERFLOW:
if(ctr){
printk("appletouch: OVERFLOW with data length %d\n", data_len);
ctr = 0;
}
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:
goto exit;
}
/* drop incomplete datasets */
if (dev->urb->actual_length != data_len) {
printk(KERN_WARNING "appletouch: incomplete data package length %d\n", dev->urb->actual_length);
goto exit;
}
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
if ( dev->data ) {
relay_write(rch, dev->data, dev->urb->actual_length);
}
#endif
dev->xy_cur[0] = dev->data[19];
dev->xy_cur[1] = dev->data[20];
dev->xy_cur[2] = dev->data[22];
dev->xy_cur[3] = dev->data[23];
dev->xy_cur[4] = dev->data[25];
dev->xy_cur[5] = dev->data[26];
dev->xy_cur[6] = dev->data[28];
dev->xy_cur[7] = dev->data[29];
dev->xy_cur[8] = dev->data[31];
dev->xy_cur[9] = dev->data[32];
dev->xy_cur[10] = dev->data[34];
dev->xy_cur[11] = dev->data[35];
dev->xy_cur[12] = dev->data[37];
dev->xy_cur[13] = dev->data[38];
dev->xy_cur[14] = dev->data[40];
dev->xy_cur[15] = dev->data[1];
dev->xy_cur[16] = dev->data[2];
dev->xy_cur[17] = dev->data[4];
dev->xy_cur[18] = dev->data[5];
dev->xy_cur[19] = dev->data[7];
dev->xy_cur[20] = dev->data[8];
dev->xy_cur[21] = dev->data[10];
/*
for(i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
printk("%2x ", dev->xy_cur[i]);
}
printk("\n");
*/
/* 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];
}*/
//dbg_dump("sample", dev->xy_cur);
if (!dev->valid) {
/* first sample */
dev->valid = 1;
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_MAX_XSENSORS; i++)
if (dev->xy_cur[i]) {
printk("appletouch: 17\" model detected.\n");
input_set_abs_params(dev->input, ABS_X, 0,
((ATP_MAX_XSENSORS - 1) * ATP_XFACT) - 1,
ATP_FUZZ, 0);
break;
}*/
goto exit;
}
for (i = 0; i < ATP_MAX_XSENSORS + ATP_MAX_YSENSORS; i++) {
/* accumulate the change */
signed char change = dev->xy_old[i] - dev->xy_cur[i];
dev->xy_acc[i] -= change;
/* prevent down drifting */
if (dev->xy_acc[i] < 0)
dev->xy_acc[i] = 0;
}
memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old));
//dbg_dump("accumulator", dev->xy_acc);
x = atp_calculate_abs(dev->xy_acc, ATP_MAX_XSENSORS,
ATP_XFACT, &x_z, &x_f);
y = atp_calculate_abs(dev->xy_acc + ATP_MAX_XSENSORS, ATP_MAX_YSENSORS,
ATP_YFACT, &y_z, &y_f);
if (x && y) {
if (dev->x_old != -1) {
x = (dev->x_old * 3 + x) >> 2;
y = (dev->y_old * 3 + y) >> 2;
dev->x_old = x;
dev->y_old = y;
if (debug > 1)
printk("appletouch: X: %3d Y: %3d "
"Xz: %3d Yz: %3d\n",
x, y, x_z, y_z);
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,
min(ATP_PRESSURE, x_z + y_z));
atp_report_fingers(dev->input, max(x_f, y_f));
}
dev->x_old = x;
dev->y_old = y;
}
else if (!x && !y) {
dev->x_old = dev->y_old = -1;
input_report_key(dev->input, BTN_TOUCH, 0);
input_report_abs(dev->input, ABS_PRESSURE, 0);
atp_report_fingers(dev->input, 0);
/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}
input_report_key(dev->input, BTN_LEFT, !!dev->data[data_len-1]);
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)) {
printk("atp: usb_submit_urb Error\n");
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_probe(struct usb_interface *iface, const struct usb_device_id *id)
{
struct atp *dev = NULL;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
int int_in_endpointAddr = 0;
int i, retval = -ENOMEM;
int regret=0;
printk(KERN_INFO "AppleTouch: Inside atp_probe\n");
/* allocate memory for our device state and initialize it */
dev = kmalloc(sizeof(struct atp), GFP_KERNEL);
if (dev == NULL) {
err("Out of memory");
goto err_kmalloc;
}
memset(dev, 0, sizeof(struct atp));
dev->mtype = atp_machine_detect();
if(dev->mtype == Unknown)
{
printk(KERN_INFO "appletouch: Unknown machine type!\n");
return -ENODEV;
}
if(dev->mtype > 1)
{
if(data_len < 256)
{
printk(KERN_INFO "appletouch: Setting URB buffer length to 256\n");
data_len = 256;
}
}
printk("appletouch: Machine type is: %d\n", dev->mtype);
dev->udev = interface_to_usbdev(iface);
/* 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 &&
(endpoint->bEndpointAddress & USB_DIR_IN) &&
((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
== USB_ENDPOINT_XFER_INT)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
printk(KERN_INFO "AppleTouch: atp_probe found interrupt in endpoint: %d\n", int_in_endpointAddr);
break;
}
}
if (!int_in_endpointAddr) {
retval = -EIO;
err("Could not find int-in endpoint");
goto err_endpoint;
}
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);
dev->urb = usb_alloc_urb(0, GFP_KERNEL);
if (!dev->urb) {
retval = -ENOMEM;
goto err_usballoc;
}
dev->data = usb_buffer_alloc(dev->udev, data_len, GFP_KERNEL,
&dev->urb->transfer_dma);
if (!dev->data) {
retval = -ENOMEM;
goto err_usbbufalloc;
}
usb_fill_int_urb(dev->urb, dev->udev,
usb_rcvintpipe(dev->udev, int_in_endpointAddr),
dev->data, data_len, atp_complete, dev, 1);
dev->input = input_allocate_device();
dev->input->name = "appletouch";
dev->input->dev = &iface->dev;
dev->input->private = dev;
dev->input->open = atp_open;
dev->input->close = atp_close;
usb_to_input_id(dev->udev, &dev->input->id);
set_bit(EV_ABS, dev->input->evbit);
/*
* 12" and 15" Powerbooks only have 16 x sensors,
* 17" models are detected later.
*/
input_set_abs_params(dev->input, ABS_X, 0,
(16 - 1) * ATP_XFACT - 1, ATP_FUZZ, 0);
input_set_abs_params(dev->input, ABS_Y, 0,
(ATP_MAX_YSENSORS - 1) * ATP_YFACT - 1, ATP_FUZZ, 0);
input_set_abs_params(dev->input, ABS_PRESSURE, 0, ATP_PRESSURE, 0, 0);
set_bit(EV_KEY, dev->input->evbit);
set_bit(BTN_TOUCH, dev->input->keybit);
set_bit(BTN_TOOL_FINGER, dev->input->keybit);
set_bit(BTN_TOOL_DOUBLETAP, dev->input->keybit);
set_bit(BTN_TOOL_TRIPLETAP, dev->input->keybit);
set_bit(BTN_LEFT, dev->input->keybit);
regret = input_register_device(dev->input);
printk(KERN_INFO "input: appletouch connected, Reg code: %d\n", regret);
return 0;
err_usbbufalloc:
usb_free_urb(dev->urb);
err_usballoc:
usb_set_intfdata(iface, NULL);
err_endpoint:
kfree(dev);
err_kmalloc:
return retval;
}
static void atp_disconnect(struct usb_interface *iface)
{
struct atp *dev = usb_get_intfdata(iface);
usb_set_intfdata(iface, NULL);
if (dev) {
usb_kill_urb(dev->urb);
input_unregister_device(dev->input);
usb_free_urb(dev->urb);
usb_buffer_free(dev->udev, data_len,
dev->data, dev->urb->transfer_dma);
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 = {
.owner = THIS_MODULE,
.name = "appletouch",
.probe = atp_probe,
.disconnect = atp_disconnect,
.suspend = atp_suspend,
.resume = atp_resume,
.id_table = atp_table,
};
static int __init atp_init(void)
{
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
rcb = kmalloc(sizeof(struct rchan_callbacks), GFP_KERNEL);
rcb->subbuf_start = NULL;
rcb->buf_mapped = NULL;
rcb->buf_unmapped = NULL;
rch = relay_open("atpdata", NULL, 256, 256, NULL);
if (!rch) return -ENOMEM;
printk("appletouch: Relayfs enabled.\n");
#endif
return usb_register(&atp_driver);
}
static void __exit atp_exit(void)
{
#if defined(CONFIG_RELAYFS_FS) || defined(CONFIG_RELAYFS_FS_MODULE)
relay_close(rch);
kfree(rcb);
#endif
usb_deregister(&atp_driver);
}
module_init(atp_init);
module_exit(atp_exit);
[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]
next prev parent reply other threads:[~2005-11-29 0:13 UTC|newest]
Thread overview: 57+ messages / expand[flat|nested] mbox.gz Atom feed top
2005-11-15 21:43 PowerBook5,8 - TrackPad update Parag Warudkar
2005-11-15 23:06 ` Benjamin Herrenschmidt
2005-11-15 23:12 ` Parag Warudkar
[not found] ` <111520052143.16540.437A5680000BE8A60000409C220076369200009A9B9CD3040A 029D0A05@comcast.net>
2005-11-16 12:02 ` Johannes Berg
2005-11-21 23:57 ` Parag Warudkar
2005-11-21 23:57 ` Parag Warudkar
2005-11-22 0:08 ` Parag Warudkar
2005-11-22 0:08 ` Parag Warudkar
2005-11-22 12:51 ` Johannes Berg
2005-11-22 12:51 ` Johannes Berg
2005-11-29 0:06 ` Michael Hanselmann [this message]
2005-11-29 0:06 ` Michael Hanselmann
2005-11-29 6:11 ` Parag Warudkar
2005-11-29 7:50 ` Michael Hanselmann
2005-11-29 7:50 ` Michael Hanselmann
2005-11-29 10:38 ` Johannes Berg
2005-11-29 10:38 ` Johannes Berg
2005-11-29 16:11 ` Parag Warudkar
2005-11-29 16:11 ` Parag Warudkar
2005-11-30 11:17 ` Johannes Berg
2005-11-30 11:17 ` Johannes Berg
2005-11-30 22:39 ` Michael Hanselmann
2005-11-30 22:39 ` Michael Hanselmann
2005-11-30 23:46 ` Michael Hanselmann
2005-11-30 23:46 ` Michael Hanselmann
2005-12-02 14:28 ` Stelian Pop
2005-12-02 14:28 ` Stelian Pop
2005-12-04 22:42 ` Michael Hanselmann
2005-12-04 22:42 ` Michael Hanselmann
2005-12-06 3:38 ` Andy Botting
2005-12-06 3:38 ` Andy Botting
2005-12-06 21:12 ` Michael Hanselmann
2005-12-06 21:12 ` Michael Hanselmann
2005-12-09 23:33 ` Michael Hanselmann
2005-12-09 23:33 ` Michael Hanselmann
2005-12-23 23:59 ` Benjamin Herrenschmidt
2005-12-23 23:59 ` Benjamin Herrenschmidt
2005-12-24 11:52 ` René Nussbaumer
2005-12-24 11:52 ` René Nussbaumer
2005-12-24 20:17 ` Pavel Machek
2005-12-24 20:17 ` Pavel Machek
2005-12-24 22:27 ` Benjamin Herrenschmidt
2005-12-24 22:27 ` Benjamin Herrenschmidt
2005-12-24 23:19 ` Pavel Machek
2005-12-24 23:19 ` Pavel Machek
2005-12-25 0:33 ` Benjamin Herrenschmidt
2005-12-25 0:33 ` Benjamin Herrenschmidt
2005-12-25 0:52 ` Dmitry Torokhov
2005-12-25 0:52 ` Dmitry Torokhov
-- strict thread matches above, loose matches on Subject: below --
2005-11-16 15:40 Parag Warudkar
2005-11-16 15:43 Parag Warudkar
2005-11-16 15:49 ` Johannes Berg
2005-11-16 16:07 Parag Warudkar
2005-11-16 16:13 ` Johannes Berg
2005-11-29 6:17 Parag Warudkar
2005-12-02 17:02 Parag Warudkar
2005-12-02 17:02 ` Parag Warudkar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20051129000615.GA20843@hansmi.ch \
--to=linux-kernel@hansmi.ch \
--cc=debian-powerpc@lists.debian.org \
--cc=kernel-stuff@comcast.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linuxppc-dev@ozlabs.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.