Sync to P4

- fix a transfer cancelling bug/segfault [1]
 - correct a return code in the transfer cancel function.
 - add new API function, libusb20_tr_bulk_intr_sync().

Submitted by:	HPS
Reported by:	Robert Jenssen [1]
This commit is contained in:
Andrew Thompson 2009-11-20 08:57:25 +00:00
parent 1c8a163c8b
commit 4594d907db
5 changed files with 155 additions and 11 deletions

View File

@ -833,8 +833,12 @@ libusb10_complete_transfer(struct libusb20_transfer *pxfer,
if (pxfer != NULL)
libusb20_tr_set_priv_sc1(pxfer, NULL);
/* set transfer status */
uxfer->status = status;
/* update super transfer state */
sxfer->state = LIBUSB_SUPER_XFER_ST_NONE;
dev = libusb_get_device(uxfer->dev_handle);
TAILQ_INSERT_TAIL(&dev->ctx->tr_done, sxfer, entry);
@ -1111,6 +1115,8 @@ libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint)
return;
case 2:
sxfer = libusb20_tr_get_priv_sc1(pxfer1);
if (sxfer == NULL)
return; /* cancelling */
if (sxfer->rem_len)
return; /* cannot queue another one */
/* swap transfers */
@ -1118,6 +1124,8 @@ libusb10_submit_transfer_sub(struct libusb20_device *pdev, uint8_t endpoint)
break;
case 1:
sxfer = libusb20_tr_get_priv_sc1(pxfer0);
if (sxfer == NULL)
return; /* cancelling */
if (sxfer->rem_len)
return; /* cannot queue another one */
/* swap transfers */
@ -1229,12 +1237,18 @@ libusb_submit_transfer(struct libusb_transfer *uxfer)
if (pxfer0 == NULL || pxfer1 == NULL) {
err = LIBUSB_ERROR_OTHER;
} else if ((sxfer->entry.tqe_prev != NULL) ||
(libusb20_tr_get_priv_sc1(pxfer0) == sxfer) ||
(libusb20_tr_get_priv_sc1(pxfer0) == sxfer) ||
(libusb20_tr_get_priv_sc1(pxfer1) == sxfer)) {
err = LIBUSB_ERROR_BUSY;
} else {
/* set pending state */
sxfer->state = LIBUSB_SUPER_XFER_ST_PEND;
/* insert transfer into transfer head list */
TAILQ_INSERT_TAIL(&dev->tr_head, sxfer, entry);
/* start work transfers */
libusb10_submit_transfer_sub(
uxfer->dev_handle, endpoint);
@ -1258,12 +1272,14 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer)
struct libusb_super_transfer *sxfer;
struct libusb_device *dev;
uint32_t endpoint;
int retval;
if (uxfer == NULL)
return (LIBUSB_ERROR_INVALID_PARAM);
/* check if not initialised */
if (uxfer->dev_handle == NULL)
return (LIBUSB_ERROR_INVALID_PARAM);
return (LIBUSB_ERROR_NOT_FOUND);
endpoint = uxfer->endpoint;
@ -1277,39 +1293,50 @@ libusb_cancel_transfer(struct libusb_transfer *uxfer)
sxfer = (struct libusb_super_transfer *)(
(uint8_t *)uxfer - sizeof(*sxfer));
retval = 0;
CTX_LOCK(dev->ctx);
pxfer0 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 0);
pxfer1 = libusb10_get_transfer(uxfer->dev_handle, endpoint, 1);
if (sxfer->entry.tqe_prev != NULL) {
if (sxfer->state != LIBUSB_SUPER_XFER_ST_PEND) {
/* only update the transfer status */
uxfer->status = LIBUSB_TRANSFER_CANCELLED;
retval = LIBUSB_ERROR_NOT_FOUND;
} else if (sxfer->entry.tqe_prev != NULL) {
/* we are lucky - transfer is on a queue */
TAILQ_REMOVE(&dev->tr_head, sxfer, entry);
sxfer->entry.tqe_prev = NULL;
libusb10_complete_transfer(NULL, sxfer, LIBUSB_TRANSFER_CANCELLED);
libusb10_complete_transfer(NULL,
sxfer, LIBUSB_TRANSFER_CANCELLED);
} else if (pxfer0 == NULL || pxfer1 == NULL) {
/* not started */
retval = LIBUSB_ERROR_NOT_FOUND;
} else if (libusb20_tr_get_priv_sc1(pxfer0) == sxfer) {
libusb10_complete_transfer(pxfer0, sxfer, LIBUSB_TRANSFER_CANCELLED);
libusb10_complete_transfer(pxfer0,
sxfer, LIBUSB_TRANSFER_CANCELLED);
libusb20_tr_stop(pxfer0);
/* make sure the queue doesn't stall */
libusb10_submit_transfer_sub(
uxfer->dev_handle, endpoint);
} else if (libusb20_tr_get_priv_sc1(pxfer1) == sxfer) {
libusb10_complete_transfer(pxfer1, sxfer, LIBUSB_TRANSFER_CANCELLED);
libusb10_complete_transfer(pxfer1,
sxfer, LIBUSB_TRANSFER_CANCELLED);
libusb20_tr_stop(pxfer1);
/* make sure the queue doesn't stall */
libusb10_submit_transfer_sub(
uxfer->dev_handle, endpoint);
} else {
/* not started */
retval = LIBUSB_ERROR_NOT_FOUND;
}
CTX_UNLOCK(dev->ctx);
DPRINTF(dev->ctx, LIBUSB_DEBUG_FUNCTION, "libusb_cancel_transfer leave");
return (0);
return (retval);
}
UNEXPORTED void

View File

@ -65,7 +65,9 @@ struct libusb_super_transfer {
uint8_t *curr_data;
uint32_t rem_len;
uint32_t last_len;
uint8_t flags;
uint8_t state;
#define LIBUSB_SUPER_XFER_ST_NONE 0
#define LIBUSB_SUPER_XFER_ST_PEND 1
};
struct libusb_context {

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd June 22, 2009
.Dd November 18, 2009
.Dt LIBUSB20 3
.Os
.Sh NAME
@ -98,6 +98,8 @@ USB access library (libusb -lusb)
.Fn libusb20_tr_setup_intr "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t timeout"
.Ft void
.Fn libusb20_tr_setup_isoc "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint61_t fr_index"
.Ft uint8_t
.Fn libusb20_tr_bulk_intr_sync "struct libusb20_transfer *xfer" "void *pbuf" "uint32_t length" "uint32_t *pactlen" "uint32_t timeout"
.Ft void
.Fn libusb20_tr_start "struct libusb20_transfer *xfer"
.Ft void
@ -451,6 +453,29 @@ is a helper function for setting up a multi frame USB ISOCHRONOUS transfer.
.
.Pp
.
.Fn libusb20_tr_bulk_intr_sync
will perform a synchronous BULK or INTERRUPT transfer having length given by the
.Fa length
argument and buffer pointer given by the
.Fa pbuf
argument on the USB transfer given by the
.Fa xfer
argument.
.
If the
.Fa pactlen
argument is non-NULL the actual transfer length will be stored at the given pointer destination.
.
If the
.Fa timeout
argument is non-zero the transfer will timeout after the given value in milliseconds.
.
This function does not change the transfer flags, like short packet not ok.
.
This function returns zero on success else a LIBUSB20_TRANSFER_XXX value is returned.
.
.Pp
.
.Fn libusb20_tr_start
will get the USB transfer started, if not already
started.

View File

@ -1,6 +1,6 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
* Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -263,6 +263,10 @@ libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer)
void
libusb20_tr_stop(struct libusb20_transfer *xfer)
{
if (!xfer->is_opened) {
/* transfer is not opened */
return;
}
if (!xfer->is_pending) {
/* transfer not pending */
return;
@ -280,6 +284,10 @@ libusb20_tr_stop(struct libusb20_transfer *xfer)
void
libusb20_tr_drain(struct libusb20_transfer *xfer)
{
if (!xfer->is_opened) {
/* transfer is not opened */
return;
}
/* make sure that we are cancelling */
libusb20_tr_stop(xfer);
@ -415,9 +423,79 @@ libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t leng
return;
}
uint8_t
libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer,
void *pbuf, uint32_t length, uint32_t *pactlen,
uint32_t timeout)
{
struct libusb20_device *pdev = xfer->pdev;
uint32_t transfer_max;
uint32_t transfer_act;
uint8_t retval;
/* set some sensible default value */
if (pactlen != NULL)
*pactlen = 0;
/* check for error condition */
if (libusb20_tr_pending(xfer))
return (LIBUSB20_ERROR_OTHER);
do {
/* compute maximum transfer length */
transfer_max =
libusb20_tr_get_max_total_length(xfer);
if (transfer_max > length)
transfer_max = length;
/* setup bulk or interrupt transfer */
libusb20_tr_setup_bulk(xfer, pbuf,
transfer_max, timeout);
/* start the transfer */
libusb20_tr_start(xfer);
/* wait for transfer completion */
while (libusb20_dev_process(pdev) == 0) {
if (libusb20_tr_pending(xfer) == 0)
break;
libusb20_dev_wait_process(pdev, -1);
}
transfer_act = libusb20_tr_get_actual_length(xfer);
/* update actual length, if any */
if (pactlen != NULL)
pactlen[0] += transfer_act;
/* check transfer status */
retval = libusb20_tr_get_status(xfer);
if (retval)
break;
/* check for short transfer */
if (transfer_act != transfer_max)
break;
/* update buffer pointer and length */
pbuf = ((uint8_t *)pbuf) + transfer_max;
length = length - transfer_max;
} while (length != 0);
return (retval);
}
void
libusb20_tr_submit(struct libusb20_transfer *xfer)
{
if (!xfer->is_opened) {
/* transfer is not opened */
return;
}
if (xfer->is_pending) {
/* should not happen */
return;
@ -433,6 +511,10 @@ libusb20_tr_submit(struct libusb20_transfer *xfer)
void
libusb20_tr_start(struct libusb20_transfer *xfer)
{
if (!xfer->is_opened) {
/* transfer is not opened */
return;
}
if (xfer->is_pending) {
if (xfer->is_cancel) {
/* cancelling - restart */
@ -461,7 +543,14 @@ libusb20_dev_close(struct libusb20_device *pdev)
for (x = 0; x != pdev->nTransfer; x++) {
xfer = pdev->pTransfer + x;
if (!xfer->is_opened) {
/* transfer is not opened */
continue;
}
libusb20_tr_drain(xfer);
libusb20_tr_close(xfer);
}
if (pdev->pTransfer != NULL) {

View File

@ -1,6 +1,6 @@
/* $FreeBSD$ */
/*-
* Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
* Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved.
* Copyright (c) 2007-2008 Daniel Drake. All rights reserved.
* Copyright (c) 2001 Johannes Erdfelt. All rights reserved.
*
@ -226,6 +226,7 @@ void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pbuf, uint32_t
void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pbuf, uint32_t timeout);
void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t timeout);
void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint16_t fr_index);
uint8_t libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t *pactlen, uint32_t timeout);
void libusb20_tr_start(struct libusb20_transfer *xfer);
void libusb20_tr_stop(struct libusb20_transfer *xfer);
void libusb20_tr_submit(struct libusb20_transfer *xfer);