From 88055d4b5913102a90ff666f75fd922c74860dc5 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Mon, 5 May 2008 20:57:43 +0100 Subject: [PATCH] Isochronous transfer helper functions --- libusb/core.c | 41 +++++++++++++++++++++++++++-- libusb/io.c | 19 +++++++------ libusb/libusb.h | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 129 insertions(+), 13 deletions(-) diff --git a/libusb/core.c b/libusb/core.c index fa70b82..1f9cfb5 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -464,6 +464,43 @@ API_EXPORTED uint8_t libusb_get_device_address(libusb_device *dev) } /** \ingroup dev + * Convenience function to retrieve the wMaxPacketSize value for a particular + * endpoint. This is useful for setting up isochronous transfers. + * + * \param dev a device + * \returns the wMaxPacketSize value, or LIBUSB_ERROR_NOT_FOUND if the endpoint + * does not exist. + */ +API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + int iface_idx; + /* FIXME: active config considerations? */ + struct libusb_config_descriptor *config = dev->config; + + for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { + const struct libusb_interface *iface = &config->interface[iface_idx]; + int altsetting_idx; + + for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; + altsetting_idx++) { + const struct libusb_interface_descriptor *altsetting + = &iface->altsetting[altsetting_idx]; + int ep_idx; + + for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { + const struct libusb_endpoint_descriptor *ep = + &altsetting->endpoint[ep_idx]; + if (ep->bEndpointAddress == endpoint) + return ep->wMaxPacketSize; + } + } + } + + return LIBUSB_ERROR_NOT_FOUND; +} + +/** \ingroup dev * Increment the reference count of a device. * \param dev the device to reference * \returns the same device @@ -818,7 +855,7 @@ API_EXPORTED int libusb_reset_device(libusb_device_handle *dev) * \returns 0 if no kernel driver is active * \returns 1 if a kernel driver is active * \returns LIBUSB_ERROR code on failure - * \see libusb_detach_kernel_driver + * \see libusb_detach_kernel_driver() */ API_EXPORTED int libusb_kernel_driver_active(libusb_device_handle *dev, int interface) @@ -840,7 +877,7 @@ API_EXPORTED int libusb_kernel_driver_active(libusb_device_handle *dev, * \returns LIBUSB_ERROR_NOT_FOUND if no kernel driver was active * \returns LIBUSB_ERROR_INVALID_PARAM if the interface does not exist * \returns another LIBUSB_ERROR code on other failure - * \see libusb_kernel_driver_active + * \see libusb_kernel_driver_active() */ API_EXPORTED int libusb_detach_kernel_driver(libusb_device_handle *dev, int interface) diff --git a/libusb/io.c b/libusb/io.c index ac153ce..dbce0b1 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -343,7 +343,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * \section asynciso Considerations for isochronous transfers * - * As isochronous transfers are more complicated than transfers to + * Isochronous transfers are more complicated than transfers to * non-isochronous endpoints. * * To perform I/O to an isochronous endpoint, allocate the transfer by calling @@ -360,10 +360,13 @@ if (r == 0 && actual_length == sizeof(data)) { * 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 + * packet length is determined by wMaxPacketSize field in the endpoint + * descriptor. Two functions can help you here: + * + * - libusb_get_max_packet_size() is an easy way to determine the max + * packet size for an endpoint. + * - libusb_set_iso_packet_lengths() assigns the same length to all packets + * within a transfer, which is usually what you want. * * For outgoing transfers, you'll obviously fill the buffer and populate the * packet descriptors in hope that all the data gets transferred. For incoming @@ -392,9 +395,9 @@ if (r == 0 && actual_length == sizeof(data)) { * - 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. + * can be calculated as if each prior packet completed in full. The + * libusb_get_iso_packet_offset() and libusb_get_iso_packet_offset_simple() + * functions may help you here. * * \section asyncmem Memory caveats * diff --git a/libusb/libusb.h b/libusb/libusb.h index b568d36..97b3847 100644 --- a/libusb/libusb.h +++ b/libusb/libusb.h @@ -652,6 +652,7 @@ libusb_device *libusb_ref_device(libusb_device *dev); void libusb_unref_device(libusb_device *dev); uint8_t libusb_get_bus_number(libusb_device *dev); uint8_t libusb_get_device_address(libusb_device *dev); +int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint); libusb_device_handle *libusb_open(libusb_device *dev); void libusb_close(libusb_device_handle *dev_handle); @@ -878,6 +879,81 @@ static inline void libusb_fill_iso_transfer(struct libusb_transfer *transfer, transfer->callback = callback; } +/** \ingroup asyncio + * Convenience function to set the length of all packets in an isochronous + * transfer, based on the num_iso_packets field in the transfer structure. + * + * \param transfer a transfer + * \param length the length to set in each isochronous packet descriptor + * \see libusb_get_max_packet_size() + */ +static inline void libusb_set_iso_packet_lengths( + struct libusb_transfer *transfer, unsigned int length) +{ + int i; + for (i = 0; i < transfer->num_iso_packets; i++) + transfer->iso_packet_desc[i].length = length; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer. + * + * This is a thorough function which loops through all preceding packets, + * accumulating their lengths to find the position of the specified packet. + * Typically you will assign equal lengths to each packet in the transfer, + * and hence the above method is sub-optimal. You may wish to use + * libusb_get_iso_packet_buffer_simple() instead. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer_simple() + */ +static inline unsigned char *libusb_get_iso_packet_buffer( + struct libusb_transfer *transfer, unsigned int packet) +{ + int i; + size_t offset = 0; + + if (packet >= transfer->num_iso_packets) + return NULL; + + for (i = 0; i < packet; i++) + offset += transfer->iso_packet_desc[i].length; + + return transfer->buffer + offset; +} + +/** \ingroup asyncio + * Convenience function to locate the position of an isochronous packet + * within the buffer of an isochronous transfer, for transfers where each + * packet is of identical size. + * + * This function relies on the assumption that every packet within the transfer + * is of identical size to the first packet. Calculating the location of + * the packet buffer is then just a simple calculation: + * buffer + (packet_size * packet) + * + * Do not use this function on transfers other than those that have identical + * packet lengths for each packet. + * + * \param transfer a transfer + * \param packet the packet to return the address of + * \returns the base address of the packet buffer inside the transfer buffer, + * or NULL if the packet does not exist. + * \see libusb_get_iso_packet_buffer() + */ +static inline unsigned char *libusb_get_iso_packet_buffer_simple( + struct libusb_transfer *transfer, unsigned int packet) +{ + if (packet >= transfer->num_iso_packets) + return NULL; + + return transfer->buffer + (transfer->iso_packet_desc[0].length * packet); +} + /* sync I/O */ int libusb_control_transfer(libusb_device_handle *dev_handle, @@ -924,7 +1000,7 @@ static inline int libusb_get_descriptor(libusb_device_handle *dev, * \param data output buffer for descriptor * \param length size of data buffer * \returns number of bytes returned in data, or LIBUSB_ERROR code on failure - * \see libusb_get_string_descriptor_ascii + * \see libusb_get_string_descriptor_ascii() */ static inline int libusb_get_string_descriptor(libusb_device_handle *dev, uint8_t desc_index, uint16_t langid, unsigned char *data, int length) @@ -964,7 +1040,7 @@ const struct libusb_pollfd **libusb_get_pollfds(void); * \param fd the new file descriptor * \param events events to monitor for, see \ref libusb_pollfd for a * description - * \see libusb_set_pollfd_notifiers + * \see libusb_set_pollfd_notifiers() */ typedef void (*libusb_pollfd_added_cb)(int fd, short events); @@ -973,7 +1049,7 @@ typedef void (*libusb_pollfd_added_cb)(int fd, short events); * the set of file descriptors being monitored for events. After returning * from this callback, do not use that file descriptor again. * \param fd the file descriptor to stop monitoring - * \see libusb_set_pollfd_notifiers + * \see libusb_set_pollfd_notifiers() */ typedef void (*libusb_pollfd_removed_cb)(int fd); void libusb_set_pollfd_notifiers(libusb_pollfd_added_cb added_cb, -- 2.7.4