diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-07-25 00:23:23 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-30 22:27:45 +0200 |
commit | 67f5dde3d4961032aeeecaf0d1c7a9232bef3f44 (patch) | |
tree | 11465eeff8ebb8b20609354b251e5142fe134198 /drivers/usb/core | |
parent | USB: fix scatterlist PIO case (IOMMU) (diff) | |
download | linux-67f5dde3d4961032aeeecaf0d1c7a9232bef3f44.tar.xz linux-67f5dde3d4961032aeeecaf0d1c7a9232bef3f44.zip |
USB: Fix a bug in usb_start_wait_urb
This patch (as941) fixes a bug recently added to the USB synchronous
API. The status of a completed URB must be preserved separately
across a completion callback. Also, the actual_length value isn't
available until after the URB has fully completed.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/message.c | 26 |
1 files changed, 16 insertions, 10 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index ad4b956380d2..b6bd05e3d439 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -18,9 +18,17 @@ #include "hcd.h" /* for usbcore internals */ #include "usb.h" +struct api_context { + struct completion done; + int status; +}; + static void usb_api_blocking_completion(struct urb *urb) { - complete((struct completion *)urb->context); + struct api_context *ctx = urb->context; + + ctx->status = urb->status; + complete(&ctx->done); } @@ -32,20 +40,21 @@ static void usb_api_blocking_completion(struct urb *urb) */ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) { - struct completion done; + struct api_context ctx; unsigned long expire; int retval; - int status = urb->status; - init_completion(&done); - urb->context = &done; + init_completion(&ctx.done); + urb->context = &ctx; urb->actual_length = 0; retval = usb_submit_urb(urb, GFP_NOIO); if (unlikely(retval)) goto out; expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; - if (!wait_for_completion_timeout(&done, expire)) { + if (!wait_for_completion_timeout(&ctx.done, expire)) { + usb_kill_urb(urb); + retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status); dev_dbg(&urb->dev->dev, "%s timed out on ep%d%s len=%d/%d\n", @@ -54,11 +63,8 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) usb_pipein(urb->pipe) ? "in" : "out", urb->actual_length, urb->transfer_buffer_length); - - usb_kill_urb(urb); - retval = status == -ENOENT ? -ETIMEDOUT : status; } else - retval = status; + retval = ctx.status; out: if (actual_length) *actual_length = urb->actual_length; |