Commit 67f5dde3 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent a12b8db0
...@@ -18,9 +18,17 @@ ...@@ -18,9 +18,17 @@
#include "hcd.h" /* for usbcore internals */ #include "hcd.h" /* for usbcore internals */
#include "usb.h" #include "usb.h"
struct api_context {
struct completion done;
int status;
};
static void usb_api_blocking_completion(struct urb *urb) 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) ...@@ -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) static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
{ {
struct completion done; struct api_context ctx;
unsigned long expire; unsigned long expire;
int retval; int retval;
int status = urb->status;
init_completion(&done); init_completion(&ctx.done);
urb->context = &done; urb->context = &ctx;
urb->actual_length = 0; urb->actual_length = 0;
retval = usb_submit_urb(urb, GFP_NOIO); retval = usb_submit_urb(urb, GFP_NOIO);
if (unlikely(retval)) if (unlikely(retval))
goto out; goto out;
expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; 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, dev_dbg(&urb->dev->dev,
"%s timed out on ep%d%s len=%d/%d\n", "%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) ...@@ -54,11 +63,8 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
usb_pipein(urb->pipe) ? "in" : "out", usb_pipein(urb->pipe) ? "in" : "out",
urb->actual_length, urb->actual_length,
urb->transfer_buffer_length); urb->transfer_buffer_length);
usb_kill_urb(urb);
retval = status == -ENOENT ? -ETIMEDOUT : status;
} else } else
retval = status; retval = ctx.status;
out: out:
if (actual_length) if (actual_length)
*actual_length = urb->actual_length; *actual_length = urb->actual_length;
......
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