diff options
Diffstat (limited to 'drivers/usb/core/hcd.c')
-rw-r--r-- | drivers/usb/core/hcd.c | 131 |
1 files changed, 67 insertions, 64 deletions
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 963520fbef9..42ef1d5f6c8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -99,12 +99,17 @@ EXPORT_SYMBOL_GPL (usb_bus_list_lock); /* used for controlling access to virtual root hubs */ static DEFINE_SPINLOCK(hcd_root_hub_lock); -/* used when updating hcd data */ -static DEFINE_SPINLOCK(hcd_data_lock); +/* used when updating an endpoint's URB list */ +static DEFINE_SPINLOCK(hcd_urb_list_lock); /* wait queue for synchronous unlinks */ DECLARE_WAIT_QUEUE_HEAD(usb_kill_urb_queue); +static inline int is_root_hub(struct usb_device *udev) +{ + return (udev->parent == NULL); +} + /*-------------------------------------------------------------------------*/ /* @@ -906,14 +911,13 @@ EXPORT_SYMBOL (usb_calc_bus_time); 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); + spin_lock_irqsave(&hcd_urb_list_lock, flags); list_del_init (&urb->urb_list); - spin_unlock_irqrestore (&hcd_data_lock, flags); + spin_unlock_irqrestore(&hcd_urb_list_lock, flags); - if (hcd->self.uses_dma && !at_root_hub) { + if (hcd->self.uses_dma && !is_root_hub(urb->dev)) { if (usb_pipecontrol (urb->pipe) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) dma_unmap_single (hcd->self.controller, urb->setup_dma, @@ -955,7 +959,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) // FIXME: verify that quiescing hc works right (RH cleans up) - spin_lock_irqsave (&hcd_data_lock, flags); + spin_lock_irqsave(&hcd_urb_list_lock, flags); ep = (usb_pipein(urb->pipe) ? urb->dev->ep_in : urb->dev->ep_out) [usb_pipeendpoint(urb->pipe)]; if (unlikely (!ep)) @@ -972,7 +976,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) status = -ESHUTDOWN; break; } - spin_unlock_irqrestore (&hcd_data_lock, flags); + spin_unlock_irqrestore(&hcd_urb_list_lock, flags); if (status) { INIT_LIST_HEAD (&urb->urb_list); usbmon_urb_submit_error(&hcd->self, urb, status); @@ -986,7 +990,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) urb = usb_get_urb (urb); atomic_inc (&urb->use_count); - if (urb->dev == hcd->self.root_hub) { + if (is_root_hub(urb->dev)) { /* NOTE: requirement on hub callers (usbfs and the hub * driver, for now) that URBs' urb->transfer_buffer be * valid and usb_buffer_{sync,unmap}() not be needed, since @@ -1033,18 +1037,6 @@ done: /*-------------------------------------------------------------------------*/ -/* called in any context */ -int usb_hcd_get_frame_number (struct usb_device *udev) -{ - struct usb_hcd *hcd = bus_to_hcd(udev->bus); - - if (!HC_IS_RUNNING (hcd->state)) - return -ESHUTDOWN; - return hcd->driver->get_frame_number (hcd); -} - -/*-------------------------------------------------------------------------*/ - /* this makes the hcd giveback() the urb more quickly, by kicking it * off hardware queues (which may take a while) and returning it as * soon as practical. we've already set up the urb's return status, @@ -1055,7 +1047,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) { int value; - if (urb->dev == hcd->self.root_hub) + if (is_root_hub(urb->dev)) value = usb_rh_urb_dequeue (hcd, urb); else { @@ -1103,11 +1095,11 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) * that it was submitted. But as a rule it can't know whether or * not it's already been unlinked ... so we respect the reversed * lock sequence needed for the usb_hcd_giveback_urb() code paths - * (urb lock, then hcd_data_lock) in case some other CPU is now + * (urb lock, then hcd_urb_list_lock) in case some other CPU is now * unlinking it. */ spin_lock_irqsave (&urb->lock, flags); - spin_lock (&hcd_data_lock); + spin_lock(&hcd_urb_list_lock); sys = &urb->dev->dev; hcd = bus_to_hcd(urb->dev->bus); @@ -1139,17 +1131,16 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) * finish unlinking the initial failed usb_set_address() * or device descriptor fetch. */ - if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) - && hcd->self.root_hub != urb->dev) { + if (!test_bit(HCD_FLAG_SAW_IRQ, &hcd->flags) && + !is_root_hub(urb->dev)) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " - "Controller is probably using the wrong IRQ." - "\n"); + "Controller is probably using the wrong IRQ.\n"); set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); } urb->status = status; - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_unlock_irqrestore (&urb->lock, flags); retval = unlink1 (hcd, urb); @@ -1158,7 +1149,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status) return retval; done: - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_unlock_irqrestore (&urb->lock, flags); if (retval != -EIDRM && sys && sys->driver) dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval); @@ -1167,6 +1158,35 @@ done: /*-------------------------------------------------------------------------*/ +/** + * usb_hcd_giveback_urb - return URB from HCD to device driver + * @hcd: host controller returning the URB + * @urb: urb being returned to the USB device driver. + * Context: in_interrupt() + * + * This hands the URB from HCD to its USB device driver, using its + * completion function. The HCD has freed all per-urb resources + * (and is done using urb->hcpriv). It also released all HCD locks; + * the device driver won't cause problems if it frees, modifies, + * or resubmits this URB. + */ +void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) +{ + 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); + if (unlikely (urb->reject)) + wake_up (&usb_kill_urb_queue); + usb_put_urb (urb); +} +EXPORT_SYMBOL (usb_hcd_giveback_urb); + +/*-------------------------------------------------------------------------*/ + /* disables the endpoint: cancels any pending urbs, then synchronizes with * the hcd to make sure all endpoint state is gone from hardware, and then * waits until the endpoint's queue is completely drained. use for @@ -1186,7 +1206,7 @@ void usb_hcd_endpoint_disable (struct usb_device *udev, /* ep is already gone from udev->ep_{in,out}[]; no more submits */ rescan: - spin_lock (&hcd_data_lock); + spin_lock(&hcd_urb_list_lock); list_for_each_entry (urb, &ep->urb_list, urb_list) { int tmp; @@ -1194,7 +1214,7 @@ rescan: if (urb->status != -EINPROGRESS) continue; usb_get_urb (urb); - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); spin_lock (&urb->lock); tmp = urb->status; @@ -1223,7 +1243,7 @@ rescan: /* list contents may have changed */ goto rescan; } - spin_unlock (&hcd_data_lock); + spin_unlock(&hcd_urb_list_lock); local_irq_enable (); /* synchronize with the hardware, so old configuration state @@ -1240,7 +1260,7 @@ rescan: * endpoint_disable methods. */ while (!list_empty (&ep->urb_list)) { - spin_lock_irq (&hcd_data_lock); + spin_lock_irq(&hcd_urb_list_lock); /* The list may have changed while we acquired the spinlock */ urb = NULL; @@ -1249,7 +1269,7 @@ rescan: urb_list); usb_get_urb (urb); } - spin_unlock_irq (&hcd_data_lock); + spin_unlock_irq(&hcd_urb_list_lock); if (urb) { usb_kill_urb (urb); @@ -1260,6 +1280,18 @@ rescan: /*-------------------------------------------------------------------------*/ +/* called in any context */ +int usb_hcd_get_frame_number (struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + if (!HC_IS_RUNNING (hcd->state)) + return -ESHUTDOWN; + return hcd->driver->get_frame_number (hcd); +} + +/*-------------------------------------------------------------------------*/ + #ifdef CONFIG_PM int hcd_bus_suspend(struct usb_device *rhdev) @@ -1395,35 +1427,6 @@ EXPORT_SYMBOL (usb_bus_start_enum); /*-------------------------------------------------------------------------*/ /** - * usb_hcd_giveback_urb - return URB from HCD to device driver - * @hcd: host controller returning the URB - * @urb: urb being returned to the USB device driver. - * Context: in_interrupt() - * - * This hands the URB from HCD to its USB device driver, using its - * completion function. The HCD has freed all per-urb resources - * (and is done using urb->hcpriv). It also released all HCD locks; - * the device driver won't cause problems if it frees, modifies, - * or resubmits this URB. - */ -void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb) -{ - 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); - if (unlikely (urb->reject)) - wake_up (&usb_kill_urb_queue); - usb_put_urb (urb); -} -EXPORT_SYMBOL (usb_hcd_giveback_urb); - -/*-------------------------------------------------------------------------*/ - -/** * usb_hcd_irq - hook IRQs to HCD framework (bus glue) * @irq: the IRQ being raised * @__hcd: pointer to the HCD whose IRQ is being signaled |