From 4594d907db0042ec024fd46cdbaa4bed9a51cd35 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Fri, 20 Nov 2009 08:57:25 +0000 Subject: [PATCH] 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] --- lib/libusb/libusb10.c | 41 +++++++++++++++---- lib/libusb/libusb10.h | 4 +- lib/libusb/libusb20.3 | 27 ++++++++++++- lib/libusb/libusb20.c | 91 ++++++++++++++++++++++++++++++++++++++++++- lib/libusb/libusb20.h | 3 +- 5 files changed, 155 insertions(+), 11 deletions(-) diff --git a/lib/libusb/libusb10.c b/lib/libusb/libusb10.c index 55ee1c509e76..9a5154e7e5f9 100644 --- a/lib/libusb/libusb10.c +++ b/lib/libusb/libusb10.c @@ -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 diff --git a/lib/libusb/libusb10.h b/lib/libusb/libusb10.h index 338c2aab8885..d2a2bd7c150b 100644 --- a/lib/libusb/libusb10.h +++ b/lib/libusb/libusb10.h @@ -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 { diff --git a/lib/libusb/libusb20.3 b/lib/libusb/libusb20.3 index f902883107bd..5404013ee999 100644 --- a/lib/libusb/libusb20.3 +++ b/lib/libusb/libusb20.3 @@ -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. diff --git a/lib/libusb/libusb20.c b/lib/libusb/libusb20.c index 1a338052c939..41f9ac72de60 100644 --- a/lib/libusb/libusb20.c +++ b/lib/libusb/libusb20.c @@ -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) { diff --git a/lib/libusb/libusb20.h b/lib/libusb/libusb20.h index 9f45861f7388..bb7d8a4ab3d2 100644 --- a/lib/libusb/libusb20.h +++ b/lib/libusb/libusb20.h @@ -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);