aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb/core
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/driver.c65
-rw-r--r--drivers/usb/core/hcd.c6
-rw-r--r--drivers/usb/core/hub.c4
-rw-r--r--drivers/usb/core/inode.c11
-rw-r--r--drivers/usb/core/usb.c46
-rw-r--r--drivers/usb/core/usb.h19
6 files changed, 103 insertions, 48 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index b1046324441..113e484c763 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -24,6 +24,7 @@
#include <linux/device.h>
#include <linux/usb.h>
+#include <linux/workqueue.h>
#include "hcd.h"
#include "usb.h"
@@ -302,11 +303,11 @@ int usb_driver_claim_interface(struct usb_driver *driver,
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
mark_active(iface);
iface->pm_usage_cnt = !(driver->supports_autosuspend);
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
/* if interface was already added, bind now; else let
* the future device_add() bind it, bypassing probe()
@@ -355,11 +356,11 @@ void usb_driver_release_interface(struct usb_driver *driver,
dev->driver = NULL;
usb_set_intfdata(iface, NULL);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
iface->condition = USB_INTERFACE_UNBOUND;
mark_quiesced(iface);
iface->needs_remote_wakeup = 0;
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
}
EXPORT_SYMBOL(usb_driver_release_interface);
@@ -788,7 +789,7 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
#ifdef CONFIG_PM
-/* Caller has locked udev->pm_mutex */
+/* Caller has locked udev's pm_mutex */
static int suspend_device(struct usb_device *udev, pm_message_t msg)
{
struct usb_device_driver *udriver;
@@ -815,7 +816,7 @@ done:
return status;
}
-/* Caller has locked udev->pm_mutex */
+/* Caller has locked udev's pm_mutex */
static int resume_device(struct usb_device *udev)
{
struct usb_device_driver *udriver;
@@ -841,7 +842,7 @@ done:
return status;
}
-/* Caller has locked intf's usb_device's pm_mutex */
+/* Caller has locked intf's usb_device's pm mutex */
static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
{
struct usb_driver *driver;
@@ -1063,7 +1064,7 @@ int usb_resume_both(struct usb_device *udev)
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
if (parent) {
- mutex_lock_nested(&parent->pm_mutex, parent->level);
+ usb_pm_lock(parent);
parent->auto_pm = 1;
status = usb_resume_both(parent);
} else {
@@ -1078,7 +1079,7 @@ int usb_resume_both(struct usb_device *udev)
if (status == 0)
status = resume_device(udev);
if (parent)
- mutex_unlock(&parent->pm_mutex);
+ usb_pm_unlock(parent);
} else {
/* Needed only for setting udev->dev.power.power_state.event
@@ -1103,8 +1104,8 @@ int usb_resume_both(struct usb_device *udev)
/**
* usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
- * @udev - the usb_device to autosuspend
- * @dec_usage_cnt - flag to decrement @udev's PM-usage counter
+ * @udev: the usb_device to autosuspend
+ * @dec_usage_cnt: flag to decrement @udev's PM-usage counter
*
* This routine should be called when a core subsystem is finished using
* @udev and wants to allow it to autosuspend. Examples would be when
@@ -1128,20 +1129,20 @@ int usb_resume_both(struct usb_device *udev)
*/
void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt)
{
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
udev->pm_usage_cnt -= dec_usage_cnt;
if (udev->pm_usage_cnt <= 0)
- schedule_delayed_work(&udev->autosuspend,
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
USB_AUTOSUSPEND_DELAY);
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
// dev_dbg(&udev->dev, "%s: cnt %d\n",
// __FUNCTION__, udev->pm_usage_cnt);
}
/**
* usb_autoresume_device - immediately autoresume a USB device and its interfaces
- * @udev - the usb_device to autoresume
- * @inc_usage_cnt - flag to increment @udev's PM-usage counter
+ * @udev: the usb_device to autoresume
+ * @inc_usage_cnt: flag to increment @udev's PM-usage counter
*
* This routine should be called when a core subsystem wants to use @udev
* and needs to guarantee that it is not suspended. In addition, the
@@ -1167,13 +1168,13 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
{
int status;
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
udev->pm_usage_cnt += inc_usage_cnt;
udev->auto_pm = 1;
status = usb_resume_both(udev);
if (status != 0)
udev->pm_usage_cnt -= inc_usage_cnt;
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
// dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
// __FUNCTION__, status, udev->pm_usage_cnt);
return status;
@@ -1181,7 +1182,7 @@ int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
/**
* usb_autopm_put_interface - decrement a USB interface's PM-usage counter
- * @intf - the usb_interface whose counter should be decremented
+ * @intf: the usb_interface whose counter should be decremented
*
* This routine should be called by an interface driver when it is
* finished using @intf and wants to allow it to autosuspend. A typical
@@ -1214,13 +1215,13 @@ void usb_autopm_put_interface(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
- if (intf->condition != USB_INTERFACE_UNBOUND) {
- if (--intf->pm_usage_cnt <= 0)
- schedule_delayed_work(&udev->autosuspend,
- USB_AUTOSUSPEND_DELAY);
+ usb_pm_lock(udev);
+ if (intf->condition != USB_INTERFACE_UNBOUND &&
+ --intf->pm_usage_cnt <= 0) {
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ USB_AUTOSUSPEND_DELAY);
}
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
// dev_dbg(&intf->dev, "%s: cnt %d\n",
// __FUNCTION__, intf->pm_usage_cnt);
}
@@ -1228,7 +1229,7 @@ EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
/**
* usb_autopm_get_interface - increment a USB interface's PM-usage counter
- * @intf - the usb_interface whose counter should be incremented
+ * @intf: the usb_interface whose counter should be incremented
*
* This routine should be called by an interface driver when it wants to
* use @intf and needs to guarantee that it is not suspended. In addition,
@@ -1262,7 +1263,7 @@ int usb_autopm_get_interface(struct usb_interface *intf)
struct usb_device *udev = interface_to_usbdev(intf);
int status;
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
if (intf->condition == USB_INTERFACE_UNBOUND)
status = -ENODEV;
else {
@@ -1272,7 +1273,7 @@ int usb_autopm_get_interface(struct usb_interface *intf)
if (status != 0)
--intf->pm_usage_cnt;
}
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
// dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
// __FUNCTION__, status, intf->pm_usage_cnt);
return status;
@@ -1288,10 +1289,10 @@ static int usb_suspend(struct device *dev, pm_message_t message)
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, message);
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
} else
status = 0;
return status;
@@ -1304,10 +1305,10 @@ static int usb_resume(struct device *dev)
if (is_usb_device(dev)) {
struct usb_device *udev = to_usb_device(dev);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_resume_both(udev);
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
/* Rebind drivers that had no suspend method? */
} else
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index e86f6295708..37f9f5e7425 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -345,7 +345,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)];
+ u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ __attribute__((aligned(4)));
const u8 *bufp = tbuf;
int len = 0;
int patch_wakeup = 0;
@@ -835,8 +836,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus)
struct usb_hcd *hcd;
hcd = container_of (bus, struct usb_hcd, self);
- if (hcd->driver->hub_irq_enable && !hcd->poll_rh &&
- hcd->state != HC_STATE_HALT)
+ if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
hcd->driver->hub_irq_enable (hcd);
}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 2a8cb3c2b19..7676690a038 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1779,7 +1779,7 @@ static int remote_wakeup(struct usb_device *udev)
* to the parent hub! */
usb_lock_device(udev);
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
/* TRSMRCY = 10 msec */
@@ -1788,7 +1788,7 @@ static int remote_wakeup(struct usb_device *udev)
if (status == 0)
udev->dev.power.power_state.event = PM_EVENT_ON;
}
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
if (status == 0)
usb_autoresume_device(udev, 0);
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index df3d152f049..7c77c2d8d30 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -263,7 +263,7 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
- inode->i_nlink++;
+ inc_nlink(inode);
break;
}
}
@@ -295,7 +295,7 @@ static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = usbfs_mknod (dir, dentry, mode, 0);
if (!res)
- dir->i_nlink++;
+ inc_nlink(dir);
return res;
}
@@ -332,7 +332,7 @@ static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
mutex_lock(&inode->i_mutex);
- dentry->d_inode->i_nlink--;
+ drop_nlink(dentry->d_inode);
dput(dentry);
mutex_unlock(&inode->i_mutex);
d_delete(dentry);
@@ -347,10 +347,11 @@ static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
mutex_lock(&inode->i_mutex);
dentry_unhash(dentry);
if (usbfs_empty(dentry)) {
- dentry->d_inode->i_nlink -= 2;
+ drop_nlink(dentry->d_inode);
+ drop_nlink(dentry->d_inode);
dput(dentry);
inode->i_flags |= S_DEAD;
- dir->i_nlink--;
+ drop_nlink(dir);
error = 0;
}
mutex_unlock(&inode->i_mutex);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 60ef4ef0101..e4df9edf1bc 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -33,6 +33,7 @@
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
@@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
+struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
+
/**
* usb_ifnum_to_if - get the interface object with a given interface number
@@ -170,9 +173,9 @@ static void usb_release_dev(struct device *dev)
udev = to_usb_device(dev);
-#ifdef CONFIG_PM
+#ifdef CONFIG_USB_SUSPEND
cancel_delayed_work(&udev->autosuspend);
- flush_scheduled_work();
+ flush_workqueue(ksuspend_usb_wq);
#endif
usb_destroy_configuration(udev);
usb_put_hcd(bus_to_hcd(udev->bus));
@@ -184,17 +187,44 @@ static void usb_release_dev(struct device *dev)
#ifdef CONFIG_PM
+static int ksuspend_usb_init(void)
+{
+ ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd");
+ if (!ksuspend_usb_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+static void ksuspend_usb_cleanup(void)
+{
+ destroy_workqueue(ksuspend_usb_wq);
+}
+
+#else
+
+#define ksuspend_usb_init() 0
+#define ksuspend_usb_cleanup() do {} while (0)
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
/* usb_autosuspend_work - callback routine to autosuspend a USB device */
static void usb_autosuspend_work(void *_udev)
{
struct usb_device *udev = _udev;
- mutex_lock_nested(&udev->pm_mutex, udev->level);
+ usb_pm_lock(udev);
udev->auto_pm = 1;
usb_suspend_both(udev, PMSG_SUSPEND);
- mutex_unlock(&udev->pm_mutex);
+ usb_pm_unlock(udev);
}
+#else
+
+static void usb_autosuspend_work(void *_udev)
+{}
+
#endif
/**
@@ -976,9 +1006,12 @@ static int __init usb_init(void)
return 0;
}
+ retval = ksuspend_usb_init();
+ if (retval)
+ goto out;
retval = bus_register(&usb_bus_type);
if (retval)
- goto out;
+ goto bus_register_failed;
retval = usb_host_init();
if (retval)
goto host_init_failed;
@@ -1014,6 +1047,8 @@ major_init_failed:
usb_host_cleanup();
host_init_failed:
bus_unregister(&usb_bus_type);
+bus_register_failed:
+ ksuspend_usb_cleanup();
out:
return retval;
}
@@ -1035,6 +1070,7 @@ static void __exit usb_exit(void)
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
+ ksuspend_usb_cleanup();
}
subsys_initcall(usb_init);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 0c09ecced6e..f69df137ec0 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -36,6 +36,16 @@ extern int usb_resume_both(struct usb_device *udev);
extern int usb_port_suspend(struct usb_device *dev);
extern int usb_port_resume(struct usb_device *dev);
+static inline void usb_pm_lock(struct usb_device *udev)
+{
+ mutex_lock_nested(&udev->pm_mutex, udev->level);
+}
+
+static inline void usb_pm_unlock(struct usb_device *udev)
+{
+ mutex_unlock(&udev->pm_mutex);
+}
+
#else
#define usb_suspend_both(udev, msg) 0
@@ -45,6 +55,8 @@ static inline int usb_resume_both(struct usb_device *udev)
}
#define usb_port_suspend(dev) 0
#define usb_port_resume(dev) 0
+static inline void usb_pm_lock(struct usb_device *udev) {}
+static inline void usb_pm_unlock(struct usb_device *udev) {}
#endif
@@ -58,10 +70,15 @@ extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt);
#else
#define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0)
-#define usb_autoresume_device(udev, inc_busy_cnt) 0
+static inline int usb_autoresume_device(struct usb_device *udev,
+ int inc_busy_cnt)
+{
+ return 0;
+}
#endif
+extern struct workqueue_struct *ksuspend_usb_wq;
extern struct bus_type usb_bus_type;
extern struct usb_device_driver usb_generic_driver;