aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-07-24 18:23:23 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-30 16:27:45 -0400
commit67f5dde3d4961032aeeecaf0d1c7a9232bef3f44 (patch)
tree11465eeff8ebb8b20609354b251e5142fe134198
parenta12b8db02035673153bbf19bb3641a08bed9e4b8 (diff)
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>
-rw-r--r--drivers/usb/core/message.c26
1 files changed, 16 insertions, 10 deletions
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index ad4b956380d..b6bd05e3d43 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -18,9 +18,17 @@
18#include "hcd.h" /* for usbcore internals */ 18#include "hcd.h" /* for usbcore internals */
19#include "usb.h" 19#include "usb.h"
20 20
21struct api_context {
22 struct completion done;
23 int status;
24};
25
21static void usb_api_blocking_completion(struct urb *urb) 26static void usb_api_blocking_completion(struct urb *urb)
22{ 27{
23 complete((struct completion *)urb->context); 28 struct api_context *ctx = urb->context;
29
30 ctx->status = urb->status;
31 complete(&ctx->done);
24} 32}
25 33
26 34
@@ -32,20 +40,21 @@ static void usb_api_blocking_completion(struct urb *urb)
32 */ 40 */
33static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) 41static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
34{ 42{
35 struct completion done; 43 struct api_context ctx;
36 unsigned long expire; 44 unsigned long expire;
37 int retval; 45 int retval;
38 int status = urb->status;
39 46
40 init_completion(&done); 47 init_completion(&ctx.done);
41 urb->context = &done; 48 urb->context = &ctx;
42 urb->actual_length = 0; 49 urb->actual_length = 0;
43 retval = usb_submit_urb(urb, GFP_NOIO); 50 retval = usb_submit_urb(urb, GFP_NOIO);
44 if (unlikely(retval)) 51 if (unlikely(retval))
45 goto out; 52 goto out;
46 53
47 expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT; 54 expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
48 if (!wait_for_completion_timeout(&done, expire)) { 55 if (!wait_for_completion_timeout(&ctx.done, expire)) {
56 usb_kill_urb(urb);
57 retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
49 58
50 dev_dbg(&urb->dev->dev, 59 dev_dbg(&urb->dev->dev,
51 "%s timed out on ep%d%s len=%d/%d\n", 60 "%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 usb_pipein(urb->pipe) ? "in" : "out", 63 usb_pipein(urb->pipe) ? "in" : "out",
55 urb->actual_length, 64 urb->actual_length,
56 urb->transfer_buffer_length); 65 urb->transfer_buffer_length);
57
58 usb_kill_urb(urb);
59 retval = status == -ENOENT ? -ETIMEDOUT : status;
60 } else 66 } else
61 retval = status; 67 retval = ctx.status;
62out: 68out:
63 if (actual_length) 69 if (actual_length)
64 *actual_length = urb->actual_length; 70 *actual_length = urb->actual_length;