2016-01-26 21:17:12 +03:00
|
|
|
#include "qemu/osdep.h"
|
2012-04-20 14:33:30 +04:00
|
|
|
|
2012-03-07 17:55:18 +04:00
|
|
|
#include "hw/usb.h"
|
|
|
|
#include "hw/usb/desc.h"
|
2010-11-17 13:03:53 +03:00
|
|
|
#include "trace.h"
|
|
|
|
|
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
|
|
|
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
2013-11-20 10:32:31 +04:00
|
|
|
bool msos, uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
|
|
|
uint8_t bLength = 0x12;
|
2012-03-29 14:04:54 +04:00
|
|
|
USBDescriptor *d = (void *)dest;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 14:04:54 +04:00
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_DEVICE;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2013-11-20 10:32:31 +04:00
|
|
|
if (msos && dev->bcdUSB < 0x0200) {
|
|
|
|
/*
|
|
|
|
* Version 2.0+ required for microsoft os descriptors to work.
|
|
|
|
* Done this way so msos-desc compat property will handle both
|
|
|
|
* the version and the new descriptors being present.
|
|
|
|
*/
|
|
|
|
d->u.device.bcdUSB_lo = usb_lo(0x0200);
|
|
|
|
d->u.device.bcdUSB_hi = usb_hi(0x0200);
|
|
|
|
} else {
|
|
|
|
d->u.device.bcdUSB_lo = usb_lo(dev->bcdUSB);
|
|
|
|
d->u.device.bcdUSB_hi = usb_hi(dev->bcdUSB);
|
|
|
|
}
|
2012-03-29 14:04:54 +04:00
|
|
|
d->u.device.bDeviceClass = dev->bDeviceClass;
|
|
|
|
d->u.device.bDeviceSubClass = dev->bDeviceSubClass;
|
|
|
|
d->u.device.bDeviceProtocol = dev->bDeviceProtocol;
|
|
|
|
d->u.device.bMaxPacketSize0 = dev->bMaxPacketSize0;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2012-03-29 14:04:54 +04:00
|
|
|
d->u.device.idVendor_lo = usb_lo(id->idVendor);
|
|
|
|
d->u.device.idVendor_hi = usb_hi(id->idVendor);
|
|
|
|
d->u.device.idProduct_lo = usb_lo(id->idProduct);
|
|
|
|
d->u.device.idProduct_hi = usb_hi(id->idProduct);
|
|
|
|
d->u.device.bcdDevice_lo = usb_lo(id->bcdDevice);
|
|
|
|
d->u.device.bcdDevice_hi = usb_hi(id->bcdDevice);
|
|
|
|
d->u.device.iManufacturer = id->iManufacturer;
|
|
|
|
d->u.device.iProduct = id->iProduct;
|
|
|
|
d->u.device.iSerialNumber = id->iSerialNumber;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2012-03-29 14:04:54 +04:00
|
|
|
d->u.device.bNumConfigurations = dev->bNumConfigurations;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
return bLength;
|
|
|
|
}
|
|
|
|
|
2010-12-08 19:35:22 +03:00
|
|
|
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
|
|
|
uint8_t *dest, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t bLength = 0x0a;
|
2012-03-29 14:15:01 +04:00
|
|
|
USBDescriptor *d = (void *)dest;
|
2010-12-08 19:35:22 +03:00
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 14:15:01 +04:00
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_DEVICE_QUALIFIER;
|
|
|
|
|
|
|
|
d->u.device_qualifier.bcdUSB_lo = usb_lo(dev->bcdUSB);
|
|
|
|
d->u.device_qualifier.bcdUSB_hi = usb_hi(dev->bcdUSB);
|
|
|
|
d->u.device_qualifier.bDeviceClass = dev->bDeviceClass;
|
|
|
|
d->u.device_qualifier.bDeviceSubClass = dev->bDeviceSubClass;
|
|
|
|
d->u.device_qualifier.bDeviceProtocol = dev->bDeviceProtocol;
|
|
|
|
d->u.device_qualifier.bMaxPacketSize0 = dev->bMaxPacketSize0;
|
|
|
|
d->u.device_qualifier.bNumConfigurations = dev->bNumConfigurations;
|
|
|
|
d->u.device_qualifier.bReserved = 0;
|
2010-12-08 19:35:22 +03:00
|
|
|
|
|
|
|
return bLength;
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
int usb_desc_config(const USBDescConfig *conf, int flags,
|
|
|
|
uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
|
|
|
uint8_t bLength = 0x09;
|
|
|
|
uint16_t wTotalLength = 0;
|
2012-03-29 14:24:08 +04:00
|
|
|
USBDescriptor *d = (void *)dest;
|
2011-04-03 09:33:20 +04:00
|
|
|
int i, rc;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 14:24:08 +04:00
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_CONFIG;
|
|
|
|
|
|
|
|
d->u.config.bNumInterfaces = conf->bNumInterfaces;
|
|
|
|
d->u.config.bConfigurationValue = conf->bConfigurationValue;
|
|
|
|
d->u.config.iConfiguration = conf->iConfiguration;
|
|
|
|
d->u.config.bmAttributes = conf->bmAttributes;
|
|
|
|
d->u.config.bMaxPower = conf->bMaxPower;
|
2010-11-17 13:03:53 +03:00
|
|
|
wTotalLength += bLength;
|
|
|
|
|
2012-03-29 14:24:08 +04:00
|
|
|
/* handle grouped interfaces if any */
|
2011-04-03 09:33:21 +04:00
|
|
|
for (i = 0; i < conf->nif_groups; i++) {
|
2012-08-28 19:28:03 +04:00
|
|
|
rc = usb_desc_iface_group(&(conf->if_groups[i]), flags,
|
2011-04-03 09:33:21 +04:00
|
|
|
dest + wTotalLength,
|
|
|
|
len - wTotalLength);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
wTotalLength += rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* handle normal (ungrouped / no IAD) interfaces if any */
|
2011-04-03 09:33:20 +04:00
|
|
|
for (i = 0; i < conf->nif; i++) {
|
2012-08-28 19:28:03 +04:00
|
|
|
rc = usb_desc_iface(conf->ifs + i, flags,
|
|
|
|
dest + wTotalLength, len - wTotalLength);
|
2010-11-17 13:03:53 +03:00
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
wTotalLength += rc;
|
|
|
|
}
|
|
|
|
|
2012-03-29 14:24:08 +04:00
|
|
|
d->u.config.wTotalLength_lo = usb_lo(wTotalLength);
|
|
|
|
d->u.config.wTotalLength_hi = usb_hi(wTotalLength);
|
2010-11-17 13:03:53 +03:00
|
|
|
return wTotalLength;
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
|
|
|
|
uint8_t *dest, size_t len)
|
2011-04-03 09:33:21 +04:00
|
|
|
{
|
|
|
|
int pos = 0;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
/* handle interface association descriptor */
|
|
|
|
uint8_t bLength = 0x08;
|
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest[0x00] = bLength;
|
|
|
|
dest[0x01] = USB_DT_INTERFACE_ASSOC;
|
|
|
|
dest[0x02] = iad->bFirstInterface;
|
|
|
|
dest[0x03] = iad->bInterfaceCount;
|
|
|
|
dest[0x04] = iad->bFunctionClass;
|
|
|
|
dest[0x05] = iad->bFunctionSubClass;
|
|
|
|
dest[0x06] = iad->bFunctionProtocol;
|
|
|
|
dest[0x07] = iad->iFunction;
|
|
|
|
pos += bLength;
|
|
|
|
|
|
|
|
/* handle associated interfaces in this group */
|
|
|
|
for (i = 0; i < iad->nif; i++) {
|
2012-08-28 19:28:03 +04:00
|
|
|
int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos);
|
2011-04-03 09:33:21 +04:00
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
pos += rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
int usb_desc_iface(const USBDescIface *iface, int flags,
|
|
|
|
uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
|
|
|
uint8_t bLength = 0x09;
|
|
|
|
int i, rc, pos = 0;
|
2012-03-29 14:30:33 +04:00
|
|
|
USBDescriptor *d = (void *)dest;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 14:30:33 +04:00
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_INTERFACE;
|
|
|
|
|
|
|
|
d->u.interface.bInterfaceNumber = iface->bInterfaceNumber;
|
|
|
|
d->u.interface.bAlternateSetting = iface->bAlternateSetting;
|
|
|
|
d->u.interface.bNumEndpoints = iface->bNumEndpoints;
|
|
|
|
d->u.interface.bInterfaceClass = iface->bInterfaceClass;
|
|
|
|
d->u.interface.bInterfaceSubClass = iface->bInterfaceSubClass;
|
|
|
|
d->u.interface.bInterfaceProtocol = iface->bInterfaceProtocol;
|
|
|
|
d->u.interface.iInterface = iface->iInterface;
|
2010-11-17 13:03:53 +03:00
|
|
|
pos += bLength;
|
|
|
|
|
|
|
|
for (i = 0; i < iface->ndesc; i++) {
|
|
|
|
rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
pos += rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < iface->bNumEndpoints; i++) {
|
2012-08-28 19:28:03 +04:00
|
|
|
rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos);
|
2010-11-17 13:03:53 +03:00
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
pos += rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
|
|
|
|
uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
2011-08-10 16:10:04 +04:00
|
|
|
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
|
|
|
|
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
|
2012-08-28 19:28:03 +04:00
|
|
|
uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0;
|
2012-03-29 18:01:21 +04:00
|
|
|
USBDescriptor *d = (void *)dest;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
if (len < bLength + extralen + superlen) {
|
2010-11-17 13:03:53 +03:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2012-03-29 18:01:21 +04:00
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_ENDPOINT;
|
|
|
|
|
|
|
|
d->u.endpoint.bEndpointAddress = ep->bEndpointAddress;
|
|
|
|
d->u.endpoint.bmAttributes = ep->bmAttributes;
|
|
|
|
d->u.endpoint.wMaxPacketSize_lo = usb_lo(ep->wMaxPacketSize);
|
|
|
|
d->u.endpoint.wMaxPacketSize_hi = usb_hi(ep->wMaxPacketSize);
|
|
|
|
d->u.endpoint.bInterval = ep->bInterval;
|
2011-08-10 16:10:04 +04:00
|
|
|
if (ep->is_audio) {
|
2012-03-29 18:01:21 +04:00
|
|
|
d->u.endpoint.bRefresh = ep->bRefresh;
|
|
|
|
d->u.endpoint.bSynchAddress = ep->bSynchAddress;
|
2011-08-10 16:10:04 +04:00
|
|
|
}
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
if (superlen) {
|
2013-01-28 18:52:57 +04:00
|
|
|
USBDescriptor *d = (void *)(dest + bLength);
|
2012-08-28 19:28:03 +04:00
|
|
|
|
|
|
|
d->bLength = 0x06;
|
|
|
|
d->bDescriptorType = USB_DT_ENDPOINT_COMPANION;
|
|
|
|
|
|
|
|
d->u.super_endpoint.bMaxBurst = ep->bMaxBurst;
|
|
|
|
d->u.super_endpoint.bmAttributes = ep->bmAttributes_super;
|
|
|
|
d->u.super_endpoint.wBytesPerInterval_lo =
|
|
|
|
usb_lo(ep->wBytesPerInterval);
|
|
|
|
d->u.super_endpoint.wBytesPerInterval_hi =
|
|
|
|
usb_hi(ep->wBytesPerInterval);
|
|
|
|
}
|
|
|
|
|
2013-01-28 18:52:57 +04:00
|
|
|
if (ep->extra) {
|
|
|
|
memcpy(dest + bLength + superlen, ep->extra, extralen);
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
return bLength + extralen + superlen;
|
2010-11-17 13:03:53 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
|
|
|
|
{
|
|
|
|
int bLength = desc->length ? desc->length : desc->data[0];
|
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(dest, desc->data, bLength);
|
|
|
|
return bLength;
|
|
|
|
}
|
|
|
|
|
2012-08-28 19:46:29 +04:00
|
|
|
static int usb_desc_cap_usb2_ext(const USBDesc *desc, uint8_t *dest, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t bLength = 0x07;
|
|
|
|
USBDescriptor *d = (void *)dest;
|
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
|
|
|
d->u.cap.bDevCapabilityType = USB_DEV_CAP_USB2_EXT;
|
|
|
|
|
|
|
|
d->u.cap.u.usb2_ext.bmAttributes_1 = (1 << 1); /* LPM */
|
|
|
|
d->u.cap.u.usb2_ext.bmAttributes_2 = 0;
|
|
|
|
d->u.cap.u.usb2_ext.bmAttributes_3 = 0;
|
|
|
|
d->u.cap.u.usb2_ext.bmAttributes_4 = 0;
|
|
|
|
|
|
|
|
return bLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_desc_cap_super(const USBDesc *desc, uint8_t *dest, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t bLength = 0x0a;
|
|
|
|
USBDescriptor *d = (void *)dest;
|
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_DEVICE_CAPABILITY;
|
|
|
|
d->u.cap.bDevCapabilityType = USB_DEV_CAP_SUPERSPEED;
|
|
|
|
|
|
|
|
d->u.cap.u.super.bmAttributes = 0;
|
|
|
|
d->u.cap.u.super.wSpeedsSupported_lo = 0;
|
|
|
|
d->u.cap.u.super.wSpeedsSupported_hi = 0;
|
|
|
|
d->u.cap.u.super.bFunctionalitySupport = 0;
|
|
|
|
d->u.cap.u.super.bU1DevExitLat = 0x0a;
|
|
|
|
d->u.cap.u.super.wU2DevExitLat_lo = 0x20;
|
|
|
|
d->u.cap.u.super.wU2DevExitLat_hi = 0;
|
|
|
|
|
|
|
|
if (desc->full) {
|
|
|
|
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 1);
|
|
|
|
d->u.cap.u.super.bFunctionalitySupport = 1;
|
|
|
|
}
|
|
|
|
if (desc->high) {
|
|
|
|
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 2);
|
|
|
|
if (!d->u.cap.u.super.bFunctionalitySupport) {
|
|
|
|
d->u.cap.u.super.bFunctionalitySupport = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (desc->super) {
|
|
|
|
d->u.cap.u.super.wSpeedsSupported_lo |= (1 << 3);
|
|
|
|
if (!d->u.cap.u.super.bFunctionalitySupport) {
|
|
|
|
d->u.cap.u.super.bFunctionalitySupport = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return bLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_desc_bos(const USBDesc *desc, uint8_t *dest, size_t len)
|
|
|
|
{
|
|
|
|
uint8_t bLength = 0x05;
|
|
|
|
uint16_t wTotalLength = 0;
|
|
|
|
uint8_t bNumDeviceCaps = 0;
|
|
|
|
USBDescriptor *d = (void *)dest;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (len < bLength) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->bLength = bLength;
|
|
|
|
d->bDescriptorType = USB_DT_BOS;
|
|
|
|
|
|
|
|
wTotalLength += bLength;
|
|
|
|
|
|
|
|
if (desc->high != NULL) {
|
|
|
|
rc = usb_desc_cap_usb2_ext(desc, dest + wTotalLength,
|
|
|
|
len - wTotalLength);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
wTotalLength += rc;
|
|
|
|
bNumDeviceCaps++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (desc->super != NULL) {
|
|
|
|
rc = usb_desc_cap_super(desc, dest + wTotalLength,
|
|
|
|
len - wTotalLength);
|
|
|
|
if (rc < 0) {
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
wTotalLength += rc;
|
|
|
|
bNumDeviceCaps++;
|
|
|
|
}
|
|
|
|
|
|
|
|
d->u.bos.wTotalLength_lo = usb_lo(wTotalLength);
|
|
|
|
d->u.bos.wTotalLength_hi = usb_hi(wTotalLength);
|
|
|
|
d->u.bos.bNumDeviceCaps = bNumDeviceCaps;
|
|
|
|
return wTotalLength;
|
|
|
|
}
|
|
|
|
|
2010-11-26 14:25:32 +03:00
|
|
|
/* ------------------------------------------------------------------ */
|
|
|
|
|
2011-08-30 18:42:03 +04:00
|
|
|
static void usb_desc_ep_init(USBDevice *dev)
|
|
|
|
{
|
|
|
|
const USBDescIface *iface;
|
|
|
|
int i, e, pid, ep;
|
|
|
|
|
|
|
|
usb_ep_init(dev);
|
|
|
|
for (i = 0; i < dev->ninterfaces; i++) {
|
|
|
|
iface = dev->ifaces[i];
|
|
|
|
if (iface == NULL) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (e = 0; e < iface->bNumEndpoints; e++) {
|
|
|
|
pid = (iface->eps[e].bEndpointAddress & USB_DIR_IN) ?
|
|
|
|
USB_TOKEN_IN : USB_TOKEN_OUT;
|
|
|
|
ep = iface->eps[e].bEndpointAddress & 0x0f;
|
|
|
|
usb_ep_set_type(dev, pid, ep, iface->eps[e].bmAttributes & 0x03);
|
|
|
|
usb_ep_set_ifnum(dev, pid, ep, iface->bInterfaceNumber);
|
2011-08-31 18:09:27 +04:00
|
|
|
usb_ep_set_max_packet_size(dev, pid, ep,
|
|
|
|
iface->eps[e].wMaxPacketSize);
|
2013-11-19 17:36:56 +04:00
|
|
|
usb_ep_set_max_streams(dev, pid, ep,
|
|
|
|
iface->eps[e].bmAttributes_super);
|
2011-08-30 18:42:03 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-30 15:21:27 +04:00
|
|
|
static const USBDescIface *usb_desc_find_interface(USBDevice *dev,
|
|
|
|
int nif, int alt)
|
|
|
|
{
|
|
|
|
const USBDescIface *iface;
|
|
|
|
int g, i;
|
|
|
|
|
|
|
|
if (!dev->config) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
for (g = 0; g < dev->config->nif_groups; g++) {
|
|
|
|
for (i = 0; i < dev->config->if_groups[g].nif; i++) {
|
|
|
|
iface = &dev->config->if_groups[g].ifs[i];
|
|
|
|
if (iface->bInterfaceNumber == nif &&
|
|
|
|
iface->bAlternateSetting == alt) {
|
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (i = 0; i < dev->config->nif; i++) {
|
|
|
|
iface = &dev->config->ifs[i];
|
|
|
|
if (iface->bInterfaceNumber == nif &&
|
|
|
|
iface->bAlternateSetting == alt) {
|
|
|
|
return iface;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int usb_desc_set_interface(USBDevice *dev, int index, int value)
|
|
|
|
{
|
|
|
|
const USBDescIface *iface;
|
|
|
|
int old;
|
|
|
|
|
|
|
|
iface = usb_desc_find_interface(dev, index, value);
|
|
|
|
if (iface == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = dev->altsetting[index];
|
|
|
|
dev->altsetting[index] = value;
|
|
|
|
dev->ifaces[index] = iface;
|
2011-08-30 18:42:03 +04:00
|
|
|
usb_desc_ep_init(dev);
|
2011-08-30 15:21:27 +04:00
|
|
|
|
2011-12-16 00:53:10 +04:00
|
|
|
if (old != value) {
|
|
|
|
usb_device_set_interface(dev, index, old, value);
|
2011-08-30 15:21:27 +04:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-30 13:11:29 +04:00
|
|
|
static int usb_desc_set_config(USBDevice *dev, int value)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (value == 0) {
|
|
|
|
dev->configuration = 0;
|
|
|
|
dev->ninterfaces = 0;
|
|
|
|
dev->config = NULL;
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < dev->device->bNumConfigurations; i++) {
|
|
|
|
if (dev->device->confs[i].bConfigurationValue == value) {
|
|
|
|
dev->configuration = value;
|
|
|
|
dev->ninterfaces = dev->device->confs[i].bNumInterfaces;
|
|
|
|
dev->config = dev->device->confs + i;
|
2011-08-30 15:21:27 +04:00
|
|
|
assert(dev->ninterfaces <= USB_MAX_INTERFACES);
|
2011-08-30 13:11:29 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < dev->device->bNumConfigurations) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
2011-08-30 15:21:27 +04:00
|
|
|
|
|
|
|
for (i = 0; i < dev->ninterfaces; i++) {
|
|
|
|
usb_desc_set_interface(dev, i, 0);
|
|
|
|
}
|
|
|
|
for (; i < USB_MAX_INTERFACES; i++) {
|
|
|
|
dev->altsetting[i] = 0;
|
|
|
|
dev->ifaces[i] = NULL;
|
|
|
|
}
|
|
|
|
|
2011-08-30 13:11:29 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2010-12-03 20:07:20 +03:00
|
|
|
static void usb_desc_setdefaults(USBDevice *dev)
|
2010-11-26 22:20:41 +03:00
|
|
|
{
|
2011-12-16 00:53:10 +04:00
|
|
|
const USBDesc *desc = usb_device_get_usb_desc(dev);
|
2010-11-26 22:20:41 +03:00
|
|
|
|
|
|
|
assert(desc != NULL);
|
2010-12-03 20:07:20 +03:00
|
|
|
switch (dev->speed) {
|
|
|
|
case USB_SPEED_LOW:
|
|
|
|
case USB_SPEED_FULL:
|
|
|
|
dev->device = desc->full;
|
|
|
|
break;
|
|
|
|
case USB_SPEED_HIGH:
|
|
|
|
dev->device = desc->high;
|
|
|
|
break;
|
2012-08-28 19:28:50 +04:00
|
|
|
case USB_SPEED_SUPER:
|
|
|
|
dev->device = desc->super;
|
|
|
|
break;
|
2010-12-03 20:07:20 +03:00
|
|
|
}
|
2011-08-30 13:11:29 +04:00
|
|
|
usb_desc_set_config(dev, 0);
|
2010-11-26 22:20:41 +03:00
|
|
|
}
|
|
|
|
|
2010-12-03 20:07:20 +03:00
|
|
|
void usb_desc_init(USBDevice *dev)
|
|
|
|
{
|
2011-12-16 00:53:10 +04:00
|
|
|
const USBDesc *desc = usb_device_get_usb_desc(dev);
|
2011-05-27 16:27:18 +04:00
|
|
|
|
|
|
|
assert(desc != NULL);
|
2010-12-03 20:07:20 +03:00
|
|
|
dev->speed = USB_SPEED_FULL;
|
2011-05-27 16:27:18 +04:00
|
|
|
dev->speedmask = 0;
|
|
|
|
if (desc->full) {
|
|
|
|
dev->speedmask |= USB_SPEED_MASK_FULL;
|
|
|
|
}
|
|
|
|
if (desc->high) {
|
|
|
|
dev->speedmask |= USB_SPEED_MASK_HIGH;
|
|
|
|
}
|
2012-08-28 19:28:50 +04:00
|
|
|
if (desc->super) {
|
|
|
|
dev->speedmask |= USB_SPEED_MASK_SUPER;
|
|
|
|
}
|
2013-11-20 10:32:31 +04:00
|
|
|
if (desc->msos && (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_ENABLE))) {
|
|
|
|
dev->flags |= (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE);
|
|
|
|
usb_desc_set_string(dev, 0xee, "MSFT100Q");
|
|
|
|
}
|
2010-12-03 20:07:20 +03:00
|
|
|
usb_desc_setdefaults(dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
void usb_desc_attach(USBDevice *dev)
|
|
|
|
{
|
|
|
|
usb_desc_setdefaults(dev);
|
|
|
|
}
|
|
|
|
|
2010-11-26 14:25:32 +03:00
|
|
|
void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
|
|
|
|
{
|
|
|
|
USBDescString *s;
|
|
|
|
|
|
|
|
QLIST_FOREACH(s, &dev->strings, next) {
|
|
|
|
if (s->index == index) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (s == NULL) {
|
2011-08-21 07:09:37 +04:00
|
|
|
s = g_malloc0(sizeof(*s));
|
2010-11-26 14:25:32 +03:00
|
|
|
s->index = index;
|
|
|
|
QLIST_INSERT_HEAD(&dev->strings, s, next);
|
|
|
|
}
|
2011-08-21 07:09:37 +04:00
|
|
|
g_free(s->str);
|
|
|
|
s->str = g_strdup(str);
|
2010-11-26 14:25:32 +03:00
|
|
|
}
|
|
|
|
|
2012-04-20 14:33:30 +04:00
|
|
|
/*
|
|
|
|
* This function creates a serial number for a usb device.
|
|
|
|
* The serial number should:
|
|
|
|
* (a) Be unique within the virtual machine.
|
|
|
|
* (b) Be constant, so you don't get a new one each
|
|
|
|
* time the guest is started.
|
|
|
|
* So we are using the physical location to generate a serial number
|
|
|
|
* from it. It has three pieces: First a fixed, device-specific
|
|
|
|
* prefix. Second the device path of the host controller (which is
|
|
|
|
* the pci address in most cases). Third the physical port path.
|
|
|
|
* Results in serial numbers like this: "314159-0000:00:1d.7-3".
|
|
|
|
*/
|
|
|
|
void usb_desc_create_serial(USBDevice *dev)
|
|
|
|
{
|
|
|
|
DeviceState *hcd = dev->qdev.parent_bus->parent;
|
|
|
|
const USBDesc *desc = usb_device_get_usb_desc(dev);
|
|
|
|
int index = desc->id.iSerialNumber;
|
2016-10-05 12:33:18 +03:00
|
|
|
char *path, *serial;
|
2012-04-20 14:33:30 +04:00
|
|
|
|
2013-06-12 15:01:49 +04:00
|
|
|
if (dev->serial) {
|
|
|
|
/* 'serial' usb bus property has priority if present */
|
|
|
|
usb_desc_set_string(dev, index, dev->serial);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-20 14:33:30 +04:00
|
|
|
assert(index != 0 && desc->str[index] != NULL);
|
2012-02-03 22:28:43 +04:00
|
|
|
path = qdev_get_dev_path(hcd);
|
|
|
|
if (path) {
|
2016-10-05 12:33:18 +03:00
|
|
|
serial = g_strdup_printf("%s-%s-%s", desc->str[index],
|
|
|
|
path, dev->port->path);
|
|
|
|
} else {
|
|
|
|
serial = g_strdup_printf("%s-%s", desc->str[index], dev->port->path);
|
2012-04-20 14:33:30 +04:00
|
|
|
}
|
|
|
|
usb_desc_set_string(dev, index, serial);
|
2016-07-15 12:48:13 +03:00
|
|
|
g_free(path);
|
2016-10-05 12:33:18 +03:00
|
|
|
g_free(serial);
|
2012-04-20 14:33:30 +04:00
|
|
|
}
|
|
|
|
|
2010-11-26 14:25:32 +03:00
|
|
|
const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
|
|
|
|
{
|
|
|
|
USBDescString *s;
|
|
|
|
|
|
|
|
QLIST_FOREACH(s, &dev->strings, next) {
|
|
|
|
if (s->index == index) {
|
|
|
|
return s->str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
|
|
|
uint8_t bLength, pos, i;
|
2010-11-26 14:25:32 +03:00
|
|
|
const char *str;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
if (len < 4) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index == 0) {
|
|
|
|
/* language ids */
|
|
|
|
dest[0] = 4;
|
|
|
|
dest[1] = USB_DT_STRING;
|
|
|
|
dest[2] = 0x09;
|
|
|
|
dest[3] = 0x04;
|
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
2010-11-26 14:25:32 +03:00
|
|
|
str = usb_desc_get_string(dev, index);
|
|
|
|
if (str == NULL) {
|
2011-12-16 00:53:10 +04:00
|
|
|
str = usb_device_get_usb_desc(dev)->str[index];
|
2010-11-26 14:25:32 +03:00
|
|
|
if (str == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
2010-11-17 13:03:53 +03:00
|
|
|
}
|
2010-11-26 14:25:32 +03:00
|
|
|
|
|
|
|
bLength = strlen(str) * 2 + 2;
|
2010-11-17 13:03:53 +03:00
|
|
|
dest[0] = bLength;
|
|
|
|
dest[1] = USB_DT_STRING;
|
|
|
|
i = 0; pos = 2;
|
|
|
|
while (pos+1 < bLength && pos+1 < len) {
|
2010-11-26 14:25:32 +03:00
|
|
|
dest[pos++] = str[i++];
|
2010-11-17 13:03:53 +03:00
|
|
|
dest[pos++] = 0;
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
int usb_desc_get_descriptor(USBDevice *dev, USBPacket *p,
|
|
|
|
int value, uint8_t *dest, size_t len)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
2013-11-20 10:32:31 +04:00
|
|
|
bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
|
2011-12-16 00:53:10 +04:00
|
|
|
const USBDesc *desc = usb_device_get_usb_desc(dev);
|
2010-12-08 19:35:22 +03:00
|
|
|
const USBDescDevice *other_dev;
|
2010-11-17 13:03:53 +03:00
|
|
|
uint8_t buf[256];
|
|
|
|
uint8_t type = value >> 8;
|
|
|
|
uint8_t index = value & 0xff;
|
2012-08-28 19:28:03 +04:00
|
|
|
int flags, ret = -1;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
2010-12-08 19:35:22 +03:00
|
|
|
if (dev->speed == USB_SPEED_HIGH) {
|
2011-12-16 00:53:10 +04:00
|
|
|
other_dev = usb_device_get_usb_desc(dev)->full;
|
2010-12-08 19:35:22 +03:00
|
|
|
} else {
|
2011-12-16 00:53:10 +04:00
|
|
|
other_dev = usb_device_get_usb_desc(dev)->high;
|
2010-12-08 19:35:22 +03:00
|
|
|
}
|
|
|
|
|
2012-08-28 19:28:03 +04:00
|
|
|
flags = 0;
|
|
|
|
if (dev->device->bcdUSB >= 0x0300) {
|
|
|
|
flags |= USB_DESC_FLAG_SUPER;
|
|
|
|
}
|
|
|
|
|
2010-11-17 13:03:53 +03:00
|
|
|
switch(type) {
|
|
|
|
case USB_DT_DEVICE:
|
2013-11-20 10:32:31 +04:00
|
|
|
ret = usb_desc_device(&desc->id, dev->device, msos, buf, sizeof(buf));
|
2010-11-17 13:03:53 +03:00
|
|
|
trace_usb_desc_device(dev->addr, len, ret);
|
|
|
|
break;
|
|
|
|
case USB_DT_CONFIG:
|
2010-11-26 22:20:41 +03:00
|
|
|
if (index < dev->device->bNumConfigurations) {
|
2012-08-28 19:28:03 +04:00
|
|
|
ret = usb_desc_config(dev->device->confs + index, flags,
|
|
|
|
buf, sizeof(buf));
|
2010-11-17 13:03:53 +03:00
|
|
|
}
|
|
|
|
trace_usb_desc_config(dev->addr, index, len, ret);
|
|
|
|
break;
|
|
|
|
case USB_DT_STRING:
|
2010-11-26 14:25:32 +03:00
|
|
|
ret = usb_desc_string(dev, index, buf, sizeof(buf));
|
2010-11-17 13:03:53 +03:00
|
|
|
trace_usb_desc_string(dev->addr, index, len, ret);
|
|
|
|
break;
|
2010-12-08 19:35:22 +03:00
|
|
|
case USB_DT_DEVICE_QUALIFIER:
|
|
|
|
if (other_dev != NULL) {
|
|
|
|
ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
|
|
|
|
}
|
|
|
|
trace_usb_desc_device_qualifier(dev->addr, len, ret);
|
|
|
|
break;
|
|
|
|
case USB_DT_OTHER_SPEED_CONFIG:
|
|
|
|
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
|
2012-08-28 19:28:03 +04:00
|
|
|
ret = usb_desc_config(other_dev->confs + index, flags,
|
|
|
|
buf, sizeof(buf));
|
2010-12-08 19:35:22 +03:00
|
|
|
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
|
|
|
|
}
|
|
|
|
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
|
|
|
|
break;
|
2012-08-28 19:46:29 +04:00
|
|
|
case USB_DT_BOS:
|
|
|
|
ret = usb_desc_bos(desc, buf, sizeof(buf));
|
|
|
|
trace_usb_desc_bos(dev->addr, len, ret);
|
|
|
|
break;
|
2010-12-08 19:35:22 +03:00
|
|
|
|
2011-06-23 19:15:43 +04:00
|
|
|
case USB_DT_DEBUG:
|
|
|
|
/* ignore silently */
|
|
|
|
break;
|
|
|
|
|
2010-11-17 13:03:53 +03:00
|
|
|
default:
|
2017-11-09 01:56:31 +03:00
|
|
|
fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __func__,
|
2010-11-17 13:03:53 +03:00
|
|
|
dev->addr, type, len);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret > 0) {
|
|
|
|
if (ret > len) {
|
|
|
|
ret = len;
|
|
|
|
}
|
|
|
|
memcpy(dest, buf, ret);
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = ret;
|
|
|
|
ret = 0;
|
2010-11-17 13:03:53 +03:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2011-02-02 18:33:13 +03:00
|
|
|
int usb_desc_handle_control(USBDevice *dev, USBPacket *p,
|
|
|
|
int request, int value, int index, int length, uint8_t *data)
|
2010-11-17 13:03:53 +03:00
|
|
|
{
|
2013-11-20 10:32:31 +04:00
|
|
|
bool msos = (dev->flags & (1 << USB_DEV_FLAG_MSOS_DESC_IN_USE));
|
2011-12-16 00:53:10 +04:00
|
|
|
const USBDesc *desc = usb_device_get_usb_desc(dev);
|
2011-08-30 13:11:29 +04:00
|
|
|
int ret = -1;
|
2010-11-17 13:03:53 +03:00
|
|
|
|
|
|
|
assert(desc != NULL);
|
|
|
|
switch(request) {
|
2010-11-26 14:35:10 +03:00
|
|
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
|
|
dev->addr = value;
|
|
|
|
trace_usb_set_addr(dev->addr);
|
|
|
|
ret = 0;
|
|
|
|
break;
|
|
|
|
|
2010-11-17 13:03:53 +03:00
|
|
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
ret = usb_desc_get_descriptor(dev, p, value, data, length);
|
2010-11-17 13:03:53 +03:00
|
|
|
break;
|
2010-11-26 22:20:41 +03:00
|
|
|
|
|
|
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
2012-02-26 20:09:21 +04:00
|
|
|
/*
|
|
|
|
* 9.4.2: 0 should be returned if the device is unconfigured, otherwise
|
|
|
|
* the non zero value of bConfigurationValue.
|
|
|
|
*/
|
|
|
|
data[0] = dev->config ? dev->config->bConfigurationValue : 0;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = 1;
|
|
|
|
ret = 0;
|
2010-11-26 22:20:41 +03:00
|
|
|
break;
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
2011-08-30 13:11:29 +04:00
|
|
|
ret = usb_desc_set_config(dev, value);
|
2010-11-26 22:20:41 +03:00
|
|
|
trace_usb_set_config(dev->addr, value, ret);
|
|
|
|
break;
|
2010-11-30 19:35:34 +03:00
|
|
|
|
2012-02-26 20:09:21 +04:00
|
|
|
case DeviceRequest | USB_REQ_GET_STATUS: {
|
|
|
|
const USBDescConfig *config = dev->config ?
|
|
|
|
dev->config : &dev->device->confs[0];
|
|
|
|
|
2010-11-30 19:35:34 +03:00
|
|
|
data[0] = 0;
|
2012-02-26 20:09:21 +04:00
|
|
|
/*
|
|
|
|
* Default state: Device behavior when this request is received while
|
|
|
|
* the device is in the Default state is not specified.
|
|
|
|
* We return the same value that a configured device would return if
|
|
|
|
* it used the first configuration.
|
|
|
|
*/
|
2013-12-16 11:42:49 +04:00
|
|
|
if (config->bmAttributes & USB_CFG_ATT_SELFPOWER) {
|
2010-11-30 19:35:34 +03:00
|
|
|
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
|
|
|
|
}
|
|
|
|
if (dev->remote_wakeup) {
|
|
|
|
data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
|
|
|
|
}
|
|
|
|
data[1] = 0x00;
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = 2;
|
|
|
|
ret = 0;
|
2010-11-30 19:35:34 +03:00
|
|
|
break;
|
2012-02-26 20:09:21 +04:00
|
|
|
}
|
2010-11-30 19:35:34 +03:00
|
|
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
|
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
|
|
dev->remote_wakeup = 0;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
trace_usb_clear_device_feature(dev->addr, value, ret);
|
|
|
|
break;
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
|
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
|
|
dev->remote_wakeup = 1;
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
trace_usb_set_device_feature(dev->addr, value, ret);
|
|
|
|
break;
|
2011-08-30 15:21:27 +04:00
|
|
|
|
2017-01-31 16:52:07 +03:00
|
|
|
case DeviceOutRequest | USB_REQ_SET_SEL:
|
|
|
|
case DeviceOutRequest | USB_REQ_SET_ISOCH_DELAY:
|
|
|
|
if (dev->speed == USB_SPEED_SUPER) {
|
|
|
|
ret = 0;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2011-08-30 15:21:27 +04:00
|
|
|
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
|
|
|
if (index < 0 || index >= dev->ninterfaces) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
data[0] = dev->altsetting[index];
|
usb: split packet result into actual_length + status
Since with the ehci and xhci controllers a single packet can be larger
then maxpacketsize, it is possible for the result of a single packet
to be both having transferred some data as well as the transfer to have
an error.
An example would be an input transfer from a bulk endpoint successfully
receiving 1 or more maxpacketsize packets from the device, followed
by a packet signalling halt.
While already touching all the devices and controllers handle_packet /
handle_data / handle_control code, also change the return type of
these functions to void, solely storing the status in the packet. To
make the code paths for regular versus async packet handling more
uniform.
This patch unfortunately is somewhat invasive, since makeing the qemu
usb core deal with this requires changes everywhere. This patch only
prepares the usb core for this, all the hcd / device changes are done
in such a way that there are no functional changes.
This patch has been tested with uhci and ehci hcds, together with usb-audio,
usb-hid and usb-storage devices, as well as with usb-redir redirection
with a wide variety of real devices.
Note that there is usually no need to directly set packet->actual_length
form devices handle_data callback, as that is done by usb_packet_copy()
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
2012-11-01 20:15:01 +04:00
|
|
|
p->actual_length = 1;
|
|
|
|
ret = 0;
|
2011-08-30 15:21:27 +04:00
|
|
|
break;
|
|
|
|
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
|
|
|
ret = usb_desc_set_interface(dev, index, value);
|
|
|
|
trace_usb_set_interface(dev->addr, index, value, ret);
|
|
|
|
break;
|
|
|
|
|
2013-11-20 10:32:31 +04:00
|
|
|
case VendorDeviceRequest | 'Q':
|
|
|
|
if (msos) {
|
|
|
|
ret = usb_desc_msos(desc, p, index, data, length);
|
|
|
|
trace_usb_desc_msos(dev->addr, index, length, ret);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case VendorInterfaceRequest | 'Q':
|
|
|
|
if (msos) {
|
|
|
|
ret = usb_desc_msos(desc, p, index, data, length);
|
|
|
|
trace_usb_desc_msos(dev->addr, index, length, ret);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2010-11-17 13:03:53 +03:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|