]> err.no Git - linux-2.6/blobdiff - drivers/usb/core/hcd.c
Merge branch 'master' of master.kernel.org:/pub/scm/linux/kernel/git/davem/net-2.6
[linux-2.6] / drivers / usb / core / hcd.c
index ec17fc4d28614462ea7f93da338bf9972bca9423..3dd997df8505bf3b2f64c7210f71b466b7101746 100644 (file)
@@ -532,7 +532,6 @@ error:
 
        /* any errors get returned through the urb completion */
        spin_lock_irq(&hcd_root_hub_lock);
-       urb->status = status;
        usb_hcd_unlink_urb_from_ep(hcd, urb);
 
        /* This peculiar use of spinlocks echoes what real HC drivers do.
@@ -540,7 +539,7 @@ error:
         * RT-friendly.
         */
        spin_unlock(&hcd_root_hub_lock);
-       usb_hcd_giveback_urb(hcd, urb);
+       usb_hcd_giveback_urb(hcd, urb, status);
        spin_lock(&hcd_root_hub_lock);
 
        spin_unlock_irq(&hcd_root_hub_lock);
@@ -578,13 +577,12 @@ void usb_hcd_poll_rh_status(struct usb_hcd *hcd)
                if (urb) {
                        hcd->poll_pending = 0;
                        hcd->status_urb = NULL;
-                       urb->status = 0;
                        urb->actual_length = length;
                        memcpy(urb->transfer_buffer, buffer, length);
 
                        usb_hcd_unlink_urb_from_ep(hcd, urb);
                        spin_unlock(&hcd_root_hub_lock);
-                       usb_hcd_giveback_urb(hcd, urb);
+                       usb_hcd_giveback_urb(hcd, urb, 0);
                        spin_lock(&hcd_root_hub_lock);
                } else {
                        length = 0;
@@ -677,7 +675,7 @@ static int usb_rh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                        usb_hcd_unlink_urb_from_ep(hcd, urb);
 
                        spin_unlock(&hcd_root_hub_lock);
-                       usb_hcd_giveback_urb(hcd, urb);
+                       usb_hcd_giveback_urb(hcd, urb, status);
                        spin_lock(&hcd_root_hub_lock);
                }
        }
@@ -1016,6 +1014,11 @@ int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb)
                goto done;
        }
 
+       if (unlikely(!urb->dev->can_submit)) {
+               rc = -EHOSTUNREACH;
+               goto done;
+       }
+
        /*
         * Check the host controller's state and add the URB to the
         * endpoint's queue.
@@ -1173,6 +1176,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
         */
        usb_get_urb(urb);
        atomic_inc(&urb->use_count);
+       atomic_inc(&urb->dev->urbnum);
        usbmon_urb_submit(&hcd->self, urb);
 
        /* NOTE requirements on root-hub callers (usbfs and the hub
@@ -1194,6 +1198,7 @@ int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
                urb->hcpriv = NULL;
                INIT_LIST_HEAD(&urb->urb_list);
                atomic_dec(&urb->use_count);
+               atomic_dec(&urb->dev->urbnum);
                if (urb->reject)
                        wake_up(&usb_kill_urb_queue);
                usb_put_urb(urb);
@@ -1252,6 +1257,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
  * 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.
+ * @status: completion status code for the URB.
  * Context: in_interrupt()
  *
  * This hands the URB from HCD to its USB device driver, using its
@@ -1260,24 +1266,26 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
  * the device driver won't cause problems if it frees, modifies,
  * or resubmits this URB.
  *
- * If @urb was unlinked, the value of @urb->status will be overridden by
+ * If @urb was unlinked, the value of @status will be overridden by
  * @urb->unlinked.  Erroneous short transfers are detected in case
  * the HCD hasn't checked for them.
  */
-void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
+void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status)
 {
-       unmap_urb_for_dma(hcd, urb);
-       usbmon_urb_complete (&hcd->self, urb);
-       usb_unanchor_urb(urb);
        urb->hcpriv = NULL;
        if (unlikely(urb->unlinked))
-               urb->status = urb->unlinked;
+               status = urb->unlinked;
        else if (unlikely((urb->transfer_flags & URB_SHORT_NOT_OK) &&
                        urb->actual_length < urb->transfer_buffer_length &&
-                       !urb->status))
-               urb->status = -EREMOTEIO;
+                       !status))
+               status = -EREMOTEIO;
+
+       unmap_urb_for_dma(hcd, urb);
+       usbmon_urb_complete(&hcd->self, urb, status);
+       usb_unanchor_urb(urb);
 
        /* pass ownership to the completion handler */
+       urb->status = status;
        urb->complete (urb);
        atomic_dec (&urb->use_count);
        if (unlikely (urb->reject))
@@ -1288,24 +1296,22 @@ 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
- * 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.
+/* Cancel all URBs pending on this endpoint and wait for the endpoint's
+ * queue to drain completely.  The caller must first insure that no more
+ * URBs can be submitted for this endpoint.
  */
-void usb_hcd_endpoint_disable (struct usb_device *udev,
+void usb_hcd_flush_endpoint(struct usb_device *udev,
                struct usb_host_endpoint *ep)
 {
        struct usb_hcd          *hcd;
        struct urb              *urb;
 
+       if (!ep)
+               return;
        might_sleep();
        hcd = bus_to_hcd(udev->bus);
 
-       /* ep is already gone from udev->ep_{in,out}[]; no more submits */
+       /* No more submits can occur */
 rescan:
        spin_lock_irq(&hcd_urb_list_lock);
        list_for_each_entry (urb, &ep->urb_list, urb_list) {
@@ -1344,18 +1350,7 @@ rescan:
        }
        spin_unlock_irq(&hcd_urb_list_lock);
 
-       /* synchronize with the hardware, so old configuration state
-        * clears out immediately (and will be freed).
-        */
-       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.
-        */
+       /* Wait until the endpoint queue is completely empty */
        while (!list_empty (&ep->urb_list)) {
                spin_lock_irq(&hcd_urb_list_lock);
 
@@ -1375,6 +1370,25 @@ rescan:
        }
 }
 
+/* Disables the endpoint: synchronizes with the hcd to make sure all
+ * endpoint state is gone from hardware.  usb_hcd_flush_endpoint() must
+ * have been called previously.  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.
+ */
+void usb_hcd_disable_endpoint(struct usb_device *udev,
+               struct usb_host_endpoint *ep)
+{
+       struct usb_hcd          *hcd;
+
+       might_sleep();
+       hcd = bus_to_hcd(udev->bus);
+       if (hcd->driver->endpoint_disable)
+               hcd->driver->endpoint_disable(hcd, ep);
+}
+
 /*-------------------------------------------------------------------------*/
 
 /* called in any context */