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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a12b8db020
commit
67f5dde3d4
@@ -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)
|
|||||||
*/
|
*/
|
||||||
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)
|
|||||||
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;
|
||||||
|
Reference in New Issue
Block a user