diff options
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/driver.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/endpoint.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/file.c | 5 | ||||
-rw-r--r-- | drivers/usb/core/hcd.c | 4 | ||||
-rw-r--r-- | drivers/usb/core/hcd.h | 6 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 102 | ||||
-rw-r--r-- | drivers/usb/core/inode.c | 18 | ||||
-rw-r--r-- | drivers/usb/core/message.c | 3 | ||||
-rw-r--r-- | drivers/usb/core/sysfs.c | 24 | ||||
-rw-r--r-- | drivers/usb/core/urb.c | 169 |
12 files changed, 314 insertions, 37 deletions
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig index cc9f397e839..e1759d17ac5 100644 --- a/drivers/usb/core/Kconfig +++ b/drivers/usb/core/Kconfig @@ -134,5 +134,5 @@ config USB_OTG_BLACKLIST_HUB If you say Y here, then Linux will refuse to enumerate external hubs. OTG hosts are allowed to reduce hardware and software costs by not supporting external hubs. So - are "Emedded Hosts" that don't offer OTG support. + are "Embedded Hosts" that don't offer OTG support. diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 7a4fa791dc1..2bccefebff1 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c @@ -413,7 +413,8 @@ static void driver_disconnect(struct usb_interface *intf) if (likely(ifnum < 8*sizeof(ps->ifclaimed))) clear_bit(ifnum, &ps->ifclaimed); else - warn("interface number %u out of range", ifnum); + dev_warn(&intf->dev, "interface number %u out of range\n", + ifnum); usb_set_intfdata(intf, NULL); @@ -624,6 +625,8 @@ static int usbdev_open(struct inode *inode, struct file *file) smp_wmb(); list_add_tail(&ps->list, &dev->filelist); file->private_data = ps; + snoop(&dev->dev, "opened by process %d: %s\n", task_pid_nr(current), + current->comm); out: if (ret) { kfree(ps); @@ -1774,19 +1777,20 @@ int __init usb_devio_init(void) retval = register_chrdev_region(USB_DEVICE_DEV, USB_DEVICE_MAX, "usb_device"); if (retval) { - err("unable to register minors for usb_device"); + printk(KERN_ERR "Unable to register minors for usb_device\n"); goto out; } cdev_init(&usb_device_cdev, &usbdev_file_operations); retval = cdev_add(&usb_device_cdev, USB_DEVICE_DEV, USB_DEVICE_MAX); if (retval) { - err("unable to get usb_device major %d", USB_DEVICE_MAJOR); + printk(KERN_ERR "Unable to get usb_device major %d\n", + USB_DEVICE_MAJOR); goto error_cdev; } #ifdef CONFIG_USB_DEVICE_CLASS usb_classdev_class = class_create(THIS_MODULE, "usb_device"); if (IS_ERR(usb_classdev_class)) { - err("unable to register usb_device class"); + printk(KERN_ERR "Unable to register usb_device class\n"); retval = PTR_ERR(usb_classdev_class); cdev_del(&usb_device_cdev); usb_classdev_class = NULL; diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 5a7fa6f0995..e935be7eb46 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1070,7 +1070,8 @@ static int autosuspend_check(struct usb_device *udev, int reschedule) struct usb_driver *driver; driver = to_usb_driver(intf->dev.driver); - if (!driver->reset_resume) + if (!driver->reset_resume || + intf->needs_remote_wakeup) return -EOPNOTSUPP; } } diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c index 22912136fc1..946fae43d62 100644 --- a/drivers/usb/core/endpoint.c +++ b/drivers/usb/core/endpoint.c @@ -169,7 +169,8 @@ static int usb_endpoint_major_init(void) error = alloc_chrdev_region(&dev, 0, MAX_ENDPOINT_MINORS, "usb_endpoint"); if (error) { - err("unable to get a dynamic major for usb endpoints"); + printk(KERN_ERR "Unable to get a dynamic major for " + "usb endpoints.\n"); return error; } usb_endpoint_major = MAJOR(dev); diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c index 55f7f310924..997e659ff69 100644 --- a/drivers/usb/core/file.c +++ b/drivers/usb/core/file.c @@ -86,7 +86,7 @@ static int init_usb_class(void) usb_class->class = class_create(THIS_MODULE, "usb"); if (IS_ERR(usb_class->class)) { result = IS_ERR(usb_class->class); - err("class_create failed for usb devices"); + printk(KERN_ERR "class_create failed for usb devices\n"); kfree(usb_class); usb_class = NULL; } @@ -115,7 +115,8 @@ int usb_major_init(void) error = register_chrdev(USB_MAJOR, "usb", &usb_fops); if (error) - err("unable to get major %d for usb devices", USB_MAJOR); + printk(KERN_ERR "Unable to get major %d for usb devices\n", + USB_MAJOR); return error; } diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index c8035a8216b..fc9018e72a0 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -81,6 +81,10 @@ /*-------------------------------------------------------------------------*/ +/* Keep track of which host controller drivers are loaded */ +unsigned long usb_hcds_loaded; +EXPORT_SYMBOL_GPL(usb_hcds_loaded); + /* host controllers we manage */ LIST_HEAD (usb_bus_list); EXPORT_SYMBOL_GPL (usb_bus_list); diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index e710ce04e22..2dcde61c465 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -482,4 +482,10 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, */ extern struct rw_semaphore ehci_cf_port_reset_rwsem; +/* Keep track of which host controller drivers are loaded */ +#define USB_UHCI_LOADED 0 +#define USB_OHCI_LOADED 1 +#define USB_EHCI_LOADED 2 +extern unsigned long usb_hcds_loaded; + #endif /* __KERNEL__ */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index d99963873e3..d73ce262c36 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -77,6 +77,7 @@ struct usb_hub { unsigned has_indicators:1; u8 indicator[USB_MAXCHILDREN]; struct delayed_work leds; + struct delayed_work init_work; }; @@ -100,6 +101,15 @@ module_param (blinkenlights, bool, S_IRUGO); MODULE_PARM_DESC (blinkenlights, "true to cycle leds on hubs"); /* + * Device SATA8000 FW1.0 from DATAST0R Technology Corp requires about + * 10 seconds to send reply for the initial 64-byte descriptor request. + */ +/* define initial 64-byte descriptor request timeout in milliseconds */ +static int initial_descriptor_timeout = USB_CTRL_GET_TIMEOUT; +module_param(initial_descriptor_timeout, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(initial_descriptor_timeout, "initial 64-byte descriptor request timeout in milliseconds (default 5000 - 5.0 seconds)"); + +/* * As of 2.6.10 we introduce a new USB device initialization scheme which * closely resembles the way Windows works. Hopefully it will be compatible * with a wider range of devices than the old scheme. However some previously @@ -515,10 +525,14 @@ void usb_hub_tt_clear_buffer (struct usb_device *udev, int pipe) } EXPORT_SYMBOL_GPL(usb_hub_tt_clear_buffer); -static void hub_power_on(struct usb_hub *hub) +/* If do_delay is false, return the number of milliseconds the caller + * needs to delay. + */ +static unsigned hub_power_on(struct usb_hub *hub, bool do_delay) { int port1; unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2; + unsigned delay; u16 wHubCharacteristics = le16_to_cpu(hub->descriptor->wHubCharacteristics); @@ -537,7 +551,10 @@ static void hub_power_on(struct usb_hub *hub) set_port_feature(hub->hdev, port1, USB_PORT_FEAT_POWER); /* Wait at least 100 msec for power to become stable */ - msleep(max(pgood_delay, (unsigned) 100)); + delay = max(pgood_delay, (unsigned) 100); + if (do_delay) + msleep(delay); + return delay; } static int hub_hub_status(struct usb_hub *hub, @@ -599,21 +616,55 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1) } enum hub_activation_type { - HUB_INIT, HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME + HUB_INIT, HUB_INIT2, HUB_INIT3, + HUB_POST_RESET, HUB_RESUME, HUB_RESET_RESUME, }; +static void hub_init_func2(struct work_struct *ws); +static void hub_init_func3(struct work_struct *ws); + static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) { struct usb_device *hdev = hub->hdev; int port1; int status; bool need_debounce_delay = false; + unsigned delay; + + /* Continue a partial initialization */ + if (type == HUB_INIT2) + goto init2; + if (type == HUB_INIT3) + goto init3; /* 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); + if (type != HUB_RESUME) { + + /* Speed up system boot by using a delayed_work for the + * hub's initial power-up delays. This is pretty awkward + * and the implementation looks like a home-brewed sort of + * setjmp/longjmp, but it saves at least 100 ms for each + * root hub (assuming usbcore is compiled into the kernel + * rather than as a module). It adds up. + * + * This can't be done for HUB_RESUME or HUB_RESET_RESUME + * because for those activation types the ports have to be + * operational when we return. In theory this could be done + * for HUB_POST_RESET, but it's easier not to. + */ + if (type == HUB_INIT) { + delay = hub_power_on(hub, false); + PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func2); + schedule_delayed_work(&hub->init_work, + msecs_to_jiffies(delay)); + return; /* Continues at init2: below */ + } else { + hub_power_on(hub, true); + } + } + init2: /* Check each port and set hub->change_bits to let khubd know * which ports need attention. @@ -692,9 +743,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) * If any port-status changes do occur during this delay, khubd * will see them later and handle them normally. */ - if (need_debounce_delay) - msleep(HUB_DEBOUNCE_STABLE); - + if (need_debounce_delay) { + delay = HUB_DEBOUNCE_STABLE; + + /* Don't do a long sleep inside a workqueue routine */ + if (type == HUB_INIT2) { + PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3); + schedule_delayed_work(&hub->init_work, + msecs_to_jiffies(delay)); + return; /* Continues at init3: below */ + } else { + msleep(delay); + } + } + init3: hub->quiescing = 0; status = usb_submit_urb(hub->urb, GFP_NOIO); @@ -707,6 +769,21 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) kick_khubd(hub); } +/* Implement the continuations for the delays above */ +static void hub_init_func2(struct work_struct *ws) +{ + struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); + + hub_activate(hub, HUB_INIT2); +} + +static void hub_init_func3(struct work_struct *ws) +{ + struct usb_hub *hub = container_of(ws, struct usb_hub, init_work.work); + + hub_activate(hub, HUB_INIT3); +} + enum hub_quiescing_type { HUB_DISCONNECT, HUB_PRE_RESET, HUB_SUSPEND }; @@ -716,6 +793,8 @@ static void hub_quiesce(struct usb_hub *hub, enum hub_quiescing_type type) struct usb_device *hdev = hub->hdev; int i; + cancel_delayed_work_sync(&hub->init_work); + /* khubd and related activity won't re-trigger */ hub->quiescing = 1; @@ -1099,6 +1178,7 @@ descriptor_error: hub->intfdev = &intf->dev; hub->hdev = hdev; INIT_DELAYED_WORK(&hub->leds, led_work); + INIT_DELAYED_WORK(&hub->init_work, NULL); usb_get_intf(intf); usb_set_intfdata (intf, hub); @@ -2467,7 +2547,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, USB_DT_DEVICE << 8, 0, buf, GET_DESCRIPTOR_BUFSIZE, - USB_CTRL_GET_TIMEOUT); + initial_descriptor_timeout); switch (buf->bMaxPacketSize0) { case 8: case 16: case 32: case 64: case 255: if (buf->bDescriptorType == @@ -3035,7 +3115,7 @@ static void hub_events(void) i); clear_port_feature(hdev, i, USB_PORT_FEAT_C_OVER_CURRENT); - hub_power_on(hub); + hub_power_on(hub, true); } if (portchange & USB_PORT_STAT_C_RESET) { @@ -3070,7 +3150,7 @@ static void hub_events(void) dev_dbg (hub_dev, "overcurrent change\n"); msleep(500); /* Cool down */ clear_hub_feature(hdev, C_HUB_OVER_CURRENT); - hub_power_on(hub); + hub_power_on(hub, true); } } diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c index 77fa7a08080..94632264dcc 100644 --- a/drivers/usb/core/inode.c +++ b/drivers/usb/core/inode.c @@ -180,8 +180,8 @@ static int parse_options(struct super_block *s, char *data) listmode = option & S_IRWXUGO; break; default: - err("usbfs: unrecognised mount option \"%s\" " - "or missing value\n", p); + printk(KERN_ERR "usbfs: unrecognised mount option " + "\"%s\" or missing value\n", p); return -EINVAL; } } @@ -240,7 +240,9 @@ static void update_sb(struct super_block *sb) update_special(bus); break; default: - warn("Unknown node %s mode %x found on remount!\n",bus->d_name.name,bus->d_inode->i_mode); + printk(KERN_WARNING "usbfs: Unknown node %s " + "mode %x found on remount!\n", + bus->d_name.name, bus->d_inode->i_mode); break; } } @@ -259,7 +261,7 @@ static int remount(struct super_block *sb, int *flags, char *data) return 0; if (parse_options(sb, data)) { - warn("usbfs: mount parameter error:"); + printk(KERN_WARNING "usbfs: mount parameter error.\n"); return -EINVAL; } @@ -599,7 +601,7 @@ static int create_special_files (void) /* create the devices special file */ retval = simple_pin_fs(&usb_fs_type, &usbfs_mount, &usbfs_mount_count); if (retval) { - err ("Unable to get usbfs mount"); + printk(KERN_ERR "Unable to get usbfs mount\n"); goto exit; } @@ -611,7 +613,7 @@ static int create_special_files (void) NULL, &usbfs_devices_fops, listuid, listgid); if (devices_usbfs_dentry == NULL) { - err ("Unable to create devices usbfs file"); + printk(KERN_ERR "Unable to create devices usbfs file\n"); retval = -ENODEV; goto error_clean_mounts; } @@ -663,7 +665,7 @@ static void usbfs_add_bus(struct usb_bus *bus) bus->usbfs_dentry = fs_create_file (name, busmode | S_IFDIR, parent, bus, NULL, busuid, busgid); if (bus->usbfs_dentry == NULL) { - err ("error creating usbfs bus entry"); + printk(KERN_ERR "Error creating usbfs bus entry\n"); return; } } @@ -694,7 +696,7 @@ static void usbfs_add_device(struct usb_device *dev) &usbdev_file_operations, devuid, devgid); if (dev->usbfs_dentry == NULL) { - err ("error creating usbfs device entry"); + printk(KERN_ERR "Error creating usbfs device entry\n"); return; } diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 286b4431a09..887738577b2 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1204,7 +1204,8 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate) alt = usb_altnum_to_altsetting(iface, alternate); if (!alt) { - warn("selecting invalid altsetting %d", alternate); + dev_warn(&dev->dev, "selecting invalid altsetting %d", + alternate); return -EINVAL; } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 5e1f5d55bf0..f66fba11fbd 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -743,6 +743,29 @@ static ssize_t show_modalias(struct device *dev, } static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); +static ssize_t show_supports_autosuspend(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_interface *intf; + struct usb_device *udev; + int ret; + + intf = to_usb_interface(dev); + udev = interface_to_usbdev(intf); + + usb_lock_device(udev); + /* Devices will be autosuspended even when an interface isn't claimed */ + if (!intf->dev.driver || + to_usb_driver(intf->dev.driver)->supports_autosuspend) + ret = sprintf(buf, "%u\n", 1); + else + ret = sprintf(buf, "%u\n", 0); + usb_unlock_device(udev); + + return ret; +} +static DEVICE_ATTR(supports_autosuspend, S_IRUGO, show_supports_autosuspend, NULL); + static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceNumber.attr, &dev_attr_bAlternateSetting.attr, @@ -751,6 +774,7 @@ static struct attribute *intf_attrs[] = { &dev_attr_bInterfaceSubClass.attr, &dev_attr_bInterfaceProtocol.attr, &dev_attr_modalias.attr, + &dev_attr_supports_autosuspend.attr, NULL, }; static struct attribute_group intf_attr_grp = { diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 47111e88f79..f2638009a46 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -10,6 +10,8 @@ #define to_urb(d) container_of(d, struct urb, kref) +static DEFINE_SPINLOCK(usb_reject_lock); + static void urb_destroy(struct kref *kref) { struct urb *urb = to_urb(kref); @@ -68,7 +70,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags) iso_packets * sizeof(struct usb_iso_packet_descriptor), mem_flags); if (!urb) { - err("alloc_urb: kmalloc failed"); + printk(KERN_ERR "alloc_urb: kmalloc failed\n"); return NULL; } usb_init_urb(urb); @@ -127,6 +129,13 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor) usb_get_urb(urb); list_add_tail(&urb->anchor_list, &anchor->urb_list); urb->anchor = anchor; + + if (unlikely(anchor->poisoned)) { + spin_lock(&usb_reject_lock); + urb->reject++; + spin_unlock(&usb_reject_lock); + } + spin_unlock_irqrestore(&anchor->lock, flags); } EXPORT_SYMBOL_GPL(usb_anchor_urb); @@ -398,7 +407,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags) /* fail if submitter gave bogus flags */ if (urb->transfer_flags != orig_flags) { - err("BOGUS urb flags, %x --> %x", + dev_err(&dev->dev, "BOGUS urb flags, %x --> %x\n", orig_flags, urb->transfer_flags); return -EINVAL; } @@ -544,25 +553,70 @@ EXPORT_SYMBOL_GPL(usb_unlink_urb); */ void usb_kill_urb(struct urb *urb) { - static DEFINE_MUTEX(reject_mutex); - might_sleep(); if (!(urb && urb->dev && urb->ep)) return; - mutex_lock(&reject_mutex); + spin_lock_irq(&usb_reject_lock); ++urb->reject; - mutex_unlock(&reject_mutex); + spin_unlock_irq(&usb_reject_lock); usb_hcd_unlink_urb(urb, -ENOENT); wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); - mutex_lock(&reject_mutex); + spin_lock_irq(&usb_reject_lock); --urb->reject; - mutex_unlock(&reject_mutex); + spin_unlock_irq(&usb_reject_lock); } EXPORT_SYMBOL_GPL(usb_kill_urb); /** + * usb_poison_urb - reliably kill a transfer and prevent further use of an URB + * @urb: pointer to URB describing a previously submitted request, + * may be NULL + * + * This routine cancels an in-progress request. It is guaranteed that + * upon return all completion handlers will have finished and the URB + * will be totally idle and cannot be reused. These features make + * this an ideal way to stop I/O in a disconnect() callback. + * If the request has not already finished or been unlinked + * the completion handler will see urb->status == -ENOENT. + * + * After and while the routine runs, attempts to resubmit the URB will fail + * with error -EPERM. Thus even if the URB's completion handler always + * tries to resubmit, it will not succeed and the URB will become idle. + * + * This routine may not be used in an interrupt context (such as a bottom + * half or a completion handler), or when holding a spinlock, or in other + * situations where the caller can't schedule(). + */ +void usb_poison_urb(struct urb *urb) +{ + might_sleep(); + if (!(urb && urb->dev && urb->ep)) + return; + spin_lock_irq(&usb_reject_lock); + ++urb->reject; + spin_unlock_irq(&usb_reject_lock); + + usb_hcd_unlink_urb(urb, -ENOENT); + wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0); +} +EXPORT_SYMBOL_GPL(usb_poison_urb); + +void usb_unpoison_urb(struct urb *urb) +{ + unsigned long flags; + + if (!urb) + return; + + spin_lock_irqsave(&usb_reject_lock, flags); + --urb->reject; + spin_unlock_irqrestore(&usb_reject_lock, flags); +} +EXPORT_SYMBOL_GPL(usb_unpoison_urb); + +/** * usb_kill_anchored_urbs - cancel transfer requests en masse * @anchor: anchor the requests are bound to * @@ -589,6 +643,35 @@ void usb_kill_anchored_urbs(struct usb_anchor *anchor) } EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs); + +/** + * usb_poison_anchored_urbs - cease all traffic from an anchor + * @anchor: anchor the requests are bound to + * + * this allows all outstanding URBs to be poisoned starting + * from the back of the queue. Newly added URBs will also be + * poisoned + */ +void usb_poison_anchored_urbs(struct usb_anchor *anchor) +{ + struct urb *victim; + + spin_lock_irq(&anchor->lock); + anchor->poisoned = 1; + 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_poison_urb(victim); + usb_put_urb(victim); + spin_lock_irq(&anchor->lock); + } + spin_unlock_irq(&anchor->lock); +} +EXPORT_SYMBOL_GPL(usb_poison_anchored_urbs); /** * usb_unlink_anchored_urbs - asynchronously cancel transfer requests en masse * @anchor: anchor the requests are bound to @@ -633,3 +716,73 @@ int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor, msecs_to_jiffies(timeout)); } EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout); + +/** + * usb_get_from_anchor - get an anchor's oldest urb + * @anchor: the anchor whose urb you want + * + * this will take the oldest urb from an anchor, + * unanchor and return it + */ +struct urb *usb_get_from_anchor(struct usb_anchor *anchor) +{ + struct urb *victim; + unsigned long flags; + + spin_lock_irqsave(&anchor->lock, flags); + if (!list_empty(&anchor->urb_list)) { + victim = list_entry(anchor->urb_list.next, struct urb, + anchor_list); + usb_get_urb(victim); + spin_unlock_irqrestore(&anchor->lock, flags); + usb_unanchor_urb(victim); + } else { + spin_unlock_irqrestore(&anchor->lock, flags); + victim = NULL; + } + + return victim; +} + +EXPORT_SYMBOL_GPL(usb_get_from_anchor); + +/** + * usb_scuttle_anchored_urbs - unanchor all an anchor's urbs + * @anchor: the anchor whose urbs you want to unanchor + * + * use this to get rid of all an anchor's urbs + */ +void usb_scuttle_anchored_urbs(struct usb_anchor *anchor) +{ + struct urb *victim; + unsigned long flags; + + spin_lock_irqsave(&anchor->lock, flags); + while (!list_empty(&anchor->urb_list)) { + victim = list_entry(anchor->urb_list.prev, struct urb, + anchor_list); + usb_get_urb(victim); + spin_unlock_irqrestore(&anchor->lock, flags); + /* this may free the URB */ + usb_unanchor_urb(victim); + usb_put_urb(victim); + spin_lock_irqsave(&anchor->lock, flags); + } + spin_unlock_irqrestore(&anchor->lock, flags); +} + +EXPORT_SYMBOL_GPL(usb_scuttle_anchored_urbs); + +/** + * usb_anchor_empty - is an anchor empty + * @anchor: the anchor you want to query + * + * returns 1 if the anchor has no urbs associated with it + */ +int usb_anchor_empty(struct usb_anchor *anchor) +{ + return list_empty(&anchor->urb_list); +} + +EXPORT_SYMBOL_GPL(usb_anchor_empty); + |