Commit 5a6eb676 authored by Soeren Sonnenburg's avatar Soeren Sonnenburg Committed by Dmitry Torokhov

Input: appletouch - improve powersaving for Geyser3 devices

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: default avatarSoeren Sonnenburg <kernel@nn7.de>
Signed-off-by: default avatarMatthew Garrett <mjg59@srcf.ucam.org>
Signed-off-by: default avatarDmitry Torokhov <dtor@mail.ru>
parent e6b20d8d
......@@ -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) \
......@@ -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)
{
......@@ -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);
......@@ -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:
......@@ -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;
}
......@@ -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");
}
......@@ -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:
......@@ -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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment