Skip to content

Commit

Permalink
Input: appletouch - improve powersaving for Geyser3 devices
Browse files Browse the repository at this point in the history
The appletouch geyser3 devices found in the Intel Macs (and possibly
some later PPC ones?) send a constant stream of packets after the first
touch. This results in the kernel waking up around once every couple of
milliseconds to process them, making it almost impossible to spend any
significant amount of time in C3 state on a dynamic HZ kernel. Sending
the mode initialization code makes the device shut up until it's touched
again. This patch does so after receiving 10 packets with no interesting
content.

Signed-off-by: Soeren Sonnenburg <kernel@nn7.de>
Signed-off-by: Matthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
  • Loading branch information
Soeren Sonnenburg authored and Dmitry Torokhov committed Jul 20, 2007
1 parent e6b20d8 commit 5a6eb67
Showing 1 changed file with 74 additions and 37 deletions.
111 changes: 74 additions & 37 deletions drivers/input/mouse/appletouch.c
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ struct atp {
int xy_acc[ATP_XSENSORS + ATP_YSENSORS];
int overflowwarn; /* overflow warning printed? */
int datalen; /* size of an USB urb transfer */
int idlecount; /* number of empty packets */
struct work_struct work;
};

#define dbg_dump(msg, tab) \
Expand Down Expand Up @@ -208,6 +210,55 @@ static inline int atp_is_geyser_3(struct atp *dev)
(productId == GEYSER4_JIS_PRODUCT_ID);
}

/*
* By default Geyser 3 device sends standard USB HID mouse
* packets (Report ID 2). This code changes device mode, so it
* sends raw sensor reports (Report ID 5).
*/
static int atp_geyser3_init(struct usb_device *udev)
{
char data[8];
int size;

size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER3_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);

if (size != 8) {
err("Could not do mode read request from device"
" (Geyser 3 mode)");
return -EIO;
}

/* Apply the mode switch */
data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;

size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);

if (size != 8) {
err("Could not do mode write request to device"
" (Geyser 3 mode)");
return -EIO;
}
return 0;
}

/* Reinitialise the device if it's a geyser 3 */
static void atp_reinit(struct work_struct *work)
{
struct atp *dev = container_of(work, struct atp, work);
struct usb_device *udev = dev->udev;

dev->idlecount = 0;
atp_geyser3_init(udev);
}

static int atp_calculate_abs(int *xy_sensors, int nb_sensors, int fact,
int *z, int *fingers)
{
Expand Down Expand Up @@ -439,8 +490,8 @@ static void atp_complete(struct urb* urb)
}
dev->x_old = x;
dev->y_old = y;
}
else if (!x && !y) {

} else if (!x && !y) {

dev->x_old = dev->y_old = -1;
input_report_key(dev->input, BTN_TOUCH, 0);
Expand All @@ -449,11 +500,21 @@ static void atp_complete(struct urb* urb)

/* reset the accumulator on release */
memset(dev->xy_acc, 0, sizeof(dev->xy_acc));
}

input_report_key(dev->input, BTN_LEFT,
!!dev->data[dev->datalen - 1]);
/* Geyser 3 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 */
if (atp_is_geyser_3(dev)) {
dev->idlecount++;
if (dev->idlecount == 10) {
dev->valid = 0;
schedule_work(&dev->work);
}
}
}

input_report_key(dev->input, BTN_LEFT, dev->data[dev->datalen - 1] & 1);
input_sync(dev->input);

exit:
Expand All @@ -480,6 +541,7 @@ static void atp_close(struct input_dev *input)
struct atp *dev = input_get_drvdata(input);

usb_kill_urb(dev->urb);
cancel_work_sync(&dev->work);
dev->open = 0;
}

Expand Down Expand Up @@ -528,40 +590,10 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
dev->datalen = 81;

if (atp_is_geyser_3(dev)) {
/*
* By default Geyser 3 device sends standard USB HID mouse
* packets (Report ID 2). This code changes device mode, so it
* sends raw sensor reports (Report ID 5).
*/
char data[8];
int size;

size = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0),
ATP_GEYSER3_MODE_READ_REQUEST_ID,
USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);

if (size != 8) {
err("Could not do mode read request from device"
" (Geyser 3 mode)");
/* switch to raw sensor mode */
if (atp_geyser3_init(udev))
goto err_free_devs;
}

/* Apply the mode switch */
data[0] = ATP_GEYSER3_MODE_VENDOR_VALUE;

size = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
ATP_GEYSER3_MODE_WRITE_REQUEST_ID,
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
ATP_GEYSER3_MODE_REQUEST_VALUE,
ATP_GEYSER3_MODE_REQUEST_INDEX, &data, 8, 5000);

if (size != 8) {
err("Could not do mode write request to device"
" (Geyser 3 mode)");
goto err_free_devs;
}
printk("appletouch Geyser 3 inited.\n");
}

Expand Down Expand Up @@ -636,6 +668,8 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
/* save our data pointer in this interface device */
usb_set_intfdata(iface, dev);

INIT_WORK(&dev->work, atp_reinit);

return 0;

err_free_buffer:
Expand Down Expand Up @@ -669,14 +703,17 @@ static void atp_disconnect(struct usb_interface *iface)
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;

Expand Down

0 comments on commit 5a6eb67

Please sign in to comment.