]> err.no Git - linux-2.6/commitdiff
Input: appletouch - improve powersaving for Geyser3 devices
authorSoeren Sonnenburg <kernel@nn7.de>
Fri, 20 Jul 2007 04:29:32 +0000 (00:29 -0400)
committerDmitry Torokhov <dtor@insightbb.com>
Fri, 20 Jul 2007 04:29:32 +0000 (00:29 -0400)
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>
drivers/input/mouse/appletouch.c

index e3215267db112dcc89818ff41b0f5ca601c30151..2bea1b2c631c81274b80304f3e4be11216013068 100644 (file)
@@ -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;