Re: PowerBook5,8 - TrackPad update
From: Michael Hanselmann
Date: Mon Nov 28 2005 - 19:05:50 EST
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/
/*
* Apple USB Touchpad (for post-February 2005 PowerBooks) driver
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@xxxxxxxxx)
* Copyright (C) 2005 Johannes Berg (johannes@xxxxxxxxxxxxxxxx)
* Copyright (C) 2005 Stelian Pop (stelian@xxxxxxxxxx)
* Copyright (C) 2005 Frank Arnold (frank@xxxxxxxxxxxxxxxxxxxx)
* Copyright (C) 2005 Peter Osterlund (petero2@xxxxxxxxx)
* Copyright (C) 2005 Parag Warudkar (parag.warudkar@xxxxxxxxx)
*
* Thanks to Alex Harper <basilisk@xxxxxxxxxx> 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:
pgp00000.pgp
Description: PGP signature