aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndiry Xu <andiry.xu@amd.com>2011-09-23 17:19:47 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2011-09-26 18:51:08 -0400
commit3148bf041d169a083aa31bd69bedd5bfb7ffe215 (patch)
tree0a6620dc6e2da37c291c195de78819d2e306296f
parent5023829969f56b78a16da94f34c605bbbb344018 (diff)
usbcore: get BOS descriptor set
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>
-rw-r--r--drivers/usb/core/config.c103
-rw-r--r--drivers/usb/core/hub.c3
-rw-r--r--drivers/usb/core/usb.c1
-rw-r--r--drivers/usb/core/usb.h2
-rw-r--r--include/linux/usb.h12
5 files changed, 121 insertions, 0 deletions
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 9d5e07af55be..f4bdd0ce8d56 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -755,3 +755,106 @@ err2:
755 dev_err(ddev, "out of memory\n"); 755 dev_err(ddev, "out of memory\n");
756 return result; 756 return result;
757} 757}
758
759void usb_release_bos_descriptor(struct usb_device *dev)
760{
761 if (dev->bos) {
762 kfree(dev->bos->desc);
763 kfree(dev->bos);
764 dev->bos = NULL;
765 }
766}
767
768/* Get BOS descriptor set */
769int usb_get_bos_descriptor(struct usb_device *dev)
770{
771 struct device *ddev = &dev->dev;
772 struct usb_bos_descriptor *bos;
773 struct usb_dev_cap_header *cap;
774 unsigned char *buffer;
775 int length, total_len, num, i;
776 int ret;
777
778 bos = kzalloc(sizeof(struct usb_bos_descriptor), GFP_KERNEL);
779 if (!bos)
780 return -ENOMEM;
781
782 /* Get BOS descriptor */
783 ret = usb_get_descriptor(dev, USB_DT_BOS, 0, bos, USB_DT_BOS_SIZE);
784 if (ret < USB_DT_BOS_SIZE) {
785 dev_err(ddev, "unable to get BOS descriptor\n");
786 if (ret >= 0)
787 ret = -ENOMSG;
788 kfree(bos);
789 return ret;
790 }
791
792 length = bos->bLength;
793 total_len = le16_to_cpu(bos->wTotalLength);
794 num = bos->bNumDeviceCaps;
795 kfree(bos);
796 if (total_len < length)
797 return -EINVAL;
798
799 dev->bos = kzalloc(sizeof(struct usb_host_bos), GFP_KERNEL);
800 if (!dev->bos)
801 return -ENOMEM;
802
803 /* Now let's get the whole BOS descriptor set */
804 buffer = kzalloc(total_len, GFP_KERNEL);
805 if (!buffer) {
806 ret = -ENOMEM;
807 goto err;
808 }
809 dev->bos->desc = (struct usb_bos_descriptor *)buffer;
810
811 ret = usb_get_descriptor(dev, USB_DT_BOS, 0, buffer, total_len);
812 if (ret < total_len) {
813 dev_err(ddev, "unable to get BOS descriptor set\n");
814 if (ret >= 0)
815 ret = -ENOMSG;
816 goto err;
817 }
818 total_len -= length;
819
820 for (i = 0; i < num; i++) {
821 buffer += length;
822 cap = (struct usb_dev_cap_header *)buffer;
823 length = cap->bLength;
824
825 if (total_len < length)
826 break;
827 total_len -= length;
828
829 if (cap->bDescriptorType != USB_DT_DEVICE_CAPABILITY) {
830 dev_warn(ddev, "descriptor type invalid, skip\n");
831 continue;
832 }
833
834 switch (cap->bDevCapabilityType) {
835 case USB_CAP_TYPE_WIRELESS_USB:
836 /* Wireless USB cap descriptor is handled by wusb */
837 break;
838 case USB_CAP_TYPE_EXT:
839 dev->bos->ext_cap =
840 (struct usb_ext_cap_descriptor *)buffer;
841 break;
842 case USB_SS_CAP_TYPE:
843 dev->bos->ss_cap =
844 (struct usb_ss_cap_descriptor *)buffer;
845 break;
846 case CONTAINER_ID_TYPE:
847 dev->bos->ss_id =
848 (struct usb_ss_container_id_descriptor *)buffer;
849 break;
850 default:
851 break;
852 }
853 }
854
855 return 0;
856
857err:
858 usb_release_bos_descriptor(dev);
859 return ret;
860}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1c155123c32f..7a2514322bfd 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3083,6 +3083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
3083 goto fail; 3083 goto fail;
3084 } 3084 }
3085 3085
3086 if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201)
3087 usb_get_bos_descriptor(udev);
3088
3086 retval = 0; 3089 retval = 0;
3087 /* notify HCD that we have a device connected and addressed */ 3090 /* notify HCD that we have a device connected and addressed */
3088 if (hcd->driver->update_device) 3091 if (hcd->driver->update_device)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 8706fc97e60f..73cd90012ec5 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev)
225 hcd = bus_to_hcd(udev->bus); 225 hcd = bus_to_hcd(udev->bus);
226 226
227 usb_destroy_configuration(udev); 227 usb_destroy_configuration(udev);
228 usb_release_bos_descriptor(udev);
228 usb_put_hcd(hcd); 229 usb_put_hcd(hcd);
229 kfree(udev->product); 230 kfree(udev->product);
230 kfree(udev->manufacturer); 231 kfree(udev->manufacturer);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index d44d4b7bbf17..0d023cd2c149 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev);
28 28
29extern int usb_get_device_descriptor(struct usb_device *dev, 29extern int usb_get_device_descriptor(struct usb_device *dev,
30 unsigned int size); 30 unsigned int size);
31extern int usb_get_bos_descriptor(struct usb_device *dev);
32extern void usb_release_bos_descriptor(struct usb_device *dev);
31extern char *usb_cache_string(struct usb_device *udev, int index); 33extern char *usb_cache_string(struct usb_device *udev, int index);
32extern int usb_set_configuration(struct usb_device *dev, int configuration); 34extern int usb_set_configuration(struct usb_device *dev, int configuration);
33extern int usb_choose_configuration(struct usb_device *udev); 35extern int usb_choose_configuration(struct usb_device *udev);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index c19f9100c307..90ab9dc1f080 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -292,6 +292,16 @@ struct usb_host_config {
292 int extralen; 292 int extralen;
293}; 293};
294 294
295/* USB2.0 and USB3.0 device BOS descriptor set */
296struct usb_host_bos {
297 struct usb_bos_descriptor *desc;
298
299 /* wireless cap descriptor is handled by wusb */
300 struct usb_ext_cap_descriptor *ext_cap;
301 struct usb_ss_cap_descriptor *ss_cap;
302 struct usb_ss_container_id_descriptor *ss_id;
303};
304
295int __usb_get_extra_descriptor(char *buffer, unsigned size, 305int __usb_get_extra_descriptor(char *buffer, unsigned size,
296 unsigned char type, void **ptr); 306 unsigned char type, void **ptr);
297#define usb_get_extra_descriptor(ifpoint, type, ptr) \ 307#define usb_get_extra_descriptor(ifpoint, type, ptr) \
@@ -381,6 +391,7 @@ struct usb_tt;
381 * @ep0: endpoint 0 data (default control pipe) 391 * @ep0: endpoint 0 data (default control pipe)
382 * @dev: generic device interface 392 * @dev: generic device interface
383 * @descriptor: USB device descriptor 393 * @descriptor: USB device descriptor
394 * @bos: USB device BOS descriptor set
384 * @config: all of the device's configs 395 * @config: all of the device's configs
385 * @actconfig: the active configuration 396 * @actconfig: the active configuration
386 * @ep_in: array of IN endpoints 397 * @ep_in: array of IN endpoints
@@ -442,6 +453,7 @@ struct usb_device {
442 struct device dev; 453 struct device dev;
443 454
444 struct usb_device_descriptor descriptor; 455 struct usb_device_descriptor descriptor;
456 struct usb_host_bos *bos;
445 struct usb_host_config *config; 457 struct usb_host_config *config;
446 458
447 struct usb_host_config *actconfig; 459 struct usb_host_config *actconfig;