diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/devices.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 94 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 168 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 10 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 6 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 4 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 536 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 16 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 22 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 7 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 19 |
12 files changed, 541 insertions, 349 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 83d9dc379d96..6ec38175a817 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 9218cca21043..54a350ccd033 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); } @@ -575,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; @@ -609,8 +625,10 @@ 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; @@ -874,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) @@ -1682,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(usb_classdev_class, &dev->dev, dev->dev.devt, + "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, diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 1e56f1cfa6dc..ddb54e14a5c5 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 fae55a31e26d..22912136fc14 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -296,7 +296,7 @@ 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; } @@ -307,7 +307,7 @@ int usb_create_ep_files(struct device *parent, 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); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index c6a95395e52a..913fb8667899 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -150,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 @@ -190,9 +190,9 @@ 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; @@ -227,7 +227,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; @@ -242,7 +242,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/hcd.c b/drivers/usb/core/hcd.c index 42a436478b78..f7bfd72ef115 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -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); @@ -1764,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; diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index b9de1569b39e..5b0b59b0d89b 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. */ @@ -235,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 4cfe32a16c37..107e1d25ddec 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,136 +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) +enum hub_activation_type { + HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME +}; + +static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) { struct usb_device *hdev = hub->hdev; - int i; + int port1; + int status; + bool need_debounce_delay = false; - /* Disconnect all the children */ - for (i = 0; i < hdev->maxchild; ++i) { - if (hdev->children[i]) - usb_disconnect(&hdev->children[i]); - } - hub_quiesce(hub); -} + /* 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); -#define HUB_RESET 1 -#define HUB_RESUME 2 -#define HUB_RESET_RESUME 3 + /* 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]; + u16 portstatus, portchange; -#ifdef CONFIG_PM + 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); -/* Try to identify which devices need USB-PERSIST handling */ -static int persistent_device(struct usb_device *udev) -{ - int i; - int retval; - struct usb_host_config *actconfig; + /* 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; + } - /* Explicitly not marked persistent? */ - if (!udev->persist_enabled) - return 0; + /* 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); + } - /* No active config? */ - actconfig = udev->actconfig; - if (!actconfig) - return 0; + 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); - /* FIXME! We should check whether it's open here or not! */ + } else if (udev->persist_enabled) { +#ifdef CONFIG_PM + udev->reset_resume = 1; +#endif + set_bit(port1, hub->change_bits); - /* - * Check that all the interface drivers have a - * 'reset_resume' entrypoint + } else { + /* The power session is gone; tell khubd */ + usb_set_device_state(udev, USB_STATE_NOTATTACHED); + set_bit(port1, hub->change_bits); + } + } + + /* 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. */ - retval = 0; - for (i = 0; i < actconfig->desc.bNumInterfaces; i++) { - struct usb_interface *intf; - struct usb_driver *driver; + if (need_debounce_delay) + msleep(HUB_DEBOUNCE_STABLE); - intf = actconfig->interface[i]; - if (!intf->dev.driver) - continue; - driver = to_usb_driver(intf->dev.driver); - if (!driver->reset_resume) - return 0; - /* - * We have at least one driver, and that one - * has a reset_resume method. - */ - retval = 1; - } - return retval; -} + hub->quiescing = 0; -static void hub_restart(struct usb_hub *hub, int type) -{ - struct usb_device *hdev = hub->hdev; - int port1; + 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); - /* 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. - */ - for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - struct usb_device *udev = hdev->children[port1-1]; - int status = 0; - u16 portstatus, portchange; + /* Scan all ports that need attention */ + kick_khubd(hub); +} - 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; - } +enum hub_quiescing_type { + HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND +}; - /* Was the power session lost while we were suspended? */ - status = hub_port_status(hub, port1, &portstatus, &portchange); +static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) +{ + struct usb_device *hdev = hub->hdev; + int i; - /* If the device is gone, khubd will handle it later */ - if (status == 0 && !(portstatus & USB_PORT_STAT_CONNECTION)) - continue; + /* khubd and related activity won't re-trigger */ + hub->quiescing = 1; - /* 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 (status == 0 && !(portstatus & USB_PORT_STAT_ENABLE) && - persistent_device(udev)) { - 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); - udev->reset_resume = 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]); } - - /* 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); } - hub_activate(hub); + /* 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); } -#endif /* CONFIG_PM */ - /* 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; } @@ -762,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; } @@ -1009,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: @@ -1042,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); @@ -1068,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"); @@ -1814,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 /* @@ -1943,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, @@ -1954,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) { @@ -2002,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 @@ -2018,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); @@ -2047,35 +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) { @@ -2085,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; } @@ -2108,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; - /* However we may need to do a reset-resume */ - if (udev->reset_resume) { + status = hub_port_status(hub, port1, &portstatus, &portchange); + status = check_port_resume_type(udev, + hub, port1, status, portchange, portstatus); + + 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; } @@ -2149,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; } @@ -2158,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; } @@ -2167,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; } @@ -2218,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; @@ -2302,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. */ @@ -2619,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 */ @@ -2629,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)); @@ -2640,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) */ @@ -2677,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 @@ -2858,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; } @@ -2877,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); @@ -2894,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, @@ -2902,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); @@ -2941,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 { @@ -3002,8 +3081,6 @@ 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]) @@ -3172,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 @@ -3201,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; @@ -3289,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; @@ -3324,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 1d253dd4ea81..db410e92c80d 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 fe47d145255a..2fcc06eb5e60 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 @@ -1611,7 +1611,7 @@ free_interfaces: 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); } @@ -1631,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/usb.c b/drivers/usb/core/usb.c index 325774375837..84fcaa6a21ec 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -308,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. */ @@ -316,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 */ @@ -328,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 1a8bc21c335e..d9a6e16dbf84 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); @@ -140,26 +142,11 @@ 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); |