Hi,

this preliminary patch should suspend your mouse, if it supports remote
wakeup. In fact it should do this to all devices which are HID, claimed by
the input layer and support remote wakeup.

It works for me with my mouse. I've tested letting it autosuspend and
resume. It survives going to a text console, init 3 and hotplugging.
Anything else is untested. I think it'll crash and burn with PID devices.
I am working on that.

I am pretty sure that it actually goes to sleep taking the root hub with it.
I'd be interested in knowing how much power it really saves. Short of
running dry my laptop's battery I have no way of telling.

You need to compile with EXPERIMENTAL & USB_SUSPEND.

        Regards
                Oliver
----

--- old/include/linux/hid.h     2007-01-31 12:25:14.000000000 +0100
+++ linux-2.6.20-6-greg/include/linux/hid.h     2007-01-30 17:25:20.000000000 
+0100
@@ -390,6 +390,8 @@
 #define HID_RESET_PENDING      4
 #define HID_SUSPENDED          5
 #define HID_CLEAR_HALT         6
+#define HID_IDLE               7
+#define HID_REPORTED_IDLE      8
 
 struct hid_input {
        struct list_head list;
--- old/drivers/usb/input/hiddev.c      2007-01-31 12:24:37.000000000 +0100
+++ linux-2.6.20-6-greg/drivers/usb/input/hiddev.c      2007-01-29 
17:01:46.000000000 +0100
@@ -496,8 +496,11 @@
                if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
                        return -EINVAL;
 
+               if (usb_autopm_get_interface(usbhid->intf) < 0)
+                       return -EIO;
                usbhid_submit_report(hid, report, USB_DIR_IN);
                usbhid_wait_io(hid);
+               usb_autopm_put_interface(usbhid->intf);
 
                return 0;
 
@@ -511,8 +514,11 @@
                if ((report = hiddev_lookup_report(hid, &rinfo)) == NULL)
                        return -EINVAL;
 
+               if (usb_autopm_get_interface(usbhid->intf) < 0)
+                       return -EIO;
                usbhid_submit_report(hid, report, USB_DIR_OUT);
                usbhid_wait_io(hid);
+               usb_autopm_put_interface(usbhid->intf);
 
                return 0;
 
--- old/drivers/usb/input/hid-core.c    2007-01-31 12:25:14.000000000 +0100
+++ linux-2.6.20-6-greg/drivers/usb/input/hid-core.c    2007-01-31 
13:27:27.000000000 +0100
@@ -5,6 +5,7 @@
  *  Copyright (c) 2000-2005 Vojtech Pavlik <[EMAIL PROTECTED]>
  *  Copyright (c) 2005 Michael Haboustak <[EMAIL PROTECTED]> for Concept2, Inc
  *  Copyright (c) 2006 Jiri Kosina
+ *  Copyright (c) 2007 Oliver Neukum
  */
 
 /*
@@ -61,6 +62,39 @@
 MODULE_PARM_DESC(pb_fnmode,
                "Mode of fn key on PowerBooks (0 = disabled, 1 = fkeyslast, 2 = 
fkeysfirst)");
 
+static spinlock_t idle_guard = SPIN_LOCK_UNLOCKED;
+static DEFINE_MUTEX(hid_open_mut);
+
+/*
+ * sysfs idle time handling
+ */
+
+static ssize_t show_idle_time(struct device *dev, struct device_attribute 
*attr, char *buf)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct hid_device *hh = usb_get_intfdata(intf);
+       struct usbhid_device *uh = hh->driver_data;
+
+       return sprintf(buf, "%d\n", uh->idle_time);
+}
+
+static ssize_t set_idle_time(struct device *dev, struct device_attribute 
*attr, const char *buf, size_t count)
+{
+       struct usb_interface *intf = to_usb_interface(dev);             \
+       struct hid_device *hh = usb_get_intfdata(intf);
+       struct usbhid_device *uh = hh->driver_data;
+       int old = uh->idle_time;
+       int temp = simple_strtoul(buf, NULL, 10);
+
+       spin_lock_irq(&idle_guard);
+       uh->idle_time = temp;
+       spin_unlock_irq(&idle_guard);
+
+       return count;
+}
+
+static DEVICE_ATTR(idle_time, S_IWUGO | S_IRUGO, show_idle_time, 
set_idle_time);
+
 /*
  * Input submission and I/O error handler.
  */
@@ -184,6 +218,8 @@
        struct usbhid_device    *usbhid = hid->driver_data;
        int                     status;
 
+       /*data recieved, errors ignored as they are handled, device not idle */
+       clear_bit(HID_IDLE, &usbhid->iofl);
        switch (urb->status) {
                case 0:                 /* success */
                        usbhid->retry_delay = 0;
@@ -519,7 +555,20 @@
 
 int usbhid_open(struct hid_device *hid)
 {
-       ++hid->open;
+       struct usbhid_device *usbhid = hid->driver_data;
+       int res;
+
+       clear_bit(HID_IDLE, &usbhid->iofl);
+       mutex_lock(&hid_open_mut);
+       if (!hid->open++) {
+               res = usb_autopm_get_interface(usbhid->intf);
+               if (res < 0) {
+                       hid->open--;
+                       mutex_unlock(&hid_open_mut);
+                       return -EIO;
+               }
+       }
+       mutex_unlock(&hid_open_mut);
        if (hid_start_in(hid))
                hid_io_error(hid);
        return 0;
@@ -529,8 +578,13 @@
 {
        struct usbhid_device *usbhid = hid->driver_data;
 
-       if (!--hid->open)
+       if (!--hid->open) {
                usb_kill_urb(usbhid->urbin);
+               del_timer_sync(&usbhid->idle_timer);
+               flush_scheduled_work();
+               if (!test_and_clear_bit(HID_REPORTED_IDLE, &usbhid->iofl))
+                       usb_autopm_put_interface(usbhid->intf);
+       }
 }
 
 static int hidinput_open(struct input_dev *dev)
@@ -1018,6 +1072,65 @@
        return 0;
 }
 
+void hid_add_idle_timer(struct usbhid_device *uhid)
+{
+       uhid->idle_timer.expires = jiffies + uhid->idle_time * HZ;
+       add_timer(&uhid->idle_timer);
+printk(KERN_ERR"Added idle timer for firing in %d seconds\n", uhid->idle_time);
+}
+
+static void hid_mod_idle_timer(struct usbhid_device *uhid)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&uhid->inlock, flags);
+       if (usb_get_intfdata(uhid->intf) && uhid->idle_time) {
+               mod_timer(&uhid->idle_timer, jiffies + uhid->idle_time * HZ);
+printk(KERN_ERR"Modded idle timer for firing in %d seconds\n", 
uhid->idle_time);
+       }
+       spin_unlock_irqrestore(&uhid->inlock, flags);
+
+}
+
+static void __hid_report_idle(struct work_struct *work)
+{
+       struct usbhid_device *usbhid =
+               container_of(work, struct usbhid_device, idle_work);
+
+       usb_autopm_put_interface(usbhid->intf);
+printk(KERN_ERR"Putting idle interface.\n");
+}
+
+static void hid_report_idle(struct usbhid_device *hid)
+{
+       struct usb_interface *intf = hid->intf;
+       unsigned long flags;
+
+       spin_lock_irqsave(&hid->inlock, flags);
+       if (!test_bit(HID_SUSPENDED, &hid->iofl)) {
+               set_bit(HID_REPORTED_IDLE, &hid->iofl);
+               if (usb_get_intfdata(intf))
+                       schedule_work(&hid->idle_work);
+       }
+       spin_unlock_irqrestore(&hid->inlock, flags);
+}
+
+static void hid_check_idle(unsigned long arg)
+{
+       struct usbhid_device *hid = (struct usbhid_device *)arg;
+       int state;
+
+       state = test_and_set_bit(HID_IDLE, &hid->iofl);
+printk(KERN_ERR"state is %d\n", state);
+
+       if (!state) {
+               hid_mod_idle_timer(hid);
+       } else {
+               hid_report_idle(hid);
+printk(KERN_ERR"Detected idleness\n");
+       }
+}
+
 static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid)
 {
        struct usbhid_device *usbhid = hid->driver_data;
@@ -1222,17 +1335,22 @@
        init_waitqueue_head(&hid->wait);
 
        INIT_WORK(&usbhid->reset_work, hid_reset);
+       INIT_WORK(&usbhid->idle_work, __hid_report_idle);
        setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid);
+       setup_timer(&usbhid->idle_timer, hid_check_idle, (unsigned long) 
usbhid);
 
        spin_lock_init(&usbhid->inlock);
        spin_lock_init(&usbhid->outlock);
        spin_lock_init(&usbhid->ctrllock);
 
+       set_bit(HID_IDLE, &usbhid->iofl);
+
        hid->version = le16_to_cpu(hdesc->bcdHID);
        hid->country = hdesc->bCountryCode;
        hid->dev = &intf->dev;
        usbhid->intf = intf;
        usbhid->ifnum = interface->desc.bInterfaceNumber;
+       usbhid->idle_time = 10; //FIXME
 
        hid->name[0] = 0;
 
@@ -1306,6 +1424,8 @@
 
        usbhid = hid->driver_data;
 
+       device_remove_file(&intf->dev, &dev_attr_idle_time);
+
        spin_lock_irq(&usbhid->inlock); /* Sync with error handler */
        usb_set_intfdata(intf, NULL);
        spin_unlock_irq(&usbhid->inlock);
@@ -1314,6 +1434,7 @@
        usb_kill_urb(usbhid->urbctrl);
 
        del_timer_sync(&usbhid->io_retry);
+       del_timer_sync(&usbhid->idle_timer);
        flush_scheduled_work();
 
        if (hid->claimed & HID_CLAIMED_INPUT)
@@ -1351,6 +1472,7 @@
                hid->claimed |= HID_CLAIMED_HIDDEV;
 
        usb_set_intfdata(intf, hid);
+       intf->needs_remote_wakeup = 1;
 
        if (!hid->claimed) {
                printk ("HID device not claimed by input or hiddev\n");
@@ -1378,6 +1500,9 @@
        if (hid->claimed & HID_CLAIMED_HIDDEV)
                printk("hiddev%d", hid->minor);
 
+       if (hid->claimed & HID_CLAIMED_INPUT)
+               hid_add_idle_timer(hid->driver_data);
+
        c = "Device";
        for (i = 0; i < hid->maxcollection; i++) {
                if (hid->collection[i].type == HID_COLLECTION_APPLICATION &&
@@ -1389,6 +1514,7 @@
        }
 
        usb_make_path(interface_to_usbdev(intf), path, 63);
+       device_create_file(&intf->dev, &dev_attr_idle_time);
 
        printk(": USB HID v%x.%02x %s [%s] on %s\n",
                hid->version >> 8, hid->version & 0xff, c, hid->name, path);
@@ -1406,6 +1532,7 @@
        spin_unlock_irq(&usbhid->inlock);
        del_timer(&usbhid->io_retry);
        usb_kill_urb(usbhid->urbin);
+       flush_scheduled_work();
        dev_dbg(&intf->dev, "suspend\n");
        return 0;
 }
@@ -1417,8 +1544,12 @@
        int status;
 
        clear_bit(HID_SUSPENDED, &usbhid->iofl);
+       clear_bit(HID_IDLE, &usbhid->iofl);
        usbhid->retry_delay = 0;
        status = hid_start_in(hid);
+       if (test_and_clear_bit(HID_REPORTED_IDLE, &usbhid->iofl))
+               intf->pm_usage_cnt++; /* get'd deadlock */
+       hid_add_idle_timer(usbhid);
        dev_dbg(&intf->dev, "resume status %d\n", status);
        return status;
 }
@@ -1457,6 +1588,7 @@
        .pre_reset =    hid_pre_reset,
        .post_reset =   hid_post_reset,
        .id_table =     hid_usb_ids,
+       .supports_autosuspend = 1,
 };
 
 static int __init hid_init(void)

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to