diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 47 | ||||
-rw-r--r-- | drivers/usb/core/config.c | 78 | ||||
-rw-r--r-- | drivers/usb/core/devices.c | 26 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 162 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 29 | ||||
-rw-r--r-- | drivers/usb/core/generic.c | 29 | ||||
-rw-r--r-- | drivers/usb/core/hcd-pci.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 126 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 14 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 650 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 38 | ||||
-rw-r--r-- | drivers/usb/core/quirks.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 109 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 105 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/usb.h | 14 |
16 files changed, 917 insertions, 543 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index f493fb1eaa27..97b09f282705 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -40,21 +40,25 @@ config USB_DEVICEFS config USB_DEVICE_CLASS bool "USB device class-devices (DEPRECATED)" depends on USB - default n + default y ---help--- Userspace access to USB devices is granted by device-nodes exported directly from the usbdev in sysfs. Old versions of the driver core and udev needed additional class devices to export device nodes. These additional devices are difficult to handle in userspace, if - information about USB interfaces must be available. One device contains - the device node, the other device contains the interface data. Both - devices are at the same level in sysfs (siblings) and one can't access - the other. The device node created directly by the usbdev is the parent - device of the interface and therefore easily accessible from the interface - event. - - This option provides backward compatibility if needed. + information about USB interfaces must be available. One device + contains the device node, the other device contains the interface + data. Both devices are at the same level in sysfs (siblings) and one + can't access the other. The device node created directly by the + usb device is the parent device of the interface and therefore + easily accessible from the interface event. + + This option provides backward compatibility for libusb device + nodes (lsusb) when usbfs is not used, and the following udev rule + doesn't exist: + SUBSYSTEM=="usb", ACTION=="add", ENV{DEVTYPE}=="usb_device", \ + NAME="bus/usb/$env{BUSNUM}/$env{DEVNUM}", MODE="0644" config USB_DYNAMIC_MINORS bool "Dynamic USB minor allocation (EXPERIMENTAL)" @@ -82,6 +86,31 @@ config USB_SUSPEND If you are unsure about this, say N here. +config USB_PERSIST + bool "USB device persistence during system suspend (DANGEROUS)" + depends on USB && PM && EXPERIMENTAL + default n + help + + If you say Y here and enable the "power/persist" attribute + for a USB device, the device's data structures will remain + persistent across system suspend, even if the USB bus loses + power. (This includes hibernation, also known as swsusp or + suspend-to-disk.) The devices will reappear as if by magic + when the system wakes up, with no need to unmount USB + filesystems, rmmod host-controller drivers, or do anything + else. + + WARNING: This option can be dangerous! + + If a USB device is replaced by another of the same type while + the system is asleep, there's a good chance the kernel won't + detect the change. Likewise if the media in a USB storage + device is replaced. When this happens it's almost certain to + cause data corruption and maybe even crash your system. + + If you are unsure, say N here. + config USB_OTG bool depends on USB && EXPERIMENTAL diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c index 2d4fd530e5e4..cb69aa1e02e8 100644 --- a/drivers/usb/core/config.c +++ b/drivers/usb/core/config.c @@ -1,4 +1,5 @@ #include <linux/usb.h> +#include <linux/usb/ch9.h> #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> @@ -49,7 +50,7 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, unsigned char *buffer0 = buffer; struct usb_endpoint_descriptor *d; struct usb_host_endpoint *endpoint; - int n, i; + int n, i, j; d = (struct usb_endpoint_descriptor *) buffer; buffer += d->bLength; @@ -84,6 +85,66 @@ static int usb_parse_endpoint(struct device *ddev, int cfgno, int inum, memcpy(&endpoint->desc, d, n); INIT_LIST_HEAD(&endpoint->urb_list); + /* Fix up bInterval values outside the legal range. Use 32 ms if no + * proper value can be guessed. */ + i = 0; /* i = min, j = max, n = default */ + j = 255; + if (usb_endpoint_xfer_int(d)) { + i = 1; + switch (to_usb_device(ddev)->speed) { + case USB_SPEED_HIGH: + /* Many device manufacturers are using full-speed + * bInterval values in high-speed interrupt endpoint + * descriptors. Try to fix those and fall back to a + * 32 ms default value otherwise. */ + n = fls(d->bInterval*8); + if (n == 0) + n = 9; /* 32 ms = 2^(9-1) uframes */ + j = 16; + break; + default: /* USB_SPEED_FULL or _LOW */ + /* For low-speed, 10 ms is the official minimum. + * But some "overclocked" devices might want faster + * polling so we'll allow it. */ + n = 32; + break; + } + } else if (usb_endpoint_xfer_isoc(d)) { + i = 1; + j = 16; + switch (to_usb_device(ddev)->speed) { + case USB_SPEED_HIGH: + n = 9; /* 32 ms = 2^(9-1) uframes */ + break; + default: /* USB_SPEED_FULL */ + n = 6; /* 32 ms = 2^(6-1) frames */ + break; + } + } + if (d->bInterval < i || d->bInterval > j) { + dev_warn(ddev, "config %d interface %d altsetting %d " + "endpoint 0x%X has an invalid bInterval %d, " + "changing to %d\n", + cfgno, inum, asnum, + d->bEndpointAddress, d->bInterval, n); + endpoint->desc.bInterval = n; + } + + /* Some buggy low-speed devices have Bulk endpoints, which is + * explicitly forbidden by the USB spec. In an attempt to make + * them usable, we will try treating them as Interrupt endpoints. + */ + if (to_usb_device(ddev)->speed == USB_SPEED_LOW && + usb_endpoint_xfer_bulk(d)) { + dev_warn(ddev, "config %d interface %d altsetting %d " + "endpoint 0x%X is Bulk; changing to Interrupt\n", + cfgno, inum, asnum, d->bEndpointAddress); + endpoint->desc.bmAttributes = USB_ENDPOINT_XFER_INT; + endpoint->desc.bInterval = 1; + if (le16_to_cpu(endpoint->desc.wMaxPacketSize) > 8) + endpoint->desc.wMaxPacketSize = cpu_to_le16(8); + } + /* Skip over any Class Specific or Vendor Specific descriptors; * find the next endpoint or interface descriptor */ endpoint->extra = buffer; @@ -234,6 +295,7 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, struct usb_descriptor_header *header; int len, retval; u8 inums[USB_MAXINTERFACES], nalts[USB_MAXINTERFACES]; + unsigned iad_num = 0; memcpy(&config->desc, buffer, USB_DT_CONFIG_SIZE); if (config->desc.bDescriptorType != USB_DT_CONFIG || @@ -311,6 +373,20 @@ static int usb_parse_configuration(struct device *ddev, int cfgidx, ++n; } + } else if (header->bDescriptorType == + USB_DT_INTERFACE_ASSOCIATION) { + if (iad_num == USB_MAXIADS) { + dev_warn(ddev, "found more Interface " + "Association Descriptors " + "than allocated for in " + "configuration %d\n", cfgno); + } else { + config->intf_assoc[iad_num] = + (struct usb_interface_assoc_descriptor + *)header; + iad_num++; + } + } else if (header->bDescriptorType == USB_DT_DEVICE || header->bDescriptorType == USB_DT_CONFIG) dev_warn(ddev, "config %d contains an unexpected " diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c index 6753ca059ee4..87c794d60aa0 100644 --- a/drivers/usb/core/devices.c +++ b/drivers/usb/core/devices.c @@ -102,6 +102,10 @@ static const char *format_config = /* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; +static const char *format_iad = +/* A: FirstIf#=dd IfCount=dd Cls=xx(sssss) Sub=xx Prot=xx */ + "A: FirstIf#=%2d IfCount=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x\n"; + static const char *format_iface = /* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ "I:%c If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; @@ -146,6 +150,7 @@ static const struct class_info clas_info[] = {USB_CLASS_STILL_IMAGE, "still"}, {USB_CLASS_CSCID, "scard"}, {USB_CLASS_CONTENT_SEC, "c-sec"}, + {USB_CLASS_VIDEO, "video"}, {-1, "unk."} /* leave as last */ }; @@ -286,6 +291,21 @@ static char *usb_dump_interface( return start; } +static char *usb_dump_iad_descriptor(char *start, char *end, + const struct usb_interface_assoc_descriptor *iad) +{ + if (start > end) + return start; + start += sprintf(start, format_iad, + iad->bFirstInterface, + iad->bInterfaceCount, + iad->bFunctionClass, + class_decode(iad->bFunctionClass), + iad->bFunctionSubClass, + iad->bFunctionProtocol); + return start; +} + /* TBD: * 0. TBDs * 1. marking active interface altsettings (code lists all, but should mark @@ -322,6 +342,12 @@ static char *usb_dump_config ( if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ return start + sprintf(start, "(null Cfg. desc.)\n"); start = usb_dump_config_descriptor(start, end, &config->desc, active); + for (i = 0; i < USB_MAXIADS; i++) { + if (config->intf_assoc[i] == NULL) + break; + start = usb_dump_iad_descriptor(start, end, + config->intf_assoc[i]); + } for (i = 0; i < config->desc.bNumInterfaces; i++) { intfc = config->intf_cache[i]; interface = config->interface[i]; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 2619986e5300..73c49362cd47 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,10 +24,19 @@ #include <linux/device.h> #include <linux/usb.h> +#include <linux/usb/quirks.h> #include <linux/workqueue.h> #include "hcd.h" #include "usb.h" +#define VERBOSE_DEBUG 0 + +#if VERBOSE_DEBUG +#define dev_vdbg dev_dbg +#else +#define dev_vdbg(dev, fmt, args...) do { } while (0) +#endif + #ifdef CONFIG_HOTPLUG /* @@ -802,18 +811,17 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg) udev->state == USB_STATE_SUSPENDED) goto done; - /* For devices that don't have a driver, we do a standard suspend. */ - if (udev->dev.driver == NULL) { + /* For devices that don't have a driver, we do a generic suspend. */ + if (udev->dev.driver) + udriver = to_usb_device_driver(udev->dev.driver); + else { udev->do_remote_wakeup = 0; - status = usb_port_suspend(udev); - goto done; + udriver = &usb_generic_driver; } - - udriver = to_usb_device_driver(udev->dev.driver); status = udriver->suspend(udev, msg); -done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + done: + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) udev->dev.power.power_state.event = msg.event; return status; @@ -825,8 +833,9 @@ static int usb_resume_device(struct usb_device *udev) struct usb_device_driver *udriver; int status = 0; - if (udev->state == USB_STATE_NOTATTACHED || - udev->state != USB_STATE_SUSPENDED) + 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. */ @@ -835,11 +844,14 @@ static int usb_resume_device(struct usb_device *udev) goto done; } + if (udev->quirks & USB_QUIRK_RESET_RESUME) + udev->reset_resume = 1; + udriver = to_usb_device_driver(udev->dev.driver); status = udriver->resume(udev); -done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + done: + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) { udev->autoresume_disabled = 0; udev->dev.power.power_state.event = PM_EVENT_ON; @@ -877,15 +889,13 @@ static int usb_suspend_interface(struct usb_interface *intf, pm_message_t msg) mark_quiesced(intf); } -done: - // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); - if (status == 0) - intf->dev.power.power_state.event = msg.event; + done: + dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); return status; } /* Caller has locked intf's usb_device's pm_mutex */ -static int usb_resume_interface(struct usb_interface *intf) +static int usb_resume_interface(struct usb_interface *intf, int reset_resume) { struct usb_driver *driver; int status = 0; @@ -905,23 +915,37 @@ static int usb_resume_interface(struct usb_interface *intf) } driver = to_usb_driver(intf->dev.driver); - if (driver->resume) { - status = driver->resume(intf); - if (status) - dev_err(&intf->dev, "%s error %d\n", - "resume", status); - else - mark_active(intf); + if (reset_resume) { + if (driver->reset_resume) { + status = driver->reset_resume(intf); + if (status) + dev_err(&intf->dev, "%s error %d\n", + "reset_resume", status); + } else { + // status = -EOPNOTSUPP; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "reset_resume", driver->name); + } } else { - dev_warn(&intf->dev, "no resume for driver %s?\n", - driver->name); - mark_active(intf); + if (driver->resume) { + status = driver->resume(intf); + if (status) + dev_err(&intf->dev, "%s error %d\n", + "resume", status); + } else { + // status = -EOPNOTSUPP; + dev_warn(&intf->dev, "no %s for driver %s?\n", + "resume", driver->name); + } } done: - // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status); if (status == 0) - intf->dev.power.power_state.event = PM_EVENT_ON; + mark_active(intf); + + /* FIXME: Unbind the driver and reprobe if the resume failed + * (not possible if auto_pm is set) */ return status; } @@ -958,6 +982,18 @@ static int autosuspend_check(struct usb_device *udev) "for autosuspend\n"); return -EOPNOTSUPP; } + + /* Don't allow autosuspend if the device will need + * a reset-resume and any of its interface drivers + * doesn't include support. + */ + if (udev->quirks & USB_QUIRK_RESET_RESUME) { + struct usb_driver *driver; + + driver = to_usb_driver(intf->dev.driver); + if (!driver->reset_resume) + return -EOPNOTSUPP; + } } } @@ -974,7 +1010,7 @@ static int autosuspend_check(struct usb_device *udev) * or for the past. */ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, - suspend_time - jiffies); + round_jiffies_relative(suspend_time - jiffies)); } return -EAGAIN; } @@ -1054,14 +1090,21 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) break; } } - if (status == 0) + if (status == 0) { + + /* Non-root devices don't need to do anything for FREEZE + * or PRETHAW. */ + if (udev->parent && (msg.event == PM_EVENT_FREEZE || + msg.event == PM_EVENT_PRETHAW)) + goto done; status = usb_suspend_device(udev, msg); + } /* If the suspend failed, resume interfaces that did get suspended */ if (status != 0) { while (--i >= 0) { intf = udev->actconfig->interface[i]; - usb_resume_interface(intf); + usb_resume_interface(intf, 0); } /* Try another autosuspend when the interfaces aren't busy */ @@ -1076,7 +1119,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) } done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); return status; } @@ -1131,7 +1174,8 @@ static int usb_resume_both(struct usb_device *udev) status = usb_autoresume_device(parent); if (status == 0) { status = usb_resume_device(udev); - if (status) { + if (status || udev->state == + USB_STATE_NOTATTACHED) { usb_autosuspend_device(parent); /* It's possible usb_resume_device() @@ -1152,28 +1196,25 @@ static int usb_resume_both(struct usb_device *udev) /* We can't progagate beyond the USB subsystem, * so if a root hub's controller is suspended * then we're stuck. */ - if (udev->dev.parent->power.power_state.event != - PM_EVENT_ON) - status = -EHOSTUNREACH; - else - status = usb_resume_device(udev); + status = usb_resume_device(udev); } } else { - /* Needed only for setting udev->dev.power.power_state.event - * and for possible debugging message. */ + /* Needed for setting udev->dev.power.power_state.event, + * for possible debugging message, and for reset_resume. */ status = usb_resume_device(udev); } if (status == 0 && udev->actconfig) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { intf = udev->actconfig->interface[i]; - usb_resume_interface(intf); + usb_resume_interface(intf, udev->reset_resume); } } done: - // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + dev_vdbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); + udev->reset_resume = 0; return status; } @@ -1240,8 +1281,8 @@ void usb_autosuspend_device(struct usb_device *udev) int status; status = usb_autopm_do_device(udev, -1); - // dev_dbg(&udev->dev, "%s: cnt %d\n", - // __FUNCTION__, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: cnt %d\n", + __FUNCTION__, udev->pm_usage_cnt); } /** @@ -1260,8 +1301,8 @@ void usb_autosuspend_device(struct usb_device *udev) void usb_try_autosuspend_device(struct usb_device *udev) { usb_autopm_do_device(udev, 0); - // dev_dbg(&udev->dev, "%s: cnt %d\n", - // __FUNCTION__, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: cnt %d\n", + __FUNCTION__, udev->pm_usage_cnt); } /** @@ -1288,8 +1329,8 @@ int usb_autoresume_device(struct usb_device *udev) int status; status = usb_autopm_do_device(udev, 1); - // dev_dbg(&udev->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, udev->pm_usage_cnt); + dev_vdbg(&udev->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, udev->pm_usage_cnt); return status; } @@ -1361,8 +1402,8 @@ void usb_autopm_put_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, -1); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); } EXPORT_SYMBOL_GPL(usb_autopm_put_interface); @@ -1405,8 +1446,8 @@ int usb_autopm_get_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, 1); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); return status; } EXPORT_SYMBOL_GPL(usb_autopm_get_interface); @@ -1427,8 +1468,8 @@ int usb_autopm_set_interface(struct usb_interface *intf) int status; status = usb_autopm_do_interface(intf, 0); - // dev_dbg(&intf->dev, "%s: status %d cnt %d\n", - // __FUNCTION__, status, intf->pm_usage_cnt); + dev_vdbg(&intf->dev, "%s: status %d cnt %d\n", + __FUNCTION__, status, intf->pm_usage_cnt); return status; } EXPORT_SYMBOL_GPL(usb_autopm_set_interface); @@ -1508,8 +1549,15 @@ static int usb_resume(struct device *dev) if (!is_usb_device(dev)) /* Ignore PM for interfaces */ return 0; udev = to_usb_device(dev); - if (udev->autoresume_disabled) - return -EPERM; + + /* If autoresume is disabled then we also want to prevent resume + * during system wakeup. However, a "persistent-device" reset-resume + * after power loss counts as a wakeup event. So allow a + * reset-resume to occur if remote wakeup is enabled. */ + if (udev->autoresume_disabled) { + if (!(udev->reset_resume && udev->do_remote_wakeup)) + return -EPERM; + } return usb_external_resume_device(udev); } diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 01c857ac27af..5d860bc9b421 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -16,15 +16,15 @@ */ #include <linux/module.h> -#include <linux/spinlock.h> #include <linux/errno.h> +#include <linux/rwsem.h> #include <linux/usb.h> #include "usb.h" #define MAX_USB_MINORS 256 static const struct file_operations *usb_minors[MAX_USB_MINORS]; -static DEFINE_SPINLOCK(minor_lock); +static DECLARE_RWSEM(minor_rwsem); static int usb_open(struct inode * inode, struct file * file) { @@ -33,14 +33,11 @@ static int usb_open(struct inode * inode, struct file * file) int err = -ENODEV; const struct file_operations *old_fops, *new_fops = NULL; - spin_lock (&minor_lock); + down_read(&minor_rwsem); c = usb_minors[minor]; - if (!c || !(new_fops = fops_get(c))) { - spin_unlock(&minor_lock); - return err; - } - spin_unlock(&minor_lock); + if (!c || !(new_fops = fops_get(c))) + goto done; old_fops = file->f_op; file->f_op = new_fops; @@ -52,6 +49,8 @@ static int usb_open(struct inode * inode, struct file * file) file->f_op = fops_get(old_fops); } fops_put(old_fops); + done: + up_read(&minor_rwsem); return err; } @@ -166,7 +165,7 @@ int usb_register_dev(struct usb_interface *intf, if (class_driver->fops == NULL) goto exit; - spin_lock (&minor_lock); + down_write(&minor_rwsem); for (minor = minor_base; minor < MAX_USB_MINORS; ++minor) { if (usb_minors[minor]) continue; @@ -176,7 +175,7 @@ int usb_register_dev(struct usb_interface *intf, retval = 0; break; } - spin_unlock (&minor_lock); + up_write(&minor_rwsem); if (retval) goto exit; @@ -197,9 +196,9 @@ int usb_register_dev(struct usb_interface *intf, intf->usb_dev = device_create(usb_class->class, &intf->dev, MKDEV(USB_MAJOR, minor), "%s", temp); if (IS_ERR(intf->usb_dev)) { - spin_lock (&minor_lock); + down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); + up_write(&minor_rwsem); retval = PTR_ERR(intf->usb_dev); } exit: @@ -236,9 +235,9 @@ void usb_deregister_dev(struct usb_interface *intf, dbg ("removing %d minor", intf->minor); - spin_lock (&minor_lock); + down_write(&minor_rwsem); usb_minors[intf->minor] = NULL; - spin_unlock (&minor_lock); + up_write(&minor_rwsem); snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base); device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor)); @@ -247,5 +246,3 @@ void usb_deregister_dev(struct usb_interface *intf, destroy_usb_class(); } EXPORT_SYMBOL(usb_deregister_dev); - - diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c index 9bbcb20e2d94..b2fc2b115256 100644 --- a/drivers/usb/core/generic.c +++ b/drivers/usb/core/generic.c @@ -19,6 +19,7 @@ #include <linux/usb.h> #include "usb.h" +#include "hcd.h" static inline const char *plural(int n) { @@ -193,16 +194,34 @@ static void generic_disconnect(struct usb_device *udev) static int generic_suspend(struct usb_device *udev, pm_message_t msg) { - /* USB devices enter SUSPEND state through their hubs, but can be - * marked for FREEZE as soon as their children are already idled. - * But those semantics are useless, so we equate the two (sigh). + int rc; + + /* Normal USB devices suspend through their upstream port. + * Root hubs don't have upstream ports to suspend, + * so we have to shut down their downstream HC-to-USB + * interfaces manually by doing a bus (or "global") suspend. */ - return usb_port_suspend(udev); + if (!udev->parent) + rc = hcd_bus_suspend(udev); + else + rc = usb_port_suspend(udev); + return rc; } static int generic_resume(struct usb_device *udev) { - return usb_port_resume(udev); + int rc; + + /* Normal USB devices resume/reset through their upstream port. + * Root hubs don't have upstream ports to resume or reset, + * so we have to start up their downstream HC-to-USB + * interfaces manually by doing a bus (or "global") resume. + */ + if (!udev->parent) + rc = hcd_bus_resume(udev); + else + rc = usb_port_resume(udev); + return rc; } #endif /* CONFIG_PM */ diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c index edf4300a3f7a..5cf6d5f9acbd 100644 --- a/drivers/usb/core/hcd-pci.c +++ b/drivers/usb/core/hcd-pci.c @@ -207,7 +207,8 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message) * We must ignore the FREEZE vs SUSPEND distinction here, because * otherwise the swsusp will save (and restore) garbage state. */ - if (hcd->self.root_hub->dev.power.power_state.event == PM_EVENT_ON) + if (!(hcd->state == HC_STATE_SUSPENDED || + hcd->state == HC_STATE_HALT)) return -EBUSY; if (hcd->driver->suspend) { diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 8969e42434b9..963520fbef90 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -582,10 +582,12 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) } /* The USB 2.0 spec says 256 ms. This is close enough and won't - * exceed that limit if HZ is 100. */ + * exceed that limit if HZ is 100. The math is more clunky than + * maybe expected, this is to make sure that all timers for USB devices + * fire at the same time to give the CPU a break inbetween */ if (hcd->uses_new_polling ? hcd->poll_rh : (length == 0 && hcd->status_urb != NULL)) - mod_timer (&hcd->rh_timer, jiffies + msecs_to_jiffies(250)); + mod_timer (&hcd->rh_timer, (jiffies/(HZ/4) + 1) * (HZ/4)); } EXPORT_SYMBOL_GPL(usb_hcd_poll_rh_status); @@ -614,8 +616,8 @@ static int rh_queue_status (struct usb_hcd *hcd, struct urb *urb) urb->hcpriv = hcd; /* indicate it's queued */ if (!hcd->uses_new_polling) - mod_timer (&hcd->rh_timer, jiffies + - msecs_to_jiffies(250)); + mod_timer (&hcd->rh_timer, + (jiffies/(HZ/4) + 1) * (HZ/4)); /* If a status change has already occurred, report it ASAP */ else if (hcd->poll_pending) @@ -901,17 +903,32 @@ EXPORT_SYMBOL (usb_calc_bus_time); /*-------------------------------------------------------------------------*/ -static void urb_unlink (struct urb *urb) +static void urb_unlink(struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; + int at_root_hub = (urb->dev == hcd->self.root_hub); /* clear all state linking urb to this dev (and hcd) */ - spin_lock_irqsave (&hcd_data_lock, flags); list_del_init (&urb->urb_list); spin_unlock_irqrestore (&hcd_data_lock, flags); -} + if (hcd->self.uses_dma && !at_root_hub) { + if (usb_pipecontrol (urb->pipe) + && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) + dma_unmap_single (hcd->self.controller, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + DMA_TO_DEVICE); + if (urb->transfer_buffer_length != 0 + && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) + dma_unmap_single (hcd->self.controller, + urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? DMA_FROM_DEVICE + : DMA_TO_DEVICE); + } +} /* may be called in any context with a valid urb->dev usecount * caller surrenders "ownership" of urb @@ -948,19 +965,9 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) else switch (hcd->state) { case HC_STATE_RUNNING: case HC_STATE_RESUMING: -doit: list_add_tail (&urb->urb_list, &ep->urb_list); status = 0; break; - case HC_STATE_SUSPENDED: - /* HC upstream links (register access, wakeup signaling) can work - * even when the downstream links (and DMA etc) are quiesced; let - * usbcore talk to the root hub. - */ - if (hcd->self.controller->power.power_state.event == PM_EVENT_ON - && urb->dev->parent == NULL) - goto doit; - /* FALL THROUGH */ default: status = -ESHUTDOWN; break; @@ -1014,7 +1021,7 @@ doit: status = hcd->driver->urb_enqueue (hcd, ep, urb, mem_flags); done: if (unlikely (status)) { - urb_unlink (urb); + urb_unlink(hcd, urb); atomic_dec (&urb->use_count); if (urb->reject) wake_up (&usb_kill_urb_queue); @@ -1255,42 +1262,59 @@ rescan: #ifdef CONFIG_PM -int hcd_bus_suspend (struct usb_bus *bus) +int hcd_bus_suspend(struct usb_device *rhdev) { - struct usb_hcd *hcd; - int status; + struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + int status; + int old_state = hcd->state; - hcd = container_of (bus, struct usb_hcd, self); - if (!hcd->driver->bus_suspend) - return -ENOENT; - hcd->state = HC_STATE_QUIESCING; - status = hcd->driver->bus_suspend (hcd); - if (status == 0) + dev_dbg(&rhdev->dev, "bus %s%s\n", + rhdev->auto_pm ? "auto-" : "", "suspend"); + if (!hcd->driver->bus_suspend) { + status = -ENOENT; + } else { + hcd->state = HC_STATE_QUIESCING; + status = hcd->driver->bus_suspend(hcd); + } + if (status == 0) { + usb_set_device_state(rhdev, USB_STATE_SUSPENDED); hcd->state = HC_STATE_SUSPENDED; - else - dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n", + } else { + hcd->state = old_state; + dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "suspend", status); + } return status; } -int hcd_bus_resume (struct usb_bus *bus) +int hcd_bus_resume(struct usb_device *rhdev) { - struct usb_hcd *hcd; - int status; + struct usb_hcd *hcd = container_of(rhdev->bus, struct usb_hcd, self); + int status; + int old_state = hcd->state; - hcd = container_of (bus, struct usb_hcd, self); + dev_dbg(&rhdev->dev, "usb %s%s\n", + rhdev->auto_pm ? "auto-" : "", "resume"); if (!hcd->driver->bus_resume) return -ENOENT; if (hcd->state == HC_STATE_RUNNING) return 0; + hcd->state = HC_STATE_RESUMING; - status = hcd->driver->bus_resume (hcd); - if (status == 0) + status = hcd->driver->bus_resume(hcd); + if (status == 0) { + /* TRSMRCY = 10 msec */ + msleep(10); + usb_set_device_state(rhdev, rhdev->actconfig + ? USB_STATE_CONFIGURED + : USB_STATE_ADDRESS); hcd->state = HC_STATE_RUNNING; - else { - dev_dbg(&bus->root_hub->dev, "%s fail, err %d\n", + } else { + hcd->state = old_state; + dev_dbg(&rhdev->dev, "bus %s fail, err %d\n", "resume", status); - usb_hc_died(hcd); + if (status != -ESHUTDOWN) + usb_hc_died(hcd); } return status; } @@ -1384,30 +1408,10 @@ EXPORT_SYMBOL (usb_bus_start_enum); */ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) { - int at_root_hub; - - at_root_hub = (urb->dev == hcd->self.root_hub); - urb_unlink (urb); - - /* lower level hcd code should use *_dma exclusively if the - * host controller does DMA */ - if (hcd->self.uses_dma && !at_root_hub) { - if (usb_pipecontrol (urb->pipe) - && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) - dma_unmap_single (hcd->self.controller, urb->setup_dma, - sizeof (struct usb_ctrlrequest), - DMA_TO_DEVICE); - if (urb->transfer_buffer_length != 0 - && !(urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)) - dma_unmap_single (hcd->self.controller, - urb->transfer_dma, - urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? DMA_FROM_DEVICE - : DMA_TO_DEVICE); - } - + urb_unlink(hcd, urb); usbmon_urb_complete (&hcd->self, urb); + usb_unanchor_urb(urb); + /* pass ownership to the completion handler */ urb->complete (urb); atomic_dec (&urb->use_count); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index ef50fa494e47..b5ebb73c2332 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -364,23 +364,13 @@ extern int usb_find_interface_driver (struct usb_device *dev, #ifdef CONFIG_PM extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd); extern void usb_root_hub_lost_power (struct usb_device *rhdev); -extern int hcd_bus_suspend (struct usb_bus *bus); -extern int hcd_bus_resume (struct usb_bus *bus); +extern int hcd_bus_suspend(struct usb_device *rhdev); +extern int hcd_bus_resume(struct usb_device *rhdev); #else static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd) { return; } - -static inline int hcd_bus_suspend(struct usb_bus *bus) -{ - return 0; -} - -static inline int hcd_bus_resume (struct usb_bus *bus) -{ - return 0; -} #endif /* CONFIG_PM */ /* diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24f10a19dbdb..50e79010401c 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -31,9 +31,16 @@ #include "hcd.h" #include "hub.h" +#ifdef CONFIG_USB_PERSIST +#define USB_PERSIST 1 +#else +#define USB_PERSIST 0 +#endif + struct usb_hub { struct device *intfdev; /* the "interface" device */ struct usb_device *hdev; + struct kref kref; struct urb *urb; /* for interrupt polling pipe */ /* buffer for urb ... with extra space in case of babble */ @@ -66,6 +73,7 @@ struct usb_hub { unsigned limited_power:1; unsigned quiescing:1; unsigned activating:1; + unsigned disconnected:1; unsigned has_indicators:1; u8 indicator[USB_MAXCHILDREN]; @@ -321,7 +329,7 @@ static void kick_khubd(struct usb_hub *hub) to_usb_interface(hub->intfdev)->pm_usage_cnt = 1; spin_lock_irqsave(&hub_event_lock, flags); - if (list_empty(&hub->event_list)) { + if (!hub->disconnected & list_empty(&hub->event_list)) { list_add_tail(&hub->event_list, &hub_event_list); wake_up(&khubd_wait); } @@ -330,6 +338,7 @@ static void kick_khubd(struct usb_hub *hub) void usb_kick_khubd(struct usb_device *hdev) { + /* FIXME: What if hdev isn't bound to the hub driver? */ kick_khubd(hdev_to_hub(hdev)); } @@ -400,9 +409,10 @@ static void hub_tt_kevent (struct work_struct *work) struct usb_hub *hub = container_of(work, struct usb_hub, tt.kevent); unsigned long flags; + int limit = 100; spin_lock_irqsave (&hub->tt.lock, flags); - while (!list_empty (&hub->tt.clear_list)) { + while (--limit && !list_empty (&hub->tt.clear_list)) { struct list_head *temp; struct usb_tt_clear *clear; struct usb_device *hdev = hub->hdev; @@ -550,48 +560,68 @@ static int hub_hub_status(struct usb_hub *hub, static int hub_port_disable(struct usb_hub *hub, int port1, int set_state) { struct usb_device *hdev = hub->hdev; - int ret; + int ret = 0; - if (hdev->children[port1-1] && set_state) { + if (hdev->children[port1-1] && set_state) usb_set_device_state(hdev->children[port1-1], USB_STATE_NOTATTACHED); - } - ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); + if (!hub->error) + ret = clear_port_feature(hdev, port1, USB_PORT_FEAT_ENABLE); if (ret) dev_err(hub->intfdev, "cannot disable port %d (err = %d)\n", - port1, ret); - + port1, ret); return ret; } +/* + * Disable a port and mark a logical connnect-change event, so that some + * time later khubd will disconnect() any existing usb_device on the port + * and will re-enumerate if there actually is a device attached. + */ +static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) +{ + dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); + hub_port_disable(hub, port1, 1); + + /* FIXME let caller ask to power down the port: + * - some devices won't enumerate without a VBUS power cycle + * - SRP saves power that way + * - ... new call, TBD ... + * That's easy if this hub can switch power per-port, and + * khubd reactivates the port later (timer, SRP, etc). + * Powerdown must be optional, because of reset/DFU. + */ + + set_bit(port1, hub->change_bits); + kick_khubd(hub); +} /* caller has locked the hub device */ -static void hub_pre_reset(struct usb_interface *intf) +static int hub_pre_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = hub->hdev; - int port1; + int i; - for (port1 = 1; port1 <= hdev->maxchild; ++port1) { - if (hdev->children[port1 - 1]) { - usb_disconnect(&hdev->children[port1 - 1]); - if (hub->error == 0) - hub_port_disable(hub, port1, 0); - } + /* Disconnect all the children */ + for (i = 0; i < hdev->maxchild; ++i) { + if (hdev->children[i]) + usb_disconnect(&hdev->children[i]); } hub_quiesce(hub); + return 0; } /* caller has locked the hub device */ -static void hub_post_reset(struct usb_interface *intf) +static int hub_post_reset(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata(intf); - hub_activate(hub); hub_power_on(hub); + hub_activate(hub); + return 0; } - static int hub_configure(struct usb_hub *hub, struct usb_endpoint_descriptor *endpoint) { @@ -845,43 +875,42 @@ fail: return ret; } +static void hub_release(struct kref *kref) +{ + struct usb_hub *hub = container_of(kref, struct usb_hub, kref); + + usb_put_intf(to_usb_interface(hub->intfdev)); + kfree(hub); +} + static unsigned highspeed_hubs; static void hub_disconnect(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); - struct usb_device *hdev; + + /* Take the hub off the event list and don't let it be added again */ + spin_lock_irq(&hub_event_lock); + list_del_init(&hub->event_list); + hub->disconnected = 1; + spin_unlock_irq(&hub_event_lock); /* Disconnect all children and quiesce the hub */ hub->error = 0; hub_pre_reset(intf); usb_set_intfdata (intf, NULL); - hdev = hub->hdev; - if (hdev->speed == USB_SPEED_HIGH) + if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; usb_free_urb(hub->urb); - hub->urb = NULL; - - spin_lock_irq(&hub_event_lock); - list_del_init(&hub->event_list); - spin_unlock_irq(&hub_event_lock); - kfree(hub->descriptor); - hub->descriptor = NULL; - kfree(hub->status); - hub->status = NULL; - - if (hub->buffer) { - usb_buffer_free(hdev, sizeof(*hub->buffer), hub->buffer, - hub->buffer_dma); - hub->buffer = NULL; - } + usb_buffer_free(hub->hdev, sizeof(*hub->buffer), hub->buffer, + hub->buffer_dma); - kfree(hub); + kref_put(&hub->kref, hub_release); } static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -929,10 +958,12 @@ descriptor_error: return -ENOMEM; } + kref_init(&hub->kref); INIT_LIST_HEAD(&hub->event_list); hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); + usb_get_intf(intf); usb_set_intfdata (intf, hub); intf->needs_remote_wakeup = 1; @@ -982,49 +1013,6 @@ hub_ioctl(struct usb_interface *intf, unsigned int code, void *user_data) } -/* grab device/port lock, returning index of that port (zero based). - * protects the upstream link used by this device from concurrent - * tree operations like suspend, resume, reset, and disconnect, which - * apply to everything downstream of a given port. - */ -static int locktree(struct usb_device *udev) -{ - int t; - struct usb_device *hdev; - - if (!udev) - return -ENODEV; - - /* root hub is always the first lock in the series */ - hdev = udev->parent; - if (!hdev) { - usb_lock_device(udev); - return 0; - } - - /* on the path from root to us, lock everything from - * top down, dropping parent locks when not needed - */ - t = locktree(hdev); - if (t < 0) - return t; - - /* everything is fail-fast once disconnect - * processing starts - */ - if (udev->state == USB_STATE_NOTATTACHED) { - usb_unlock_device(hdev); - return -ENODEV; - } - - /* when everyone grabs locks top->bottom, - * non-overlapping work may be concurrent - */ - usb_lock_device(udev); - usb_unlock_device(hdev); - return udev->portnum; -} - static void recursively_mark_NOTATTACHED(struct usb_device *udev) { int i; @@ -1089,46 +1077,6 @@ void usb_set_device_state(struct usb_device *udev, spin_unlock_irqrestore(&device_state_lock, flags); } - -#ifdef CONFIG_PM - -/** - * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power - * @rhdev: struct usb_device for the root hub - * - * The USB host controller driver calls this function when its root hub - * is resumed and Vbus power has been interrupted or the controller - * has been reset. The routine marks all the children of the root hub - * as NOTATTACHED and marks logical connect-change events on their ports. - */ -void usb_root_hub_lost_power(struct usb_device *rhdev) -{ - struct usb_hub *hub; - int port1; - unsigned long flags; - - dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); - - /* Make sure no potential wakeup events get lost, - * by forcing the root hub to be resumed. - */ - rhdev->dev.power.prev_state.event = PM_EVENT_ON; - - spin_lock_irqsave(&device_state_lock, flags); - hub = hdev_to_hub(rhdev); - for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { - if (rhdev->children[port1 - 1]) { - recursively_mark_NOTATTACHED( - rhdev->children[port1 - 1]); - set_bit(port1, hub->change_bits); - } - } - spin_unlock_irqrestore(&device_state_lock, flags); -} -EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); - -#endif /* CONFIG_PM */ - static void choose_address(struct usb_device *udev) { int devnum; @@ -1269,7 +1217,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string) #ifdef CONFIG_USB_OTG #include "otg_whitelist.h" -static int __usb_port_suspend(struct usb_device *, int port1); #endif /** @@ -1375,11 +1322,11 @@ int usb_new_device(struct usb_device *udev) * (Includes HNP test device.) */ if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { - err = __usb_port_suspend(udev, udev->bus->otg_port); + err = usb_port_suspend(udev); if (err < 0) dev_dbg(&udev->dev, "HNP fail, %d\n", err); } - err = -ENODEV; + err = -ENOTSUPP; goto fail; } #endif @@ -1476,9 +1423,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, if (!(portstatus & USB_PORT_STAT_CONNECTION)) return -ENOTCONN; - /* bomb out completely if something weird happened */ + /* bomb out completely if the connection bounced */ if ((portchange & USB_PORT_STAT_C_CONNECTION)) - return -EINVAL; + return -ENOTCONN; /* if we`ve finished resetting, then break out of the loop */ if (!(portstatus & USB_PORT_STAT_RESET) && @@ -1557,34 +1504,24 @@ static int hub_port_reset(struct usb_hub *hub, int port1, return status; } -/* - * Disable a port and mark a logical connnect-change event, so that some - * time later khubd will disconnect() any existing usb_device on the port - * and will re-enumerate if there actually is a device attached. - */ -static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) -{ - dev_dbg(hub->intfdev, "logical disconnect on port %d\n", port1); - hub_port_disable(hub, port1, 1); - - /* FIXME let caller ask to power down the port: - * - some devices won't enumerate without a VBUS power cycle - * - SRP saves power that way - * - ... new call, TBD ... - * That's easy if this hub can switch power per-port, and - * khubd reactivates the port later (timer, SRP, etc). - * Powerdown must be optional, because of reset/DFU. - */ - - set_bit(port1, hub->change_bits); - kick_khubd(hub); -} - #ifdef CONFIG_PM #ifdef CONFIG_USB_SUSPEND /* + * usb_port_suspend - suspend a usb device's upstream port + * @udev: device that's no longer in active use, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * Suspends a USB device that isn't in active use, conserving power. + * Devices may wake out of a suspend, if anything important happens, + * using the remote wakeup mechanism. They may also be taken out of + * suspend by the host, using usb_port_resume(). It's also routine + * to disconnect devices while they are suspended. + * + * This only affects the USB hardware for a device; its interfaces + * (and, for hubs, child devices) must already have been suspended. + * * Selective port suspend reduces power; most suspended devices draw * less than 500 uA. It's also used in OTG, along with remote wakeup. * All devices below the suspended port are also suspended. @@ -1593,11 +1530,35 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) * also support "remote wakeup", where the device can activate the USB * tree above them to deliver data, such as a keypress or packet. In * some cases, this wakes the USB host. + * + * Suspending OTG devices may trigger HNP, if that's been enabled + * between a pair of dual-role devices. That will change roles, such + * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. + * + * Devices on USB hub ports have only one "suspend" state, corresponding + * to ACPI D2, "may cause the device to lose some context". + * State transitions include: + * + * - suspend, resume ... when the VBUS power link stays live + * - suspend, disconnect ... VBUS lost + * + * Once VBUS drop breaks the circuit, the port it's using has to go through + * normal re-enumeration procedures, starting with enabling VBUS power. + * Other than re-initializing the hub (plug/unplug, except for root hubs), + * Linux (2.6) currently has NO mechanisms to initiate that: no khubd + * timer, no SRP, no requests through sysfs. + * + * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when + * the root hub for their bus goes into global suspend ... so we don't + * (falsely) update the device power state to say it suspended. + * + * Returns 0 on success, else negative errno. */ -static int hub_port_suspend(struct usb_hub *hub, int port1, - struct usb_device *udev) +int usb_port_suspend(struct usb_device *udev) { - int status; + struct usb_hub *hub = hdev_to_hub(udev->parent); + int port1 = udev->portnum; + int status; // dev_dbg(hub->intfdev, "suspend port %d\n", port1); @@ -1614,17 +1575,15 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, NULL, 0, USB_CTRL_SET_TIMEOUT); if (status) - dev_dbg(&udev->dev, - "won't remote wakeup, status %d\n", - status); + dev_dbg(&udev->dev, "won't remote wakeup, status %d\n", + status); } /* see 7.1.7.6 */ status = set_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't suspend port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't suspend port %d, status %d\n", + port1, status); /* paranoia: "should not happen" */ (void) usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, USB_RECIP_DEVICE, @@ -1642,85 +1601,24 @@ static int hub_port_suspend(struct usb_hub *hub, int port1, } /* - * Devices on USB hub ports have only one "suspend" state, corresponding - * to ACPI D2, "may cause the device to lose some context". - * State transitions include: - * - * - suspend, resume ... when the VBUS power link stays live - * - suspend, disconnect ... VBUS lost - * - * Once VBUS drop breaks the circuit, the port it's using has to go through - * normal re-enumeration procedures, starting with enabling VBUS power. - * Other than re-initializing the hub (plug/unplug, except for root hubs), - * Linux (2.6) currently has NO mechanisms to initiate that: no khubd - * timer, no SRP, no requests through sysfs. - * - * If CONFIG_USB_SUSPEND isn't enabled, devices only really suspend when - * the root hub for their bus goes into global suspend ... so we don't - * (falsely) update the device power state to say it suspended. - */ -static int __usb_port_suspend (struct usb_device *udev, int port1) -{ - int status = 0; - - /* caller owns the udev device lock */ - if (port1 < 0) - return port1; - - /* we change the device's upstream USB link, - * but root hubs have no upstream USB link. - */ - if (udev->parent) - status = hub_port_suspend(hdev_to_hub(udev->parent), port1, - udev); - else { - dev_dbg(&udev->dev, "usb %ssuspend\n", - udev->auto_pm ? "auto-" : ""); - usb_set_device_state(udev, USB_STATE_SUSPENDED); - } - return status; -} - -/* - * usb_port_suspend - suspend a usb device's upstream port - * @udev: device that's no longer in active use - * Context: must be able to sleep; device not locked; pm locks held - * - * Suspends a USB device that isn't in active use, conserving power. - * Devices may wake out of a suspend, if anything important happens, - * using the remote wakeup mechanism. They may also be taken out of - * suspend by the host, using usb_port_resume(). It's also routine - * to disconnect devices while they are suspended. - * - * This only affects the USB hardware for a device; its interfaces - * (and, for hubs, child devices) must already have been suspended. - * - * Suspending OTG devices may trigger HNP, if that's been enabled - * between a pair of dual-role devices. That will change roles, such - * as from A-Host to A-Peripheral or from B-Host back to B-Peripheral. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_suspend(struct usb_device *udev) -{ - return __usb_port_suspend(udev, udev->portnum); -} - -/* * If the USB "suspend" state is in use (rather than "global suspend"), * many devices will be individually taken out of suspend state using - * special" resume" signaling. These routines kick in shortly after + * special "resume" signaling. This routine kicks in shortly after * hardware resume signaling is finished, either because of selective * resume (by host) or remote wakeup (by device) ... now see what changed * in the tree that's rooted at this device. + * + * If @udev->reset_resume is set then the device is reset before the + * status check is done. */ static int finish_port_resume(struct usb_device *udev) { - int status; + int status = 0; u16 devstatus; /* caller owns the udev device lock */ - dev_dbg(&udev->dev, "finish resume\n"); + dev_dbg(&udev->dev, "finish %sresume\n", + udev->reset_resume ? "reset-" : ""); /* usb ch9 identifies four variants of SUSPENDED, based on what * state the device resumes to. Linux currently won't see the @@ -1731,22 +1629,30 @@ static int finish_port_resume(struct usb_device *udev) ? USB_STATE_CONFIGURED : USB_STATE_ADDRESS); + /* 10.5.4.5 says not to reset a suspended port if the attached + * device is enabled for remote wakeup. Hence the reset + * operation is carried out here, after the port has been + * resumed. + */ + if (udev->reset_resume) + status = usb_reset_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, * and device drivers will know about any resume quirks. */ - status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); - if (status >= 0) - status = (status == 2 ? 0 : -ENODEV); + if (status == 0) { + status = usb_get_status(udev, USB_RECIP_DEVICE, 0, &devstatus); + if (status >= 0) + status = (status == 2 ? 0 : -ENODEV); + } - if (status) - dev_dbg(&udev->dev, - "gone after usb resume? status %d\n", - status); - else if (udev->actconfig) { + if (status) { + dev_dbg(&udev->dev, "gone after usb resume? status %d\n", + status); + } else if (udev->actconfig) { le16_to_cpus(&devstatus); - if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) - && udev->parent) { + if (devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP)) { status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), USB_REQ_CLEAR_FEATURE, @@ -1759,19 +1665,52 @@ static int finish_port_resume(struct usb_device *udev) "wakeup, status %d\n", status); } status = 0; - - } else if (udev->devnum <= 0) { - dev_dbg(&udev->dev, "bogus resume!\n"); - status = -EINVAL; } return status; } -static int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) +/* + * usb_port_resume - re-activate a suspended usb device's upstream port + * @udev: device to re-activate, not a root hub + * Context: must be able to sleep; device not locked; pm locks held + * + * This will re-activate the suspended device, increasing power usage + * while letting drivers communicate again with its endpoints. + * USB resume explicitly guarantees that the power session between + * the host and the device is the same as it was when the device + * suspended. + * + * If CONFIG_USB_PERSIST and @udev->reset_resume are both set then this + * routine won't check that the port is still enabled. Furthermore, + * if @udev->reset_resume is set then finish_port_resume() above will + * reset @udev. The end result is that a broken power session can be + * recovered and @udev will appear to persist across a loss of VBUS power. + * + * For example, if a host controller doesn't maintain VBUS suspend current + * during a system sleep or is reset when the system wakes up, all the USB + * power sessions below it will be broken. This is especially troublesome + * for mass-storage devices containing mounted filesystems, since the + * device will appear to have disconnected and all the memory mappings + * 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 is inherently dangerous. Although usb_reset_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 + * changed. If the user replaces a flash memory card while the system is + * asleep, he will have only himself to blame when the filesystem on the + * new card is corrupted and the system crashes. + * + * Returns 0 on success, else negative errno. + */ +int usb_port_resume(struct usb_device *udev) { - int status; - u16 portchange, portstatus; + struct usb_hub *hub = hdev_to_hub(udev->parent); + 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); @@ -1786,30 +1725,31 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) status = clear_port_feature(hub->hdev, port1, USB_PORT_FEAT_SUSPEND); if (status) { - dev_dbg(hub->intfdev, - "can't resume port %d, status %d\n", - port1, status); + dev_dbg(hub->intfdev, "can't resume port %d, status %d\n", + port1, status); } else { /* drive resume for at least 20 msec */ - if (udev) - dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + dev_dbg(&udev->dev, "usb %sresume\n", + udev->auto_pm ? "auto-" : ""); msleep(25); -#define LIVE_FLAGS ( USB_PORT_STAT_POWER \ - | USB_PORT_STAT_ENABLE \ - | USB_PORT_STAT_CONNECTION) - /* Virtual root hubs can trigger on GET_PORT_STATUS to * stop resume signaling. Then finish the resume * sequence. */ status = hub_port_status(hub, port1, &portstatus, &portchange); -SuspendCleared: - if (status < 0 - || (portstatus & LIVE_FLAGS) != LIVE_FLAGS - || (portstatus & USB_PORT_STAT_SUSPEND) != 0 - ) { + + SuspendCleared: + if (USB_PERSIST && 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; + + 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); @@ -1821,51 +1761,19 @@ SuspendCleared: USB_PORT_FEAT_C_SUSPEND); /* TRSMRCY = 10 msec */ msleep(10); - if (udev) - status = finish_port_resume(udev); } } - if (status < 0) - hub_port_logical_disconnect(hub, port1); clear_bit(port1, hub->busy_bits); if (!hub->hdev->parent && !hub->busy_bits[0]) usb_enable_root_hub_irq(hub->hdev->bus); - return status; -} - -/* - * usb_port_resume - re-activate a suspended usb device's upstream port - * @udev: device to re-activate - * Context: must be able to sleep; device not locked; pm locks held - * - * This will re-activate the suspended device, increasing power usage - * while letting drivers communicate again with its endpoints. - * USB resume explicitly guarantees that the power session between - * the host and the device is the same as it was when the device - * suspended. - * - * Returns 0 on success, else negative errno. - */ -int usb_port_resume(struct usb_device *udev) -{ - int status; - - /* we change the device's upstream USB link, - * but root hubs have no upstream USB link. - */ - if (udev->parent) { - // NOTE this fails if parent is also suspended... - status = hub_port_resume(hdev_to_hub(udev->parent), - udev->portnum, udev); - } else { - dev_dbg(&udev->dev, "usb %sresume\n", - udev->auto_pm ? "auto-" : ""); + if (status == 0) status = finish_port_resume(udev); - } - if (status < 0) + if (status < 0) { dev_dbg(&udev->dev, "can't resume, status %d\n", status); + hub_port_logical_disconnect(hub, port1); + } return status; } @@ -1892,21 +1800,16 @@ int usb_port_suspend(struct usb_device *udev) return 0; } -static inline int -finish_port_resume(struct usb_device *udev) -{ - return 0; -} - -static inline int -hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev) -{ - return 0; -} - int usb_port_resume(struct usb_device *udev) { - return 0; + int status = 0; + + /* However we may need to do a reset-resume */ + if (udev->reset_resume) { + dev_dbg(&udev->dev, "reset-resume\n"); + status = usb_reset_device(udev); + } + return status; } static inline int remote_wakeup(struct usb_device *udev) @@ -1921,7 +1824,6 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) struct usb_hub *hub = usb_get_intfdata (intf); struct usb_device *hdev = hub->hdev; unsigned port1; - int status = 0; /* fail if children aren't already suspended */ for (port1 = 1; port1 <= hdev->maxchild; port1++) { @@ -1947,49 +1849,75 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) /* stop khubd and related activity */ hub_quiesce(hub); - - /* "global suspend" of the downstream HC-to-USB interface */ - if (!hdev->parent) { - status = hcd_bus_suspend(hdev->bus); - if (status != 0) { - dev_dbg(&hdev->dev, "'global' suspend %d\n", status); - hub_activate(hub); - } - } - return status; + return 0; } static int hub_resume(struct usb_interface *intf) { struct usb_hub *hub = usb_get_intfdata (intf); - struct usb_device *hdev = hub->hdev; - int status; dev_dbg(&intf->dev, "%s\n", __FUNCTION__); - /* "global resume" of the downstream HC-to-USB interface */ - if (!hdev->parent) { - struct usb_bus *bus = hdev->bus; - if (bus) { - status = hcd_bus_resume (bus); - if (status) { - dev_dbg(&intf->dev, "'global' resume %d\n", - status); - return status; + /* tell khubd to look for changes on this hub */ + hub_activate(hub); + return 0; +} + +static int hub_reset_resume(struct usb_interface *intf) +{ + struct usb_hub *hub = usb_get_intfdata(intf); + struct usb_device *hdev = hub->hdev; + int port1; + + hub_power_on(hub); + + for (port1 = 1; port1 <= hdev->maxchild; ++port1) { + struct usb_device *child = hdev->children[port1-1]; + + if (child) { + + /* For "USB_PERSIST"-enabled children we must + * mark the child device for reset-resume and + * turn off the connect-change status to prevent + * khubd from disconnecting it later. + */ + if (USB_PERSIST && child->persist_enabled) { + child->reset_resume = 1; + clear_port_feature(hdev, port1, + USB_PORT_FEAT_C_CONNECTION); + + /* Otherwise we must disconnect the child, + * but as we may not lock the child device here + * we have to do a "logical" disconnect. + */ + } else { + hub_port_logical_disconnect(hub, port1); } - } else - return -EOPNOTSUPP; - if (status == 0) { - /* TRSMRCY = 10 msec */ - msleep(10); } } - /* tell khubd to look for changes on this hub */ hub_activate(hub); return 0; } +/** + * usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power + * @rhdev: struct usb_device for the root hub + * + * The USB host controller driver calls this function when its root hub + * is resumed and Vbus power has been interrupted or the controller + * has been reset. The routine marks @rhdev as having lost power. When + * the hub driver is resumed it will take notice; if CONFIG_USB_PERSIST + * is enabled then it will carry out power-session recovery, otherwise + * it will disconnect all the child devices. + */ +void usb_root_hub_lost_power(struct usb_device *rhdev) +{ + dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); + rhdev->reset_resume = 1; +} +EXPORT_SYMBOL_GPL(usb_root_hub_lost_power); + #else /* CONFIG_PM */ static inline int remote_wakeup(struct usb_device *udev) @@ -1997,8 +1925,9 @@ static inline int remote_wakeup(struct usb_device *udev) return 0; } -#define hub_suspend NULL -#define hub_resume NULL +#define hub_suspend NULL +#define hub_resume NULL +#define hub_reset_resume NULL #endif @@ -2461,19 +2390,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1, return; } -#ifdef CONFIG_USB_SUSPEND - /* If something is connected, but the port is suspended, wake it up. */ - if (portstatus & USB_PORT_STAT_SUSPEND) { - status = hub_port_resume(hub, port1, NULL); - if (status < 0) { - dev_dbg(hub_dev, - "can't clear suspend on port %d; %d\n", - port1, status); - goto done; - } - } -#endif - for (i = 0; i < SET_CONFIG_TRIES; i++) { struct usb_device *udev; @@ -2584,7 +2500,7 @@ loop: ep0_reinit(udev); release_address(udev); usb_put_dev(udev); - if (status == -ENOTCONN) + if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; } @@ -2625,10 +2541,12 @@ static void hub_events(void) list_del_init(tmp); hub = list_entry(tmp, struct usb_hub, event_list); - hdev = hub->hdev; - intf = to_usb_interface(hub->intfdev); - hub_dev = &intf->dev; + kref_get(&hub->kref); + spin_unlock_irq(&hub_event_lock); + hdev = hub->hdev; + hub_dev = hub->intfdev; + intf = to_usb_interface(hub_dev); dev_dbg(hub_dev, "state %d ports %d chg %04x evt %04x\n", hdev->state, hub->descriptor ? hub->descriptor->bNbrPorts @@ -2637,16 +2555,10 @@ static void hub_events(void) (u16) hub->change_bits[0], (u16) hub->event_bits[0]); - usb_get_intf(intf); - spin_unlock_irq(&hub_event_lock); - /* Lock the device, then check to see if we were * disconnected while waiting for the lock to succeed. */ - if (locktree(hdev) < 0) { - usb_put_intf(intf); - continue; - } - if (hub != usb_get_intfdata(intf)) + usb_lock_device(hdev); + if (unlikely(hub->disconnected)) goto loop; /* If the hub has died, clean up after it */ @@ -2809,7 +2721,7 @@ loop_autopm: usb_autopm_enable(intf); loop: usb_unlock_device(hdev); - usb_put_intf(intf); + kref_put(&hub->kref, hub_release); } /* end while (1) */ } @@ -2844,6 +2756,7 @@ static struct usb_driver hub_driver = { .disconnect = hub_disconnect, .suspend = hub_suspend, .resume = hub_resume, + .reset_resume = hub_reset_resume, .pre_reset = hub_pre_reset, .post_reset = hub_post_reset, .ioctl = hub_ioctl, @@ -2946,6 +2859,11 @@ static int config_descriptors_changed(struct usb_device *udev) * 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(). + * + * Locking exception: This routine may also be called from within an + * autoresume handler. Such usage won't conflict with other tasks + * 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) { @@ -2976,7 +2894,7 @@ int usb_reset_device(struct usb_device *udev) * Other endpoints will be handled by re-enumeration. */ ep0_reinit(udev); ret = hub_port_init(parent_hub, udev, port1, i); - if (ret >= 0) + if (ret >= 0 || ret == -ENOTCONN || ret == -ENODEV) break; } clear_bit(port1, parent_hub->busy_bits); @@ -3092,6 +3010,7 @@ int usb_reset_composite_device(struct usb_device *udev, 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 */ } } } @@ -3110,6 +3029,7 @@ int usb_reset_composite_device(struct usb_device *udev, 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 */ } if (cintf != iface) up(&cintf->dev.sem); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index f9fed34bf7d8..530e854961ce 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -404,8 +404,6 @@ int usb_sg_init ( io->urbs [i]->complete = sg_complete; io->urbs [i]->context = io; - io->urbs [i]->status = -EINPROGRESS; - io->urbs [i]->actual_length = 0; /* * Some systems need to revert to PIO when DMA is temporarily @@ -499,7 +497,8 @@ void usb_sg_wait (struct usb_sg_request *io) /* queue the urbs. */ spin_lock_irq (&io->lock); - for (i = 0; i < entries && !io->status; i++) { + i = 0; + while (i < entries && !io->status) { int retval; io->urbs [i]->dev = io->dev; @@ -516,7 +515,6 @@ void usb_sg_wait (struct usb_sg_request *io) case -ENOMEM: io->urbs[i]->dev = NULL; retval = 0; - i--; yield (); break; @@ -527,6 +525,7 @@ void usb_sg_wait (struct usb_sg_request *io) * URBs are queued at once; N milliseconds? */ case 0: + ++i; cpu_relax (); break; @@ -1385,6 +1384,36 @@ struct device_type usb_if_device_type = { .uevent = usb_if_uevent, }; +static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev, + struct usb_host_config *config, + u8 inum) +{ + struct usb_interface_assoc_descriptor *retval = NULL; + struct usb_interface_assoc_descriptor *intf_assoc; + int first_intf; + int last_intf; + int i; + + for (i = 0; (i < USB_MAXIADS && config->intf_assoc[i]); i++) { + intf_assoc = config->intf_assoc[i]; + if (intf_assoc->bInterfaceCount == 0) + continue; + + first_intf = intf_assoc->bFirstInterface; + last_intf = first_intf + (intf_assoc->bInterfaceCount - 1); + if (inum >= first_intf && inum <= last_intf) { + if (!retval) + retval = intf_assoc; + else + dev_err(&dev->dev, "Interface #%d referenced" + " by multiple IADs\n", inum); + } + } + + return retval; +} + + /* * usb_set_configuration - Makes a particular device setting be current * @dev: the device whose configuration is being updated @@ -1531,6 +1560,7 @@ free_interfaces: intfc = cp->intf_cache[i]; intf->altsetting = intfc->altsetting; intf->num_altsetting = intfc->num_altsetting; + intf->intf_assoc = find_iad(dev, cp, i); kref_get(&intfc->ref); alt = usb_altnum_to_altsetting(intf, 0); diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c index 739f520908aa..aa21b38a31ce 100644 --- a/drivers/usb/core/quirks.c +++ b/drivers/usb/core/quirks.c @@ -30,10 +30,28 @@ static const struct usb_device_id usb_quirk_list[] = { /* HP 5300/5370C scanner */ { USB_DEVICE(0x03f0, 0x0701), .driver_info = USB_QUIRK_STRING_FETCH_255 }, + /* Benq S2W 3300U */ + { USB_DEVICE(0x04a5, 0x20b0), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Seiko Epson Corp. Perfection 1200 */ + { USB_DEVICE(0x04b8, 0x0104), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, /* Seiko Epson Corp - Perfection 1670 */ { USB_DEVICE(0x04b8, 0x011f), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Samsung ML-2510 Series printer */ + { USB_DEVICE(0x04e8, 0x327e), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, /* Elsa MicroLink 56k (V.250) */ { USB_DEVICE(0x05cc, 0x2267), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Ultima Electronics Corp.*/ + { USB_DEVICE(0x05d8, 0x4005), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + /* Umax [hex] Astra 3400U */ + { USB_DEVICE(0x1606, 0x0060), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + + /* Philips PSC805 audio device */ + { USB_DEVICE(0x0471, 0x0155), .driver_info = USB_QUIRK_RESET_RESUME }, + + /* RIM Blackberry */ + { USB_DEVICE(0x0fca, 0x0001), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + { USB_DEVICE(0x0fca, 0x0004), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, + { USB_DEVICE(0x0fca, 0x0006), .driver_info = USB_QUIRK_NO_AUTOSUSPEND }, { } /* terminating entry must be last */ }; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index be37c863fdfb..d47ae89154a7 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -169,6 +169,73 @@ show_quirks(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL); + +#if defined(CONFIG_USB_PERSIST) || defined(CONFIG_USB_SUSPEND) +static const char power_group[] = "power"; +#endif + +#ifdef CONFIG_USB_PERSIST + +static ssize_t +show_persist(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_device *udev = to_usb_device(dev); + + return sprintf(buf, "%d\n", udev->persist_enabled); +} + +static ssize_t +set_persist(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_device *udev = to_usb_device(dev); + int value; + + /* Hubs are always enabled for USB_PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) + return -EPERM; + + if (sscanf(buf, "%d", &value) != 1) + return -EINVAL; + usb_pm_lock(udev); + udev->persist_enabled = !!value; + usb_pm_unlock(udev); + return count; +} + +static DEVICE_ATTR(persist, S_IRUGO | S_IWUSR, show_persist, set_persist); + +static int add_persist_attributes(struct device *dev) +{ + int rc = 0; + + if (is_usb_device(dev)) { + struct usb_device *udev = to_usb_device(dev); + + /* Hubs are automatically enabled for USB_PERSIST */ + if (udev->descriptor.bDeviceClass == USB_CLASS_HUB) + udev->persist_enabled = 1; + rc = sysfs_add_file_to_group(&dev->kobj, + &dev_attr_persist.attr, + power_group); + } + return rc; +} + +static void remove_persist_attributes(struct device *dev) +{ + sysfs_remove_file_from_group(&dev->kobj, + &dev_attr_persist.attr, + power_group); +} + +#else + +#define add_persist_attributes(dev) 0 +#define remove_persist_attributes(dev) do {} while (0) + +#endif /* CONFIG_USB_PERSIST */ + #ifdef CONFIG_USB_SUSPEND static ssize_t @@ -276,8 +343,6 @@ set_level(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(level, S_IRUGO | S_IWUSR, show_level, set_level); -static char power_group[] = "power"; - static int add_power_attributes(struct device *dev) { int rc = 0; @@ -311,6 +376,7 @@ static void remove_power_attributes(struct device *dev) #endif /* CONFIG_USB_SUSPEND */ + /* Descriptor fields */ #define usb_descriptor_attr_le16(field, format_string) \ static ssize_t \ @@ -384,6 +450,10 @@ int usb_create_sysfs_dev_files(struct usb_device *udev) if (retval) return retval; + retval = add_persist_attributes(dev); + if (retval) + goto error; + retval = add_power_attributes(dev); if (retval) goto error; @@ -421,9 +491,29 @@ void usb_remove_sysfs_dev_files(struct usb_device *udev) device_remove_file(dev, &dev_attr_product); device_remove_file(dev, &dev_attr_serial); remove_power_attributes(dev); + remove_persist_attributes(dev); sysfs_remove_group(&dev->kobj, &dev_attr_grp); } +/* Interface Accociation Descriptor fields */ +#define usb_intf_assoc_attr(field, format_string) \ +static ssize_t \ +show_iad_##field (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct usb_interface *intf = to_usb_interface (dev); \ + \ + return sprintf (buf, format_string, \ + intf->intf_assoc->field); \ +} \ +static DEVICE_ATTR(iad_##field, S_IRUGO, show_iad_##field, NULL); + +usb_intf_assoc_attr (bFirstInterface, "%02x\n") +usb_intf_assoc_attr (bInterfaceCount, "%02d\n") +usb_intf_assoc_attr (bFunctionClass, "%02x\n") +usb_intf_assoc_attr (bFunctionSubClass, "%02x\n") +usb_intf_assoc_attr (bFunctionProtocol, "%02x\n") + /* Interface fields */ #define usb_intf_attr(field, format_string) \ static ssize_t \ @@ -487,6 +577,18 @@ 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, @@ -538,6 +640,8 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) 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); usb_create_intf_ep_files(intf, udev); return 0; } @@ -549,4 +653,5 @@ void usb_remove_sysfs_intf_files(struct usb_interface *intf) 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); } diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 94ea9727ff55..52ec44b828f3 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -4,6 +4,7 @@ #include <linux/slab.h> #include <linux/init.h> #include <linux/usb.h> +#include <linux/wait.h> #include "hcd.h" #define to_urb(d) container_of(d, struct urb, kref) @@ -11,6 +12,10 @@ static void urb_destroy(struct kref *kref) { struct urb *urb = to_urb(kref); + + if (urb->transfer_flags & URB_FREE_BUFFER) + kfree(urb->transfer_buffer); + kfree(urb); } @@ -34,6 +39,7 @@ void usb_init_urb(struct urb *urb) memset(urb, 0, sizeof(*urb)); kref_init(&urb->kref); spin_lock_init(&urb->lock); + INIT_LIST_HEAD(&urb->anchor_list); } } @@ -100,8 +106,60 @@ struct urb * usb_get_urb(struct urb *urb) kref_get(&urb->kref); return urb; } - - + +/** + * usb_anchor_urb - anchors an URB while it is processed + * @urb: pointer to the urb to anchor + * @anchor: pointer to the anchor + * + * This can be called to have access to URBs which are to be executed + * without bothering to track them + */ +void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) +{ + unsigned long flags; + + spin_lock_irqsave(&anchor->lock, flags); + usb_get_urb(urb); + list_add_tail(&urb->anchor_list, &anchor->urb_list); + urb->anchor = anchor; + spin_unlock_irqrestore(&anchor->lock, flags); +} +EXPORT_SYMBOL_GPL(usb_anchor_urb); + +/** + * usb_unanchor_urb - unanchors an URB + * @urb: pointer to the urb to anchor + * + * Call this to stop the system keeping track of this URB + */ +void usb_unanchor_urb(struct urb *urb) +{ + unsigned long flags; + struct usb_anchor *anchor; + + if (!urb) + return; + + anchor = urb->anchor; + if (!anchor) + return; + + spin_lock_irqsave(&anchor->lock, flags); + if (unlikely(anchor != urb->anchor)) { + /* we've lost the race to another thread */ + spin_unlock_irqrestore(&anchor->lock, flags); + return; + } + urb->anchor = NULL; + list_del(&urb->anchor_list); + spin_unlock_irqrestore(&anchor->lock, flags); + usb_put_urb(urb); + if (list_empty(&anchor->urb_list)) + wake_up(&anchor->wait); +} +EXPORT_SYMBOL_GPL(usb_unanchor_urb); + /*-------------------------------------------------------------------*/ /** @@ -478,6 +536,48 @@ void usb_kill_urb(struct urb *urb) spin_unlock_irq(&urb->lock); } +/** + * usb_kill_anchored_urbs - cancel transfer requests en masse + * @anchor: anchor the requests are bound to + * + * this allows all outstanding URBs to be killed starting + * from the back of the queue + */ +void usb_kill_anchored_urbs(struct usb_anchor *anchor) +{ + struct urb *victim; + + spin_lock_irq(&anchor->lock); + while (!list_empty(&anchor->urb_list)) { + victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list); + /* we must make sure the URB isn't freed before we kill it*/ + usb_get_urb(victim); + spin_unlock_irq(&anchor->lock); + /* this will unanchor the URB */ + usb_kill_urb(victim); + usb_put_urb(victim); + spin_lock_irq(&anchor->lock); + } + spin_unlock_irq(&anchor->lock); +} +EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs); + +/** + * usb_wait_anchor_empty_timeout - wait for an anchor to be unused + * @anchor: the anchor you want to become unused + * @timeout: how long you are willing to wait in milliseconds + * + * Call this is you want to be sure all an anchor's + * URBs have finished + */ +int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, + unsigned int timeout) +{ + return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), + msecs_to_jiffies(timeout)); +} +EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); + EXPORT_SYMBOL(usb_init_urb); EXPORT_SYMBOL(usb_alloc_urb); EXPORT_SYMBOL(usb_free_urb); @@ -485,4 +585,3 @@ EXPORT_SYMBOL(usb_get_urb); EXPORT_SYMBOL(usb_submit_urb); EXPORT_SYMBOL(usb_unlink_urb); EXPORT_SYMBOL(usb_kill_urb); - diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 4a6299bd0047..0fee5c66fd64 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -253,6 +253,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1) dev->dev.bus = &usb_bus_type; dev->dev.type = &usb_device_type; dev->dev.dma_mask = bus->controller->dma_mask; + set_dev_node(&dev->dev, dev_to_node(bus->controller)); dev->state = USB_STATE_ATTACHED; INIT_LIST_HEAD(&dev->ep0.urb_list); @@ -578,11 +579,12 @@ int __usb_get_extra_descriptor(char *buffer, unsigned size, * address (through the pointer provided). * * These buffers are used with URB_NO_xxx_DMA_MAP set in urb->transfer_flags - * to avoid behaviors like using "DMA bounce buffers", or tying down I/O - * mapping hardware for long idle periods. The implementation varies between + * to avoid behaviors like using "DMA bounce buffers", or thrashing IOMMU + * hardware during URB completion/resubmit. The implementation varies between * platforms, depending on details of how DMA will work to this device. - * Using these buffers also helps prevent cacheline sharing problems on - * architectures where CPU caches are not DMA-coherent. + * Using these buffers also eliminates cacheline sharing problems on + * architectures where CPU caches are not DMA-coherent. On systems without + * bus-snooping caches, these buffers are uncached. * * When the buffer is no longer used, free it with usb_buffer_free(). */ @@ -607,7 +609,7 @@ void *usb_buffer_alloc( * * This reclaims an I/O buffer, letting it be reused. The memory must have * been allocated using usb_buffer_alloc(), and the parameters must match - * those provided in that allocation request. + * those provided in that allocation request. */ void usb_buffer_free( struct usb_device *dev, diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index bf2eb0dae2ec..ad5fa0338f49 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h @@ -52,8 +52,16 @@ static inline void usb_pm_unlock(struct usb_device *udev) #else -#define usb_port_suspend(dev) 0 -#define usb_port_resume(dev) 0 +static inline int usb_port_suspend(struct usb_device *udev) +{ + return 0; +} + +static inline int usb_port_resume(struct usb_device *udev) +{ + return 0; +} + static inline void usb_pm_lock(struct usb_device *udev) {} static inline void usb_pm_unlock(struct usb_device *udev) {} @@ -100,11 +108,13 @@ static inline int is_usb_device_driver(struct device_driver *drv) static inline void mark_active(struct usb_interface *f) { f->is_active = 1; + f->dev.power.power_state.event = PM_EVENT_ON; } static inline void mark_quiesced(struct usb_interface *f) { f->is_active = 0; + f->dev.power.power_state.event = PM_EVENT_SUSPEND; } static inline int is_active(const struct usb_interface *f) |