usbcore: get BOS descriptor set
authorAndiry Xu <andiry.xu@amd.com>
Fri, 23 Sep 2011 21:19:47 +0000 (14:19 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 26 Sep 2011 22:51:08 +0000 (15:51 -0700)
This commit gets BOS(Binary Device Object Store) descriptor set for Super
Speed devices and High Speed devices which support BOS descriptor.

BOS descriptor is used to report additional USB device-level capabilities
that are not reported via the Device descriptor. By getting BOS descriptor
set, driver can check device's device-level capability such as LPM
capability.

Signed-off-by: Andiry Xu <andiry.xu@amd.com>
Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/config.c
drivers/usb/core/hub.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
include/linux/usb.h

index 9d5e07a..f4bdd0c 100644 (file)
@@ -755,3 +755,106 @@ err2:
                dev_err(ddev, "out of memory\n");
        return result;
 }
+
+void usb_release_bos_descriptor(struct usb_device *dev)
+{
+       if (dev->bos) {
+               kfree(dev->bos->desc);
+               kfree(dev->bos);
+               dev->bos = NULL;
+       }
+}
+
+/* Get BOS descriptor set */
+int usb_get_bos_descriptor(struct usb_device *dev)
+{
+       struct device *ddev = &dev->dev;
+       struct usb_bos_descriptor *bos;
+       struct usb_dev_cap_header *cap;
+       unsigned char *buffer;
+       int length, total_len, num, i;
+       int ret;
+
+       bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
+       if (!bos)
+               return -ENOMEM;
+
+       /* Get BOS descriptor */
+       ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
+       if (ret < USB_DT_BOS_SIZE) {
+               dev_err(ddev, "unable to get BOS descriptor\n");
+               if (ret >= 0)
+                       ret = -ENOMSG;
+               kfree(bos);
+               return ret;
+       }
+
+       length = bos->bLength;
+       total_len = le16_to_cpu(bos->wTotalLength);
+       num = bos->bNumDeviceCaps;
+       kfree(bos);
+       if (total_len < length)
+               return -EINVAL;
+
+       dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL);
+       if (!dev->bos)
+               return -ENOMEM;
+
+       /* Now let's get the whole BOS descriptor set */
+       buffer = kzalloc(total_len, GFP_KERNEL);
+       if (!buffer) {
+               ret = -ENOMEM;
+               goto err;
+       }
+       dev->bos->desc = (struct usb_bos_descriptor *)buffer;
+
+       ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
+       if (ret < total_len) {
+               dev_err(ddev, "unable to get BOS descriptor set\n");
+               if (ret >= 0)
+                       ret = -ENOMSG;
+               goto err;
+       }
+       total_len -= length;
+
+       for (i = 0; i < num; i++) {
+               buffer += length;
+               cap = (struct usb_dev_cap_header *)buffer;
+               length = cap->bLength;
+
+               if (total_len < length)
+                       break;
+               total_len -= length;
+
+               if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
+                       dev_warn(ddev, "descriptor type invalid, skip\n");
+                       continue;
+               }
+
+               switch (cap->bDevCapabilityType) {
+               case USB_CAP_TYPE_WIRELESS_USB:
+                       /* Wireless USB cap descriptor is handled by wusb */
+                       break;
+               case USB_CAP_TYPE_EXT:
+                       dev->bos->ext_cap =
+                               (struct usb_ext_cap_descriptor *)buffer;
+                       break;
+               case USB_SS_CAP_TYPE:
+                       dev->bos->ss_cap =
+                               (struct usb_ss_cap_descriptor *)buffer;
+                       break;
+               case CONTAINER_ID_TYPE:
+                       dev->bos->ss_id =
+                               (struct usb_ss_container_id_descriptor *)buffer;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+
+err:
+       usb_release_bos_descriptor(dev);
+       return ret;
+}
index 1c15512..7a25143 100644 (file)
@@ -3083,6 +3083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                goto fail;
        }
 
+       if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201)
+               usb_get_bos_descriptor(udev);
+
        retval = 0;
        /* notify HCD that we have a device connected and addressed */
        if (hcd->driver->update_device)
index 8706fc9..73cd900 100644 (file)
@@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev)
        hcd = bus_to_hcd(udev->bus);
 
        usb_destroy_configuration(udev);
+       usb_release_bos_descriptor(udev);
        usb_put_hcd(hcd);
        kfree(udev->product);
        kfree(udev->manufacturer);
index d44d4b7..0d023cd 100644 (file)
@@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev);
 
 extern int usb_get_device_descriptor(struct usb_device *dev,
                unsigned int size);
+extern int usb_get_bos_descriptor(struct usb_device *dev);
+extern void usb_release_bos_descriptor(struct usb_device *dev);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
index c19f910..90ab9dc 100644 (file)
@@ -292,6 +292,16 @@ struct usb_host_config {
        int extralen;
 };
 
+/* USB2.0 and USB3.0 device BOS descriptor set */
+struct usb_host_bos {
+       struct usb_bos_descriptor       *desc;
+
+       /* wireless cap descriptor is handled by wusb */
+       struct usb_ext_cap_descriptor   *ext_cap;
+       struct usb_ss_cap_descriptor    *ss_cap;
+       struct usb_ss_container_id_descriptor   *ss_id;
+};
+
 int __usb_get_extra_descriptor(char *buffer, unsigned size,
        unsigned char type, void **ptr);
 #define usb_get_extra_descriptor(ifpoint, type, ptr) \
@@ -381,6 +391,7 @@ struct usb_tt;
  * @ep0: endpoint 0 data (default control pipe)
  * @dev: generic device interface
  * @descriptor: USB device descriptor
+ * @bos: USB device BOS descriptor set
  * @config: all of the device's configs
  * @actconfig: the active configuration
  * @ep_in: array of IN endpoints
@@ -442,6 +453,7 @@ struct usb_device {
        struct device dev;
 
        struct usb_device_descriptor descriptor;
+       struct usb_host_bos *bos;
        struct usb_host_config *config;
 
        struct usb_host_config *actconfig;