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>
This commit is contained in:
parent
5023829969
commit
3148bf041d
|
@ -755,3 +755,106 @@ err2:
|
||||||
dev_err(ddev, "out of memory\n");
|
dev_err(ddev, "out of memory\n");
|
||||||
return result;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -3083,6 +3083,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (udev->wusb == 0 && le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0201)
|
||||||
|
usb_get_bos_descriptor(udev);
|
||||||
|
|
||||||
retval = 0;
|
retval = 0;
|
||||||
/* notify HCD that we have a device connected and addressed */
|
/* notify HCD that we have a device connected and addressed */
|
||||||
if (hcd->driver->update_device)
|
if (hcd->driver->update_device)
|
||||||
|
|
|
@ -225,6 +225,7 @@ static void usb_release_dev(struct device *dev)
|
||||||
hcd = bus_to_hcd(udev->bus);
|
hcd = bus_to_hcd(udev->bus);
|
||||||
|
|
||||||
usb_destroy_configuration(udev);
|
usb_destroy_configuration(udev);
|
||||||
|
usb_release_bos_descriptor(udev);
|
||||||
usb_put_hcd(hcd);
|
usb_put_hcd(hcd);
|
||||||
kfree(udev->product);
|
kfree(udev->product);
|
||||||
kfree(udev->manufacturer);
|
kfree(udev->manufacturer);
|
||||||
|
|
|
@ -28,6 +28,8 @@ extern int usb_remove_device(struct usb_device *udev);
|
||||||
|
|
||||||
extern int usb_get_device_descriptor(struct usb_device *dev,
|
extern int usb_get_device_descriptor(struct usb_device *dev,
|
||||||
unsigned int size);
|
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 char *usb_cache_string(struct usb_device *udev, int index);
|
||||||
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
extern int usb_set_configuration(struct usb_device *dev, int configuration);
|
||||||
extern int usb_choose_configuration(struct usb_device *udev);
|
extern int usb_choose_configuration(struct usb_device *udev);
|
||||||
|
|
|
@ -292,6 +292,16 @@ struct usb_host_config {
|
||||||
int extralen;
|
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,
|
int __usb_get_extra_descriptor(char *buffer, unsigned size,
|
||||||
unsigned char type, void **ptr);
|
unsigned char type, void **ptr);
|
||||||
#define usb_get_extra_descriptor(ifpoint, type, ptr) \
|
#define usb_get_extra_descriptor(ifpoint, type, ptr) \
|
||||||
|
@ -381,6 +391,7 @@ struct usb_tt;
|
||||||
* @ep0: endpoint 0 data (default control pipe)
|
* @ep0: endpoint 0 data (default control pipe)
|
||||||
* @dev: generic device interface
|
* @dev: generic device interface
|
||||||
* @descriptor: USB device descriptor
|
* @descriptor: USB device descriptor
|
||||||
|
* @bos: USB device BOS descriptor set
|
||||||
* @config: all of the device's configs
|
* @config: all of the device's configs
|
||||||
* @actconfig: the active configuration
|
* @actconfig: the active configuration
|
||||||
* @ep_in: array of IN endpoints
|
* @ep_in: array of IN endpoints
|
||||||
|
@ -442,6 +453,7 @@ struct usb_device {
|
||||||
struct device dev;
|
struct device dev;
|
||||||
|
|
||||||
struct usb_device_descriptor descriptor;
|
struct usb_device_descriptor descriptor;
|
||||||
|
struct usb_host_bos *bos;
|
||||||
struct usb_host_config *config;
|
struct usb_host_config *config;
|
||||||
|
|
||||||
struct usb_host_config *actconfig;
|
struct usb_host_config *actconfig;
|
||||||
|
|
Loading…
Reference in a new issue