Isochronous transfer helper functions
authorDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 19:57:43 +0000 (20:57 +0100)
committerDaniel Drake <dsd@gentoo.org>
Mon, 5 May 2008 19:57:43 +0000 (20:57 +0100)
libusb/core.c
libusb/io.c
libusb/libusb.h

index fa70b82..1f9cfb5 100644 (file)
@@ -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)
index ac153ce..dbce0b1 100644 (file)
@@ -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
  *
index b568d36..97b3847 100644 (file)
@@ -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:
+ * <tt>buffer + (packet_size * packet)</tt>
+ *
+ * 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,