diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2007-07-24 18:23:23 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2007-07-30 16:27:45 -0400 |
commit | 67f5dde3d4961032aeeecaf0d1c7a9232bef3f44 (patch) | |
tree | 11465eeff8ebb8b20609354b251e5142fe134198 | |
parent | a12b8db02035673153bbf19bb3641a08bed9e4b8 (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.c | 26 |
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 | ||
21 | struct api_context { | ||
22 | struct completion done; | ||
23 | int status; | ||
24 | }; | ||
25 | |||
21 | static void usb_api_blocking_completion(struct urb *urb) | 26 | static 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 | */ |
33 | static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) | 41 | static 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; |
62 | out: | 68 | out: |
63 | if (actual_length) | 69 | if (actual_length) |
64 | *actual_length = urb->actual_length; | 70 | *actual_length = urb->actual_length; |