From 211f80c9f2a4a58cd2bbf5b7751f45089c8961e7 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Tue, 25 Mar 2008 16:24:30 +0000 Subject: [PATCH] Isochronous endpoint I/O Due to variable-sized structures, this involved changing allocation mechanism. All transfers must now be allocated and freed through libusb. A synchronous function is missing, and I could do with writing a few more helper functions to simplify things. --- TODO | 2 +- examples/dpfp.c | 6 +- libusb/io.c | 179 ++++++++++++++++++++++++++++-------------------- libusb/libusb.h | 62 ++++++++++++++++- libusb/libusbi.h | 37 ++++++++-- libusb/os/linux_usbfs.c | 81 +++++++++++++++------- libusb/sync.c | 4 +- 7 files changed, 256 insertions(+), 115 deletions(-) diff --git a/TODO b/TODO index ed8ace8..f02ea3a 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ for 1.0 ======= cancellation race concerns - better tracking of kernel feedback? -isochronous endpoint I/O thread safety error codes fixme review @@ -11,6 +10,7 @@ rename poll to handle_events make libusb_get_pollfds return const? doxygen warnings make descriptor things const? +isochronous sync I/O? 1.0 API style/naming points to reconsider ========================================= diff --git a/examples/dpfp.c b/examples/dpfp.c index 5d50598..00aaa73 100644 --- a/examples/dpfp.c +++ b/examples/dpfp.c @@ -172,7 +172,7 @@ static int set_mode_async(unsigned char data) if (!buf) return -ENOMEM; - transfer = libusb_alloc_transfer(); + transfer = libusb_alloc_transfer(0); if (!transfer) { free(buf); return -ENOMEM; @@ -398,11 +398,11 @@ static int do_init(void) static int alloc_transfers(void) { - img_transfer = libusb_alloc_transfer(); + img_transfer = libusb_alloc_transfer(0); if (!img_transfer) return -ENOMEM; - irq_transfer = libusb_alloc_transfer(); + irq_transfer = libusb_alloc_transfer(0); if (!irq_transfer) return -ENOMEM; diff --git a/libusb/io.c b/libusb/io.c index 96d98db..e28b8e0 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -32,8 +32,6 @@ #include "libusbi.h" -#define TRANSFER_TO_PRIV(trf) (container_of((trf), struct usbi_transfer, pub)) - /* this is a list of in-flight rb_handles, sorted by timeout expiration. * URBs to timeout the soonest are placed at the beginning of the list, URBs * that will time out later are placed after, and urbs with infinite timeout @@ -215,15 +213,8 @@ if (r == 0 && actual_length == sizeof(data)) { * generic transfer object mentioned above. At this stage, the transfer * is "blank" with no details about what type of I/O it will be used for. * - * Allocation is done with the libusb_alloc_transfer() function. It is also - * possible to allocate your own libusb_transfer structure, but only if you - * take the following into account: - * -# For implementation reasons, the memory allocated for the transfer must - * actually be larger than sizeof(struct libusb_transfer). If you wish to - * allocate your own transfers, you must allocate them with the size - * determined by libusb_get_transfer_alloc_size(). - * -# After allocating space for a transfer, you must initialize it with - * libusb_init_transfer(). + * Allocation is done with the libusb_alloc_transfer() function. You must use + * this function rather than allocating your own transfers. * * \subsection asyncfill Filling * @@ -259,11 +250,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * When a transfer has completed (i.e. the callback function has been invoked), * you are advised to free the transfer (unless you wish to resubmit it, see - * below). - * - * If you allocated the transfer with libusb_alloc_transfer(), deallocate it - * with libusb_free_transfer(). If you're using your own memory management - * scheme, free it through your scheme. + * below). Transfers are deallocated with libusb_free_transfer(). * * It is undefined behaviour to free a transfer which has not completed. * @@ -342,6 +329,61 @@ if (r == 0 && actual_length == sizeof(data)) { * and libusb_control_transfer_get_setup() functions within your transfer * callback. * + * \section asynciso Considerations for isochronous transfers + * + * As isochronous transfers are more complicated than transfers to + * non-isochronous endpoints. + * + * To perform I/O to an isochronous endpoint, allocate the transfer by calling + * libusb_alloc_transfer() with an appropriate number of isochronous packets. + * + * During filling, set \ref libusb_transfer::endpoint_type "endpoint_type" to + * \ref libusb_endpoint_type::LIBUSB_ENDPOINT_TYPE_ISOCHRONOUS + * "LIBUSB_ENDPOINT_TYPE_ISOCHRONOUS", and set + * \ref libusb_transfer::num_iso_packets "num_iso_packets" to a value less than + * or equal to the number of packets you requested during allocation. + * libusb_alloc_transfer() does not set either of these fields for you, given + * that you might not even use the transfer on an isochronous endpoint. + * + * Next, populate the length field for the first num_iso_packets entries in + * the \ref libusb_transfer::iso_packet_desc "iso_packet_desc" array. Section + * 5.6.3 of the USB2 specifications describe how the maximum isochronous + * packet length is determined by the endpoint descriptor. FIXME need a helper + * function to find this. + * FIXME, write a helper function to set the length for all iso packets in an + * array + * + * For outgoing transfers, you'll obviously fill the buffer and populate the + * packet descriptors in hope that all the data gets transferred. For incoming + * transfers, you must ensure the buffer has sufficient capacity for + * the situation where all packets transfer the full amount of requested data. + * + * Completion handling requires some extra consideration. The + * \ref libusb_transfer::actual_length "actual_length" field of the transfer + * is meaningless and should not be examined; instead you must refer to the + * \ref libusb_iso_packet_descriptor::actual_length "actual_length" field of + * each individual packet. + * + * The \ref libusb_transfer::status "status" field of the transfer is also a + * little misleading: + * - If the packets were submitted and the isochronous data microframes + * completed normally, status will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_COMPLETED + * "LIBUSB_TRANSFER_COMPLETED". Note that bus errors and software-incurred + * delays are not counted as transfer errors; the transfer.status field may + * indicate COMPLETED even if some or all of the packets failed. Refer to + * the \ref libusb_iso_packet_descriptor::status "status" field of each + * individual packet to determine packet failures. + * - The status field will have value + * \ref libusb_transfer_status::LIBUSB_TRANSFER_ERROR + * "LIBUSB_TRANSFER_ERROR" only when serious errors were encountered. + * - Other transfer status codes occur with normal behaviour. + * + * The data for each packet will be found at an offset into the buffer that + * can be calculated as if each prior packet completed in full. FIXME write + * a helper function to determine this, and flesh this description out a bit + * more. + * * \section asyncmem Memory caveats * * In most circumstances, it is not safe to use stack memory for transfer @@ -513,7 +555,8 @@ static int calculate_timeout(struct usbi_transfer *transfer) { int r; struct timespec current_time; - unsigned int timeout = transfer->pub.timeout; + unsigned int timeout = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout; if (!timeout) return 0; @@ -581,63 +624,44 @@ static int submit_transfer(struct usbi_transfer *itransfer) } /** \ingroup asyncio - * Obtain the memory-resident size of a transfer structure. Normally, you do - * not care about this as you will use libusb_alloc_transfer() and - * libusb_free_transfer() which hide this implementation detail from you. - * - * This function is useful when you wish to allocate transfers using your - * own custom memory allocator. libusb's transfer structure is bigger than - * it appears, so you must use this function to find out how much space is - * required for each transfer allocation. - * - * If you are allocating your own transfers, remember to initialize them - * with libusb_init_transfer(). - * - * \returns the true size of a libusb_transfer structure - */ -API_EXPORTED size_t libusb_get_transfer_alloc_size(void) -{ - return sizeof(struct usbi_transfer) + usbi_backend->transfer_priv_size; -} - -void __init_transfer(struct usbi_transfer *transfer) -{ - memset(transfer, 0, sizeof(*transfer)); -} - -/** \ingroup asyncio - * Initialize a libusb transfer. This function is only for users who allocate - * their transfers using their own memory allocator. The more standard - * libusb_alloc_transfer() returns pre-initialized transfers. - * \param transfer a transfer to initialize - */ -API_EXPORTED void libusb_init_transfer(struct libusb_transfer *transfer) -{ - __init_transfer(TRANSFER_TO_PRIV(transfer)); -} - -/** \ingroup asyncio - * Allocate a libusb transfer using the standard system memory allocator. The - * returned transfer is pre-initialized for you. When the new transfer is no - * longer needed, it should be freed with libusb_free_transfer(). - * - * \note - * Instead of using this function, it is legal for you to allocate transfers - * using a memory allocator of your choosing, but only if you consider the - * hidden size requirement (see libusb_get_transfer_alloc_size()) and - * initialize them before use (see libusb_init_transfer()). - * + * Allocate a libusb transfer with a specified number of isochronous packet + * descriptors. The returned transfer is pre-initialized for you. When the new + * transfer is no longer needed, it should be freed with + * libusb_free_transfer(). + * + * Transfers intended for non-isochronous endpoints (e.g. control, bulk, + * interrupt) should specify an iso_packets count of zero. + * + * For transfers intended for isochronous endpoints, specify an appropriate + * number of packet descriptors to be allocated as part of the transfer. + * The returned transfer is not specially initialized for isochronous I/O; + * you are still required to set the + * \ref libusb_transfer::num_iso_packets "num_iso_packets" and + * \ref libusb_transfer::endpoint_type "endpoint_type" fields accordingly. + * + * It is safe to allocate a transfer with some isochronous packets and then + * use it on a non-isochronous endpoint. If you do this, ensure that at time + * of submission, num_iso_packets is 0 and that endpoint_type is set + * appropriately. + * + * \param iso_packets number of isochronous packet descriptors to allocate * \returns a newly allocated transfer, or NULL on error */ -API_EXPORTED struct libusb_transfer *libusb_alloc_transfer(void) +API_EXPORTED struct libusb_transfer *libusb_alloc_transfer(int iso_packets) { - struct usbi_transfer *transfer = - malloc(sizeof(*transfer) + usbi_backend->transfer_priv_size); - if (!transfer) + size_t os_alloc_size = usbi_backend->transfer_priv_size + + (usbi_backend->add_iso_packet_size * iso_packets); + int alloc_size = sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets) + + os_alloc_size; + struct usbi_transfer *itransfer = malloc(alloc_size); + if (!itransfer) return NULL; - __init_transfer(transfer); - return &transfer->pub; + memset(itransfer, 0, alloc_size); + itransfer->num_iso_packets = iso_packets; + return __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); } /** \ingroup asyncio @@ -663,7 +687,7 @@ API_EXPORTED void libusb_free_transfer(struct libusb_transfer *transfer) if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer) free(transfer->buffer); - itransfer = TRANSFER_TO_PRIV(transfer); + itransfer = __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); free(itransfer); } @@ -680,7 +704,8 @@ API_EXPORTED void libusb_free_transfer(struct libusb_transfer *transfer) */ API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer) { - struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); + struct usbi_transfer *itransfer = + __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); int r; itransfer->transferred = 0; @@ -720,7 +745,8 @@ API_EXPORTED int libusb_submit_transfer(struct libusb_transfer *transfer) */ API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer) { - struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); + struct usbi_transfer *itransfer = + __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); int r; usbi_dbg(""); @@ -743,7 +769,8 @@ API_EXPORTED int libusb_cancel_transfer(struct libusb_transfer *transfer) */ API_EXPORTED int libusb_cancel_transfer_sync(struct libusb_transfer *transfer) { - struct usbi_transfer *itransfer = TRANSFER_TO_PRIV(transfer); + struct usbi_transfer *itransfer = + __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); int r; usbi_dbg(""); @@ -766,7 +793,8 @@ API_EXPORTED int libusb_cancel_transfer_sync(struct libusb_transfer *transfer) void usbi_handle_transfer_completion(struct usbi_transfer *itransfer, enum libusb_transfer_status status) { - struct libusb_transfer *transfer = &itransfer->pub; + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); uint8_t flags; if (status == LIBUSB_TRANSFER_SILENT_COMPLETION) @@ -826,7 +854,8 @@ static void handle_timeout(struct usbi_transfer *itransfer) * completed. we asynchronously cancel the URB and report timeout * to the user when the URB cancellation completes (or not at all if the * URB actually gets delivered as per this race) */ - struct libusb_transfer *transfer = &itransfer->pub; + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); int r; itransfer->flags |= USBI_TRANSFER_TIMED_OUT; diff --git a/libusb/libusb.h b/libusb/libusb.h index 4aa0e13..4d94224 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -528,6 +528,19 @@ enum libusb_transfer_flags { LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, }; +/** \ingroup asyncio + * Isochronous packet descriptor. */ +struct libusb_iso_packet_descriptor { + /** Length of data to request in this packet */ + unsigned int length; + + /** Amount of data that was actually transferred */ + unsigned int actual_length; + + /** Status code for this packet */ + enum libusb_transfer_status status; +}; + struct libusb_transfer; typedef void (*libusb_transfer_cb_fn)(struct libusb_transfer *transfer); @@ -555,14 +568,20 @@ struct libusb_transfer { /* FIXME: make const? */ /** The status of the transfer. Read-only, and only for use within - * transfer callback function. */ + * transfer callback function. + * + * If this is an isochronous transfer, this field may read COMPLETED even + * if there were errors in the frames. Use the + * \ref libusb_iso_packet_descriptor::status "status" field in each packet + * to determine if errors occurred. */ enum libusb_transfer_status status; /** Length of the data buffer */ int length; /** Actual length of data that was transferred. Read-only, and only for - * use within transfer callback function. */ + * use within transfer callback function. Not valid for isochronous + * endpoint transfers. */ int actual_length; /** Callback function. This will be invoked when the transfer completes, @@ -574,6 +593,13 @@ struct libusb_transfer { /** Data buffer */ unsigned char *buffer; + + /** Number of isochronous packets. Only used for I/O with isochronous + * endpoints. */ + int num_iso_packets; + + /** Isochronous packet descriptors, for isochronous transfers only. */ + struct libusb_iso_packet_descriptor iso_packet_desc[0]; }; int libusb_init(void); @@ -674,7 +700,7 @@ static inline void libusb_fill_control_setup(unsigned char *buffer, size_t libusb_get_transfer_alloc_size(void); void libusb_init_transfer(struct libusb_transfer *transfer); -struct libusb_transfer *libusb_alloc_transfer(void); +struct libusb_transfer *libusb_alloc_transfer(int iso_packets); int libusb_submit_transfer(struct libusb_transfer *transfer); int libusb_cancel_transfer(struct libusb_transfer *transfer); int libusb_cancel_transfer_sync(struct libusb_transfer *transfer); @@ -780,6 +806,36 @@ static inline void libusb_fill_interrupt_transfer( transfer->callback = callback; } +/** \ingroup asyncio + * Helper function to populate the required \ref libusb_transfer fields + * for an isochronous transfer. + * + * \param transfer the transfer to populate + * \param dev_handle handle of the device that will handle the transfer + * \param endpoint address of the endpoint where this transfer will be sent + * \param buffer data buffer + * \param length length of data buffer + * \param num_iso_packets the number of isochronous packets + * \param callback callback function to be invoked on transfer completion + * \param user_data user data to pass to callback function + * \param timeout timeout for the transfer in milliseconds + */ +static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, + libusb_device_handle *dev_handle, unsigned char endpoint, + unsigned char *buffer, int length, int num_iso_packets, + libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) +{ + transfer->dev_handle = dev_handle; + transfer->endpoint = endpoint; + transfer->endpoint_type = LIBUSB_ENDPOINT_TYPE_BULK; + transfer->timeout = timeout; + transfer->buffer = buffer; + transfer->length = length; + transfer->num_iso_packets = num_iso_packets; + transfer->user_data = user_data; + transfer->callback = callback; +} + /* sync I/O */ int libusb_control_transfer(libusb_device_handle *dev_handle, diff --git a/libusb/libusbi.h b/libusb/libusbi.h index 934fd60..3cc33bf 100644 --- a/libusb/libusbi.h +++ b/libusb/libusbi.h @@ -159,18 +159,42 @@ struct libusb_device_handle { #define USBI_TRANSFER_SYNC_CANCELLED (1<<0) #define USBI_TRANSFER_TIMED_OUT (1<<1) -struct usbi_transfer { - /* must come first */ - struct libusb_transfer pub; +/* in-memory transfer layout: + * + * 1. struct usbi_transfer + * 2. struct libusb_transfer (which includes iso packets) [variable size] + * 3. os private data [variable size] + * + * from a libusb_transfer, you can get the usbi_transfer by rewinding the + * appropriate number of bytes. + * the usbi_transfer includes the number of allocated packets, so you can + * determine the size of the transfer and hence the start and length of the + * OS-private data. + */ +struct usbi_transfer { + int num_iso_packets; struct list_head list; struct timeval timeout; int transferred; uint8_t flags; - - unsigned char os_priv[0]; }; +#define __USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ + ((struct libusb_transfer *)(((void *)(transfer)) \ + + sizeof(struct usbi_transfer))) +#define __LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *)(((void *)(transfer)) \ + - sizeof(struct usbi_transfer))) + +static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) +{ + return ((void *)transfer) + sizeof(struct usbi_transfer) + + sizeof(struct libusb_transfer) + + (transfer->num_iso_packets + * sizeof(struct libusb_iso_packet_descriptor)); +} + /* bus structures */ /* All standard descriptors have these 2 fields in common */ @@ -257,6 +281,9 @@ struct usbi_os_backend { /* number of bytes to reserve for usbi_transfer.os_priv */ size_t transfer_priv_size; + + /* number of additional bytes for os_priv for each iso packet */ + size_t add_iso_packet_size; }; extern const struct usbi_os_backend * const usbi_backend; diff --git a/libusb/os/linux_usbfs.c b/libusb/os/linux_usbfs.c index f173930..0b746a2 100644 --- a/libusb/os/linux_usbfs.c +++ b/libusb/os/linux_usbfs.c @@ -46,10 +46,6 @@ struct linux_device_handle_priv { int fd; }; -struct linux_transfer_priv { - struct usbfs_urb urb; -}; - static struct linux_device_priv *__device_priv(struct libusb_device *dev) { return (struct linux_device_priv *) dev->os_priv; @@ -61,16 +57,6 @@ static struct linux_device_handle_priv *__device_handle_priv( return (struct linux_device_handle_priv *) handle->os_priv; } -static struct linux_transfer_priv *__transfer_priv( - struct usbi_transfer *transfer) -{ - return (struct linux_transfer_priv *) transfer->os_priv; -} - -#define TRANSFER_PRIV_GET_ITRANSFER(tpriv) \ - ((struct usbi_transfer *) \ - container_of((tpriv), struct usbi_transfer, os_priv)) - static int check_usb_vfs(const char *dirname) { DIR *dir; @@ -432,15 +418,22 @@ static void op_destroy_device(struct libusb_device *dev) static int submit_transfer(struct usbi_transfer *itransfer) { - struct libusb_transfer *transfer = &itransfer->pub; - struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb; + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + struct usbfs_urb *urb = usbi_transfer_get_os_priv(itransfer); struct linux_device_handle_priv *dpriv = __device_handle_priv(transfer->dev_handle); int to_be_transferred = transfer->length - itransfer->transferred; int r; urb->buffer = transfer->buffer + itransfer->transferred; - urb->buffer_length = MIN(to_be_transferred, MAX_URB_BUFFER_LENGTH); + if (urb->type == USBFS_URB_TYPE_ISO) { + /* FIXME: iso stuff needs reworking. if a big transfer is submitted, + * split it up into multiple URBs. */ + urb->buffer_length = to_be_transferred; + } else { + urb->buffer_length = MIN(to_be_transferred, MAX_URB_BUFFER_LENGTH); + } /* FIXME: for requests that we have to split into multiple URBs, we should * submit all the URBs instantly: submit, submit, submit, reap, reap, reap @@ -457,12 +450,29 @@ static int submit_transfer(struct usbi_transfer *itransfer) return r; } +static void fill_iso_packet_descriptors(struct usbfs_urb *urb, + struct usbi_transfer *itransfer) +{ + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + int i; + + for (i = 0; i < transfer->num_iso_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[i]; + urb_desc->length = lib_desc->length; + } +} + static int op_submit_transfer(struct usbi_transfer *itransfer) { - struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb; - struct libusb_transfer *transfer = &itransfer->pub; + struct usbfs_urb *urb = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); memset(urb, 0, sizeof(*urb)); + urb->usercontext = itransfer; switch (transfer->endpoint_type) { case LIBUSB_ENDPOINT_TYPE_CONTROL: urb->type = USBFS_URB_TYPE_CONTROL; @@ -473,6 +483,13 @@ static int op_submit_transfer(struct usbi_transfer *itransfer) case LIBUSB_ENDPOINT_TYPE_INTERRUPT: urb->type = USBFS_URB_TYPE_INTERRUPT; break; + case LIBUSB_ENDPOINT_TYPE_ISOCHRONOUS: + urb->type = USBFS_URB_TYPE_ISO; + /* FIXME: interface for non-ASAP data? */ + urb->flags = USBFS_URB_ISO_ASAP; + fill_iso_packet_descriptors(urb, itransfer); + urb->number_of_packets = transfer->num_iso_packets; + break; default: usbi_err("unknown endpoint type %d", transfer->endpoint_type); return -EINVAL; @@ -484,8 +501,9 @@ static int op_submit_transfer(struct usbi_transfer *itransfer) static int op_cancel_transfer(struct usbi_transfer *itransfer) { - struct usbfs_urb *urb = &__transfer_priv(itransfer)->urb; - struct libusb_transfer *transfer = &itransfer->pub; + struct usbfs_urb *urb = usbi_transfer_get_os_priv(itransfer); + struct libusb_transfer *transfer = + __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct linux_device_handle_priv *dpriv = __device_handle_priv(transfer->dev_handle); @@ -497,7 +515,6 @@ static int reap_for_handle(struct libusb_device_handle *handle) struct linux_device_handle_priv *hpriv = __device_handle_priv(handle); int r; struct usbfs_urb *urb; - struct linux_transfer_priv *tpriv; struct usbi_transfer *itransfer; struct libusb_transfer *transfer; int trf_requested; @@ -511,9 +528,8 @@ static int reap_for_handle(struct libusb_device_handle *handle) return r; } - tpriv = container_of(urb, struct linux_transfer_priv, urb); - itransfer = TRANSFER_PRIV_GET_ITRANSFER(tpriv); - transfer = &itransfer->pub; + itransfer = urb->usercontext; + transfer = __USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); usbi_dbg("urb type=%d status=%d transferred=%d", urb->type, urb->status, urb->actual_length); @@ -528,6 +544,18 @@ static int reap_for_handle(struct libusb_device_handle *handle) if (urb->status != 0) usbi_warn("unrecognised urb status %d", urb->status); + /* copy isochronous packet results back */ + if (transfer->endpoint_type == LIBUSB_ENDPOINT_TYPE_ISOCHRONOUS) { + int i; + for (i = 0; i < urb->number_of_packets; i++) { + struct usbfs_iso_packet_desc *urb_desc = &urb->iso_frame_desc[i]; + struct libusb_iso_packet_descriptor *lib_desc = + &transfer->iso_packet_desc[i]; + lib_desc->status = urb_desc->status; + lib_desc->actual_length = urb_desc->actual_length; + } + } + /* determine how much data was asked for */ length = transfer->length; if (transfer->endpoint_type == LIBUSB_ENDPOINT_TYPE_CONTROL) @@ -598,6 +626,7 @@ const struct usbi_os_backend linux_usbfs_backend = { .device_priv_size = sizeof(struct linux_device_priv), .device_handle_priv_size = sizeof(struct linux_device_handle_priv), - .transfer_priv_size = sizeof(struct linux_transfer_priv), + .transfer_priv_size = sizeof(struct usbfs_urb), + .add_iso_packet_size = sizeof(struct usbfs_iso_packet_desc), }; diff --git a/libusb/sync.c b/libusb/sync.c index 15be26d..c59122e 100644 --- a/libusb/sync.c +++ b/libusb/sync.c @@ -66,7 +66,7 @@ API_EXPORTED int libusb_control_transfer(libusb_device_handle *dev_handle, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, unsigned char *data, uint16_t wLength, unsigned int timeout) { - struct libusb_transfer *transfer = libusb_alloc_transfer(); + struct libusb_transfer *transfer = libusb_alloc_transfer(0); unsigned char *buffer; int completed = 0; int r; @@ -134,7 +134,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, unsigned char endpoint, unsigned char *buffer, int length, int *transferred, unsigned int timeout, unsigned char endpoint_type) { - struct libusb_transfer *transfer = libusb_alloc_transfer(); + struct libusb_transfer *transfer = libusb_alloc_transfer(0); int completed = 0; int r; -- 2.7.4