summaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAnton Vorontsov <cbouatmailru@gmail.com>2008-07-30 02:05:23 +0400
committerAnton Vorontsov <cbouatmailru@gmail.com>2008-07-30 02:05:23 +0400
commit9fec6060d9e48ed7db0dac0e16d0f0f0e615b7f6 (patch)
tree74b41f31a08f6500ff3dfcf64ba21e2d9a8e87e5 /drivers/usb/core
parentfece418418f51e92dd7e67e17c5e3fe5a28d3279 (diff)
parent6e86841d05f371b5b9b86ce76c02aaee83352298 (diff)
Merge branch 'master' of /home/cbou/linux-2.6
Conflicts: drivers/power/Kconfig drivers/power/Makefile
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/devices.c4
-rw-r--r--drivers/usb/core/devio.c101
-rw-r--r--drivers/usb/core/driver.c168
-rw-r--r--drivers/usb/core/endpoint.c15
-rw-r--r--drivers/usb/core/file.c18
-rw-r--r--drivers/usb/core/generic.c5
-rw-r--r--drivers/usb/core/hcd.c59
-rw-r--r--drivers/usb/core/hcd.h8
-rw-r--r--drivers/usb/core/hub.c527
-rw-r--r--drivers/usb/core/inode.c16
-rw-r--r--drivers/usb/core/message.c23
-rw-r--r--drivers/usb/core/quirks.c7
-rw-r--r--drivers/usb/core/sysfs.c181
-rw-r--r--drivers/usb/core/usb.c8
-rw-r--r--drivers/usb/core/usb.h23
15 files changed, 746 insertions, 417 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index 83d9dc379d9..6ec38175a81 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -46,8 +46,6 @@
* 2000-07-05: Ashley Montanaro <ashley@compsoc.man.ac.uk>
* Converted file reading routine to dump to buffer once
* per device, not per bus
- *
- * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $
*/
#include <linux/fs.h>
@@ -63,8 +61,6 @@
#include "usb.h"
#include "hcd.h"
-#define MAX_TOPO_LEVEL 6
-
/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */
#define ALLOW_SERIAL_NUMBER
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index de17738f3ac..20290c5b156 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -19,8 +19,6 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $
- *
* This file implements the usbfs/x/y files, where
* x is the bus number and y the device number.
*
@@ -61,6 +59,22 @@
/* Mutual exclusion for removal, open, and release */
DEFINE_MUTEX(usbfs_mutex);
+struct dev_state {
+ struct list_head list; /* state list */
+ struct usb_device *dev;
+ struct file *file;
+ spinlock_t lock; /* protects the async urb lists */
+ struct list_head async_pending;
+ struct list_head async_completed;
+ wait_queue_head_t wait; /* wake up if a request completed */
+ unsigned int discsignr;
+ struct pid *disc_pid;
+ uid_t disc_uid, disc_euid;
+ void __user *disccontext;
+ unsigned long ifclaimed;
+ u32 secid;
+};
+
struct async {
struct list_head asynclist;
struct dev_state *ps;
@@ -536,23 +550,19 @@ static int check_ctrlrecip(struct dev_state *ps, unsigned int requesttype,
return ret;
}
-static int __match_minor(struct device *dev, void *data)
+static int match_devt(struct device *dev, void *data)
{
- int minor = *((int *)data);
-
- if (dev->devt == MKDEV(USB_DEVICE_MAJOR, minor))
- return 1;
- return 0;
+ return dev->devt == (dev_t) (unsigned long) data;
}
-static struct usb_device *usbdev_lookup_by_minor(int minor)
+static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
{
struct device *dev;
- dev = bus_find_device(&usb_bus_type, NULL, &minor, __match_minor);
+ dev = bus_find_device(&usb_bus_type, NULL,
+ (void *) (unsigned long) devt, match_devt);
if (!dev)
return NULL;
- put_device(dev);
return container_of(dev, struct usb_device, dev);
}
@@ -565,6 +575,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
struct dev_state *ps;
int ret;
+ lock_kernel();
/* Protect against simultaneous removal or release */
mutex_lock(&usbfs_mutex);
@@ -574,21 +585,27 @@ static int usbdev_open(struct inode *inode, struct file *file)
goto out;
ret = -ENOENT;
+
/* usbdev device-node */
if (imajor(inode) == USB_DEVICE_MAJOR)
- dev = usbdev_lookup_by_minor(iminor(inode));
+ dev = usbdev_lookup_by_devt(inode->i_rdev);
#ifdef CONFIG_USB_DEVICEFS
/* procfs file */
- if (!dev)
+ if (!dev) {
dev = inode->i_private;
+ if (dev && dev->usbfs_dentry &&
+ dev->usbfs_dentry->d_inode == inode)
+ usb_get_dev(dev);
+ else
+ dev = NULL;
+ }
#endif
- if (!dev)
+ if (!dev || dev->state == USB_STATE_NOTATTACHED)
goto out;
ret = usb_autoresume_device(dev);
if (ret)
goto out;
- usb_get_dev(dev);
ret = 0;
ps->dev = dev;
ps->file = file;
@@ -608,9 +625,12 @@ static int usbdev_open(struct inode *inode, struct file *file)
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
- if (ret)
+ if (ret) {
kfree(ps);
+ usb_put_dev(dev);
+ }
mutex_unlock(&usbfs_mutex);
+ unlock_kernel();
return ret;
}
@@ -872,7 +892,7 @@ static int proc_connectinfo(struct dev_state *ps, void __user *arg)
static int proc_resetdevice(struct dev_state *ps)
{
- return usb_reset_composite_device(ps->dev, NULL);
+ return usb_reset_device(ps->dev);
}
static int proc_setintf(struct dev_state *ps, void __user *arg)
@@ -1680,25 +1700,49 @@ const struct file_operations usbdev_file_operations = {
.release = usbdev_release,
};
+void usb_fs_classdev_common_remove(struct usb_device *udev)
+{
+ struct dev_state *ps;
+ struct siginfo sinfo;
+
+ while (!list_empty(&udev->filelist)) {
+ ps = list_entry(udev->filelist.next, struct dev_state, list);
+ destroy_all_async(ps);
+ wake_up_all(&ps->wait);
+ list_del_init(&ps->list);
+ if (ps->discsignr) {
+ sinfo.si_signo = ps->discsignr;
+ sinfo.si_errno = EPIPE;
+ sinfo.si_code = SI_ASYNCIO;
+ sinfo.si_addr = ps->disccontext;
+ kill_pid_info_as_uid(ps->discsignr, &sinfo,
+ ps->disc_pid, ps->disc_uid,
+ ps->disc_euid, ps->secid);
+ }
+ }
+}
+
#ifdef CONFIG_USB_DEVICE_CLASS
static struct class *usb_classdev_class;
static int usb_classdev_add(struct usb_device *dev)
{
- int minor = ((dev->bus->busnum-1) * 128) + (dev->devnum-1);
-
- dev->usb_classdev = device_create(usb_classdev_class, &dev->dev,
- MKDEV(USB_DEVICE_MAJOR, minor),
- "usbdev%d.%d", dev->bus->busnum, dev->devnum);
- if (IS_ERR(dev->usb_classdev))
- return PTR_ERR(dev->usb_classdev);
-
+ struct device *cldev;
+
+ cldev = device_create_drvdata(usb_classdev_class, &dev->dev,
+ dev->dev.devt, NULL, "usbdev%d.%d",
+ dev->bus->busnum, dev->devnum);
+ if (IS_ERR(cldev))
+ return PTR_ERR(cldev);
+ dev->usb_classdev = cldev;
return 0;
}
static void usb_classdev_remove(struct usb_device *dev)
{
- device_unregister(dev->usb_classdev);
+ if (dev->usb_classdev)
+ device_unregister(dev->usb_classdev);
+ usb_fs_classdev_common_remove(dev);
}
static int usb_classdev_notify(struct notifier_block *self,
@@ -1748,6 +1792,11 @@ int __init usb_devio_init(void)
usb_classdev_class = NULL;
goto out;
}
+ /* devices of this class shadow the major:minor of their parent
+ * device, so clear ->dev_kobj to prevent adding duplicate entries
+ * to /sys/dev
+ */
+ usb_classdev_class->dev_kobj = NULL;
usb_register_notify(&usbdev_nb);
#endif
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 1e56f1cfa6d..ddb54e14a5c 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -201,6 +201,7 @@ static int usb_probe_interface(struct device *dev)
intf = to_usb_interface(dev);
udev = interface_to_usbdev(intf);
+ intf->needs_binding = 0;
if (udev->authorized == 0) {
dev_err(&intf->dev, "Device is not authorized for usage\n");
@@ -257,15 +258,16 @@ static int usb_unbind_interface(struct device *dev)
udev = interface_to_usbdev(intf);
error = usb_autoresume_device(udev);
- /* release all urbs for this interface */
- usb_disable_interface(interface_to_usbdev(intf), intf);
+ /* Terminate all URBs for this interface unless the driver
+ * supports "soft" unbinding.
+ */
+ if (!driver->soft_unbind)
+ usb_disable_interface(udev, intf);
driver->disconnect(intf);
/* reset other interface state */
- usb_set_interface(interface_to_usbdev(intf),
- intf->altsetting[0].desc.bInterfaceNumber,
- 0);
+ usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
usb_set_intfdata(intf, NULL);
intf->condition = USB_INTERFACE_UNBOUND;
@@ -310,6 +312,7 @@ int usb_driver_claim_interface(struct usb_driver *driver,
dev->driver = &driver->drvwrap.driver;
usb_set_intfdata(iface, priv);
+ iface->needs_binding = 0;
usb_pm_lock(udev);
iface->condition = USB_INTERFACE_BOUND;
@@ -586,7 +589,7 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
struct usb_device *usb_dev;
/* driver is often null here; dev_dbg() would oops */
- pr_debug("usb %s: uevent\n", dev->bus_id);
+ pr_debug("usb %s: uevent\n", dev_name(dev));
if (is_usb_device(dev))
usb_dev = to_usb_device(dev);
@@ -596,11 +599,11 @@ static int usb_uevent(struct device *dev, struct kobj_uevent_env *env)
}
if (usb_dev->devnum < 0) {
- pr_debug("usb %s: already deleted?\n", dev->bus_id);
+ pr_debug("usb %s: already deleted?\n", dev_name(dev));
return -ENODEV;
}
if (!usb_dev->bus) {
- pr_debug("usb %s: bus removed?\n", dev->bus_id);
+ pr_debug("usb %s: bus removed?\n", dev_name(dev));
return -ENODEV;
}
@@ -771,6 +774,104 @@ void usb_deregister(struct usb_driver *driver)
}
EXPORT_SYMBOL_GPL(usb_deregister);
+
+/* Forced unbinding of a USB interface driver, either because
+ * it doesn't support pre_reset/post_reset/reset_resume or
+ * because it doesn't support suspend/resume.
+ *
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
+ */
+void usb_forced_unbind_intf(struct usb_interface *intf)
+{
+ struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+
+ dev_dbg(&intf->dev, "forced unbind\n");
+ usb_driver_release_interface(driver, intf);
+
+ /* Mark the interface for later rebinding */
+ intf->needs_binding = 1;
+}
+
+/* Delayed forced unbinding of a USB interface driver and scan
+ * for rebinding.
+ *
+ * The caller must hold @intf's device's lock, but not its pm_mutex
+ * and not @intf->dev.sem.
+ *
+ * FIXME: The caller must block system sleep transitions.
+ */
+void usb_rebind_intf(struct usb_interface *intf)
+{
+ int rc;
+
+ /* Delayed unbind of an existing driver */
+ if (intf->dev.driver) {
+ struct usb_driver *driver =
+ to_usb_driver(intf->dev.driver);
+
+ dev_dbg(&intf->dev, "forced unbind\n");
+ usb_driver_release_interface(driver, intf);
+ }
+
+ /* Try to rebind the interface */
+ intf->needs_binding = 0;
+ rc = device_attach(&intf->dev);
+ if (rc < 0)
+ dev_warn(&intf->dev, "rebind failed: %d\n", rc);
+}
+
+#define DO_UNBIND 0
+#define DO_REBIND 1
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume,
+ * or rebind interfaces that have been unbound, according to @action.
+ *
+ * The caller must hold @udev's device lock.
+ * FIXME: For rebinds, the caller must block system sleep transitions.
+ */
+static void do_unbind_rebind(struct usb_device *udev, int action)
+{
+ struct usb_host_config *config;
+ int i;
+ struct usb_interface *intf;
+ struct usb_driver *drv;
+
+ config = udev->actconfig;
+ if (config) {
+ for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+ intf = config->interface[i];
+ switch (action) {
+ case DO_UNBIND:
+ if (intf->dev.driver) {
+ drv = to_usb_driver(intf->dev.driver);
+ if (!drv->suspend || !drv->resume)
+ usb_forced_unbind_intf(intf);
+ }
+ break;
+ case DO_REBIND:
+ if (intf->needs_binding) {
+
+ /* FIXME: The next line is needed because we are going to probe
+ * the interface, but as far as the PM core is concerned the
+ * interface is still suspended. The problem wouldn't exist
+ * if we could rebind the interface during the interface's own
+ * resume() call, but at the time the usb_device isn't locked!
+ *
+ * The real solution will be to carry this out during the device's
+ * complete() callback. Until that is implemented, we have to
+ * use this hack.
+ */
+// intf->dev.power.sleeping = 0;
+
+ usb_rebind_intf(intf);
+ }
+ break;
+ }
+ }
+ }
+}
+
#ifdef CONFIG_PM
/* Caller has locked udev's pm_mutex */
@@ -805,8 +906,6 @@ static int usb_resume_device(struct usb_device *udev)
if (udev->state == USB_STATE_NOTATTACHED)
goto done;
- if (udev->state != USB_STATE_SUSPENDED && !udev->reset_resume)
- goto done;
/* Can't resume it if it doesn't have a driver. */
if (udev->dev.driver == NULL) {
@@ -842,7 +941,7 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
goto done;
driver = to_usb_driver(intf->dev.driver);
- if (driver->suspend && driver->resume) {
+ if (driver->suspend) {
status = driver->suspend(intf, msg);
if (status == 0)
mark_quiesced(intf);
@@ -850,12 +949,10 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg)
dev_err(&intf->dev, "%s error %d\n",
"suspend", status);
} else {
- /*
- * FIXME else if there's no suspend method, disconnect...
- * Not possible if auto_pm is set...
- */
- dev_warn(&intf->dev, "no suspend for driver %s?\n",
- driver->name);
+ /* Later we will unbind the driver and reprobe */
+ intf->needs_binding = 1;
+ dev_warn(&intf->dev, "no %s for driver %s?\n",
+ "suspend", driver->name);
mark_quiesced(intf);
}
@@ -879,10 +976,12 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
goto done;
/* Can't resume it if it doesn't have a driver. */
- if (intf->condition == USB_INTERFACE_UNBOUND) {
- status = -ENOTCONN;
+ if (intf->condition == USB_INTERFACE_UNBOUND)
+ goto done;
+
+ /* Don't resume if the interface is marked for rebinding */
+ if (intf->needs_binding)
goto done;
- }
driver = to_usb_driver(intf->dev.driver);
if (reset_resume) {
@@ -892,7 +991,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"reset_resume", status);
} else {
- /* status = -EOPNOTSUPP; */
+ intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"reset_resume", driver->name);
}
@@ -903,7 +1002,7 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
dev_err(&intf->dev, "%s error %d\n",
"resume", status);
} else {
- /* status = -EOPNOTSUPP; */
+ intf->needs_binding = 1;
dev_warn(&intf->dev, "no %s for driver %s?\n",
"resume", driver->name);
}
@@ -911,11 +1010,10 @@ static int usb_resume_interface(struct usb_interface *intf, int reset_resume)
done:
dev_vdbg(&intf->dev, "%s: status %d\n", __func__, status);
- if (status == 0)
+ if (status == 0 && intf->condition == USB_INTERFACE_BOUND)
mark_active(intf);
- /* FIXME: Unbind the driver and reprobe if the resume failed
- * (not possible if auto_pm is set) */
+ /* Later we will unbind the driver and/or reprobe, if necessary */
return status;
}
@@ -1173,11 +1271,8 @@ static int usb_resume_both(struct usb_device *udev)
* then we're stuck. */
status = usb_resume_device(udev);
}
- } else {
-
- /* Needed for reset-resume */
+ } else if (udev->reset_resume)
status = usb_resume_device(udev);
- }
if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
@@ -1474,6 +1569,7 @@ int usb_external_suspend_device(struct usb_device *udev, pm_message_t msg)
{
int status;
+ do_unbind_rebind(udev, DO_UNBIND);
usb_pm_lock(udev);
udev->auto_pm = 0;
status = usb_suspend_both(udev, msg);
@@ -1501,6 +1597,7 @@ int usb_external_resume_device(struct usb_device *udev)
status = usb_resume_both(udev);
udev->last_busy = jiffies;
usb_pm_unlock(udev);
+ do_unbind_rebind(udev, DO_REBIND);
/* Now that the device is awake, we can start trying to autosuspend
* it again. */
@@ -1542,14 +1639,11 @@ static int usb_resume(struct device *dev)
udev = to_usb_device(dev);
/* If udev->skip_sys_resume is set then udev was already suspended
- * when the system suspend started, so we don't want to resume
- * udev during this system wakeup. However a reset-resume counts
- * as a wakeup event, so allow a reset-resume to occur if remote
- * wakeup is enabled. */
- if (udev->skip_sys_resume) {
- if (!(udev->reset_resume && udev->do_remote_wakeup))
- return -EHOSTUNREACH;
- }
+ * when the system sleep started, so we don't want to resume it
+ * during this system wakeup.
+ */
+ if (udev->skip_sys_resume)
+ return 0;
return usb_external_resume_device(udev);
}
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 99e5a68a3f1..22912136fc1 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -156,6 +156,10 @@ static struct attribute *ep_dev_attrs[] = {
static struct attribute_group ep_dev_attr_grp = {
.attrs = ep_dev_attrs,
};
+static struct attribute_group *ep_dev_groups[] = {
+ &ep_dev_attr_grp,
+ NULL
+};
static int usb_endpoint_major_init(void)
{
@@ -292,26 +296,24 @@ int usb_create_ep_files(struct device *parent,
retval = endpoint_get_minor(ep_dev);
if (retval) {
dev_err(parent, "can not allocate minor number for %s\n",
- ep_dev->dev.bus_id);
+ dev_name(&ep_dev->dev));
goto error_register;
}
ep_dev->desc = &endpoint->desc;
ep_dev->udev = udev;
+ ep_dev->dev.groups = ep_dev_groups;
ep_dev->dev.devt = MKDEV(usb_endpoint_major, ep_dev->minor);
ep_dev->dev.class = ep_class->class;
ep_dev->dev.parent = parent;
ep_dev->dev.release = ep_device_release;
- snprintf(ep_dev->dev.bus_id, BUS_ID_SIZE, "usbdev%d.%d_ep%02x",
+ dev_set_name(&ep_dev->dev, "usbdev%d.%d_ep%02x",
udev->bus->busnum, udev->devnum,
endpoint->desc.bEndpointAddress);
retval = device_register(&ep_dev->dev);
if (retval)
goto error_chrdev;
- retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
- if (retval)
- goto error_group;
/* create the symlink to the old-style "ep_XX" directory */
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
@@ -322,8 +324,6 @@ int usb_create_ep_files(struct device *parent,
return retval;
error_link:
- sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
-error_group:
device_unregister(&ep_dev->dev);
destroy_endpoint_class();
return retval;
@@ -348,7 +348,6 @@ void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
sysfs_remove_link(&ep_dev->dev.parent->kobj, name);
- sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
device_unregister(&ep_dev->dev);
endpoint->ep_dev = NULL;
destroy_endpoint_class();
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 8133c99c6c5..6b1b229e38c 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/rwsem.h>
+#include <linux/smp_lock.h>
#include <linux/usb.h>
#include "usb.h"
@@ -33,6 +34,7 @@ static int usb_open(struct inode * inode, struct file * file)
int err = -ENODEV;
const struct file_operations *old_fops, *new_fops = NULL;
+ lock_kernel();
down_read(&minor_rwsem);
c = usb_minors[minor];
@@ -51,6 +53,7 @@ static int usb_open(struct inode * inode, struct file * file)
fops_put(old_fops);
done:
up_read(&minor_rwsem);
+ unlock_kernel();
return err;
}
@@ -147,7 +150,7 @@ int usb_register_dev(struct usb_interface *intf,
int retval = -EINVAL;
int minor_base = class_driver->minor_base;
int minor = 0;
- char name[BUS_ID_SIZE];
+ char name[20];
char *temp;
#ifdef CONFIG_USB_DYNAMIC_MINORS
@@ -187,14 +190,15 @@ int usb_register_dev(struct usb_interface *intf,
intf->minor = minor;
/* create a usb class device for this usb interface */
- snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
+ snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
- if (temp && (temp[1] != 0x00))
+ if (temp && (temp[1] != '\0'))
++temp;
else
temp = name;
- intf->usb_dev = device_create(usb_class->class, &intf->dev,
- MKDEV(USB_MAJOR, minor), "%s", temp);
+ intf->usb_dev = device_create_drvdata(usb_class->class, &intf->dev,
+ MKDEV(USB_MAJOR, minor), NULL,
+ "%s", temp);
if (IS_ERR(intf->usb_dev)) {
down_write(&minor_rwsem);
usb_minors[intf->minor] = NULL;
@@ -224,7 +228,7 @@ void usb_deregister_dev(struct usb_interface *intf,
struct usb_class_driver *class_driver)
{
int minor_base = class_driver->minor_base;
- char name[BUS_ID_SIZE];
+ char name[20];
#ifdef CONFIG_USB_DYNAMIC_MINORS
minor_base = 0;
@@ -239,7 +243,7 @@ void usb_deregister_dev(struct usb_interface *intf,
usb_minors[intf->minor] = NULL;
up_write(&minor_rwsem);
- snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
+ snprintf(name, sizeof(name), class_driver->name, intf->minor - minor_base);
device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
intf->usb_dev = NULL;
intf->minor = -1;
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
index c1cb94e9f24..7e912f21fd3 100644
--- a/drivers/usb/core/generic.c
+++ b/drivers/usb/core/generic.c
@@ -155,9 +155,6 @@ static int generic_probe(struct usb_device *udev)
{
int err, c;
- /* put device-specific files into sysfs */
- usb_create_sysfs_dev_files(udev);
-
/* Choose and set the configuration. This registers the interfaces
* with the driver core and lets interface drivers bind to them.
*/
@@ -189,8 +186,6 @@ static void generic_disconnect(struct usb_device *udev)
* unconfigure the device */
if (udev->actconfig)
usb_set_configuration(udev, -1);
-
- usb_remove_sysfs_dev_files(udev);
}
#ifdef CONFIG_PM
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index bf10e9c4195..f7bfd72ef11 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -818,12 +818,12 @@ static int usb_register_bus(struct usb_bus *bus)
set_bit (busnum, busmap.busmap);
bus->busnum = busnum;
- bus->dev = device_create(usb_host_class, bus->controller, MKDEV(0, 0),
- "usb_host%d", busnum);
+ bus->dev = device_create_drvdata(usb_host_class, bus->controller,
+ MKDEV(0, 0), bus,
+ "usb_host%d", busnum);
result = PTR_ERR(bus->dev);
if (IS_ERR(bus->dev))
goto error_create_class_dev;
- dev_set_drvdata(bus->dev, bus);
/* Add it to the local list of buses */
list_add (&bus->bus_list, &usb_bus_list);
@@ -900,14 +900,14 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval != sizeof usb_dev->descriptor) {
mutex_unlock(&usb_bus_list_lock);
dev_dbg (parent_dev, "can't read %s device descriptor %d\n",
- usb_dev->dev.bus_id, retval);
+ dev_name(&usb_dev->dev), retval);
return (retval < 0) ? retval : -EMSGSIZE;
}
retval = usb_new_device (usb_dev);
if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n",
- usb_dev->dev.bus_id, retval);
+ dev_name(&usb_dev->dev), retval);
}
mutex_unlock(&usb_bus_list_lock);
@@ -924,6 +924,15 @@ static int register_root_hub(struct usb_hcd *hcd)
return retval;
}
+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->state != HC_STATE_HALT)
+ hcd->driver->hub_irq_enable (hcd);
+}
+
/*-------------------------------------------------------------------------*/
@@ -1684,19 +1693,30 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
irqreturn_t usb_hcd_irq (int irq, void *__hcd)
{
struct usb_hcd *hcd = __hcd;
- int start = hcd->state;
+ unsigned long flags;
+ irqreturn_t rc;
- if (unlikely(start == HC_STATE_HALT ||
- !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
- return IRQ_NONE;
- if (hcd->driver->irq (hcd) == IRQ_NONE)
- return IRQ_NONE;
+ /* IRQF_DISABLED doesn't work correctly with shared IRQs
+ * when the first handler doesn't use it. So let's just
+ * assume it's never used.
+ */
+ local_irq_save(flags);
- set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+ if (unlikely(hcd->state == HC_STATE_HALT ||
+ !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
+ rc = IRQ_NONE;
+ } else if (hcd->driver->irq(hcd) == IRQ_NONE) {
+ rc = IRQ_NONE;
+ } else {
+ set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags);
+
+ if (unlikely(hcd->state == HC_STATE_HALT))
+ usb_hc_died(hcd);
+ rc = IRQ_HANDLED;
+ }
- if (unlikely(hcd->state == HC_STATE_HALT))
- usb_hc_died (hcd);
- return IRQ_HANDLED;
+ local_irq_restore(flags);
+ return rc;
}
/*-------------------------------------------------------------------------*/
@@ -1744,7 +1764,7 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
* If memory is unavailable, returns NULL.
*/
struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
- struct device *dev, char *bus_name)
+ struct device *dev, const char *bus_name)
{
struct usb_hcd *hcd;
@@ -1860,6 +1880,13 @@ int usb_add_hcd(struct usb_hcd *hcd,
/* enable irqs just before we start the controller */
if (hcd->driver->irq) {
+
+ /* IRQF_DISABLED doesn't work as advertised when used together
+ * with IRQF_SHARED. As usb_hcd_irq() will always disable
+ * interrupts we can remove it here.
+ */
+ irqflags &= ~IRQF_DISABLED;
+
snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
hcd->driver->description, hcd->self.busnum);
if ((retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 1e4b81e9eb5..5b0b59b0d89 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -21,6 +21,8 @@
#include <linux/rwsem.h>
+#define MAX_TOPO_LEVEL 6
+
/* This file contains declarations of usbcore internals that are mostly
* used or exposed by Host Controller Drivers.
*/
@@ -210,9 +212,13 @@ struct hc_driver {
int (*bus_suspend)(struct usb_hcd *);
int (*bus_resume)(struct usb_hcd *);
int (*start_port_reset)(struct usb_hcd *, unsigned port_num);
+ void (*hub_irq_enable)(struct usb_hcd *);
+ /* Needed only if port-change IRQs are level-triggered */
/* force handover of high-speed port to full-speed companion */
void (*relinquish_port)(struct usb_hcd *, int);
+ /* has a port been handed over to a companion? */
+ int (*port_handed_over)(struct usb_hcd *, int);
};
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
@@ -231,7 +237,7 @@ extern void usb_hcd_disable_endpoint(struct usb_device *udev,
extern int usb_hcd_get_frame_number(struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd(const struct hc_driver *driver,
- struct device *dev, char *bus_name);
+ struct device *dev, const char *bus_name);
extern struct usb_hcd *usb_get_hcd(struct usb_hcd *hcd);
extern void usb_put_hcd(struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index eb57fcc701d..107e1d25dde 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -72,7 +72,6 @@ struct usb_hub {
unsigned limited_power:1;
unsigned quiescing:1;
- unsigned activating:1;
unsigned disconnected:1;
unsigned has_indicators:1;
@@ -131,6 +130,12 @@ MODULE_PARM_DESC(use_both_schemes,
DECLARE_RWSEM(ehci_cf_port_reset_rwsem);
EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
+#define HUB_DEBOUNCE_TIMEOUT 1500
+#define HUB_DEBOUNCE_STEP 25
+#define HUB_DEBOUNCE_STABLE 100
+
+
+static int usb_reset_and_verify_device(struct usb_device *udev);
static inline char *portspeed(int portstatus)
{
@@ -535,37 +540,6 @@ static void hub_power_on(struct usb_hub *hub)
msleep(max(pgood_delay, (unsigned) 100));
}
-static void hub_quiesce(struct usb_hub *hub)
-{
- /* (nonblocking) khubd and related activity won't re-trigger */
- hub->quiescing = 1;
- hub->activating = 0;
-
- /* (blocking) stop khubd and related activity */
- usb_kill_urb(hub->urb);
- if (hub->has_indicators)
- cancel_delayed_work_sync(&hub->leds);
- if (hub->tt.hub)
- cancel_work_sync(&hub->tt.kevent);
-}
-
-static void hub_activate(struct usb_hub *hub)
-{
- int status;
-
- hub->quiescing = 0;
- hub->activating = 1;
-
- status = usb_submit_urb(hub->urb, GFP_NOIO);
- if (status < 0)
- dev_err(hub->intfdev, "activate --> %d\n", status);
- if (hub->has_indicators && blinkenlights)
- schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
-
- /* scan all ports ASAP */
- kick_khubd(hub);
-}
-
static int hub_hub_status(struct usb_hub *hub,
u16 *status, u16 *change)
{
@@ -624,101 +598,149 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
-/* caller has locked the hub device */
-static void hub_stop(struct usb_hub *hub)
-{
- struct usb_device *hdev = hub->hdev;
- int i;
-
- /* Disconnect all the children */
- for (i = 0; i < hdev->maxchild; ++i) {
- if (hdev->children[i])
- usb_disconnect(&hdev->children[i]);
- }
- hub_quiesce(hub);
-}
-
-#define HUB_RESET 1
-#define HUB_RESUME 2
-#define HUB_RESET_RESUME 3
-
-#ifdef CONFIG_PM
+enum hub_activation_type {
+ HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME
+};
-static void hub_restart(struct usb_hub *hub, int type)
+static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
{
struct usb_device *hdev = hub->hdev;
int port1;
+ int status;
+ bool need_debounce_delay = false;
+
+ /* After a resume, port power should still be on.
+ * For any other type of activation, turn it on.
+ */
+ if (type != HUB_RESUME)
+ hub_power_on(hub);
- /* Check each of the children to see if they require
- * USB-PERSIST handling or disconnection. Also check
- * each unoccupied port to make sure it is still disabled.
+ /* Check each port and set hub->change_bits to let khubd know
+ * which ports need attention.
*/
for (port1 = 1; port1 <= hdev->maxchild; ++port1) {
struct usb_device *udev = hdev->children[port1-1];
- int status = 0;
u16 portstatus, portchange;
- if (!udev || udev->state == USB_STATE_NOTATTACHED) {
- if (type != HUB_RESET) {
- status = hub_port_status(hub, port1,
- &portstatus, &portchange);
- if (status == 0 && (portstatus &
- USB_PORT_STAT_ENABLE))
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_ENABLE);
- }
- continue;
- }
+ portstatus = portchange = 0;
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ dev_dbg(hub->intfdev,
+ "port %d: status %04x change %04x\n",
+ port1, portstatus, portchange);
- /* Was the power session lost while we were suspended? */
- switch (type) {
- case HUB_RESET_RESUME:
- portstatus = 0;
- portchange = USB_PORT_STAT_C_CONNECTION;
- break;
+ /* After anything other than HUB_RESUME (i.e., initialization
+ * or any sort of reset), every port should be disabled.
+ * Unconnected ports should likewise be disabled (paranoia),
+ * and so should ports for which we have no usb_device.
+ */
+ if ((portstatus & USB_PORT_STAT_ENABLE) && (
+ type != HUB_RESUME ||
+ !(portstatus & USB_PORT_STAT_CONNECTION) ||
+ !udev ||
+ udev->state == USB_STATE_NOTATTACHED)) {
+ clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE);
+ portstatus &= ~USB_PORT_STAT_ENABLE;
+ }
- case HUB_RESET:
- case HUB_RESUME:
- status = hub_port_status(hub, port1,
- &portstatus, &portchange);
- break;
+ /* Clear status-change flags; we'll debounce later */
+ if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ need_debounce_delay = true;
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ }
+ if (portchange & USB_PORT_STAT_C_ENABLE) {
+ need_debounce_delay = true;
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_ENABLE);
}
- /* For "USB_PERSIST"-enabled children we must
- * mark the child device for reset-resume and
- * turn off the various status changes to prevent
- * khubd from disconnecting it later.
- */
- if (udev->persist_enabled && status == 0 &&
- !(portstatus & USB_PORT_STAT_ENABLE)) {
- if (portchange & USB_PORT_STAT_C_ENABLE)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_ENABLE);
- if (portchange & USB_PORT_STAT_C_CONNECTION)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_CONNECTION);
+ if (!udev || udev->state == USB_STATE_NOTATTACHED) {
+ /* Tell khubd to disconnect the device or
+ * check for a new connection
+ */
+ if (udev || (portstatus & USB_PORT_STAT_CONNECTION))
+ set_bit(port1, hub->change_bits);
+
+ } else if (portstatus & USB_PORT_STAT_ENABLE) {
+ /* The power session apparently survived the resume.
+ * If there was an overcurrent or suspend change
+ * (i.e., remote wakeup request), have khubd
+ * take care of it.
+ */
+ if (portchange)
+ set_bit(port1, hub->change_bits);
+
+ } else if (udev->persist_enabled) {
+#ifdef CONFIG_PM
udev->reset_resume = 1;
- }
+#endif
+ set_bit(port1, hub->change_bits);
- /* Otherwise for a reset_resume we must disconnect the child,
- * but as we may not lock the child device here
- * we have to do a "logical" disconnect.
- */
- else if (type == HUB_RESET_RESUME)
- hub_port_logical_disconnect(hub, port1);
+ } else {
+ /* The power session is gone; tell khubd */
+ usb_set_device_state(udev, USB_STATE_NOTATTACHED);
+ set_bit(port1, hub->change_bits);
+ }
}
- hub_activate(hub);
+ /* If no port-status-change flags were set, we don't need any
+ * debouncing. If flags were set we can try to debounce the
+ * ports all at once right now, instead of letting khubd do them
+ * one at a time later on.
+ *
+ * If any port-status changes do occur during this delay, khubd
+ * will see them later and handle them normally.
+ */
+ if (need_debounce_delay)
+ msleep(HUB_DEBOUNCE_STABLE);
+
+ hub->quiescing = 0;
+
+ status = usb_submit_urb(hub->urb, GFP_NOIO);
+ if (status < 0)
+ dev_err(hub->intfdev, "activate --> %d\n", status);
+ if (hub->has_indicators && blinkenlights)
+ schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+
+ /* Scan all ports that need attention */
+ kick_khubd(hub);
}
-#endif /* CONFIG_PM */
+enum hub_quiescing_type {
+ HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND
+};
+
+static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type)
+{
+ struct usb_device *hdev = hub->hdev;
+ int i;
+
+ /* khubd and related activity won't re-trigger */
+ hub->quiescing = 1;
+
+ if (type != HUB_SUSPEND) {
+ /* Disconnect all the children */
+ for (i = 0; i < hdev->maxchild; ++i) {
+ if (hdev->children[i])
+ usb_disconnect(&hdev->children[i]);
+ }
+ }
+
+ /* Stop khubd and related activity */
+ usb_kill_urb(hub->urb);
+ if (hub->has_indicators)
+ cancel_delayed_work_sync(&hub->leds);
+ if (hub->tt.hub)
+ cancel_work_sync(&hub->tt.kevent);
+}
/* caller has locked the hub device */
static int hub_pre_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
- hub_stop(hub);
+ hub_quiesce(hub, HUB_PRE_RESET);
return 0;
}
@@ -727,8 +749,7 @@ static int hub_post_reset(struct usb_interface *intf)
{
struct usb_hub *hub = usb_get_intfdata(intf);
- hub_power_on(hub);
- hub_activate(hub);
+ hub_activate(hub, HUB_POST_RESET);
return 0;
}
@@ -974,8 +995,7 @@ static int hub_configure(struct usb_hub *hub,
if (hub->has_indicators && blinkenlights)
hub->indicator [0] = INDICATOR_CYCLE;
- hub_power_on(hub);
- hub_activate(hub);
+ hub_activate(hub, HUB_INIT);
return 0;
fail:
@@ -1007,7 +1027,7 @@ static void hub_disconnect(struct usb_interface *intf)
/* Disconnect all children and quiesce the hub */
hub->error = 0;
- hub_stop(hub);
+ hub_quiesce(hub, HUB_DISCONNECT);
usb_set_intfdata (intf, NULL);
@@ -1033,6 +1053,12 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
desc = intf->cur_altsetting;
hdev = interface_to_usbdev(intf);
+ if (hdev->level == MAX_TOPO_LEVEL) {
+ dev_err(&intf->dev, "Unsupported bus topology: "
+ "hub nested too deep\n");
+ return -E2BIG;
+ }
+
#ifdef CONFIG_USB_OTG_BLACKLIST_HUB
if (hdev->parent) {
dev_warn(&intf->dev, "ignoring external hub\n");
@@ -1326,6 +1352,12 @@ void usb_disconnect(struct usb_device **pdev)
usb_unlock_device(udev);
+ /* Remove the device-specific files from sysfs. This must be
+ * done with udev unlocked, because some of the attribute
+ * routines try to acquire the device lock.
+ */
+ usb_remove_sysfs_dev_files(udev);
+
/* Unregister the device. The device driver is responsible
* for removing the device files from usbfs and sysfs and for
* de-configuring the device.
@@ -1541,6 +1573,9 @@ int usb_new_device(struct usb_device *udev)
goto fail;
}
+ /* put device-specific files into sysfs */
+ usb_create_sysfs_dev_files(udev);
+
/* Tell the world! */
announce_device(udev);
return err;
@@ -1770,6 +1805,51 @@ static int hub_port_reset(struct usb_hub *hub, int port1,
#ifdef CONFIG_PM
+#define MASK_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION | \
+ USB_PORT_STAT_SUSPEND)
+#define WANT_BITS (USB_PORT_STAT_POWER | USB_PORT_STAT_CONNECTION)
+
+/* Determine whether the device on a port is ready for a normal resume,
+ * is ready for a reset-resume, or should be disconnected.
+ */
+static int check_port_resume_type(struct usb_device *udev,
+ struct usb_hub *hub, int port1,
+ int status, unsigned portchange, unsigned portstatus)
+{
+ /* Is the device still present? */
+ if (status || (portstatus & MASK_BITS) != WANT_BITS) {
+ if (status >= 0)
+ status = -ENODEV;
+ }
+
+ /* Can't do a normal resume if the port isn't enabled,
+ * so try a reset-resume instead.
+ */
+ else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
+ if (udev->persist_enabled)
+ udev->reset_resume = 1;
+ else
+ status = -ENODEV;
+ }
+
+ if (status) {
+ dev_dbg(hub->intfdev,
+ "port %d status %04x.%04x after resume, %d\n",
+ port1, portchange, portstatus, status);
+ } else if (udev->reset_resume) {
+
+ /* Late port handoff can set status-change bits */
+ if (portchange & USB_PORT_STAT_C_CONNECTION)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_CONNECTION);
+ if (portchange & USB_PORT_STAT_C_ENABLE)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_ENABLE);
+ }
+
+ return status;
+}
+
#ifdef CONFIG_USB_SUSPEND
/*
@@ -1899,7 +1979,8 @@ static int finish_port_resume(struct usb_device *udev)
* resumed.
*/
if (udev->reset_resume)
- status = usb_reset_device(udev);
+ retry_reset_resume:
+ status = usb_reset_and_verify_device(udev);
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
@@ -1910,6 +1991,13 @@ static int finish_port_resume(struct usb_device *udev)
status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus);
if (status >= 0)
status = (status > 0 ? 0 : -ENODEV);
+
+ /* If a normal resume failed, try doing a reset-resume */
+ if (status && !udev->reset_resume && udev->persist_enabled) {
+ dev_dbg(&udev->dev, "retry with reset-resume\n");
+ udev->reset_resume = 1;
+ goto retry_reset_resume;
+ }
}
if (status) {
@@ -1958,7 +2046,7 @@ static int finish_port_resume(struct usb_device *udev)
* to it will be lost. Using the USB_PERSIST facility, the device can be
* made to appear as if it had not disconnected.
*
- * This facility can be dangerous. Although usb_reset_device() makes
+ * This facility can be dangerous. Although usb_reset_and_verify_device() makes
* every effort to insure that the same device is present after the
* reset as before, it cannot provide a 100% guarantee. Furthermore it's
* quite possible for a device to remain unaltered but its media to be
@@ -1974,7 +2062,6 @@ int usb_port_resume(struct usb_device *udev)
int port1 = udev->portnum;
int status;
u16 portchange, portstatus;
- unsigned mask_flags, want_flags;
/* Skip the initial Clear-Suspend step for a remote wakeup */
status = hub_port_status(hub, port1, &portstatus, &portchange);
@@ -2003,33 +2090,23 @@ int usb_port_resume(struct usb_device *udev)
*/
status = hub_port_status(hub, port1, &portstatus, &portchange);
- SuspendCleared:
- if (udev->reset_resume)
- want_flags = USB_PORT_STAT_POWER
- | USB_PORT_STAT_CONNECTION;
- else
- want_flags = USB_PORT_STAT_POWER
- | USB_PORT_STAT_CONNECTION
- | USB_PORT_STAT_ENABLE;
- mask_flags = want_flags | USB_PORT_STAT_SUSPEND;
+ /* TRSMRCY = 10 msec */
+ msleep(10);
+ }
- if (status < 0 || (portstatus & mask_flags) != want_flags) {
- dev_dbg(hub->intfdev,
- "port %d status %04x.%04x after resume, %d\n",
- port1, portchange, portstatus, status);
- if (status >= 0)
- status = -ENODEV;
- } else {
- if (portchange & USB_PORT_STAT_C_SUSPEND)
- clear_port_feature(hub->hdev, port1,
- USB_PORT_FEAT_C_SUSPEND);
- /* TRSMRCY = 10 msec */
- msleep(10);
- }
+ SuspendCleared:
+ if (status == 0) {
+ if (portchange & USB_PORT_STAT_C_SUSPEND)
+ clear_port_feature(hub->hdev, port1,
+ USB_PORT_FEAT_C_SUSPEND);
}
clear_bit(port1, hub->busy_bits);
+ if (!hub->hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hub->hdev->bus);
+ status = check_port_resume_type(udev,
+ hub, port1, status, portchange, portstatus);
if (status == 0)
status = finish_port_resume(udev);
if (status < 0) {
@@ -2039,17 +2116,16 @@ int usb_port_resume(struct usb_device *udev)
return status;
}
+/* caller has locked udev */
static int remote_wakeup(struct usb_device *udev)
{
int status = 0;
- usb_lock_device(udev);
if (udev->state == USB_STATE_SUSPENDED) {
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
usb_mark_last_busy(udev);
status = usb_external_resume_device(udev);
}
- usb_unlock_device(udev);
return status;
}
@@ -2062,14 +2138,25 @@ int usb_port_suspend(struct usb_device *udev)
return 0;
}
+/* However we may need to do a reset-resume */
+
int usb_port_resume(struct usb_device *udev)
{
- int status = 0;
+ struct usb_hub *hub = hdev_to_hub(udev->parent);
+ int port1 = udev->portnum;
+ int status;
+ u16 portchange, portstatus;
+
+ status = hub_port_status(hub, port1, &portstatus, &portchange);
+ status = check_port_resume_type(udev,
+ hub, port1, status, portchange, portstatus);
- /* However we may need to do a reset-resume */
- if (udev->reset_resume) {
+ if (status) {
+ dev_dbg(&udev->dev, "can't resume, status %d\n", status);
+ hub_port_logical_disconnect(hub, port1);
+ } else if (udev->reset_resume) {
dev_dbg(&udev->dev, "reset-resume\n");
- status = usb_reset_device(udev);
+ status = usb_reset_and_verify_device(udev);
}
return status;
}
@@ -2103,7 +2190,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
dev_dbg(&intf->dev, "%s\n", __func__);
/* stop khubd and related activity */
- hub_quiesce(hub);
+ hub_quiesce(hub, HUB_SUSPEND);
return 0;
}
@@ -2112,7 +2199,7 @@ static int hub_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
- hub_restart(hub, HUB_RESUME);
+ hub_activate(hub, HUB_RESUME);
return 0;
}
@@ -2121,8 +2208,7 @@ static int hub_reset_resume(struct usb_interface *intf)
struct usb_hub *hub = usb_get_intfdata(intf);
dev_dbg(&intf->dev, "%s\n", __func__);
- hub_power_on(hub);
- hub_restart(hub, HUB_RESET_RESUME);
+ hub_activate(hub, HUB_RESET_RESUME);
return 0;
}
@@ -2172,11 +2258,6 @@ static inline int remote_wakeup(struct usb_device *udev)
* every 25ms for transient disconnects. When the port status has been
* unchanged for 100ms it returns the port status.
*/
-
-#define HUB_DEBOUNCE_TIMEOUT 1500
-#define HUB_DEBOUNCE_STEP 25
-#define HUB_DEBOUNCE_STABLE 100
-
static int hub_port_debounce(struct usb_hub *hub, int port1)
{
int ret;
@@ -2256,7 +2337,7 @@ static int hub_set_address(struct usb_device *udev, int devnum)
* Returns device in USB_STATE_ADDRESS, except on error.
*
* If this is called for an already-existing device (as part of
- * usb_reset_device), the caller must own the device lock. For a
+ * usb_reset_and_verify_device), the caller must own the device lock. For a
* newly detected device that is not accessible through any global
* pointers, it's not necessary to lock the device.
*/
@@ -2573,7 +2654,7 @@ hub_power_remaining (struct usb_hub *hub)
* This routine is called when:
* a port connection-change occurs;
* a port enable-change occurs (often caused by EMI);
- * usb_reset_device() encounters changed descriptors (as from
+ * usb_reset_and_verify_device() encounters changed descriptors (as from
* a firmware download)
* caller already locked the hub
*/
@@ -2583,9 +2664,11 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
struct usb_device *hdev = hub->hdev;
struct device *hub_dev = hub->intfdev;
struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
- u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics);
+ unsigned wHubCharacteristics =
+ le16_to_cpu(hub->descriptor->wHubCharacteristics);
+ struct usb_device *udev;
int status, i;
-
+
dev_dbg (hub_dev,
"port %d, status %04x, change %04x, %s\n",
port1, portstatus, portchange, portspeed (portstatus));
@@ -2594,30 +2677,73 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
set_port_led(hub, port1, HUB_LED_AUTO);
hub->indicator[port1-1] = INDICATOR_AUTO;
}
-
- /* Disconnect any existing devices under this port */
- if (hdev->children[port1-1])
- usb_disconnect(&hdev->children[port1-1]);
- clear_bit(port1, hub->change_bits);
#ifdef CONFIG_USB_OTG
/* during HNP, don't repeat the debounce */
if (hdev->bus->is_b_host)
- portchange &= ~USB_PORT_STAT_C_CONNECTION;
+ portchange &= ~(USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE);
#endif
- if (portchange & USB_PORT_STAT_C_CONNECTION) {
+ /* Try to use the debounce delay for protection against
+ * port-enable changes caused, for example, by EMI.
+ */
+ if (portchange & (USB_PORT_STAT_C_CONNECTION |
+ USB_PORT_STAT_C_ENABLE)) {
status = hub_port_debounce(hub, port1);
if (status < 0) {
if (printk_ratelimit())
dev_err (hub_dev, "connect-debounce failed, "
"port %d disabled\n", port1);
- goto done;
+ portstatus &= ~USB_PORT_STAT_CONNECTION;
+ } else {
+ portstatus = status;
+ }
+ }
+
+ /* Try to resuscitate an existing device */
+ udev = hdev->children[port1-1];
+ if ((portstatus & USB_PORT_STAT_CONNECTION) && udev &&
+ udev->state != USB_STATE_NOTATTACHED) {
+
+ usb_lock_device(udev);
+ if (portstatus & USB_PORT_STAT_ENABLE) {
+ status = 0; /* Nothing to do */
+ } else if (!udev->persist_enabled) {
+ status = -ENODEV; /* Mustn't resuscitate */
+
+#ifdef CONFIG_USB_SUSPEND
+ } else if (udev->state == USB_STATE_SUSPENDED) {
+ /* For a suspended device, treat this as a
+ * remote wakeup event.
+ */
+ if (udev->do_remote_wakeup)
+ status = remote_wakeup(udev);
+
+ /* Otherwise leave it be; devices can't tell the
+ * difference between suspended and disabled.
+ */
+ else
+ status = 0;
+#endif
+
+ } else {
+ status = usb_reset_device(udev);
+ }
+ usb_unlock_device(udev);
+
+ if (status == 0) {
+ clear_bit(port1, hub->change_bits);
+ return;
}
- portstatus = status;
}
- /* Return now if nothing is connected */
+ /* Disconnect any existing devices under this port */
+ if (udev)
+ usb_disconnect(&hdev->children[port1-1]);
+ clear_bit(port1, hub->change_bits);
+
+ /* Return now if debouncing failed or nothing is connected */
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/* maybe switch power back on (e.g. root hub was reset) */
@@ -2631,7 +2757,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
}
for (i = 0; i < SET_CONFIG_TRIES; i++) {
- struct usb_device *udev;
/* reallocate for each attempt, since references
* to the previous one can escape in various ways
@@ -2744,7 +2869,11 @@ loop:
if ((status == -ENOTCONN) || (status == -ENOTSUPP))
break;
}
- dev_err(hub_dev, "unable to enumerate USB device on port %d\n", port1);
+ if (hub->hdev->parent ||
+ !hcd->driver->port_handed_over ||
+ !(hcd->driver->port_handed_over)(hcd, port1))
+ dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
+ port1);
done:
hub_port_disable(hub, port1, 1);
@@ -2808,7 +2937,7 @@ static void hub_events(void)
/* If the hub has died, clean up after it */
if (hdev->state == USB_STATE_NOTATTACHED) {
hub->error = -ENODEV;
- hub_stop(hub);
+ hub_quiesce(hub, HUB_DISCONNECT);
goto loop;
}
@@ -2827,7 +2956,7 @@ static void hub_events(void)
dev_dbg (hub_dev, "resetting for error %d\n",
hub->error);
- ret = usb_reset_composite_device(hdev, intf);
+ ret = usb_reset_device(hdev);
if (ret) {
dev_dbg (hub_dev,
"error resetting hub: %d\n", ret);
@@ -2844,7 +2973,7 @@ static void hub_events(void)
continue;
connect_change = test_bit(i, hub->change_bits);
if (!test_and_clear_bit(i, hub->event_bits) &&
- !connect_change && !hub->activating)
+ !connect_change)
continue;
ret = hub_port_status(hub, i,
@@ -2852,11 +2981,6 @@ static void hub_events(void)
if (ret < 0)
continue;
- if (hub->activating && !hdev->children[i-1] &&
- (portstatus &
- USB_PORT_STAT_CONNECTION))
- connect_change = 1;
-
if (portchange & USB_PORT_STAT_C_CONNECTION) {
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_CONNECTION);
@@ -2891,11 +3015,16 @@ static void hub_events(void)
}
if (portchange & USB_PORT_STAT_C_SUSPEND) {
+ struct usb_device *udev;
+
clear_port_feature(hdev, i,
USB_PORT_FEAT_C_SUSPEND);
- if (hdev->children[i-1]) {
+ udev = hdev->children[i-1];
+ if (udev) {
+ usb_lock_device(udev);
ret = remote_wakeup(hdev->
children[i-1]);
+ usb_unlock_device(udev);
if (ret < 0)
connect_change = 1;
} else {
@@ -2952,7 +3081,10 @@ static void hub_events(void)
}
}
- hub->activating = 0;
+ /* If this is a root hub, tell the HCD it's okay to
+ * re-enable port-change interrupts now. */
+ if (!hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hdev->bus);
loop_autopm:
/* Allow autosuspend if we're not going to run again */
@@ -3117,12 +3249,12 @@ static int descriptors_changed(struct usb_device *udev,
}
/**
- * usb_reset_device - perform a USB port reset to reinitialize a device
+ * usb_reset_and_verify_device - perform a USB port reset to reinitialize a device
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
*
* WARNING - don't use this routine to reset a composite device
* (one with multiple interfaces owned by separate drivers)!
- * Use usb_reset_composite_device() instead.
+ * Use usb_reset_device() instead.
*
* Do a port reset, reassign the device's address, and establish its
* former operating configuration. If the reset fails, or the device's
@@ -3146,7 +3278,7 @@ static int descriptors_changed(struct usb_device *udev,
* holding the device lock because these tasks should always call
* usb_autopm_resume_device(), thereby preventing any unwanted autoresume.
*/
-int usb_reset_device(struct usb_device *udev)
+static int usb_reset_and_verify_device(struct usb_device *udev)
{
struct usb_device *parent_hdev = udev->parent;
struct usb_hub *parent_hub;
@@ -3179,6 +3311,8 @@ int usb_reset_device(struct usb_device *udev)
break;
}
clear_bit(port1, parent_hub->busy_bits);
+ if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+ usb_enable_root_hub_irq(parent_hdev->bus);
if (ret < 0)
goto re_enumerate;
@@ -3232,26 +3366,28 @@ re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
-EXPORT_SYMBOL_GPL(usb_reset_device);
/**
- * usb_reset_composite_device - warn interface drivers and perform a USB port reset
+ * usb_reset_device - warn interface drivers and perform a USB port reset
* @udev: device to reset (not in SUSPENDED or NOTATTACHED state)
- * @iface: interface bound to the driver making the request (optional)
*
* Warns all drivers bound to registered interfaces (using their pre_reset
* method), performs the port reset, and then lets the drivers know that
* the reset is over (using their post_reset method).
*
- * Return value is the same as for usb_reset_device().
+ * Return value is the same as for usb_reset_and_verify_device().
*
* The caller must own the device lock. For example, it's safe to use
* this from a driver probe() routine after downloading new firmware.
* For calls that might not occur during probe(), drivers should lock
* the device using usb_lock_device_for_reset().
+ *
+ * If an interface is currently being probed or disconnected, we assume
+ * its driver knows how to handle resets. For all other interfaces,
+ * if the driver doesn't have pre_reset and post_reset methods then
+ * we attempt to unbind it and rebind afterward.
*/
-int usb_reset_composite_device(struct usb_device *udev,
- struct usb_interface *iface)
+int usb_reset_device(struct usb_device *udev)
{
int ret;
int i;
@@ -3267,40 +3403,47 @@ int usb_reset_composite_device(struct usb_device *udev,
/* Prevent autosuspend during the reset */
usb_autoresume_device(udev);
- if (iface && iface->condition != USB_INTERFACE_BINDING)
- iface = NULL;
-
if (config) {
for (i = 0; i < config->desc.bNumInterfaces; ++i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
+ int unbind = 0;
if (cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
- if (drv->pre_reset)
- (drv->pre_reset)(cintf);
- /* FIXME: Unbind if pre_reset returns an error or isn't defined */
+ if (drv->pre_reset && drv->post_reset)
+ unbind = (drv->pre_reset)(cintf);
+ else if (cintf->condition ==
+ USB_INTERFACE_BOUND)
+ unbind = 1;
+ if (unbind)
+ usb_forced_unbind_intf(cintf);
}
}
}
- ret = usb_reset_device(udev);
+ ret = usb_reset_and_verify_device(udev);
if (config) {
for (i = config->desc.bNumInterfaces - 1; i >= 0; --i) {
struct usb_interface *cintf = config->interface[i];
struct usb_driver *drv;
+ int rebind = cintf->needs_binding;
- if (cintf->dev.driver) {
+ if (!rebind && cintf->dev.driver) {
drv = to_usb_driver(cintf->dev.driver);
if (drv->post_reset)
- (drv->post_reset)(cintf);
- /* FIXME: Unbind if post_reset returns an error or isn't defined */
+ rebind = (drv->post_reset)(cintf);
+ else if (cintf->condition ==
+ USB_INTERFACE_BOUND)
+ rebind = 1;
}
+ if (rebind)
+ usb_rebind_intf(cintf);
}
}
usb_autosuspend_device(udev);
return ret;
}
-EXPORT_SYMBOL_GPL(usb_reset_composite_device);
+EXPORT_SYMBOL_GPL(usb_reset_device);
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 1d253dd4ea8..db410e92c80 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -712,25 +712,11 @@ static void usbfs_add_device(struct usb_device *dev)
static void usbfs_remove_device(struct usb_device *dev)
{
- struct dev_state *ds;
- struct siginfo sinfo;
-
if (dev->usbfs_dentry) {
fs_remove_file (dev->usbfs_dentry);
dev->usbfs_dentry = NULL;
}
- while (!list_empty(&dev->filelist)) {
- ds = list_entry(dev->filelist.next, struct dev_state, list);
- wake_up_all(&ds->wait);
- list_del_init(&ds->list);
- if (ds->discsignr) {
- sinfo.si_signo = ds->discsignr;
- sinfo.si_errno = EPIPE;
- sinfo.si_code = SI_ASYNCIO;
- sinfo.si_addr = ds->disccontext;
- kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
- }
- }
+ usb_fs_classdev_common_remove(dev);
}
static int usbfs_notify(struct notifier_block *self, unsigned long action, void *dev)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 3e69266e1f4..2fcc06eb5e6 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -400,7 +400,7 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
if (usb_pipein(pipe))
urb_flags |= URB_SHORT_NOT_OK;
- for (i = 0; i < io->entries; i++) {
+ for_each_sg(sg, sg, io->entries, i) {
unsigned len;
io->urbs[i] = usb_alloc_urb(0, mem_flags);
@@ -434,17 +434,17 @@ int usb_sg_init(struct usb_sg_request *io, struct usb_device *dev,
* to prevent stale pointers and to help spot bugs.
*/
if (dma) {
- io->urbs[i]->transfer_dma = sg_dma_address(sg + i);
- len = sg_dma_len(sg + i);
+ io->urbs[i]->transfer_dma = sg_dma_address(sg);
+ len = sg_dma_len(sg);
#if defined(CONFIG_HIGHMEM) || defined(CONFIG_GART_IOMMU)
io->urbs[i]->transfer_buffer = NULL;
#else
- io->urbs[i]->transfer_buffer = sg_virt(&sg[i]);
+ io->urbs[i]->transfer_buffer = sg_virt(sg);
#endif
} else {
/* hc may use _only_ transfer_buffer */
- io->urbs[i]->transfer_buffer = sg_virt(&sg[i]);
- len = sg[i].length;
+ io->urbs[i]->transfer_buffer = sg_virt(sg);
+ len = sg->length;
}
if (length) {
@@ -1090,7 +1090,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
if (!device_is_registered(&interface->dev))
continue;
dev_dbg(&dev->dev, "unregistering interface %s\n",
- interface->dev.bus_id);
+ dev_name(&interface->dev));
device_del(&interface->dev);
usb_remove_sysfs_intf_files(interface);
}
@@ -1476,7 +1476,7 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
*
* This call is synchronous. The calling context must be able to sleep,
* must own the device lock, and must not hold the driver model's USB
- * bus mutex; usb device driver probe() methods cannot use this routine.
+ * bus mutex; usb interface driver probe() methods cannot use this routine.
*
* Returns zero on success, or else the status code returned by the
* underlying call that failed. On successful completion, each interface
@@ -1607,10 +1607,11 @@ free_interfaces:
intf->dev.driver = NULL;
intf->dev.bus = &usb_bus_type;
intf->dev.type = &usb_if_device_type;
+ intf->dev.groups = usb_interface_groups;
intf->dev.dma_mask = dev->dev.dma_mask;
device_initialize(&intf->dev);
mark_quiesced(intf);
- sprintf(&intf->dev.bus_id[0], "%d-%s:%d.%d",
+ dev_set_name(&intf->dev, "%d-%s:%d.%d",
dev->bus->busnum, dev->devpath,
configuration, alt->desc.bInterfaceNumber);
}
@@ -1630,12 +1631,12 @@ free_interfaces:
dev_dbg(&dev->dev,
"adding %s (config #%d, interface %d)\n",
- intf->dev.bus_id, configuration,
+ dev_name(&intf->dev), configuration,
intf->cur_altsetting->desc.bInterfaceNumber);
ret = device_add(&intf->dev);
if (ret != 0) {
dev_err(&dev->dev, "device_add(%s) --> %d\n",
- intf->dev.bus_id, ret);
+ dev_name(&intf->dev), ret);
continue;
}
usb_create_sysfs_intf_files(intf);
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 2e201939029..c070b34b669 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -47,6 +47,13 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Edirol SD-20 */
{ USB_DEVICE(0x0582, 0x0027), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* appletouch */
+ { USB_DEVICE(0x05ac, 0x021a), .driver_info = USB_QUIRK_RESET_RESUME },
+
+ /* Avision AV600U */
+ { USB_DEVICE(0x0638, 0x0a13), .driver_info =
+ USB_QUIRK_STRING_FETCH_255 },
+
/* M-Systems Flash Disk Pioneers */
{ USB_DEVICE(0x08ec, 0x1000), .driver_info = USB_QUIRK_RESET_RESUME },
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 5b20a60de8b..5e1f5d55bf0 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -538,6 +538,46 @@ static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
+/* When modifying this list, be sure to modify dev_string_attrs_are_visible()
+ * accordingly.
+ */
+static struct attribute *dev_string_attrs[] = {
+ &dev_attr_manufacturer.attr,
+ &dev_attr_product.attr,
+ &dev_attr_serial.attr,
+ NULL
+};
+
+static mode_t dev_string_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct usb_device *udev = to_usb_device(
+ container_of(kobj, struct device, kobj));
+
+ if (a == &dev_attr_manufacturer.attr) {
+ if (udev->manufacturer == NULL)
+ return 0;
+ } else if (a == &dev_attr_product.attr) {
+ if (udev->product == NULL)
+ return 0;
+ } else if (a == &dev_attr_serial.attr) {
+ if (udev->serial == NULL)
+ return 0;
+ }
+ return a->mode;
+}
+
+static struct attribute_group dev_string_attr_grp = {
+ .attrs = dev_string_attrs,
+ .is_visible = dev_string_attrs_are_visible,
+};
+
+struct attribute_group *usb_device_groups[] = {
+ &dev_attr_grp,
+ &dev_string_attr_grp,
+ NULL
+};
+
/* Binary descriptors */
static ssize_t
@@ -548,35 +588,33 @@ read_descriptors(struct kobject *kobj, struct bin_attribute *attr,
container_of(kobj, struct device, kobj));
size_t nleft = count;
size_t srclen, n;
+ int cfgno;
+ void *src;
- usb_lock_device(udev);
-
- /* The binary attribute begins with the device descriptor */
- srclen = sizeof(struct usb_device_descriptor);
- if (off < srclen) {
- n = min_t(size_t, nleft, srclen - off);
- memcpy(buf, off + (char *) &udev->descriptor, n);
- nleft -= n;
- buf += n;
- off = 0;
- } else {
- off -= srclen;
- }
-
- /* Then follows the raw descriptor entry for the current
- * configuration (config plus subsidiary descriptors).
+ /* The binary attribute begins with the device descriptor.
+ * Following that are the raw descriptor entries for all the
+ * configurations (config plus subsidiary descriptors).
*/
- if (udev->actconfig) {
- int cfgno = udev->actconfig - udev->config;
-
- srclen = __le16_to_cpu(udev->actconfig->desc.wTotalLength);
+ for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
+ nleft > 0; ++cfgno) {
+ if (cfgno < 0) {
+ src = &udev->descriptor;
+ srclen = sizeof(struct usb_device_descriptor);
+ } else {
+ src = udev->rawdescriptors[cfgno];
+ srclen = __le16_to_cpu(udev->config[cfgno].desc.
+ wTotalLength);
+ }
if (off < srclen) {
- n = min_t(size_t, nleft, srclen - off);
- memcpy(buf, off + udev->rawdescriptors[cfgno], n);
+ n = min(nleft, srclen - (size_t) off);
+ memcpy(buf, src + off, n);
nleft -= n;
+ buf += n;
+ off = 0;
+ } else {
+ off -= srclen;
}
}
- usb_unlock_device(udev);
return count - nleft;
}
@@ -591,10 +629,9 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
struct device *dev = &udev->dev;
int retval;
- retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
- if (retval)
- return retval;
-
+ /* Unforunately these attributes cannot be created before
+ * the uevent is broadcast.
+ */
retval = device_create_bin_file(dev, &dev_bin_attr_descriptors);
if (retval)
goto error;
@@ -607,21 +644,6 @@ int usb_create_sysfs_dev_files(struct usb_device *udev)
if (retval)
goto error;
- if (udev->manufacturer) {
- retval = device_create_file(dev, &dev_attr_manufacturer);
- if (retval)
- goto error;
- }
- if (udev->product) {
- retval = device_create_file(dev, &dev_attr_product);
- if (retval)
- goto error;
- }
- if (udev->serial) {
- retval = device_create_file(dev, &dev_attr_serial);
- if (retval)
- goto error;
- }
retval = usb_create_ep_files(dev, &udev->ep0, udev);
if (retval)
goto error;
@@ -636,13 +658,9 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev)
struct device *dev = &udev->dev;
usb_remove_ep_files(&udev->ep0);
- device_remove_file(dev, &dev_attr_manufacturer);
- device_remove_file(dev, &dev_attr_product);
- device_remove_file(dev, &dev_attr_serial);
remove_power_attributes(dev);
remove_persist_attributes(dev);
device_remove_bin_file(dev, &dev_bin_attr_descriptors);
- sysfs_remove_group(&dev->kobj, &dev_attr_grp);
}
/* Interface Accociation Descriptor fields */
@@ -688,17 +706,15 @@ static ssize_t show_interface_string(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct usb_interface *intf;
- struct usb_device *udev;
- int len;
+ char *string;
intf = to_usb_interface(dev);
- udev = interface_to_usbdev(intf);
- len = snprintf(buf, 256, "%s", intf->cur_altsetting->string);
- if (len < 0)
+ string = intf->cur_altsetting->string;
+ barrier(); /* The altsetting might change! */
+
+ if (!string)
return 0;
- buf[len] = '\n';
- buf[len+1] = 0;
- return len+1;
+ return sprintf(buf, "%s\n", string);
}
static DEVICE_ATTR(interface, S_IRUGO, show_interface_string, NULL);
@@ -727,18 +743,6 @@ static ssize_t show_modalias(struct device *dev,
}
static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
-static struct attribute *intf_assoc_attrs[] = {
- &dev_attr_iad_bFirstInterface.attr,
- &dev_attr_iad_bInterfaceCount.attr,
- &dev_attr_iad_bFunctionClass.attr,
- &dev_attr_iad_bFunctionSubClass.attr,
- &dev_attr_iad_bFunctionProtocol.attr,
- NULL,
-};
-static struct attribute_group intf_assoc_attr_grp = {
- .attrs = intf_assoc_attrs,
-};
-
static struct attribute *intf_attrs[] = {
&dev_attr_bInterfaceNumber.attr,
&dev_attr_bAlternateSetting.attr,
@@ -753,6 +757,37 @@ static struct attribute_group intf_attr_grp = {
.attrs = intf_attrs,
};
+static struct attribute *intf_assoc_attrs[] = {
+ &dev_attr_iad_bFirstInterface.attr,
+ &dev_attr_iad_bInterfaceCount.attr,
+ &dev_attr_iad_bFunctionClass.attr,
+ &dev_attr_iad_bFunctionSubClass.attr,
+ &dev_attr_iad_bFunctionProtocol.attr,
+ NULL,
+};
+
+static mode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct usb_interface *intf = to_usb_interface(
+ container_of(kobj, struct device, kobj));
+
+ if (intf->intf_assoc == NULL)
+ return 0;
+ return a->mode;
+}
+
+static struct attribute_group intf_assoc_attr_grp = {
+ .attrs = intf_assoc_attrs,
+ .is_visible = intf_assoc_attrs_are_visible,
+};
+
+struct attribute_group *usb_interface_groups[] = {
+ &intf_attr_grp,
+ &intf_assoc_attr_grp,
+ NULL
+};
+
static inline void usb_create_intf_ep_files(struct usb_interface *intf,
struct usb_device *udev)
{
@@ -777,23 +812,21 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
int usb_create_sysfs_intf_files(struct usb_interface *intf)
{
- struct device *dev = &intf->dev;
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting;
int retval;
if (intf->sysfs_files_created)
return 0;
- retval = sysfs_create_group(&dev->kobj, &intf_attr_grp);
- if (retval)
- return retval;
+ /* The interface string may be present in some altsettings
+ * and missing in others. Hence its attribute cannot be created
+ * before the uevent is broadcast.
+ */
if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
- retval = device_create_file(dev, &dev_attr_interface);
- if (intf->intf_assoc)
- retval = sysfs_create_group(&dev->kobj, &intf_assoc_attr_grp);
+ retval = device_create_file(&intf->dev, &dev_attr_interface);
usb_create_intf_ep_files(intf, udev);
intf->sysfs_files_created = 1;
return 0;
@@ -807,7 +840,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf)
return;
usb_remove_intf_ep_files(intf);
device_remove_file(dev, &dev_attr_interface);
- sysfs_remove_group(&dev->kobj, &intf_attr_grp);
- sysfs_remove_group(&intf->dev.kobj, &intf_assoc_attr_grp);
intf->sysfs_files_created = 0;
}
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 1f0db51190c..84fcaa6a21e 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -291,6 +291,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.type = &usb_device_type;
+ dev->dev.groups = usb_device_groups;
dev->dev.dma_mask = bus->controller->dma_mask;
set_dev_node(&dev->dev, dev_to_node(bus->controller));
dev->state = USB_STATE_ATTACHED;
@@ -307,7 +308,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
* by location for diagnostics, tools, driver model, etc. The
* string is a path along hub ports, from the root. Each device's
* dev->devpath will be stable until USB is re-cabled, and hubs
- * are often labeled with these port numbers. The bus_id isn't
+ * are often labeled with these port numbers. The name isn't
* as stable: bus->busnum changes easily from modprobe order,
* cardbus or pci hotplugging, and so on.
*/
@@ -315,7 +316,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
dev->devpath[0] = '0';
dev->dev.parent = bus->controller;
- sprintf(&dev->dev.bus_id[0], "usb%d", bus->busnum);
+ dev_set_name(&dev->dev, "usb%d", bus->busnum);
root_hub = 1;
} else {
/* match any labeling on the hubs; it's one-based */
@@ -327,8 +328,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
"%s.%d", parent->devpath, port1);
dev->dev.parent = &parent->dev;
- sprintf(&dev->dev.bus_id[0], "%d-%s",
- bus->busnum, dev->devpath);
+ dev_set_name(&dev->dev, "%d-%s", bus->busnum, dev->devpath);
/* hub driver sets up TT records */
}
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 1bf8ccb9c58..d9a6e16dbf8 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -29,6 +29,8 @@ extern int usb_choose_configuration(struct usb_device *udev);
extern void usb_kick_khubd(struct usb_device *dev);
extern int usb_match_device(struct usb_device *dev,
const struct usb_device_id *id);
+extern void usb_forced_unbind_intf(struct usb_interface *intf);
+extern void usb_rebind_intf(struct usb_interface *intf);
extern int usb_hub_init(void);
extern void usb_hub_cleanup(void);
@@ -130,32 +132,21 @@ static inline int is_active(const struct usb_interface *f)
/* for labeling diagnostics */
extern const char *usbcore_name;
+/* sysfs stuff */
+extern struct attribute_group *usb_device_groups[];
+extern struct attribute_group *usb_interface_groups[];
+
/* usbfs stuff */
extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
extern const struct file_operations usbfs_devices_fops;
extern const struct file_operations usbdev_file_operations;
extern void usbfs_conn_disc_event(void);
+extern void usb_fs_classdev_common_remove(struct usb_device *udev);
extern int usb_devio_init(void);
extern void usb_devio_cleanup(void);
-struct dev_state {
- struct list_head list; /* state list */
- struct usb_device *dev;
- struct file *file;
- spinlock_t lock; /* protects the async urb lists */
- struct list_head async_pending;
- struct list_head async_completed;
- wait_queue_head_t wait; /* wake up if a request completed */
- unsigned int discsignr;
- struct pid *disc_pid;
- uid_t disc_uid, disc_euid;
- void __user *disccontext;
- unsigned long ifclaimed;
- u32 secid;
-};
-
/* internal notify stuff */
extern void usb_notify_add_device(struct usb_device *udev);
extern void usb_notify_remove_device(struct usb_device *udev);