From 0334ee642b47dfe1ca9db64b22e7702ea14b3f09 Mon Sep 17 00:00:00 2001 From: Daniel Drake Date: Sun, 28 Jun 2009 19:49:10 +0100 Subject: [PATCH] Add libusb_get_max_iso_packet_size() As pointed out by Dennis Muhlestein, libusb_get_max_packet_size() doesn't really do what the documentation might suggest because it does not consider the number of transaction opportunities per microframe. Add a new function to do what is useful for isochronous I/O. --- libusb/core.c | 113 ++++++++++++++++++++++++++++++++++++++++++++++------------ libusb/io.c | 15 +++++--- 2 files changed, 101 insertions(+), 27 deletions(-) diff --git a/libusb/core.c b/libusb/core.c index b188a38..ce914ce 100644 --- a/libusb/core.c +++ b/libusb/core.c @@ -626,10 +626,40 @@ API_EXPORTED uint8_t libusb_get_device_address(libusb_device *dev) return dev->device_address; } +static const struct libusb_endpoint_descriptor *find_endpoint( + struct libusb_config_descriptor *config, unsigned char endpoint) +{ + int iface_idx; + 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; + } + } + } + return NULL; +} + /** \ingroup dev * Convenience function to retrieve the wMaxPacketSize value for a particular - * endpoint in the active device configuration. This is useful for setting up - * isochronous transfers. + * endpoint in the active device configuration. + * + * This function was originally intended to be of assistance when setting up + * isochronous transfers, but a design mistake resulted in this function + * instead. It simply returns the wMaxPacketSize value without considering + * its contents. If you're dealing with isochronous transfers, you probably + * want libusb_get_max_iso_packet_size() instead. * * \param dev a device * \param endpoint address of the endpoint in question @@ -640,8 +670,8 @@ API_EXPORTED uint8_t libusb_get_device_address(libusb_device *dev) API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev, unsigned char endpoint) { - int iface_idx; struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; int r; r = libusb_get_active_config_descriptor(dev, &config); @@ -651,30 +681,69 @@ API_EXPORTED int libusb_get_max_packet_size(libusb_device *dev, return LIBUSB_ERROR_OTHER; } - r = LIBUSB_ERROR_NOT_FOUND; - for (iface_idx = 0; iface_idx < config->bNumInterfaces; iface_idx++) { - const struct libusb_interface *iface = &config->interface[iface_idx]; - int altsetting_idx; + ep = find_endpoint(config, endpoint); + if (!ep) + return LIBUSB_ERROR_NOT_FOUND; - for (altsetting_idx = 0; altsetting_idx < iface->num_altsetting; - altsetting_idx++) { - const struct libusb_interface_descriptor *altsetting - = &iface->altsetting[altsetting_idx]; - int ep_idx; + r = ep->wMaxPacketSize; + libusb_free_config_descriptor(config); + return r; +} - for (ep_idx = 0; ep_idx < altsetting->bNumEndpoints; ep_idx++) { - const struct libusb_endpoint_descriptor *ep = - &altsetting->endpoint[ep_idx]; - if (ep->bEndpointAddress == endpoint) { - r = ep->wMaxPacketSize; - goto out; - } - } - } +/** \ingroup dev + * Calculate the maximum packet size which a specific endpoint is capable is + * sending or receiving in the duration of 1 microframe + * + * Only the active configution is examined. The calculation is based on the + * wMaxPacketSize field in the endpoint descriptor as described in section + * 9.6.6 in the USB 2.0 specifications. + * + * If acting on an isochronous or interrupt endpoint, this function will + * multiply the value found in bits 0:10 by the number of transactions per + * microframe (determined by bits 11:12). Otherwise, this function just + * returns the numeric value found in bits 0:10. + * + * This function is useful for setting up isochronous transfers, for example + * you might pass the return value from this function to + * libusb_set_iso_packet_lengths() in order to set the length field of every + * isochronous packet in a transfer. + * + * Since v1.0.3. + * + * \param dev a device + * \param endpoint address of the endpoint in question + * \returns the maximum packet size which can be sent/received on this endpoint + * \returns LIBUSB_ERROR_NOT_FOUND if the endpoint does not exist + * \returns LIBUSB_ERROR_OTHER on other failure + */ +API_EXPORTED int libusb_get_max_iso_packet_size(libusb_device *dev, + unsigned char endpoint) +{ + struct libusb_config_descriptor *config; + const struct libusb_endpoint_descriptor *ep; + enum libusb_transfer_type ep_type; + uint16_t val; + int r; + + r = libusb_get_active_config_descriptor(dev, &config); + if (r < 0) { + usbi_err(DEVICE_CTX(dev), + "could not retrieve active config descriptor"); + return LIBUSB_ERROR_OTHER; } -out: + ep = find_endpoint(config, endpoint); + if (!ep) + return LIBUSB_ERROR_NOT_FOUND; + + val = ep->wMaxPacketSize; + ep_type = ep->bmAttributes & 0x3; libusb_free_config_descriptor(config); + + r = val & 0x07ff; + if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); return r; } diff --git a/libusb/io.c b/libusb/io.c index f0e3c33..b63c000 100644 --- a/libusb/io.c +++ b/libusb/io.c @@ -410,11 +410,16 @@ 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 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. + * packet length is determined by the wMaxPacketSize field in the endpoint + * descriptor. + * Two functions can help you here: + * + * - libusb_get_max_iso_packet_size() is an easy way to determine the max + * packet size for an isochronous endpoint. Note that the maximum packet + * size is actually the maximum number of bytes that can be transmitted in + * a single microframe, therefore this function multiplies the maximum number + * of bytes per transaction by the number of transaction opportunities per + * microframe. * - libusb_set_iso_packet_lengths() assigns the same length to all packets * within a transfer, which is usually what you want. * -- 2.7.4