diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/mouse/appletouch.c | 266 |
1 files changed, 197 insertions, 69 deletions
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c index 1f41ae94f26b..36ebe5c25ee3 100644 --- a/drivers/input/mouse/appletouch.c +++ b/drivers/input/mouse/appletouch.c @@ -327,11 +327,14 @@ static inline void atp_report_fingers(struct input_dev *input, int fingers) input_report_key(input, BTN_TOOL_TRIPLETAP, fingers > 2); } -static void atp_complete(struct urb *urb) +/* Check URB status and for correct length of data package */ + +#define ATP_URB_STATUS_SUCCESS 0 +#define ATP_URB_STATUS_ERROR 1 +#define ATP_URB_STATUS_ERROR_FATAL 2 + +static int atp_status_check(struct urb *urb) { - int x, y, x_z, y_z, x_f, y_f; - int retval, i, j; - int key; struct atp *dev = urb->context; switch (urb->status) { @@ -351,11 +354,12 @@ static void atp_complete(struct urb *urb) /* This urb is terminated, clean up */ dbg("atp_complete: urb shutting down with status: %d", urb->status); - return; + return ATP_URB_STATUS_ERROR_FATAL; + default: dbg("atp_complete: nonzero urb status received: %d", urb->status); - goto exit; + return ATP_URB_STATUS_ERROR; } /* drop incomplete datasets */ @@ -363,30 +367,33 @@ static void atp_complete(struct urb *urb) dprintk("appletouch: incomplete data package" " (first byte: %d, length: %d).\n", dev->data[0], dev->urb->actual_length); - goto exit; + return ATP_URB_STATUS_ERROR; } - /* reorder the sensors values */ - if (dev->type == ATP_GEYSER3 || dev->type == ATP_GEYSER4) { - memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); + return ATP_URB_STATUS_SUCCESS; +} - /* - * The values are laid out like this: - * -, Y1, Y2, -, Y3, Y4, -, ..., -, X1, X2, -, X3, X4, ... - * '-' is an unused value. - */ +/* + * USB interrupt callback functions + */ - /* 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 == ATP_GEYSER2) { +/* Interrupt function for older touchpads: FOUNTAIN/GEYSER1/GEYSER2 */ + +static void atp_complete_geyser_1_2(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* reorder the sensors values */ + if (dev->type == ATP_GEYSER2) { memset(dev->xy_cur, 0, sizeof(dev->xy_cur)); /* @@ -427,33 +434,146 @@ static void atp_complete(struct urb *urb) /* first sample */ dev->valid = true; dev->x_old = dev->y_old = -1; + + /* Store first sample */ memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); - if (dev->size_detect_done || - dev->type == ATP_GEYSER3) /* No 17" Macbooks (yet) */ + /* Perform size detection, if not done already */ + if (!dev->size_detect_done) { + + /* 17" Powerbooks have extra X sensors */ + for (i = (dev->type == ATP_GEYSER2 ? 15 : 16); + i < ATP_XSENSORS; i++) { + if (!dev->xy_cur[i]) + continue; + + printk(KERN_INFO + "appletouch: 17\" model detected.\n"); + + if (dev->type == ATP_GEYSER2) + 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, + (26 - 1) * + ATP_XFACT - 1, + ATP_FUZZ, 0); + break; + } + + dev->size_detect_done = 1; goto exit; + } + } - /* 17" Powerbooks have extra X sensors */ - for (i = (dev->type == ATP_GEYSER2 ? 15 : 16); - i < ATP_XSENSORS; i++) { - if (!dev->xy_cur[i]) - continue; - - printk(KERN_INFO "appletouch: 17\" model detected.\n"); - if (dev->type == ATP_GEYSER2) - 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; + for (i = 0; i < ATP_XSENSORS + ATP_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_XSENSORS, + ATP_XFACT, &x_z, &x_f); + y = atp_calculate_abs(dev->xy_acc + ATP_XSENSORS, ATP_YSENSORS, + ATP_YFACT, &y_z, &y_f); + key = dev->data[dev->datalen - 1] & 1; + + 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(KERN_DEBUG "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, key); + input_sync(dev->input); + + exit: + retval = usb_submit_urb(dev->urb, GFP_ATOMIC); + if (retval) + err("atp_complete: usb_submit_urb failed with result %d", + retval); +} + +/* Interrupt function for older touchpads: GEYSER3/GEYSER4 */ + +static void atp_complete_geyser_3_4(struct urb *urb) +{ + int x, y, x_z, y_z, x_f, y_f; + int retval, i, j; + int key; + struct atp *dev = urb->context; + int status = atp_status_check(urb); + + if (status == ATP_URB_STATUS_ERROR_FATAL) + return; + else if (status == ATP_URB_STATUS_ERROR) + goto exit; + + /* Reorder the sensors values: + * + * 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 + 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]; + } + + dbg_dump("sample", dev->xy_cur); + + if (!dev->valid) { + /* first sample */ + dev->valid = true; + dev->x_old = dev->y_old = -1; + memcpy(dev->xy_old, dev->xy_cur, sizeof(dev->xy_old)); - dev->size_detect_done = 1; goto exit; } @@ -514,28 +634,26 @@ static void atp_complete(struct urb *urb) input_sync(dev->input); /* - * Many Geysers will continue to send packets continually after + * Geysers 3/4 will continue to send packets continually after * the first touch 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. Re-initialization does not - * work on Fountain touchpads. + * several hundred times a second. */ - if (dev->type != ATP_FOUNTAIN) { - /* - * Button must not be pressed when entering suspend, - * otherwise we will never release the button. - */ - if (!x && !y && !key) { - dev->idlecount++; - if (dev->idlecount == 10) { - dev->valid = false; - schedule_work(&dev->work); - /* Don't resubmit urb here, wait for reinit */ - return; - } - } else - dev->idlecount = 0; - } + + /* + * Button must not be pressed when entering suspend, + * otherwise we will never release the button. + */ + if (!x && !y && !key) { + dev->idlecount++; + if (dev->idlecount == 10) { + dev->valid = false; + schedule_work(&dev->work); + /* Don't resubmit urb here, wait for reinit */ + return; + } + } else + dev->idlecount = 0; exit: retval = usb_submit_urb(dev->urb, GFP_ATOMIC); @@ -632,9 +750,19 @@ static int atp_probe(struct usb_interface *iface, if (!dev->data) 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); + /* Select the USB complete (callback) function */ + if (dev->type == ATP_FOUNTAIN || + dev->type == ATP_GEYSER1 || + dev->type == ATP_GEYSER2) + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, + atp_complete_geyser_1_2, dev, 1); + else + usb_fill_int_urb(dev->urb, udev, + usb_rcvintpipe(udev, int_in_endpointAddr), + dev->data, dev->datalen, + atp_complete_geyser_3_4, dev, 1); error = atp_handle_geyser(dev); if (error) |