USB: Fix a bug in usb_start_wait_urb
authorAlan Stern <stern@rowland.harvard.edu>
Tue, 24 Jul 2007 22:23:23 +0000 (18:23 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 30 Jul 2007 20:27:45 +0000 (13:27 -0700)
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>
drivers/usb/core/message.c

index ad4b956..b6bd05e 100644 (file)
 #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;