mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-04 09:59:32 +00:00
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:
parent
a12b8db020
commit
67f5dde3d4
1 changed files with 16 additions and 10 deletions
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue