#include <linux/utsname.h>
#include <linux/mm.h>
#include <asm/io.h>
-#include <asm/scatterlist.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/mutex.h>
/* 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.
* 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);
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;
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);
}
}
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.
*/
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
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);
* 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
* 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)
{
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, urb->status);
+ 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))
/*-------------------------------------------------------------------------*/
-/* 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 */
-rescan:
+ /* No more submits can occur */
spin_lock_irq(&hcd_urb_list_lock);
+rescan:
list_for_each_entry (urb, &ep->urb_list, urb_list) {
int is_in;
usb_put_urb (urb);
/* list contents may have changed */
+ spin_lock(&hcd_urb_list_lock);
goto 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);
}
}
+/* 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 */