summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2008-05-05 20:57:43 +0100
committerDaniel Drake <dsd@gentoo.org>2008-05-05 20:57:43 +0100
commit88055d4b5913102a90ff666f75fd922c74860dc5 (patch)
tree88b3006b30dba75662b3a1980c341ef5479277b4
parenta95c943ed301dcd20e92b0b3b255568899a4c42e (diff)
downloadlibusb-88055d4b5913102a90ff666f75fd922c74860dc5.tar.gz
Isochronous transfer helper functions
-rw-r--r--libusb/core.c41
-rw-r--r--libusb/io.c19
-rw-r--r--libusb/libusb.h82
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:
+ * <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,