summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Drake <dsd@gentoo.org>2009-06-28 19:49:10 +0100
committerDaniel Drake <dsd@gentoo.org>2009-06-28 19:49:10 +0100
commit0334ee642b47dfe1ca9db64b22e7702ea14b3f09 (patch)
treef7f8f8450575efcd05537e5e0ef47720178b6acd
parent615f18e64e96ae4ecc8e43d0de00933059a5209a (diff)
downloadlibusb-0334ee642b47dfe1ca9db64b22e7702ea14b3f09.tar.gz
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.
-rw-r--r--libusb/core.c113
-rw-r--r--libusb/io.c15
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.
*