X-Git-Url: http://pilppa.org/gitweb/gitweb.cgi?a=blobdiff_plain;f=drivers%2Fusb%2Fcore%2Fhcd.c;h=42ef1d5f6c8ad07c78416bdbaa2c3e9c60e7516e;hb=532df780a2012ad75b3f078647f229c4dabd99d1;hp=4bf914d00a14ddc9a9110c1c87b098e3a412e539;hpb=1323523f505606cfd24af6122369afddefc3b09d;p=linux-2.6-omap-h63xx.git diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 4bf914d00a1..42ef1d5f6c8 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -22,7 +22,6 @@ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include #include #include @@ -37,6 +36,8 @@ #include #include #include +#include +#include #include @@ -45,8 +46,6 @@ #include "hub.h" -// #define USB_BANDWIDTH_MESSAGES - /*-------------------------------------------------------------------------*/ /* @@ -100,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); +} + /*-------------------------------------------------------------------------*/ /* @@ -256,7 +260,9 @@ static const u8 hs_rh_config_descriptor [] = { 0x05, /* __u8 ep_bDescriptorType; Endpoint */ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x02, 0x00, /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */ + /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) + * see hub.c:hub_configure() for details. */ + (USB_MAXCHILDREN + 1 + 7) / 8, 0x00, 0x0c /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */ }; @@ -318,8 +324,8 @@ static int rh_string ( // id 3 == vendor description } else if (id == 3) { - snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname, - system_utsname.release, hcd->driver->description); + snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname, + init_utsname()->release, hcd->driver->description); // unsupported IDs --> "protocol stall" } else @@ -345,7 +351,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb) struct usb_ctrlrequest *cmd; u16 typeReq, wValue, wIndex, wLength; u8 *ubuf = urb->transfer_buffer; - u8 tbuf [sizeof (struct usb_hub_descriptor)]; + u8 tbuf [sizeof (struct usb_hub_descriptor)] + __attribute__((aligned(4))); const u8 *bufp = tbuf; int len = 0; int patch_wakeup = 0; @@ -521,7 +528,7 @@ error: if (urb->status == -EINPROGRESS) urb->status = status; spin_unlock (&urb->lock); - usb_hcd_giveback_urb (hcd, urb, NULL); + usb_hcd_giveback_urb (hcd, urb); local_irq_restore (flags); return 0; } @@ -543,6 +550,8 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) unsigned long flags; char buffer[4]; /* Any root hubs with > 31 ports? */ + if (unlikely(!hcd->rh_registered)) + return; if (!hcd->uses_new_polling && !hcd->status_urb) return; @@ -571,17 +580,19 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd) /* local irqs are always blocked in completions */ if (length > 0) - usb_hcd_giveback_urb (hcd, urb, NULL); + usb_hcd_giveback_urb (hcd, urb); else hcd->poll_pending = 1; local_irq_restore (flags); } /* 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); @@ -610,8 +621,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) @@ -633,31 +644,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb) /*-------------------------------------------------------------------------*/ -/* Asynchronous unlinks of root-hub control URBs are legal, but they - * don't do anything. Status URB unlinks must be made in process context - * with interrupts enabled. +/* Unlinks of root-hub control URBs are legal, but they don't do anything + * since these URBs always execute synchronously. */ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) { - if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ - if (in_interrupt()) - return 0; /* nothing to do */ - - spin_lock_irq(&urb->lock); /* from usb_kill_urb */ - ++urb->reject; - spin_unlock_irq(&urb->lock); - - wait_event(usb_kill_urb_queue, - atomic_read(&urb->use_count) == 0); + unsigned long flags; - spin_lock_irq(&urb->lock); - --urb->reject; - spin_unlock_irq(&urb->lock); + if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */ + ; /* Do nothing */ } else { /* Status URB */ if (!hcd->uses_new_polling) - del_timer_sync (&hcd->rh_timer); - local_irq_disable (); + del_timer (&hcd->rh_timer); + local_irq_save (flags); spin_lock (&hcd_root_hub_lock); if (urb == hcd->status_urb) { hcd->status_urb = NULL; @@ -666,8 +666,8 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) urb = NULL; /* wasn't fully queued */ spin_unlock (&hcd_root_hub_lock); if (urb) - usb_hcd_giveback_urb (hcd, urb, NULL); - local_irq_enable (); + usb_hcd_giveback_urb (hcd, urb); + local_irq_restore (flags); } return 0; @@ -675,31 +675,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb) /*-------------------------------------------------------------------------*/ -/* exported only within usbcore */ -struct usb_bus *usb_bus_get(struct usb_bus *bus) -{ - if (bus) - kref_get(&bus->kref); - return bus; -} - -static void usb_host_release(struct kref *kref) -{ - struct usb_bus *bus = container_of(kref, struct usb_bus, kref); - - if (bus->release) - bus->release(bus); -} - -/* exported only within usbcore */ -void usb_bus_put(struct usb_bus *bus) -{ - if (bus) - kref_put(&bus->kref, usb_host_release); -} - -/*-------------------------------------------------------------------------*/ - static struct class *usb_host_class; int usb_host_init(void) @@ -731,39 +706,12 @@ static void usb_bus_init (struct usb_bus *bus) bus->devnum_next = 1; bus->root_hub = NULL; - bus->hcpriv = NULL; bus->busnum = -1; bus->bandwidth_allocated = 0; bus->bandwidth_int_reqs = 0; bus->bandwidth_isoc_reqs = 0; INIT_LIST_HEAD (&bus->bus_list); - - kref_init(&bus->kref); -} - -/** - * usb_alloc_bus - creates a new USB host controller structure - * @op: pointer to a struct usb_operations that this bus structure should use - * Context: !in_interrupt() - * - * Creates a USB host controller bus structure with the specified - * usb_operations and initializes all the necessary internal objects. - * - * If no memory is available, NULL is returned. - * - * The caller should call usb_put_bus() when it is finished with the structure. - */ -struct usb_bus *usb_alloc_bus (struct usb_operations *op) -{ - struct usb_bus *bus; - - bus = kzalloc (sizeof *bus, GFP_KERNEL); - if (!bus) - return NULL; - usb_bus_init (bus); - bus->op = op; - return bus; } /*-------------------------------------------------------------------------*/ @@ -898,8 +846,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus) struct usb_hcd *hcd; hcd = container_of (bus, struct usb_hcd, self); - if (hcd->driver->hub_irq_enable && !hcd->poll_rh && - hcd->state != HC_STATE_HALT) + if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT) hcd->driver->hub_irq_enable (hcd); } @@ -952,136 +899,6 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount) } EXPORT_SYMBOL (usb_calc_bus_time); -/* - * usb_check_bandwidth(): - * - * old_alloc is from host_controller->bandwidth_allocated in microseconds; - * bustime is from calc_bus_time(), but converted to microseconds. - * - * returns if successful, - * or -ENOSPC if bandwidth request fails. - * - * FIXME: - * This initial implementation does not use Endpoint.bInterval - * in managing bandwidth allocation. - * It probably needs to be expanded to use Endpoint.bInterval. - * This can be done as a later enhancement (correction). - * - * This will also probably require some kind of - * frame allocation tracking...meaning, for example, - * that if multiple drivers request interrupts every 10 USB frames, - * they don't all have to be allocated at - * frame numbers N, N+10, N+20, etc. Some of them could be at - * N+11, N+21, N+31, etc., and others at - * N+12, N+22, N+32, etc. - * - * Similarly for isochronous transfers... - * - * Individual HCDs can schedule more directly ... this logic - * is not correct for high speed transfers. - */ -int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) -{ - unsigned int pipe = urb->pipe; - long bustime; - int is_in = usb_pipein (pipe); - int is_iso = usb_pipeisoc (pipe); - int old_alloc = dev->bus->bandwidth_allocated; - int new_alloc; - - - bustime = NS_TO_US (usb_calc_bus_time (dev->speed, is_in, is_iso, - usb_maxpacket (dev, pipe, !is_in))); - if (is_iso) - bustime /= urb->number_of_packets; - - new_alloc = old_alloc + (int) bustime; - if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) { -#ifdef DEBUG - char *mode = -#ifdef CONFIG_USB_BANDWIDTH - ""; -#else - "would have "; -#endif - dev_dbg (&dev->dev, "usb_check_bandwidth %sFAILED: %d + %ld = %d usec\n", - mode, old_alloc, bustime, new_alloc); -#endif -#ifdef CONFIG_USB_BANDWIDTH - bustime = -ENOSPC; /* report error */ -#endif - } - - return bustime; -} -EXPORT_SYMBOL (usb_check_bandwidth); - - -/** - * usb_claim_bandwidth - records bandwidth for a periodic transfer - * @dev: source/target of request - * @urb: request (urb->dev == dev) - * @bustime: bandwidth consumed, in (average) microseconds per frame - * @isoc: true iff the request is isochronous - * - * Bus bandwidth reservations are recorded purely for diagnostic purposes. - * HCDs are expected not to overcommit periodic bandwidth, and to record such - * reservations whenever endpoints are added to the periodic schedule. - * - * FIXME averaging per-frame is suboptimal. Better to sum over the HCD's - * entire periodic schedule ... 32 frames for OHCI, 1024 for UHCI, settable - * for EHCI (256/512/1024 frames, default 1024) and have the bus expose how - * large its periodic schedule is. - */ -void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) -{ - dev->bus->bandwidth_allocated += bustime; - if (isoc) - dev->bus->bandwidth_isoc_reqs++; - else - dev->bus->bandwidth_int_reqs++; - urb->bandwidth = bustime; - -#ifdef USB_BANDWIDTH_MESSAGES - dev_dbg (&dev->dev, "bandwidth alloc increased by %d (%s) to %d for %d requesters\n", - bustime, - isoc ? "ISOC" : "INTR", - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); -#endif -} -EXPORT_SYMBOL (usb_claim_bandwidth); - - -/** - * usb_release_bandwidth - reverses effect of usb_claim_bandwidth() - * @dev: source/target of request - * @urb: request (urb->dev == dev) - * @isoc: true iff the request is isochronous - * - * This records that previously allocated bandwidth has been released. - * Bandwidth is released when endpoints are removed from the host controller's - * periodic schedule. - */ -void usb_release_bandwidth (struct usb_device *dev, struct urb *urb, int isoc) -{ - dev->bus->bandwidth_allocated -= urb->bandwidth; - if (isoc) - dev->bus->bandwidth_isoc_reqs--; - else - dev->bus->bandwidth_int_reqs--; - -#ifdef USB_BANDWIDTH_MESSAGES - dev_dbg (&dev->dev, "bandwidth alloc reduced by %d (%s) to %d for %d requesters\n", - urb->bandwidth, - isoc ? "ISOC" : "INTR", - dev->bus->bandwidth_allocated, - dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); -#endif - urb->bandwidth = 0; -} -EXPORT_SYMBOL (usb_release_bandwidth); - /*-------------------------------------------------------------------------*/ @@ -1091,32 +908,41 @@ EXPORT_SYMBOL (usb_release_bandwidth); /*-------------------------------------------------------------------------*/ -static void urb_unlink (struct urb *urb) +static void urb_unlink(struct usb_hcd *hcd, struct urb *urb) { unsigned long flags; - /* Release any periodic transfer bandwidth */ - if (urb->bandwidth) - usb_release_bandwidth (urb->dev, urb, - usb_pipeisoc (urb->pipe)); - /* 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 && !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, + 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 * expects usb_submit_urb() to have sanity checked and conditioned all * inputs in the urb */ -static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags) +int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags) { int status; - struct usb_hcd *hcd = urb->dev->bus->hcpriv; + struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus); struct usb_host_endpoint *ep; unsigned long flags; @@ -1133,7 +959,7 @@ static int 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)) @@ -1143,24 +969,14 @@ static int 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; } - 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); @@ -1174,7 +990,7 @@ doit: 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 @@ -1187,7 +1003,7 @@ doit: /* lower level hcd code should use *_dma exclusively, * unless it uses pio or talks to another transport. */ - if (hcd->self.controller->dma_mask) { + if (hcd->self.uses_dma) { if (usb_pipecontrol (urb->pipe) && !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP)) urb->setup_dma = dma_map_single ( @@ -1209,29 +1025,18 @@ 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); - usb_put_urb (urb); usbmon_urb_submit_error(&hcd->self, urb, status); + usb_put_urb (urb); } return status; } /*-------------------------------------------------------------------------*/ -/* called in any context */ -static int hcd_get_frame_number (struct usb_device *udev) -{ - struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv; - 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, @@ -1242,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 { @@ -1264,7 +1069,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb) * caller guarantees urb won't be recycled till both unlink() * and the urb's completion function return */ -static int hcd_unlink_urb (struct urb *urb, int status) +int usb_hcd_unlink_urb (struct urb *urb, int status) { struct usb_host_endpoint *ep; struct usb_hcd *hcd = NULL; @@ -1290,14 +1095,14 @@ static int 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 = urb->dev->bus->hcpriv; + hcd = bus_to_hcd(urb->dev->bus); if (hcd == NULL) { retval = -ENODEV; goto done; @@ -1326,17 +1131,16 @@ static int 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); @@ -1345,7 +1149,7 @@ static int 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); @@ -1354,46 +1158,63 @@ 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. use for + * 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 * set_configuration, set_interface, driver removal, physical disconnect. * * example: a qh stored in ep->hcpriv, holding state related to endpoint * type, maxpacket size, toggle, halt status, and scheduling. */ -static void -hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep) +void usb_hcd_endpoint_disable (struct usb_device *udev, + struct usb_host_endpoint *ep) { struct usb_hcd *hcd; struct urb *urb; - hcd = udev->bus->hcpriv; - - WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT && - udev->state != USB_STATE_NOTATTACHED); - + hcd = bus_to_hcd(udev->bus); local_irq_disable (); - /* FIXME move most of this into message.c as part of its - * endpoint disable logic - */ - /* 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; - /* another cpu may be in hcd, spinning on hcd_data_lock - * to giveback() this urb. the races here should be - * small, but a full fix needs a new "can't submit" - * urb state. - * FIXME urb->reject should allow that... - */ + /* the urb may already have been unlinked */ 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; @@ -1422,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 @@ -1431,95 +1252,116 @@ rescan: might_sleep (); if (hcd->driver->endpoint_disable) hcd->driver->endpoint_disable (hcd, ep); + + /* Wait until the endpoint queue is completely empty. Most HCDs + * will have done this already in their endpoint_disable method, + * but some might not. And there could be root-hub control URBs + * still pending since they aren't affected by the HCDs' + * endpoint_disable methods. + */ + while (!list_empty (&ep->urb_list)) { + spin_lock_irq(&hcd_urb_list_lock); + + /* The list may have changed while we acquired the spinlock */ + urb = NULL; + if (!list_empty (&ep->urb_list)) { + urb = list_entry (ep->urb_list.prev, struct urb, + urb_list); + usb_get_urb (urb); + } + spin_unlock_irq(&hcd_urb_list_lock); + + if (urb) { + usb_kill_urb (urb); + usb_put_urb (urb); + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* 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_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; } -/* - * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports - * @hcd: host controller for this root hub - * - * This call arranges that usb_hcd_resume_root_hub() is safe to call later; - * that the HCD's root hub polling is deactivated; and that the root's hub - * driver is suspended. HCDs may call this to autosuspend when their root - * hub's downstream ports are all inactive: unpowered, disconnected, - * disabled, or suspended. - * - * The HCD will autoresume on device connect change detection (using SRP - * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling - * from any ports that are suspended (if that is enabled). In most cases, - * overcurrent signaling (on powered ports) will also start autoresume. - * - * Always called with IRQs blocked. - */ -void usb_hcd_suspend_root_hub (struct usb_hcd *hcd) +/* Workqueue routine for root-hub remote wakeup */ +static void hcd_resume_work(struct work_struct *work) { - struct urb *urb; + struct usb_hcd *hcd = container_of(work, struct usb_hcd, wakeup_work); + struct usb_device *udev = hcd->self.root_hub; - spin_lock (&hcd_root_hub_lock); - usb_suspend_root_hub (hcd->self.root_hub); - - /* force status urb to complete/unlink while suspended */ - if (hcd->status_urb) { - urb = hcd->status_urb; - urb->status = -ECONNRESET; - urb->hcpriv = NULL; - urb->actual_length = 0; - - del_timer (&hcd->rh_timer); - hcd->poll_pending = 0; - hcd->status_urb = NULL; - } else - urb = NULL; - spin_unlock (&hcd_root_hub_lock); - hcd->state = HC_STATE_SUSPENDED; - - if (urb) - usb_hcd_giveback_urb (hcd, urb, NULL); + usb_lock_device(udev); + usb_mark_last_busy(udev); + usb_external_resume_device(udev); + usb_unlock_device(udev); } -EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub); /** * usb_hcd_resume_root_hub - called by HCD to resume its root hub @@ -1527,8 +1369,8 @@ EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub); * * The USB host controller calls this function when its root hub is * suspended (with the remote wakeup feature enabled) and a remote - * wakeup request is received. It queues a request for khubd to - * resume the root hub (that is, manage its downstream ports again). + * wakeup request is received. The routine submits a workqueue request + * to resume the root hub (that is, manage its downstream ports again). */ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) { @@ -1536,7 +1378,7 @@ void usb_hcd_resume_root_hub (struct usb_hcd *hcd) spin_lock_irqsave (&hcd_root_hub_lock, flags); if (hcd->rh_registered) - usb_resume_root_hub (hcd->self.root_hub); + queue_work(ksuspend_usb_wq, &hcd->wakeup_work); spin_unlock_irqrestore (&hcd_root_hub_lock, flags); } EXPORT_SYMBOL_GPL(usb_hcd_resume_root_hub); @@ -1584,69 +1426,6 @@ EXPORT_SYMBOL (usb_bus_start_enum); /*-------------------------------------------------------------------------*/ -/* - * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue) - */ -static struct usb_operations usb_hcd_operations = { - .get_frame_number = hcd_get_frame_number, - .submit_urb = hcd_submit_urb, - .unlink_urb = hcd_unlink_urb, - .buffer_alloc = hcd_buffer_alloc, - .buffer_free = hcd_buffer_free, - .disable = hcd_endpoint_disable, -}; - -/*-------------------------------------------------------------------------*/ - -/** - * 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. - * @regs: pt_regs, passed down to the URB completion handler - * 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, struct pt_regs *regs) -{ - 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 (hcd->self.controller->dma_mask && !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); - } - - usbmon_urb_complete (&hcd->self, urb); - /* pass ownership to the completion handler */ - urb->complete (urb, regs); - 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 @@ -1656,7 +1435,7 @@ EXPORT_SYMBOL (usb_hcd_giveback_urb); * If the controller isn't HALTed, calls the driver's irq handler. * Checks whether the controller is now dead. */ -irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) +irqreturn_t usb_hcd_irq (int irq, void *__hcd) { struct usb_hcd *hcd = __hcd; int start = hcd->state; @@ -1664,7 +1443,7 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd, struct pt_regs * r) if (unlikely(start == HC_STATE_HALT || !test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) return IRQ_NONE; - if (hcd->driver->irq (hcd, r) == IRQ_NONE) + if (hcd->driver->irq (hcd) == IRQ_NONE) return IRQ_NONE; set_bit(HCD_FLAG_SAW_IRQ, &hcd->flags); @@ -1705,14 +1484,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died); /*-------------------------------------------------------------------------*/ -static void hcd_release (struct usb_bus *bus) -{ - struct usb_hcd *hcd; - - hcd = container_of(bus, struct usb_hcd, self); - kfree(hcd); -} - /** * usb_create_hcd - create and initialize an HCD structure * @driver: HC driver that will use this hcd @@ -1737,17 +1508,19 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, return NULL; } dev_set_drvdata(dev, hcd); + kref_init(&hcd->kref); usb_bus_init(&hcd->self); - hcd->self.op = &usb_hcd_operations; - hcd->self.hcpriv = hcd; - hcd->self.release = &hcd_release; hcd->self.controller = dev; hcd->self.bus_name = bus_name; + hcd->self.uses_dma = (dev->dma_mask != NULL); init_timer(&hcd->rh_timer); hcd->rh_timer.function = rh_timer_func; hcd->rh_timer.data = (unsigned long) hcd; +#ifdef CONFIG_PM + INIT_WORK(&hcd->wakeup_work, hcd_resume_work); +#endif hcd->driver = driver; hcd->product_desc = (driver->product_desc) ? driver->product_desc : @@ -1757,10 +1530,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, } EXPORT_SYMBOL (usb_create_hcd); +static void hcd_release (struct kref *kref) +{ + struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref); + + kfree(hcd); +} + +struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd) +{ + if (hcd) + kref_get (&hcd->kref); + return hcd; +} +EXPORT_SYMBOL (usb_get_hcd); + void usb_put_hcd (struct usb_hcd *hcd) { - dev_set_drvdata(hcd->self.controller, NULL); - usb_bus_put(&hcd->self); + if (hcd) + kref_put (&hcd->kref, hcd_release); } EXPORT_SYMBOL (usb_put_hcd); @@ -1899,16 +1687,20 @@ void usb_remove_hcd(struct usb_hcd *hcd) hcd->rh_registered = 0; spin_unlock_irq (&hcd_root_hub_lock); +#ifdef CONFIG_PM + cancel_work_sync(&hcd->wakeup_work); +#endif + mutex_lock(&usb_bus_list_lock); usb_disconnect(&hcd->self.root_hub); mutex_unlock(&usb_bus_list_lock); - hcd->poll_rh = 0; - del_timer_sync(&hcd->rh_timer); - hcd->driver->stop(hcd); hcd->state = HC_STATE_HALT; + hcd->poll_rh = 0; + del_timer_sync(&hcd->rh_timer); + if (hcd->irq >= 0) free_irq(hcd->irq, hcd); usb_deregister_bus(&hcd->self); @@ -1916,6 +1708,16 @@ void usb_remove_hcd(struct usb_hcd *hcd) } EXPORT_SYMBOL (usb_remove_hcd); +void +usb_hcd_platform_shutdown(struct platform_device* dev) +{ + struct usb_hcd *hcd = platform_get_drvdata(dev); + + if (hcd->driver->shutdown) + hcd->driver->shutdown(hcd); +} +EXPORT_SYMBOL (usb_hcd_platform_shutdown); + /*-------------------------------------------------------------------------*/ #if defined(CONFIG_USB_MON)