diff options
author | Anton Vorontsov <cbouatmailru@gmail.com> | 2008-07-30 02:05:23 +0400 |
---|---|---|
committer | Anton Vorontsov <cbouatmailru@gmail.com> | 2008-07-30 02:05:23 +0400 |
commit | 9fec6060d9e48ed7db0dac0e16d0f0f0e615b7f6 (patch) | |
tree | 74b41f31a08f6500ff3dfcf64ba21e2d9a8e87e5 /drivers/usb/core | |
parent | fece418418f51e92dd7e67e17c5e3fe5a28d3279 (diff) | |
parent | 6e86841d05f371b5b9b86ce76c02aaee83352298 (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.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 101 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 168 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 15 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 59 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 8 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 527 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 16 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 23 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 181 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 8 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 23 |
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); |