USB: make autosuspend delay a module parameter

This patch (as859) makes the default USB autosuspend delay a module
parameter of usbcore.  By setting the delay value at boot time, users
will be able to prevent the system from autosuspending devices which
for some reason can't handle it.

The patch also stores the autosuspend delay as a per-device value.  A
later patch will allow the user to change the value, tailoring the
delay for each individual device.  A delay value of 0 will prevent
autosuspend.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index f9196a0..a420d72 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -963,12 +963,16 @@
 	int			i;
 	struct usb_interface	*intf;
 
-	/* For autosuspend, fail fast if anything is in use.
-	 * Also fail if any interfaces require remote wakeup but it
-	 * isn't available. */
+	/* For autosuspend, fail fast if anything is in use or autosuspend
+	 * is disabled.  Also fail if any interfaces require remote wakeup
+	 * but it isn't available.
+	 */
 	udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
 	if (udev->pm_usage_cnt > 0)
 		return -EBUSY;
+	if (!udev->autosuspend_delay)
+		return -EPERM;
+
 	if (udev->actconfig) {
 		for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
 			intf = udev->actconfig->interface[i];
@@ -991,7 +995,7 @@
 
 #define autosuspend_check(udev)		0
 
-#endif
+#endif	/* CONFIG_USB_SUSPEND */
 
 /**
  * usb_suspend_both - suspend a USB device and its interfaces
@@ -1186,7 +1190,7 @@
 			udev->pm_usage_cnt -= inc_usage_cnt;
 	} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
 		queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-				USB_AUTOSUSPEND_DELAY);
+				udev->autosuspend_delay);
 	usb_pm_unlock(udev);
 	return status;
 }
@@ -1270,7 +1274,7 @@
 				intf->pm_usage_cnt -= inc_usage_cnt;
 		} else if (inc_usage_cnt <= 0 && autosuspend_check(udev) == 0)
 			queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
-					USB_AUTOSUSPEND_DELAY);
+					udev->autosuspend_delay);
 	}
 	usb_pm_unlock(udev);
 	return status;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index ea0e48e..0e5c646 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -39,8 +39,10 @@
 
 static void usb_autosuspend_quirk(struct usb_device *udev)
 {
-	/* unbalanced resume to prevent autosuspends */
-	usb_autoresume_device(udev);
+#ifdef	CONFIG_USB_SUSPEND
+	/* disable autosuspend, but allow the user to re-enable it via sysfs */
+	udev->autosuspend_delay = 0;
+#endif
 }
 
 static const struct usb_device_id *find_id(struct usb_device *udev)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 3db721c..54b42ce 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -22,6 +22,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/moduleparam.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
@@ -50,6 +51,16 @@
 
 struct workqueue_struct *ksuspend_usb_wq;	/* For autosuspend */
 
+#ifdef	CONFIG_USB_SUSPEND
+static int usb_autosuspend_delay = 2;		/* Default delay value,
+						 * in seconds */
+module_param_named(autosuspend, usb_autosuspend_delay, uint, 0644);
+MODULE_PARM_DESC(autosuspend, "default autosuspend delay");
+
+#else
+#define usb_autosuspend_delay		0
+#endif
+
 
 /**
  * usb_ifnum_to_if - get the interface object with a given interface number
@@ -306,6 +317,7 @@
 #ifdef	CONFIG_PM
 	mutex_init(&dev->pm_mutex);
 	INIT_DELAYED_WORK(&dev->autosuspend, usb_autosuspend_work);
+	dev->autosuspend_delay = usb_autosuspend_delay * HZ;
 #endif
 	return dev;
 }
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 045cbd1..b0a35f4 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -65,8 +65,6 @@
 
 #ifdef CONFIG_USB_SUSPEND
 
-#define USB_AUTOSUSPEND_DELAY	(HZ*2)
-
 extern void usb_autosuspend_device(struct usb_device *udev);
 extern int usb_autoresume_device(struct usb_device *udev);