Isochronous endpoint I/O
authorDaniel Drake <dsd@gentoo.org>
Tue, 25 Mar 2008 16:24:30 +0000 (16:24 +0000)
committerDaniel Drake <dsd@gentoo.org>
Sun, 30 Mar 2008 21:17:34 +0000 (22:17 +0100)
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
examples/dpfp.c
libusb/io.c
libusb/libusb.h
libusb/libusbi.h
libusb/os/linux_usbfs.c
libusb/sync.c

diff --git a/TODO b/TODO
index ed8ace8..f02ea3a 100644 (file)
--- 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
 =========================================
index 5d50598..00aaa73 100644 (file)
@@ -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;
 
index 96d98db..e28b8e0 100644 (file)
@@ -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;
index 4aa0e13..4d94224 100644 (file)
@@ -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,
index 934fd60..3cc33bf 100644 (file)
@@ -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;
index f173930..0b746a2 100644 (file)
@@ -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),
 };
 
index 15be26d..c59122e 100644 (file)
@@ -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;