Merge branch 'usb.64' of git://git.kraxel.org/qemu

* 'usb.64' of git://git.kraxel.org/qemu: (54 commits)
  xhci: allow bytewise capability register reads
  xhci: kill xhci_mem_{read,write} dispatcher functions
  xhci: support multiple interrupters
  xhci: pick target interrupter
  xhci: prepare xhci_runtime_{read,write} for multiple interrupters
  xhci: add XHCIInterrupter
  xhci: move register update into xhci_intr_raise
  xhci: add msix support
  xhci: rework interrupt handling
  xhci: fix & cleanup msi.
  usb-storage: usb3 support
  usb3: bos decriptor
  usb3: superspeed endpoint companion
  usb3: superspeed descriptors
  xhci: update port handling
  xhci: update register layout
  xhci: fix runtime write tracepoint
  xhci: add trace_usb_xhci_ep_set_dequeue
  xhci: trace cc codes in cleartext
  xhci: iso xfer support
  ...
This commit is contained in:
Aurelien Jarno
2012-09-11 18:06:56 +02:00
13 changed files with 1384 additions and 1050 deletions
Vendored
+1 -1
View File
@@ -2758,7 +2758,7 @@ fi
# check for usbredirparser for usb network redirection support
if test "$usb_redir" != "no" ; then
if $pkg_config --atleast-version=0.3.4 libusbredirparser >/dev/null 2>&1 ; then
if $pkg_config --atleast-version=0.5 libusbredirparser >/dev/null 2>&1 ; then
usb_redir="yes"
usb_redir_cflags=$($pkg_config --cflags libusbredirparser 2>/dev/null)
usb_redir_libs=$($pkg_config --libs libusbredirparser 2>/dev/null)
+9
View File
@@ -135,8 +135,15 @@
#define USB_DT_OTHER_SPEED_CONFIG 0x07
#define USB_DT_DEBUG 0x0A
#define USB_DT_INTERFACE_ASSOC 0x0B
#define USB_DT_BOS 0x0F
#define USB_DT_DEVICE_CAPABILITY 0x10
#define USB_DT_CS_INTERFACE 0x24
#define USB_DT_CS_ENDPOINT 0x25
#define USB_DT_ENDPOINT_COMPANION 0x30
#define USB_DEV_CAP_WIRELESS 0x01
#define USB_DEV_CAP_USB2_EXT 0x02
#define USB_DEV_CAP_SUPERSPEED 0x03
#define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1
@@ -377,6 +384,8 @@ void usb_ep_set_max_packet_size(USBDevice *dev, int pid, int ep,
uint16_t raw);
int usb_ep_get_max_packet_size(USBDevice *dev, int pid, int ep);
void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled);
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id);
void usb_attach(USBPort *port);
void usb_detach(USBPort *port);
+20 -3
View File
@@ -398,9 +398,11 @@ int usb_handle_packet(USBDevice *dev, USBPacket *p)
* When pipelining is enabled usb-devices must always return async,
* otherwise packets can complete out of order!
*/
assert(!p->ep->pipeline);
p->result = ret;
usb_packet_set_state(p, USB_PACKET_COMPLETE);
assert(!p->ep->pipeline || QTAILQ_EMPTY(&p->ep->queue));
if (ret != USB_RET_NAK) {
p->result = ret;
usb_packet_set_state(p, USB_PACKET_COMPLETE);
}
}
} else {
ret = USB_RET_ASYNC;
@@ -724,3 +726,18 @@ void usb_ep_set_pipeline(USBDevice *dev, int pid, int ep, bool enabled)
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
uep->pipeline = enabled;
}
USBPacket *usb_ep_find_packet_by_id(USBDevice *dev, int pid, int ep,
uint64_t id)
{
struct USBEndpoint *uep = usb_ep_get(dev, pid, ep);
USBPacket *p;
while ((p = QTAILQ_FIRST(&uep->queue)) != NULL) {
if (p->id == id) {
return p;
}
}
return NULL;
}
+158 -16
View File
@@ -76,7 +76,8 @@ int usb_desc_device_qualifier(const USBDescDevice *dev,
return bLength;
}
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
int usb_desc_config(const USBDescConfig *conf, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
uint16_t wTotalLength = 0;
@@ -99,7 +100,7 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
/* handle grouped interfaces if any */
for (i = 0; i < conf->nif_groups; i++) {
rc = usb_desc_iface_group(&(conf->if_groups[i]),
rc = usb_desc_iface_group(&(conf->if_groups[i]), flags,
dest + wTotalLength,
len - wTotalLength);
if (rc < 0) {
@@ -110,7 +111,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
/* handle normal (ungrouped / no IAD) interfaces if any */
for (i = 0; i < conf->nif; i++) {
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
rc = usb_desc_iface(conf->ifs + i, flags,
dest + wTotalLength, len - wTotalLength);
if (rc < 0) {
return rc;
}
@@ -122,8 +124,8 @@ int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
return wTotalLength;
}
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
size_t len)
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
uint8_t *dest, size_t len)
{
int pos = 0;
int i = 0;
@@ -147,7 +149,7 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
/* handle associated interfaces in this group */
for (i = 0; i < iad->nif; i++) {
int rc = usb_desc_iface(&(iad->ifs[i]), dest + pos, len - pos);
int rc = usb_desc_iface(&(iad->ifs[i]), flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
@@ -157,7 +159,8 @@ int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
return pos;
}
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
int usb_desc_iface(const USBDescIface *iface, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = 0x09;
int i, rc, pos = 0;
@@ -188,7 +191,7 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
}
for (i = 0; i < iface->bNumEndpoints; i++) {
rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
rc = usb_desc_endpoint(iface->eps + i, flags, dest + pos, len - pos);
if (rc < 0) {
return rc;
}
@@ -198,13 +201,15 @@ int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
return pos;
}
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
uint8_t *dest, size_t len)
{
uint8_t bLength = ep->is_audio ? 0x09 : 0x07;
uint8_t extralen = ep->extra ? ep->extra[0] : 0;
uint8_t superlen = (flags & USB_DESC_FLAG_SUPER) ? 0x06 : 0;
USBDescriptor *d = (void *)dest;
if (len < bLength + extralen) {
if (len < bLength + extralen + superlen) {
return -1;
}
@@ -224,7 +229,21 @@ int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
memcpy(dest + bLength, ep->extra, extralen);
}
return bLength + extralen;
if (superlen) {
USBDescriptor *d = (void *)(dest + bLength + extralen);
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);
}
return bLength + extralen + superlen;
}
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
@@ -239,6 +258,111 @@ int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
return bLength;
}
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;
}
/* ------------------------------------------------------------------ */
static void usb_desc_ep_init(USBDevice *dev)
@@ -359,6 +483,9 @@ static void usb_desc_setdefaults(USBDevice *dev)
case USB_SPEED_HIGH:
dev->device = desc->high;
break;
case USB_SPEED_SUPER:
dev->device = desc->super;
break;
}
usb_desc_set_config(dev, 0);
}
@@ -376,6 +503,9 @@ void usb_desc_init(USBDevice *dev)
if (desc->high) {
dev->speedmask |= USB_SPEED_MASK_HIGH;
}
if (desc->super) {
dev->speedmask |= USB_SPEED_MASK_SUPER;
}
usb_desc_setdefaults(dev);
}
@@ -384,7 +514,9 @@ void usb_desc_attach(USBDevice *dev)
const USBDesc *desc = usb_device_get_usb_desc(dev);
assert(desc != NULL);
if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
if (desc->super && (dev->port->speedmask & USB_SPEED_MASK_SUPER)) {
dev->speed = USB_SPEED_SUPER;
} else if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
dev->speed = USB_SPEED_HIGH;
} else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
dev->speed = USB_SPEED_FULL;
@@ -501,7 +633,7 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
uint8_t buf[256];
uint8_t type = value >> 8;
uint8_t index = value & 0xff;
int ret = -1;
int flags, ret = -1;
if (dev->speed == USB_SPEED_HIGH) {
other_dev = usb_device_get_usb_desc(dev)->full;
@@ -509,6 +641,11 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
other_dev = usb_device_get_usb_desc(dev)->high;
}
flags = 0;
if (dev->device->bcdUSB >= 0x0300) {
flags |= USB_DESC_FLAG_SUPER;
}
switch(type) {
case USB_DT_DEVICE:
ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
@@ -516,7 +653,8 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
break;
case USB_DT_CONFIG:
if (index < dev->device->bNumConfigurations) {
ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
ret = usb_desc_config(dev->device->confs + index, flags,
buf, sizeof(buf));
}
trace_usb_desc_config(dev->addr, index, len, ret);
break;
@@ -524,7 +662,6 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
ret = usb_desc_string(dev, index, buf, sizeof(buf));
trace_usb_desc_string(dev->addr, index, len, ret);
break;
case USB_DT_DEVICE_QUALIFIER:
if (other_dev != NULL) {
ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
@@ -533,11 +670,16 @@ int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len
break;
case USB_DT_OTHER_SPEED_CONFIG:
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
ret = usb_desc_config(other_dev->confs + index, flags,
buf, sizeof(buf));
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
}
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
break;
case USB_DT_BOS:
ret = usb_desc_bos(desc, buf, sizeof(buf));
trace_usb_desc_bos(dev->addr, len, ret);
break;
case USB_DT_DEBUG:
/* ignore silently */
+47 -5
View File
@@ -63,6 +63,37 @@ typedef struct USBDescriptor {
uint8_t bRefresh; /* only audio ep */
uint8_t bSynchAddress; /* only audio ep */
} endpoint;
struct {
uint8_t bMaxBurst;
uint8_t bmAttributes;
uint8_t wBytesPerInterval_lo;
uint8_t wBytesPerInterval_hi;
} super_endpoint;
struct {
uint8_t wTotalLength_lo;
uint8_t wTotalLength_hi;
uint8_t bNumDeviceCaps;
} bos;
struct {
uint8_t bDevCapabilityType;
union {
struct {
uint8_t bmAttributes_1;
uint8_t bmAttributes_2;
uint8_t bmAttributes_3;
uint8_t bmAttributes_4;
} usb2_ext;
struct {
uint8_t bmAttributes;
uint8_t wSpeedsSupported_lo;
uint8_t wSpeedsSupported_hi;
uint8_t bFunctionalitySupport;
uint8_t bU1DevExitLat;
uint8_t wU2DevExitLat_lo;
uint8_t wU2DevExitLat_hi;
} super;
} u;
} cap;
} u;
} QEMU_PACKED USBDescriptor;
@@ -139,6 +170,11 @@ struct USBDescEndpoint {
uint8_t is_audio; /* has bRefresh + bSynchAddress */
uint8_t *extra;
/* superspeed endpoint companion */
uint8_t bMaxBurst;
uint8_t bmAttributes_super;
uint16_t wBytesPerInterval;
};
struct USBDescOther {
@@ -152,19 +188,25 @@ struct USBDesc {
USBDescID id;
const USBDescDevice *full;
const USBDescDevice *high;
const USBDescDevice *super;
const char* const *str;
};
#define USB_DESC_FLAG_SUPER (1 << 1)
/* generate usb packages from structs */
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_device_qualifier(const USBDescDevice *dev,
uint8_t *dest, size_t len);
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, uint8_t *dest,
size_t len);
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
int usb_desc_config(const USBDescConfig *conf, int flags,
uint8_t *dest, size_t len);
int usb_desc_iface_group(const USBDescIfaceAssoc *iad, int flags,
uint8_t *dest, size_t len);
int usb_desc_iface(const USBDescIface *iface, int flags,
uint8_t *dest, size_t len);
int usb_desc_endpoint(const USBDescEndpoint *ep, int flags,
uint8_t *dest, size_t len);
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
/* control message emulation helpers */
+1 -1
View File
@@ -217,7 +217,7 @@ static const USBDescIface desc_iface[] = {
};
static const USBDescDevice desc_device = {
.bcdUSB = 0x0200,
.bcdUSB = 0x0100,
.bMaxPacketSize0 = 64,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
+1 -1
View File
@@ -113,7 +113,7 @@ enum {
static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "QEMU",
[STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
[STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
[STR_PRODUCT_BRAILLE] = "QEMU USB BAUM BRAILLE",
[STR_SERIALNUMBER] = "1",
};
+43 -3
View File
@@ -78,6 +78,7 @@ enum {
STR_SERIALNUMBER,
STR_CONFIG_FULL,
STR_CONFIG_HIGH,
STR_CONFIG_SUPER,
};
static const USBDescStrings desc_strings = {
@@ -86,6 +87,7 @@ static const USBDescStrings desc_strings = {
[STR_SERIALNUMBER] = "1",
[STR_CONFIG_FULL] = "Full speed config (usb 1.1)",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
[STR_CONFIG_SUPER] = "Super speed config (usb 3.0)",
};
static const USBDescIface desc_iface_full = {
@@ -158,6 +160,43 @@ static const USBDescDevice desc_device_high = {
},
};
static const USBDescIface desc_iface_super = {
.bInterfaceNumber = 0,
.bNumEndpoints = 2,
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
.bInterfaceSubClass = 0x06, /* SCSI */
.bInterfaceProtocol = 0x50, /* Bulk */
.eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | 0x01,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 1024,
.bMaxBurst = 15,
},{
.bEndpointAddress = USB_DIR_OUT | 0x02,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 1024,
.bMaxBurst = 15,
},
}
};
static const USBDescDevice desc_device_super = {
.bcdUSB = 0x0300,
.bMaxPacketSize0 = 9,
.bNumConfigurations = 1,
.confs = (USBDescConfig[]) {
{
.bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_SUPER,
.bmAttributes = 0xc0,
.nif = 1,
.ifs = &desc_iface_super,
},
},
};
static const USBDesc desc = {
.id = {
.idVendor = 0x46f4, /* CRC16() of "QEMU" */
@@ -167,9 +206,10 @@ static const USBDesc desc = {
.iProduct = STR_PRODUCT,
.iSerialNumber = STR_SERIALNUMBER,
},
.full = &desc_device_full,
.high = &desc_device_high,
.str = desc_strings,
.full = &desc_device_full,
.high = &desc_device_high,
.super = &desc_device_super,
.str = desc_strings,
};
static void usb_msd_copy_data(MSDState *s, USBPacket *p)
+117 -63
View File
@@ -2,6 +2,11 @@
* QEMU USB EHCI Emulation
*
* Copyright(c) 2008 Emutex Ltd. (address@hidden)
* Copyright(c) 2011-2012 Red Hat, Inc.
*
* Red Hat Authors:
* Gerd Hoffmann <kraxel@redhat.com>
* Hans de Goede <hdegoede@redhat.com>
*
* EHCI project was started by Mark Burkley, with contributions by
* Niels de Vos. David S. Ahern continued working on it. Kevin Wolf,
@@ -340,6 +345,7 @@ typedef struct EHCIState EHCIState;
enum async_state {
EHCI_ASYNC_NONE = 0,
EHCI_ASYNC_INITIALIZED,
EHCI_ASYNC_INFLIGHT,
EHCI_ASYNC_FINISHED,
};
@@ -365,7 +371,6 @@ struct EHCIQueue {
uint32_t seen;
uint64_t ts;
int async;
int revalidate;
/* cached data from guest - needs to be flushed
* when guest removes an entry (doorbell, handshake sequence)
@@ -485,6 +490,9 @@ static const char *ehci_mmio_names[] = {
[CONFIGFLAG] = "CONFIGFLAG",
};
static int ehci_state_executing(EHCIQueue *q);
static int ehci_state_writeback(EHCIQueue *q);
static const char *nr2str(const char **n, size_t len, uint32_t nr)
{
if (nr < len && n[nr] != NULL) {
@@ -709,6 +717,12 @@ static void ehci_trace_sitd(EHCIState *s, target_phys_addr_t addr,
(bool)(sitd->results & SITD_RESULTS_ACTIVE));
}
static void ehci_trace_guest_bug(EHCIState *s, const char *message)
{
trace_usb_ehci_guest_bug(message);
fprintf(stderr, "ehci warning: %s\n", message);
}
static inline bool ehci_enabled(EHCIState *s)
{
return s->usbcmd & USBCMD_RUNSTOP;
@@ -740,9 +754,25 @@ static EHCIPacket *ehci_alloc_packet(EHCIQueue *q)
static void ehci_free_packet(EHCIPacket *p)
{
if (p->async == EHCI_ASYNC_FINISHED) {
int state = ehci_get_state(p->queue->ehci, p->queue->async);
/* This is a normal, but rare condition (cancel racing completion) */
fprintf(stderr, "EHCI: Warning packet completed but not processed\n");
ehci_state_executing(p->queue);
ehci_state_writeback(p->queue);
ehci_set_state(p->queue->ehci, p->queue->async, state);
/* state_writeback recurses into us with async == EHCI_ASYNC_NONE!! */
return;
}
trace_usb_ehci_packet_action(p->queue, p, "free");
if (p->async == EHCI_ASYNC_INITIALIZED) {
usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl);
}
if (p->async == EHCI_ASYNC_INFLIGHT) {
usb_cancel_packet(&p->packet);
usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl);
}
QTAILQ_REMOVE(&p->queue->packets, p, next);
usb_packet_cleanup(&p->packet);
@@ -766,27 +796,45 @@ static EHCIQueue *ehci_alloc_queue(EHCIState *ehci, uint32_t addr, int async)
return q;
}
static void ehci_cancel_queue(EHCIQueue *q)
static int ehci_cancel_queue(EHCIQueue *q)
{
EHCIPacket *p;
int packets = 0;
p = QTAILQ_FIRST(&q->packets);
if (p == NULL) {
return;
return 0;
}
trace_usb_ehci_queue_action(q, "cancel");
do {
ehci_free_packet(p);
packets++;
} while ((p = QTAILQ_FIRST(&q->packets)) != NULL);
return packets;
}
static void ehci_free_queue(EHCIQueue *q)
static int ehci_reset_queue(EHCIQueue *q)
{
int packets;
trace_usb_ehci_queue_action(q, "reset");
packets = ehci_cancel_queue(q);
q->dev = NULL;
q->qtdaddr = 0;
return packets;
}
static void ehci_free_queue(EHCIQueue *q, const char *warn)
{
EHCIQueueHead *head = q->async ? &q->ehci->aqueues : &q->ehci->pqueues;
int cancelled;
trace_usb_ehci_queue_action(q, "free");
ehci_cancel_queue(q);
cancelled = ehci_cancel_queue(q);
if (warn && cancelled > 0) {
ehci_trace_guest_bug(q->ehci, warn);
}
QTAILQ_REMOVE(head, q, next);
g_free(q);
}
@@ -805,20 +853,10 @@ static EHCIQueue *ehci_find_queue_by_qh(EHCIState *ehci, uint32_t addr,
return NULL;
}
static void ehci_queues_tag_unused_async(EHCIState *ehci)
{
EHCIQueue *q;
QTAILQ_FOREACH(q, &ehci->aqueues, next) {
if (!q->seen) {
q->revalidate = 1;
}
}
}
static void ehci_queues_rip_unused(EHCIState *ehci, int async)
static void ehci_queues_rip_unused(EHCIState *ehci, int async, int flush)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
const char *warn = (async && !flush) ? "guest unlinked busy QH" : NULL;
uint64_t maxage = FRAME_TIMER_NS * ehci->maxframes * 4;
EHCIQueue *q, *tmp;
@@ -828,10 +866,10 @@ static void ehci_queues_rip_unused(EHCIState *ehci, int async)
q->ts = ehci->last_run_ns;
continue;
}
if (ehci->last_run_ns < q->ts + maxage) {
if (!flush && ehci->last_run_ns < q->ts + maxage) {
continue;
}
ehci_free_queue(q);
ehci_free_queue(q, warn);
}
}
@@ -844,17 +882,18 @@ static void ehci_queues_rip_device(EHCIState *ehci, USBDevice *dev, int async)
if (q->dev != dev) {
continue;
}
ehci_free_queue(q);
ehci_free_queue(q, NULL);
}
}
static void ehci_queues_rip_all(EHCIState *ehci, int async)
{
EHCIQueueHead *head = async ? &ehci->aqueues : &ehci->pqueues;
const char *warn = async ? "guest stopped busy async schedule" : NULL;
EHCIQueue *q, *tmp;
QTAILQ_FOREACH_SAFE(q, head, next, tmp) {
ehci_free_queue(q);
ehci_free_queue(q, warn);
}
}
@@ -1213,6 +1252,7 @@ static void ehci_mem_writel(void *ptr, target_phys_addr_t addr, uint32_t val)
*/
s->async_stepdown = 0;
qemu_bh_schedule(s->async_bh);
trace_usb_ehci_doorbell_ring();
}
if (((USBCMD_RUNSTOP | USBCMD_PSE | USBCMD_ASE) & val) !=
@@ -1450,8 +1490,8 @@ static void ehci_execute_complete(EHCIQueue *q)
assert(p != NULL);
assert(p->qtdaddr == q->qtdaddr);
assert(p->async != EHCI_ASYNC_INFLIGHT);
p->async = EHCI_ASYNC_NONE;
assert(p->async == EHCI_ASYNC_INITIALIZED ||
p->async == EHCI_ASYNC_FINISHED);
DPRINTF("execute_complete: qhaddr 0x%x, next %x, qtdaddr 0x%x, status %d\n",
q->qhaddr, q->qh.next, q->qtdaddr, q->usb_status);
@@ -1481,10 +1521,6 @@ static void ehci_execute_complete(EHCIQueue *q)
assert(0);
break;
}
} else if ((p->usb_status > p->tbytes) && (p->pid == USB_TOKEN_IN)) {
p->usb_status = USB_RET_BABBLE;
q->qh.token |= (QTD_TOKEN_HALT | QTD_TOKEN_BABBLE);
ehci_raise_irq(q->ehci, USBSTS_ERRINT);
} else {
// TODO check 4.12 for splits
@@ -1500,6 +1536,7 @@ static void ehci_execute_complete(EHCIQueue *q)
ehci_finish_transfer(q, p->usb_status);
usb_packet_unmap(&p->packet, &p->sgl);
qemu_sglist_destroy(&p->sgl);
p->async = EHCI_ASYNC_NONE;
q->qh.token ^= QTD_TOKEN_DTOGGLE;
q->qh.token &= ~QTD_TOKEN_ACTIVE;
@@ -1517,6 +1554,9 @@ static int ehci_execute(EHCIPacket *p, const char *action)
int ret;
int endp;
assert(p->async == EHCI_ASYNC_NONE ||
p->async == EHCI_ASYNC_INITIALIZED);
if (!(p->qtd.token & QTD_TOKEN_ACTIVE)) {
fprintf(stderr, "Attempting to execute inactive qtd\n");
return USB_RET_PROCERR;
@@ -1524,7 +1564,8 @@ static int ehci_execute(EHCIPacket *p, const char *action)
p->tbytes = (p->qtd.token & QTD_TOKEN_TBYTES_MASK) >> QTD_TOKEN_TBYTES_SH;
if (p->tbytes > BUFF_SIZE) {
fprintf(stderr, "Request for more bytes than allowed\n");
ehci_trace_guest_bug(p->queue->ehci,
"guest requested more bytes than allowed");
return USB_RET_PROCERR;
}
@@ -1544,15 +1585,18 @@ static int ehci_execute(EHCIPacket *p, const char *action)
break;
}
if (ehci_init_transfer(p) != 0) {
return USB_RET_PROCERR;
}
endp = get_field(p->queue->qh.epchar, QH_EPCHAR_EP);
ep = usb_ep_get(p->queue->dev, p->pid, endp);
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
usb_packet_map(&p->packet, &p->sgl);
if (p->async == EHCI_ASYNC_NONE) {
if (ehci_init_transfer(p) != 0) {
return USB_RET_PROCERR;
}
usb_packet_setup(&p->packet, p->pid, ep, p->qtdaddr);
usb_packet_map(&p->packet, &p->sgl);
p->async = EHCI_ASYNC_INITIALIZED;
}
trace_usb_ehci_packet_action(p->queue, p, action);
ret = usb_handle_packet(p->queue->dev, &p->packet);
@@ -1688,7 +1732,7 @@ static int ehci_state_waitlisthead(EHCIState *ehci, int async)
ehci_set_usbsts(ehci, USBSTS_REC);
}
ehci_queues_rip_unused(ehci, async);
ehci_queues_rip_unused(ehci, async, 0);
/* Find the head of the list (4.9.1.1) */
for(i = 0; i < MAX_QH; i++) {
@@ -1771,7 +1815,7 @@ out:
static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
{
EHCIPacket *p;
uint32_t entry, devaddr;
uint32_t entry, devaddr, endp;
EHCIQueue *q;
EHCIqh qh;
@@ -1792,26 +1836,26 @@ static EHCIQueue *ehci_state_fetchqh(EHCIState *ehci, int async)
get_dwords(ehci, NLPTR_GET(q->qhaddr),
(uint32_t *) &qh, sizeof(EHCIqh) >> 2);
if (q->revalidate && (q->qh.epchar != qh.epchar ||
q->qh.epcap != qh.epcap ||
q->qh.current_qtd != qh.current_qtd)) {
ehci_free_queue(q);
q = ehci_alloc_queue(ehci, entry, async);
q->seen++;
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &qh);
/*
* The overlay area of the qh should never be changed by the guest,
* except when idle, in which case the reset is a nop.
*/
devaddr = get_field(qh.epchar, QH_EPCHAR_DEVADDR);
endp = get_field(qh.epchar, QH_EPCHAR_EP);
if ((devaddr != get_field(q->qh.epchar, QH_EPCHAR_DEVADDR)) ||
(endp != get_field(q->qh.epchar, QH_EPCHAR_EP)) ||
(memcmp(&qh.current_qtd, &q->qh.current_qtd,
9 * sizeof(uint32_t)) != 0) ||
(q->dev != NULL && q->dev->addr != devaddr)) {
if (ehci_reset_queue(q) > 0) {
ehci_trace_guest_bug(ehci, "guest updated active QH");
}
p = NULL;
}
q->qh = qh;
q->revalidate = 0;
ehci_trace_qh(q, NLPTR_GET(q->qhaddr), &q->qh);
devaddr = get_field(q->qh.epchar, QH_EPCHAR_DEVADDR);
if (q->dev != NULL && q->dev->addr != devaddr) {
if (!QTAILQ_EMPTY(&q->packets)) {
/* should not happen (guest bug) */
ehci_cancel_queue(q);
}
q->dev = NULL;
}
if (q->dev == NULL) {
q->dev = ehci_find_device(q->ehci, devaddr);
}
@@ -1969,8 +2013,8 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
(!NLPTR_TBIT(p->qtd.next) && (p->qtd.next != qtd.next)) ||
(!NLPTR_TBIT(p->qtd.altnext) && (p->qtd.altnext != qtd.altnext)) ||
p->qtd.bufptr[0] != qtd.bufptr[0]) {
/* guest bug: guest updated active QH or qTD underneath us */
ehci_cancel_queue(q);
ehci_trace_guest_bug(q->ehci, "guest updated active QH or qTD");
p = NULL;
} else {
p->qtd = qtd;
@@ -1989,15 +2033,22 @@ static int ehci_state_fetchqtd(EHCIQueue *q)
} else if (p != NULL) {
switch (p->async) {
case EHCI_ASYNC_NONE:
/* Should never happen packet should at least be initialized */
assert(0);
break;
case EHCI_ASYNC_INITIALIZED:
/* Previously nacked packet (likely interrupt ep) */
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
break;
ehci_set_state(q->ehci, q->async, EST_EXECUTE);
break;
case EHCI_ASYNC_INFLIGHT:
/* Unfinyshed async handled packet, go horizontal */
/* Unfinished async handled packet, go horizontal */
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
break;
case EHCI_ASYNC_FINISHED:
/* Should never happen, as this case is caught by fetchqh */
/*
* We get here when advqueue moves to a packet which is already
* finished, which can happen with packets queued up by fill_queue
*/
ehci_set_state(q->ehci, q->async, EST_EXECUTING);
break;
}
@@ -2028,7 +2079,7 @@ static int ehci_state_horizqh(EHCIQueue *q)
return again;
}
static void ehci_fill_queue(EHCIPacket *p)
static int ehci_fill_queue(EHCIPacket *p)
{
EHCIQueue *q = p->queue;
EHCIqtd qtd = p->qtd;
@@ -2052,9 +2103,13 @@ static void ehci_fill_queue(EHCIPacket *p)
p->qtdaddr = qtdaddr;
p->qtd = qtd;
p->usb_status = ehci_execute(p, "queue");
if (p->usb_status == USB_RET_PROCERR) {
break;
}
assert(p->usb_status == USB_RET_ASYNC);
p->async = EHCI_ASYNC_INFLIGHT;
}
return p->usb_status;
}
static int ehci_state_execute(EHCIQueue *q)
@@ -2096,8 +2151,7 @@ static int ehci_state_execute(EHCIQueue *q)
trace_usb_ehci_packet_action(p->queue, p, "async");
p->async = EHCI_ASYNC_INFLIGHT;
ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH);
again = 1;
ehci_fill_queue(p);
again = (ehci_fill_queue(p) == USB_RET_PROCERR) ? -1 : 1;
goto out;
}
@@ -2310,8 +2364,8 @@ static void ehci_advance_async_state(EHCIState *ehci)
*/
if (ehci->usbcmd & USBCMD_IAAD) {
/* Remove all unseen qhs from the async qhs queue */
ehci_queues_tag_unused_async(ehci);
DPRINTF("ASYNC: doorbell request acknowledged\n");
ehci_queues_rip_unused(ehci, async, 1);
trace_usb_ehci_doorbell_ack();
ehci->usbcmd &= ~USBCMD_IAAD;
ehci_raise_irq(ehci, USBSTS_IAA);
}
@@ -2363,7 +2417,7 @@ static void ehci_advance_periodic_state(EHCIState *ehci)
ehci_set_fetch_addr(ehci, async,entry);
ehci_set_state(ehci, async, EST_FETCHENTRY);
ehci_advance_state(ehci, async);
ehci_queues_rip_unused(ehci, async);
ehci_queues_rip_unused(ehci, async, 0);
break;
default:
-5
View File
@@ -729,11 +729,6 @@ static int uhci_complete_td(UHCIState *s, UHCI_TD *td, UHCIAsync *async, uint32_
*int_mask |= 0x01;
if (pid == USB_TOKEN_IN) {
if (len > max_len) {
ret = USB_RET_BABBLE;
goto out;
}
if ((td->ctrl & TD_CTRL_SPD) && len < max_len) {
*int_mask |= 0x02;
/* short packet: do not update QH */
+754 -682
View File
File diff suppressed because it is too large Load Diff
+223 -268
View File
File diff suppressed because it is too large Load Diff
+10 -2
View File
@@ -263,6 +263,9 @@ usb_ehci_data(int rw, uint32_t cpage, uint32_t offset, uint32_t addr, uint32_t l
usb_ehci_queue_action(void *q, const char *action) "q %p: %s"
usb_ehci_packet_action(void *q, void *p, const char *action) "q %p p %p: %s"
usb_ehci_irq(uint32_t level, uint32_t frindex, uint32_t sts, uint32_t mask) "level %d, frindex 0x%04x, sts 0x%x, mask 0x%x"
usb_ehci_guest_bug(const char *reason) "%s"
usb_ehci_doorbell_ring(void) ""
usb_ehci_doorbell_ack(void) ""
# hw/usb/hcd-uhci.c
usb_uhci_reset(void) "=== RESET ==="
@@ -310,7 +313,10 @@ usb_xhci_runtime_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
usb_xhci_doorbell_write(uint32_t off, uint32_t val) "off 0x%04x, val 0x%08x"
usb_xhci_irq_intx(uint32_t level) "level %d"
usb_xhci_irq_msi(uint32_t nr) "nr %d"
usb_xhci_queue_event(uint32_t idx, const char *name, uint64_t param, uint32_t status, uint32_t control) "idx %d, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_irq_msix(uint32_t nr) "nr %d"
usb_xhci_irq_msix_use(uint32_t nr) "nr %d"
usb_xhci_irq_msix_unuse(uint32_t nr) "nr %d"
usb_xhci_queue_event(uint32_t vector, uint32_t idx, const char *trb, const char *evt, uint64_t param, uint32_t status, uint32_t control) "v %d, idx %d, %s, %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_fetch_trb(uint64_t addr, const char *name, uint64_t param, uint32_t status, uint32_t control) "addr %016" PRIx64 ", %s, p %016" PRIx64 ", s %08x, c 0x%08x"
usb_xhci_slot_enable(uint32_t slotid) "slotid %d"
usb_xhci_slot_disable(uint32_t slotid) "slotid %d"
@@ -320,10 +326,11 @@ usb_xhci_slot_evaluate(uint32_t slotid) "slotid %d"
usb_xhci_slot_reset(uint32_t slotid) "slotid %d"
usb_xhci_ep_enable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_disable(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_set_dequeue(uint32_t slotid, uint32_t epid, uint64_t param) "slotid %d, epid %d, ptr %016" PRIx64
usb_xhci_ep_kick(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_stop(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_ep_reset(uint32_t slotid, uint32_t epid) "slotid %d, epid %d"
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid, uint32_t length) "%p: slotid %d, epid %d, length %d"
usb_xhci_xfer_start(void *xfer, uint32_t slotid, uint32_t epid) "%p: slotid %d, epid %d"
usb_xhci_xfer_async(void *xfer) "%p"
usb_xhci_xfer_nak(void *xfer) "%p"
usb_xhci_xfer_retry(void *xfer) "%p"
@@ -336,6 +343,7 @@ usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device quali
usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
usb_desc_bos(int addr, int len, int ret) "dev %d bos, len %d, ret %d"
usb_set_addr(int addr) "dev %d"
usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, altsetting %d, ret %d"