Add superspeed endpoint companion descriptor support
authorHans de Goede <hdegoede@redhat.com>
Mon, 27 May 2013 09:12:28 +0000 (11:12 +0200)
committerHans de Goede <hdegoede@redhat.com>
Thu, 30 May 2013 12:20:44 +0000 (14:20 +0200)
Based on earlier work done on this by Maya Erez <merez@codeaurora.org>,
Nathan Hjelm <hjelmn@me.com> and Pete Batard <pete@akeo.ie>.

Signed-off-by: Hans de Goede <hdegoede@redhat.com>
examples/xusb.c
libusb/descriptor.c
libusb/libusb-1.0.def
libusb/libusb.h
libusb/libusbi.h
libusb/version_nano.h

index 5bb92a6..e1b1931 100644 (file)
@@ -808,6 +808,7 @@ static int test_device(uint16_t vid, uint16_t pid)
                                test_mode = USE_SCSI;
                        }
                        for (k=0; k<conf_desc->usb_interface[i].altsetting[j].bNumEndpoints; k++) {
+                               struct libusb_ss_endpoint_companion_descriptor *ep_comp = NULL;
                                endpoint = &conf_desc->usb_interface[i].altsetting[j].endpoint[k];
                                printf("       endpoint[%d].address: %02X\n", k, endpoint->bEndpointAddress);
                                // Use the first interrupt or bulk IN/OUT endpoints as default for testing
@@ -822,6 +823,12 @@ static int test_device(uint16_t vid, uint16_t pid)
                                }
                                printf("           max packet size: %04X\n", endpoint->wMaxPacketSize);
                                printf("          polling interval: %02X\n", endpoint->bInterval);
+                               libusb_get_ss_endpoint_companion_descriptor(NULL, endpoint, &ep_comp);
+                               if (ep_comp) {
+                                       printf("                 max burst: %02X   (USB 3.0)\n", ep_comp->bMaxBurst);
+                                       printf("        bytes per interval: %04X (USB 3.0)\n", ep_comp->wBytesPerInterval);
+                                       libusb_free_ss_endpoint_companion_descriptor(ep_comp);
+                               }
                        }
                }
        }
index b53df77..14482ca 100644 (file)
 
 /* set host_endian if the w values are already in host endian format,
  * as opposed to bus endian. */
-int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
        void *dest, int host_endian)
 {
-       unsigned char *sp = source, *dp = dest;
+       const unsigned char *sp = source;
+       unsigned char *dp = dest;
        uint16_t w;
        const char *cp;
+       uint32_t d;
 
        for (cp = descriptor; *cp; cp++) {
                switch (*cp) {
@@ -64,6 +66,24 @@ int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
                                sp += 2;
                                dp += 2;
                                break;
+                       case 'd':       /* 32-bit word, convert from little endian to CPU */
+                               dp += ((uintptr_t)dp & 1);      /* Align to word boundary */
+
+                               if (host_endian) {
+                                       memcpy(dp, sp, 4);
+                               } else {
+                                       d = (sp[3] << 24) | (sp[2] << 16) |
+                                               (sp[1] << 8) | sp[0];
+                                       *((uint32_t *)dp) = d;
+                               }
+                               sp += 4;
+                               dp += 4;
+                               break;
+                       case 'u':       /* 16 byte UUID */
+                               memcpy(dp, sp, 16);
+                               sp += 16;
+                               dp += 16;
+                               break;
                }
        }
 
@@ -721,6 +741,70 @@ void API_EXPORTED libusb_free_config_descriptor(
 }
 
 /** \ingroup desc
+ * Get an endpoints superspeed endpoint companion descriptor (if any)
+ *
+ * \param ctx the context to operate on, or NULL for the default context
+ * \param endpoint endpoint descriptor from which to get the superspeed
+ * endpoint companion descriptor
+ * \param ep_comp output location for the superspeed endpoint companion
+ * descriptor. Only valid if 0 was returned. Must be freed with
+ * libusb_free_ss_endpoint_companion_descriptor() after use.
+ * \returns 0 on success
+ * \returns LIBUSB_ERROR_NOT_FOUND if the configuration does not exist
+ * \returns another LIBUSB_ERROR code on error
+ */
+int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor(
+       struct libusb_context *ctx,
+       const struct libusb_endpoint_descriptor *endpoint,
+       struct libusb_ss_endpoint_companion_descriptor **ep_comp)
+{
+       struct usb_descriptor_header header;
+       int size = endpoint->extra_length;
+       const unsigned char *buffer = endpoint->extra;
+
+       *ep_comp = NULL;
+
+       while (size >= DESC_HEADER_LENGTH) {
+               usbi_parse_descriptor(buffer, "bb", &header, 0);
+               if (header.bLength < 2 || header.bLength > size) {
+                       usbi_err(ctx, "invalid descriptor length %d",
+                                header.bLength);
+                       return LIBUSB_ERROR_IO;
+               }
+               if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) {
+                       buffer += header.bLength;
+                       size -= header.bLength;
+                       continue;
+               }
+               if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) {
+                       usbi_err(ctx, "invalid ss-ep-comp-desc length %d",
+                                header.bLength);
+                       return LIBUSB_ERROR_IO;
+               }
+               *ep_comp = malloc(sizeof(**ep_comp));
+               if (*ep_comp == NULL)
+                       return LIBUSB_ERROR_NO_MEM;
+               usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0);
+               return LIBUSB_SUCCESS;
+       }
+       return LIBUSB_ERROR_NOT_FOUND;
+}
+
+/** \ingroup desc
+ * Free a superspeed endpoint companion descriptor obtained from
+ * libusb_get_ss_endpoint_companion_descriptor().
+ * It is safe to call this function with a NULL ep_comp parameter, in which
+ * case the function simply returns.
+ *
+ * \param ep_comp the superspeed endpoint companion descriptor to free
+ */
+void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor(
+       struct libusb_ss_endpoint_companion_descriptor *ep_comp)
+{
+       free(ep_comp);
+}
+
+/** \ingroup desc
  * Retrieve a string descriptor in C style ASCII.
  *
  * Wrapper around libusb_get_string_descriptor(). Uses the first language
index 0343116..52047ea 100644 (file)
@@ -30,6 +30,8 @@ EXPORTS
   libusb_free_config_descriptor@4 = libusb_free_config_descriptor
   libusb_free_device_list
   libusb_free_device_list@8 = libusb_free_device_list
+  libusb_free_ss_endpoint_companion_descriptor
+  libusb_free_ss_endpoint_companion_descriptor@4 = libusb_free_ss_endpoint_companion_descriptor
   libusb_free_transfer
   libusb_free_transfer@4 = libusb_free_transfer
   libusb_get_active_config_descriptor
@@ -66,6 +68,8 @@ EXPORTS
   libusb_get_port_number@4 = libusb_get_port_number
   libusb_get_port_path
   libusb_get_port_path@16 = libusb_get_port_path
+  libusb_get_ss_endpoint_companion_descriptor
+  libusb_get_ss_endpoint_companion_descriptor@12 = libusb_get_ss_endpoint_companion_descriptor
   libusb_get_string_descriptor_ascii
   libusb_get_string_descriptor_ascii@16 = libusb_get_string_descriptor_ascii
   libusb_get_version
index a67cbcb..593dc09 100644 (file)
@@ -274,16 +274,33 @@ enum libusb_descriptor_type {
        LIBUSB_DT_HUB = 0x29,
 
        /** SuperSpeed Hub descriptor */
-       LIBUSB_DT_SUPERSPEED_HUB = 0x2A,
+       LIBUSB_DT_SUPERSPEED_HUB = 0x2a,
+
+       /** SuperSpeed Endpoint Companion descriptor */
+       LIBUSB_DT_SS_ENDPOINT_COMPANION = 0x30
 };
 
 /* Descriptor sizes per descriptor type */
 #define LIBUSB_DT_DEVICE_SIZE                  18
 #define LIBUSB_DT_CONFIG_SIZE                  9
 #define LIBUSB_DT_INTERFACE_SIZE               9
-#define LIBUSB_DT_ENDPOINT_SIZE                7
-#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE  9       /* Audio extension */
+#define LIBUSB_DT_ENDPOINT_SIZE                        7
+#define LIBUSB_DT_ENDPOINT_AUDIO_SIZE          9       /* Audio extension */
 #define LIBUSB_DT_HUB_NONVAR_SIZE              7
+#define LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE   6
+#define LIBUSB_DT_BOS_SIZE                     5
+#define LIBUSB_DT_DEVICE_CAPABILITY_SIZE       3
+
+/* BOS descriptor sizes */
+#define LIBUSB_BT_USB_2_0_EXTENSION_SIZE       7
+#define LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE        10
+#define LIBUSB_BT_CONTAINER_ID_SIZE            20
+
+/* We unwrap the BOS => define its max size */
+#define LIBUSB_DT_BOS_MAX_SIZE         ((LIBUSB_DT_BOS_SIZE)     +\
+                                       (LIBUSB_BT_USB_2_0_EXTENSION_SIZE)       +\
+                                       (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\
+                                       (LIBUSB_BT_CONTAINER_ID_SIZE))
 
 #define LIBUSB_ENDPOINT_ADDRESS_MASK   0x0f    /* in bEndpointAddress */
 #define LIBUSB_ENDPOINT_DIR_MASK               0x80
@@ -655,6 +672,38 @@ struct libusb_config_descriptor {
        int extra_length;
 };
 
+/** \ingroup desc
+ * A structure representing the superspeed endpoint companion
+ * descriptor. This descriptor is documented in section 9.6.7 of
+ * the USB 3.0 specification. All multiple-byte fields are represented in
+ * host-endian format.
+ */
+struct libusb_ss_endpoint_companion_descriptor {
+
+       /** Size of this descriptor (in bytes) */
+       uint8_t  bLength;
+
+       /** Descriptor type. Will have value
+        * \ref libusb_descriptor_type::LIBUSB_DT_SS_ENDPOINT_COMPANION in
+        * this context. */
+       uint8_t  bDescriptorType;
+
+
+       /** The maximum number of packets the endpoint can send or
+        *  recieve as part of a burst. */
+       uint8_t  bMaxBurst;
+
+       /** In bulk EP: bits 4:0 represents the maximum number of
+        *  streams the EP supports. In isochronous EP: bits 1:0
+        *  represents the Mult - a zero based value that determines
+        *  the maximum number of packets within a service interval  */
+       uint8_t  bmAttributes;
+
+       /** The total number of bytes this EP will transfer every
+        *  service interval. valid only for periodic EPs. */
+       uint16_t wBytesPerInterval;
+};
+
 /** \ingroup asyncio
  * Setup packet for control transfers. */
 struct libusb_control_setup {
@@ -1060,6 +1109,12 @@ int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev,
        uint8_t bConfigurationValue, struct libusb_config_descriptor **config);
 void LIBUSB_CALL libusb_free_config_descriptor(
        struct libusb_config_descriptor *config);
+int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor(
+       struct libusb_context *ctx,
+       const struct libusb_endpoint_descriptor *endpoint,
+       struct libusb_ss_endpoint_companion_descriptor **ep_comp);
+void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor(
+       struct libusb_ss_endpoint_companion_descriptor *ep_comp);
 uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev);
 uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev);
 int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len);
index 4dda97e..eabf33d 100644 (file)
@@ -413,7 +413,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer,
        enum libusb_transfer_status status);
 int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer);
 
-int usbi_parse_descriptor(unsigned char *source, const char *descriptor,
+int usbi_parse_descriptor(const unsigned char *source, const char *descriptor,
        void *dest, int host_endian);
 int usbi_device_cache_descriptor(libusb_device *dev);
 int usbi_get_config_index_by_value(struct libusb_device *dev,
index a4f4d24..d69b0cc 100644 (file)
@@ -1 +1 @@
-#define LIBUSB_NANO 10724
+#define LIBUSB_NANO 10725