Skip to content

Commit

Permalink
usbcore: get BOS descriptor set
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Andiry Xu authored and Greg Kroah-Hartman committed Sep 26, 2011
1 parent 5023829 commit 3148bf0
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 0 deletions.
103 changes: 103 additions & 0 deletions drivers/usb/core/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -755,3 +755,106 @@ int usb_get_configuration(struct usb_device *dev)
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;
}
3 changes: 3 additions & 0 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions drivers/usb/core/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions drivers/usb/core/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 12 additions & 0 deletions include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 3148bf0

Please sign in to comment.