Commit 46303a39 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] USB: usb_start_wait_urb() rewrite

The code that manges the synchronous control/bulk calls has
been a mess for ages.  This patch rewrites it using:

 - "struct completion" instead of a usb-internal clone therof,
 - prepare_to_wait()/finish_wait() instead of the tangled
   mess it now uses (or a new wait_event_timeout call, as in
   previous versions of this patch).

It's a net code shrink and simplification.
parent abcd7d5d
...@@ -16,77 +16,66 @@ ...@@ -16,77 +16,66 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/timer.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
#include "hcd.h" /* for usbcore internals */ #include "hcd.h" /* for usbcore internals */
#include "usb.h" #include "usb.h"
struct usb_api_data {
wait_queue_head_t wqh;
int done;
};
static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs) static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
{ {
struct usb_api_data *awd = (struct usb_api_data *)urb->context; complete((struct completion *)urb->context);
}
static void timeout_kill(unsigned long data)
{
struct urb *urb = (struct urb *) data;
awd->done = 1; dev_warn(&urb->dev->dev, "%s timeout on ep%d%s\n",
wmb(); usb_pipecontrol(urb->pipe) ? "control" : "bulk",
wake_up(&awd->wqh); usb_pipeendpoint(urb->pipe),
usb_pipein(urb->pipe) ? "in" : "out");
usb_unlink_urb(urb);
} }
// Starts urb and waits for completion or timeout // Starts urb and waits for completion or timeout
// note that this call is NOT interruptible, while
// many device driver i/o requests should be interruptible
static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length) static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
{ {
DECLARE_WAITQUEUE(wait, current); struct completion done;
struct usb_api_data awd; struct timer_list timer;
int status; int status;
init_waitqueue_head(&awd.wqh); init_completion(&done);
awd.done = 0; urb->context = &done;
urb->transfer_flags |= URB_ASYNC_UNLINK;
set_current_state(TASK_UNINTERRUPTIBLE); urb->actual_length = 0;
add_wait_queue(&awd.wqh, &wait); status = usb_submit_urb(urb, GFP_NOIO);
urb->context = &awd; if (status == 0) {
status = usb_submit_urb(urb, GFP_ATOMIC); if (timeout > 0) {
if (status) { init_timer(&timer);
// something went wrong timer.expires = jiffies + timeout;
usb_free_urb(urb); timer.data = (unsigned long)urb;
set_current_state(TASK_RUNNING); timer.function = timeout_kill;
remove_wait_queue(&awd.wqh, &wait); /* grr. timeout _should_ include submit delays. */
return status; add_timer(&timer);
}
while (timeout && !awd.done)
{
timeout = schedule_timeout(timeout);
set_current_state(TASK_UNINTERRUPTIBLE);
rmb();
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&awd.wqh, &wait);
if (!timeout && !awd.done) {
if (urb->status != -EINPROGRESS) { /* No callback?!! */
printk(KERN_ERR "usb: raced timeout, "
"pipe 0x%x status %d time left %d\n",
urb->pipe, urb->status, timeout);
status = urb->status;
} else {
warn("usb_control/bulk_msg: timeout");
usb_unlink_urb(urb); // remove urb safely
status = -ETIMEDOUT;
} }
} else wait_for_completion(&done);
status = urb->status; status = urb->status;
/* note: HCDs return ETIMEDOUT for other reasons too */
if (status == -ECONNRESET)
status = -ETIMEDOUT;
if (timeout > 0)
del_timer_sync(&timer);
}
if (actual_length) if (actual_length)
*actual_length = urb->actual_length; *actual_length = urb->actual_length;
usb_free_urb(urb); usb_free_urb(urb);
return status; return status;
} }
/*-------------------------------------------------------------------*/ /*-------------------------------------------------------------------*/
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment