mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
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:
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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:
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+223
-268
File diff suppressed because it is too large
Load Diff
+10
-2
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user